summary refs log tree commit diff stats
path: root/ranger/ext/img_display.py
blob: 75501f4822a78c313b2354b470e3b25b08750aba (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# This software is distributed under the terms of the GNU GPL version 3.

"""Interface for w3mimgdisplay to draw images into the console

This module provides functions to draw images in the terminal using
w3mimgdisplay, an utilitary program from w3m (a text-based web browser).
w3mimgdisplay can display images either in virtual tty (using linux
framebuffer) or in a Xorg session.

w3m need to be installed for this to work.
"""

import termios, fcntl, struct, sys
from subprocess import Popen, PIPE

W3MIMGDISPLAY_PATH = '/usr/lib/w3m/w3mimgdisplay'
W3MIMGDISPLAY_OPTIONS = []

class ImgDisplayUnsupportedException(Exception):
    pass

def _get_font_dimensions():
    # Get the height and width of a character displayed in the terminal in
    # pixels.
    s = struct.pack("HHHH", 0, 0, 0, 0)
    fd_stdout = sys.stdout.fileno()
    x = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s)
    rows, cols, xpixels, ypixels = struct.unpack("HHHH", x)

    return (xpixels // cols), (ypixels // rows)


def _w3mimgdisplay(commands):
    """Invoke w3mimgdisplay and send commands on its standard input."""
    process = Popen([W3MIMGDISPLAY_PATH] + W3MIMGDISPLAY_OPTIONS, stdin=PIPE,
            stdout=PIPE, universal_newlines=True)

    # wait for the external program to finish
    output, _ = process.communicate(input=commands)

    return output

def generate_w3m_input(path, start_x, start_y, max_width, max_height):
    """Prepare the input string for w3mimgpreview

    start_x, start_y, max_height and max_width specify the drawing area.
    They are expressed in number of characters.
    """
    fontw, fonth = _get_font_dimensions()
    if fontw == 0 or fonth == 0:
        raise ImgDisplayUnsupportedException()

    max_width_pixels = max_width * fontw
    max_height_pixels = max_height * fonth

    # get image size
    cmd = "5;{}".format(path)
    output = _w3mimgdisplay(cmd).split()

    if len(output) != 2:
        raise Exception('Failed to execute w3mimgdisplay')

    width = int(output[0])
    height = int(output[1])

    # get the maximum image size preserving ratio
    if width > max_width_pixels:
        height = (height * max_width_pixels) // width
        width = max_width_pixels
    if height > max_height_pixels:
        width = (width * max_height_pixels) // height
        height = max_height_pixels

    return "0;1;{x};{y};{w};{h};;;;;{filename}\n4;\n3;".format(
            x = start_x * fontw,
            y = start_y * fonth,
            w = width,
            h = height,
            filename = path)

def draw(path, start_x, start_y, max_width, max_height):
    """Draw an image file in the terminal.

    start_x, start_y, max_height and max_width specify the drawing area.
    They are expressed in number of characters.
    """
    _w3mimgdisplay(generate_w3m_input(path, start_x, start_y, max_width, max_height))

def clear(start_x, start_y, width, height):
    """Clear a part of terminal display."""
    fontw, fonth = _get_font_dimensions()

    cmd = "6;{x};{y};{w};{h}\n4;\n3;".format(
            x = start_x * fontw,
            y = start_y * fonth,
            w = width * fontw,
            h = height * fonth)

    _w3mimgdisplay(cmd)