Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image placement using Unicode placeholders #5664

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ kitty/emoji.h linguist-generated=true
kitty/charsets.c linguist-generated=true
kitty/key_encoding.py linguist-generated=true
kitty/unicode-data.c linguist-generated=true
kitty/rowcolumn-diacritics.c linguist-generated=true
kitty/rgb.py linguist-generated=true
kitty/srgb_gamma.c linguist-generated=true
kitty/gl-wrapper.* linguist-generated=true
Expand Down
2 changes: 2 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ Detailed list of changes

- macOS: Export kitty selected text to the system for use with services that accept it (patch by Sertaç Ö. Yıldız)

- Image placement using Unicode placeholders (:pull:`5664`)


0.26.5 [2022-11-07]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
81 changes: 81 additions & 0 deletions docs/graphics-protocol.rst
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,87 @@ z-index and the same id, then the behavior is undefined.
Support for the C=1 cursor movement policy


Unicode placeholders
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. versionadded:: 0.27.0
Unicode placeholders

You can also use a special Unicode character ``U+10EEEE`` as a placeholder for
an image. This approach is less flexible, but it works for any application that
supports Unicode and foreground colors (tmux, vim, etc). To use it, you need to
create a virtual image placement by specifying ``U=1`` and the desirable number
of lines and columns::

<ESC>_Ga=p,U=1,i=<image_id>,c=<columns>,r=<rows><ESC>\

The image will be fit to the specified rectangle, its aspect ratio preserved (in
the future more scaling modes may be added to the protocol). Now you can display
the image using the placeholder character, encoding the image ID in its
foreground color. The row and column values are specified with diacritics listed
in ``rowcolumn-diacritics.txt``. For example, here is how you can print a 2x2
placeholder for image ID 42::

printf "\e[38;5;42m\U10EEEE\U0305\U0305\U10EEEE\U0305\U030D\n"
printf "\e[38;5;42m\U10EEEE\U030D\U0305\U10EEEE\U030D\U030D\n"

By using only the foreground color you are limited to 8-bit IDs in 256 color
mode and to 24-bit IDs in true color mode. If you need more bits for the image
ID, you can specify the most significant byte via the third diacritic. For
example, this is the placeholder for the image ID ``738197504 = 42 + 2 << 24``::

printf "\e[38;5;42m\U10EEEE\U0305\U0305\U030E\U10EEEE\U0305\U030D\U030E\n"
printf "\e[38;5;42m\U10EEEE\U030D\U0305\U030E\U10EEEE\U030D\U030D\U030E\n"

You can also specify a placement ID using the underline color (if it's omitted
or zero, the terminal may choose any virtual placement of the given image). The
background color is interpreted as the background color, visible if the image is
transparent. Other text attributes are reserved for future use.

Row, column and most significant byte diacritics may also be omitted, in which
case the placeholder cell will inherit the missing values from the placeholder
cell to the left:

- If no diacritics are present, and the previous placeholder cell has the same
foreground and underline colors, then the row of the current cell will be the
row of the cell to the left, the column will be the column of the cell to the
left plus one, and the most significant image ID byte will be the most
significant image ID byte of the cell to the left.
- If only the row diacritic is present, and the previous placeholder cell has
the same row and the same foreground and underline colors, then the column of
the current cell will be the column of the cell to the left plus one, and the
most significant image ID byte will be the most significant image ID byte of
the cell to the left.
- If only the row and column diacritics are present, and the previous
placeholder cell has the same row, the same foreground and underline colors,
and its column is one less than the current column, then the most significant
image ID byte of the current cell will be the most significant image ID byte
of the cell to the left.

These rules are applied left-to-right, which allows specifying only row
diacritics of the first column, i.e. here is a 2 rows by 3 columns placeholder::

printf "\e[38;5;42m\U10EEEE\U0305\U10EEEE\U10EEEE\n"
printf "\e[38;5;42m\U10EEEE\U030D\U10EEEE\U10EEEE\n"

This will not work for horizontal scrolling and overlapping images since the two
given rules will fail to guess the missing information. In such cases, the
terminal may apply other heuristics (but it doesn't have to).

It is important to distinguish between virtual image placements and real images
displayed on top of placeholders. Virtual placements are invisible and only play
the role of prototypes for real images. Virtual placements can be deleted by a
deletion command only when the `d` key is equal to ``i``, ``I``, ``n`` or ``N``.
The key values ``a``, ``c``, ``p``, ``q``, ``x``, ``y``, ``z`` and their capital
variants never affect virtual placements because they do not have a physical
location on the screen.

Real images displayed on top of placeholders are not considered placements from
the protocol perspective. They cannot be manipulated using graphics commands,
instead they should be moved, deleted, or modified by manipulating the
underlying placeholder as normal text.


Deleting images
---------------------

Expand Down
1 change: 1 addition & 0 deletions gen-apc-parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ def graphics_parser() -> None:
'Y': ('cell_y_offset', 'uint'),
'z': ('z_index', 'int'),
'C': ('cursor_movement', 'uint'),
'U': ('unicode_placement', 'uint'),
}
text = generate('parse_graphics_code', 'screen_handle_graphics_command', 'graphics_command', keymap, 'GraphicsCommand')
write_header(text, 'kitty/parse-graphics-command.h')
Expand Down
47 changes: 47 additions & 0 deletions gen-wcwidth.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,52 @@ def add_all(p: Callable[..., None], for_go: bool = False) -> None:
subprocess.check_call(['gofmt', '-w', '-s', gof.name])


def gen_rowcolumn_diacritics() -> None:
# codes of all row/column diacritics
codes = []
with open("./rowcolumn-diacritics.txt", "r") as file:
for line in file.readlines():
if line.startswith('#'):
continue
code = int(line.split(";")[0], 16)
codes.append(code)

with create_header('kitty/rowcolumn-diacritics.c') as p:
p('#include "unicode-data.h"')
p('int diacritic_to_num(char_type code) {')
p('\tswitch (code) {')

range_start_num = 1
range_start = 0
range_end = 0

def print_range() -> None:
if range_start >= range_end:
return
write_case((range_start, range_end), p)
p('\t\treturn code - ' + hex(range_start) + ' + ' +
str(range_start_num) + ';')

for code in codes:
if range_end == code:
range_end += 1
else:
print_range()
range_start_num += range_end - range_start
range_start = code
range_end = code + 1
print_range()

p('\t}')
p('\treturn 0;')
p('}')

with open('kittens/icat/rowcolumn_diacritics.py', 'w') as f:
f.write('# generated by gen-wcwidth.py, do not edit\n\n')
strings_utf8 = ', '.join(repr(chr(c).encode('utf-8')) for c in codes)
f.write("rowcolumn_diacritics_utf8 = ({}) # noqa\n".format(strings_utf8))


parse_ucd()
parse_prop_list()
parse_emoji()
Expand All @@ -537,3 +583,4 @@ def add_all(p: Callable[..., None], for_go: bool = False) -> None:
gen_wcwidth()
gen_emoji()
gen_names()
gen_rowcolumn_diacritics()
4 changes: 4 additions & 0 deletions kitty/data-types.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ typedef struct ImageAnchorPosition {
#define DECORATION_FG_CODE 58
#define CHAR_IS_BLANK(ch) ((ch) == 32 || (ch) == 0)

// PUA character used as an image placeholder.
#define IMAGE_PLACEHOLDER_CHAR 0x10EEEE

#define FG 1
#define BG 2

Expand Down Expand Up @@ -192,6 +195,7 @@ typedef union LineAttrs {
struct {
uint8_t is_continued : 1;
uint8_t has_dirty_text : 1;
uint8_t has_image_placeholders : 1;
PromptKind prompt_kind : 2;
};
uint8_t val;
Expand Down
1 change: 1 addition & 0 deletions kitty/fast_data_types.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ from kitty.options.types import Options
from kitty.types import SignalInfo

# Constants {{{
IMAGE_PLACEHOLDER_CHAR: int
GLFW_PRIMARY_SELECTION: int
GLFW_CLIPBOARD: int
CLD_KILLED: int
Expand Down
1 change: 1 addition & 0 deletions kitty/fonts.c
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,7 @@ START_ALLOW_CASE_RANGE
case 0:
case ' ':
case '\t':
case IMAGE_PLACEHOLDER_CHAR:
return BLANK_FONT;
case 0x2500 ... 0x2573:
case 0x2574 ... 0x259f:
Expand Down
Loading