mirror of
https://github.com/kovidgoyal/kitty.git
synced 2024-08-17 10:30:25 +03:00
Image placement using Unicode placeholders
This commit introduces the Unicode placeholder image placement method. In particular: - Virtual placements can be created by passing `U=1` in a put command. - Images with virtual placements can be displayed using the placeholder character `U+10EEEE` with diacritics indicating rows and columns. - The image ID is indicated by the foreground color of the placeholder. Additionally, the most significant byte of the ID can be specified via the third diacritic. - Underline color can be optionally used to specify the placement ID. - A bug was fixed, which caused incomplete image removal when it was overwritten by another image with the same ID.
This commit is contained in:
parent
1f84e2d4e5
commit
d63eeada73
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -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
|
||||
|
@ -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]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -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
|
||||
---------------------
|
||||
|
||||
|
@ -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')
|
||||
|
@ -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()
|
||||
@ -537,3 +583,4 @@ def add_all(p: Callable[..., None], for_go: bool = False) -> None:
|
||||
gen_wcwidth()
|
||||
gen_emoji()
|
||||
gen_names()
|
||||
gen_rowcolumn_diacritics()
|
||||
|
@ -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
|
||||
|
||||
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
228
kitty/graphics.c
228
kitty/graphics.c
@ -149,6 +149,7 @@ img_by_client_number(GraphicsManager *self, uint32_t number) {
|
||||
|
||||
static void
|
||||
remove_image(GraphicsManager *self, size_t idx) {
|
||||
assert(idx < self->image_count);
|
||||
free_image(self, self->images + idx);
|
||||
remove_i_from_array(self->images, idx, self->image_count);
|
||||
self->layers_dirty = true;
|
||||
@ -555,7 +556,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
|
||||
img->root_frame_data_loaded = false;
|
||||
img->is_drawn = false;
|
||||
img->current_frame_shown_at = 0;
|
||||
free_refs_data(img);
|
||||
free_image(self, img);
|
||||
*is_dirty = true;
|
||||
self->layers_dirty = true;
|
||||
} else {
|
||||
@ -651,12 +652,12 @@ static void
|
||||
update_dest_rect(ImageRef *ref, uint32_t num_cols, uint32_t num_rows, CellPixelSize cell) {
|
||||
uint32_t t;
|
||||
if (num_cols == 0) {
|
||||
t = ref->src_width + ref->cell_x_offset;
|
||||
t = (uint32_t)(ref->src_width + ref->cell_x_offset);
|
||||
num_cols = t / cell.width;
|
||||
if (t > num_cols * cell.width) num_cols += 1;
|
||||
}
|
||||
if (num_rows == 0) {
|
||||
t = ref->src_height + ref->cell_y_offset;
|
||||
t = (uint32_t)(ref->src_height + ref->cell_y_offset);
|
||||
num_rows = t / cell.height;
|
||||
if (t > num_rows * cell.height) num_rows += 1;
|
||||
}
|
||||
@ -664,6 +665,176 @@ update_dest_rect(ImageRef *ref, uint32_t num_cols, uint32_t num_rows, CellPixelS
|
||||
ref->effective_num_cols = num_cols;
|
||||
}
|
||||
|
||||
// Create a real image ref for a virtual image ref (placement) positioned in the
|
||||
// given cells. This is used for images positioned using Unicode placeholders.
|
||||
//
|
||||
// The image is resized to fit a box of cells with dimensions
|
||||
// `image_ref->columns` by `image_ref->rows`. The parameters `img_col`,
|
||||
// `img_row, `columns`, `rows` describe a part of this box that we want to
|
||||
// display.
|
||||
//
|
||||
// Parameters:
|
||||
// - `self` - the graphics manager
|
||||
// - `screen_row` - the starting row of the screen
|
||||
// - `screen_col` - the starting column of the screen
|
||||
// - `image_id` - the id of the image
|
||||
// - `placement_id` - the id of the placement (0 to find it automatically), it
|
||||
// must be a virtual placement
|
||||
// - `img_col` - the column of the image box we want to start with (base 0)
|
||||
// - `img_row` - the row of the image box we want to start with (base 0)
|
||||
// - `columns` - the number of columns we want to display
|
||||
// - `rows` - the number of rows we want to display
|
||||
// - `cell` - the size of a screen cell
|
||||
Image *grman_put_cell_image(GraphicsManager *self, uint32_t screen_row,
|
||||
uint32_t screen_col, uint32_t image_id,
|
||||
uint32_t placement_id, uint32_t img_col,
|
||||
uint32_t img_row, uint32_t columns, uint32_t rows,
|
||||
CellPixelSize cell) {
|
||||
Image *img = img_by_client_id(self, image_id);
|
||||
if (img == NULL) return NULL;
|
||||
|
||||
ImageRef *virt_img_ref = NULL;
|
||||
if (placement_id) {
|
||||
// Find the placement by the id. It must be a virtual placement.
|
||||
for (size_t i = 0; i < img->refcnt; i++) {
|
||||
if (img->refs[i].is_virtual_ref &&
|
||||
img->refs[i].client_id == placement_id) {
|
||||
virt_img_ref = img->refs + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Find the first virtual image placement.
|
||||
for (size_t i = 0; i < img->refcnt; i++) {
|
||||
if (img->refs[i].is_virtual_ref) {
|
||||
virt_img_ref = img->refs + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!virt_img_ref) return NULL;
|
||||
|
||||
// Create the ref structure on stack first. We will not create a real
|
||||
// reference if the image is completely out of bounds.
|
||||
ImageRef ref = {0};
|
||||
ref.is_cell_image = true;
|
||||
|
||||
uint32_t img_rows = virt_img_ref->num_rows;
|
||||
uint32_t img_columns = virt_img_ref->num_cols;
|
||||
// If the number of columns or rows for the image is not set, compute them
|
||||
// in such a way that the image is as close as possible to its natural size.
|
||||
if (img_columns == 0)
|
||||
img_columns = (img->width + cell.width - 1) / cell.width;
|
||||
if (img_rows == 0) img_rows = (img->height + cell.height - 1) / cell.height;
|
||||
|
||||
ref.start_row = screen_row;
|
||||
ref.start_column = screen_col;
|
||||
ref.num_cols = columns;
|
||||
ref.num_rows = rows;
|
||||
|
||||
// The image is fit to the destination box of size
|
||||
// (cell.width * img_columns) by (cell.height * img_rows)
|
||||
// The conversion from source (image) coordinates to destination (box)
|
||||
// coordinates is done by the following formula:
|
||||
// x_dst = x_src * x_scale + x_offset
|
||||
// y_dst = y_src * y_scale + y_offset
|
||||
float x_offset, y_offset, x_scale, y_scale;
|
||||
|
||||
// Fit the image to the box while preserving aspect ratio
|
||||
if (img->width * img_rows * cell.height >
|
||||
img->height * img_columns * cell.width) {
|
||||
// Fit to width and center vertically.
|
||||
x_offset = 0;
|
||||
x_scale = (float)(img_columns * cell.width) / img->width;
|
||||
y_scale = x_scale;
|
||||
y_offset = (img_rows * cell.height - img->height * y_scale) / 2;
|
||||
} else {
|
||||
// Fit to height and center horizontally.
|
||||
y_offset = 0;
|
||||
y_scale = (float)(img_rows * cell.height) / img->height;
|
||||
x_scale = y_scale;
|
||||
x_offset = (img_columns * cell.width - img->width * x_scale) / 2;
|
||||
}
|
||||
|
||||
// Now we can compute source (image) coordinates from destination (box)
|
||||
// coordinates by formula:
|
||||
// x_src = (x_dst - x_offset) / x_scale
|
||||
// y_src = (y_dst - y_offset) / y_scale
|
||||
|
||||
// Destination (box) coordinates of the rectangle we want to display.
|
||||
uint32_t x_dst = img_col * cell.width;
|
||||
uint32_t y_dst = img_row * cell.height;
|
||||
uint32_t w_dst = columns * cell.width;
|
||||
uint32_t h_dst = rows * cell.height;
|
||||
|
||||
// Compute the source coordinates of the rectangle.
|
||||
ref.src_x = (x_dst - x_offset) / x_scale;
|
||||
ref.src_y = (y_dst - y_offset) / y_scale;
|
||||
ref.src_width = w_dst / x_scale;
|
||||
ref.src_height = h_dst / y_scale;
|
||||
|
||||
// If the top left corner is out of bounds of the source image, we can
|
||||
// adjust cell offsets and the starting row/column. And if the rectangle is
|
||||
// completely out of bounds, we can avoid creating a real reference. This
|
||||
// is just an optimization, the image will be displayed correctly even if we
|
||||
// do not do this.
|
||||
if (ref.src_x < 0) {
|
||||
ref.src_width += ref.src_x;
|
||||
ref.cell_x_offset = (uint32_t)(-ref.src_x * x_scale);
|
||||
ref.src_x = 0;
|
||||
uint32_t col_offset = ref.cell_x_offset / cell.width;
|
||||
ref.cell_x_offset %= cell.width;
|
||||
ref.start_column += col_offset;
|
||||
if (ref.num_cols <= col_offset)
|
||||
return img;
|
||||
ref.num_cols -= col_offset;
|
||||
}
|
||||
if (ref.src_y < 0) {
|
||||
ref.src_height += ref.src_y;
|
||||
ref.cell_y_offset = (uint32_t)(-ref.src_y * y_scale);
|
||||
ref.src_y = 0;
|
||||
uint32_t row_offset = ref.cell_y_offset / cell.height;
|
||||
ref.cell_y_offset %= cell.height;
|
||||
ref.start_row += row_offset;
|
||||
if (ref.num_rows <= row_offset)
|
||||
return img;
|
||||
ref.num_rows -= row_offset;
|
||||
}
|
||||
|
||||
// For the bottom right corner we can remove only completely empty rows and
|
||||
// columns.
|
||||
if (ref.src_x + ref.src_width > img->width) {
|
||||
float redundant_w = ref.src_x + ref.src_width - img->width;
|
||||
uint32_t redundant_cols = (uint32_t)(redundant_w * x_scale) / cell.width;
|
||||
if (ref.num_cols <= redundant_cols)
|
||||
return img;
|
||||
ref.src_width -= redundant_cols * cell.width / x_scale;
|
||||
ref.num_cols -= redundant_cols;
|
||||
}
|
||||
if (ref.src_y + ref.src_height > img->height) {
|
||||
float redundant_h = ref.src_y + ref.src_height - img->height;
|
||||
uint32_t redundant_rows = (uint32_t)(redundant_h * y_scale) / cell.height;
|
||||
if (ref.num_rows <= redundant_rows)
|
||||
return img;
|
||||
ref.src_height -= redundant_rows * cell.height / y_scale;
|
||||
ref.num_rows -= redundant_rows;
|
||||
}
|
||||
|
||||
// The cursor will be drawn on top of the image.
|
||||
ref.z_index = -1;
|
||||
|
||||
// Create a real ref.
|
||||
ensure_space_for(img, refs, ImageRef, img->refcnt + 1, refcap, 16, true);
|
||||
self->layers_dirty = true;
|
||||
ImageRef *real_ref = img->refs + img->refcnt++;
|
||||
*real_ref = ref;
|
||||
img->atime = monotonic();
|
||||
|
||||
update_src_rect(real_ref, img);
|
||||
update_dest_rect(real_ref, ref.num_cols, ref.num_rows, cell);
|
||||
return img;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, bool *is_dirty, Image *img, CellPixelSize cell) {
|
||||
@ -691,8 +862,8 @@ handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, b
|
||||
}
|
||||
img->atime = monotonic();
|
||||
ref->src_x = g->x_offset; ref->src_y = g->y_offset; ref->src_width = g->width ? g->width : img->width; ref->src_height = g->height ? g->height : img->height;
|
||||
ref->src_width = MIN(ref->src_width, img->width - (img->width > ref->src_x ? ref->src_x : img->width));
|
||||
ref->src_height = MIN(ref->src_height, img->height - (img->height > ref->src_y ? ref->src_y : img->height));
|
||||
ref->src_width = MIN(ref->src_width, img->width - ((float)img->width > ref->src_x ? ref->src_x : (float)img->width));
|
||||
ref->src_height = MIN(ref->src_height, img->height - ((float)img->height > ref->src_y ? ref->src_y : (float)img->height));
|
||||
ref->z_index = g->z_index;
|
||||
ref->start_row = c->y; ref->start_column = c->x;
|
||||
ref->cell_x_offset = MIN(g->cell_x_offset, cell.width - 1);
|
||||
@ -701,8 +872,12 @@ handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, b
|
||||
if (img->client_id) ref->client_id = g->placement_id;
|
||||
update_src_rect(ref, img);
|
||||
update_dest_rect(ref, g->num_cells, g->num_lines, cell);
|
||||
if (g->unicode_placement) {
|
||||
ref->is_virtual_ref = true;
|
||||
ref->start_row = ref->start_column = 0;
|
||||
}
|
||||
// Move the cursor, the screen will take care of ensuring it is in bounds
|
||||
if (g->cursor_movement != 1) {
|
||||
if (g->cursor_movement != 1 && !g->unicode_placement) {
|
||||
c->x += ref->effective_num_cols; c->y += ref->effective_num_rows - 1;
|
||||
}
|
||||
return img->client_id;
|
||||
@ -759,6 +934,7 @@ grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float scree
|
||||
img->is_drawn = false;
|
||||
|
||||
for (j = 0; j < img->refcnt; j++) { ref = img->refs + j;
|
||||
if (ref->is_virtual_ref) continue;
|
||||
r.top = y0 - ref->start_row * dy - dy * (float)ref->cell_y_offset / (float)cell.height;
|
||||
if (ref->num_rows > 0) r.bottom = y0 - (ref->start_row + (int32_t)ref->num_rows) * dy;
|
||||
else r.bottom = r.top - screen_height * (float)ref->src_height / screen_height_px;
|
||||
@ -1414,6 +1590,7 @@ modify_refs(GraphicsManager *self, const void* data, bool (*filter_func)(ImageRe
|
||||
|
||||
static bool
|
||||
scroll_filter_func(ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {
|
||||
if (ref->is_virtual_ref) return false;
|
||||
ScrollData *d = (ScrollData*)data;
|
||||
ref->start_row += d->amt;
|
||||
return ref->start_row + (int32_t)ref->effective_num_rows <= d->limit;
|
||||
@ -1431,6 +1608,7 @@ ref_outside_region(const ImageRef *ref, index_type margin_top, index_type margin
|
||||
|
||||
static bool
|
||||
scroll_filter_margins_func(ImageRef* ref, Image* img, const void* data, CellPixelSize cell) {
|
||||
if (ref->is_virtual_ref) return false;
|
||||
ScrollData *d = (ScrollData*)data;
|
||||
if (ref_within_region(ref, d->margin_top, d->margin_bottom)) {
|
||||
ref->start_row += d->amt;
|
||||
@ -1468,13 +1646,44 @@ grman_scroll_images(GraphicsManager *self, const ScrollData *data, CellPixelSize
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
cell_image_row_filter_func(const ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {
|
||||
if (ref->is_virtual_ref || !ref->is_cell_image)
|
||||
return false;
|
||||
int32_t top = *(int32_t *)data;
|
||||
int32_t bottom = *((int32_t *)data + 1);
|
||||
return ref_within_region(ref, top, bottom);
|
||||
}
|
||||
|
||||
static bool
|
||||
cell_image_filter_func(const ImageRef *ref, Image UNUSED *img, const void *data UNUSED, CellPixelSize cell UNUSED) {
|
||||
return !ref->is_virtual_ref && ref->is_cell_image;
|
||||
}
|
||||
|
||||
// Remove cell images within the given region.
|
||||
void
|
||||
grman_remove_cell_images(GraphicsManager *self, int32_t top, int32_t bottom) {
|
||||
CellPixelSize dummy = {0};
|
||||
int32_t data[] = {top, bottom};
|
||||
filter_refs(self, data, false, cell_image_row_filter_func, dummy, false);
|
||||
}
|
||||
|
||||
void
|
||||
grman_remove_all_cell_images(GraphicsManager *self) {
|
||||
CellPixelSize dummy = {0};
|
||||
filter_refs(self, NULL, false, cell_image_filter_func, dummy, false);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
clear_filter_func(const ImageRef *ref, Image UNUSED *img, const void UNUSED *data, CellPixelSize cell UNUSED) {
|
||||
if (ref->is_virtual_ref || ref->is_cell_image) return false;
|
||||
return ref->start_row + (int32_t)ref->effective_num_rows > 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
clear_all_filter_func(const ImageRef *ref UNUSED, Image UNUSED *img, const void UNUSED *data, CellPixelSize cell UNUSED) {
|
||||
if (ref->is_virtual_ref) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1500,18 +1709,21 @@ number_filter_func(const ImageRef *ref, Image *img, const void *data, CellPixelS
|
||||
|
||||
static bool
|
||||
x_filter_func(const ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {
|
||||
if (ref->is_virtual_ref || ref->is_cell_image) return false;
|
||||
const GraphicsCommand *g = data;
|
||||
return ref->start_column <= (int32_t)g->x_offset - 1 && ((int32_t)g->x_offset - 1) < ((int32_t)(ref->start_column + ref->effective_num_cols));
|
||||
}
|
||||
|
||||
static bool
|
||||
y_filter_func(const ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {
|
||||
if (ref->is_virtual_ref || ref->is_cell_image) return false;
|
||||
const GraphicsCommand *g = data;
|
||||
return ref->start_row <= (int32_t)g->y_offset - 1 && ((int32_t)g->y_offset - 1) < ((int32_t)(ref->start_row + ref->effective_num_rows));
|
||||
}
|
||||
|
||||
static bool
|
||||
z_filter_func(const ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {
|
||||
if (ref->is_virtual_ref || ref->is_cell_image) return false;
|
||||
const GraphicsCommand *g = data;
|
||||
return ref->z_index == g->z_index;
|
||||
}
|
||||
@ -1519,11 +1731,13 @@ z_filter_func(const ImageRef *ref, Image UNUSED *img, const void *data, CellPixe
|
||||
|
||||
static bool
|
||||
point_filter_func(const ImageRef *ref, Image *img, const void *data, CellPixelSize cell) {
|
||||
if (ref->is_virtual_ref || ref->is_cell_image) return false;
|
||||
return x_filter_func(ref, img, data, cell) && y_filter_func(ref, img, data, cell);
|
||||
}
|
||||
|
||||
static bool
|
||||
point3d_filter_func(const ImageRef *ref, Image *img, const void *data, CellPixelSize cell) {
|
||||
if (ref->is_virtual_ref || ref->is_cell_image) return false;
|
||||
return z_filter_func(ref, img, data, cell) && point_filter_func(ref, img, data, cell);
|
||||
}
|
||||
|
||||
@ -1584,6 +1798,7 @@ grman_rescale(GraphicsManager *self, CellPixelSize cell) {
|
||||
img = self->images + i;
|
||||
for (size_t j = img->refcnt; j-- > 0;) {
|
||||
ref = img->refs + j;
|
||||
if (ref->is_virtual_ref || ref->is_cell_image) continue;
|
||||
ref->cell_x_offset = MIN(ref->cell_x_offset, cell.width - 1);
|
||||
ref->cell_y_offset = MIN(ref->cell_y_offset, cell.height - 1);
|
||||
update_dest_rect(ref, ref->num_cols, ref->num_rows, cell);
|
||||
@ -1851,6 +2066,7 @@ init_graphics(PyObject *module) {
|
||||
if (PyType_Ready(&GraphicsManager_Type) < 0) return false;
|
||||
if (PyModule_AddObject(module, "GraphicsManager", (PyObject *)&GraphicsManager_Type) != 0) return false;
|
||||
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
||||
if (PyModule_AddIntMacro(module, IMAGE_PLACEHOLDER_CHAR) != 0) return false;
|
||||
Py_INCREF(&GraphicsManager_Type);
|
||||
return true;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ typedef struct {
|
||||
union { uint32_t num_cells, other_frame_number; };
|
||||
union { int32_t z_index, gap; };
|
||||
size_t payload_sz;
|
||||
bool unicode_placement;
|
||||
} GraphicsCommand;
|
||||
|
||||
typedef struct {
|
||||
@ -28,12 +29,18 @@ typedef struct {
|
||||
} ImageRect;
|
||||
|
||||
typedef struct {
|
||||
uint32_t src_width, src_height, src_x, src_y;
|
||||
float src_width, src_height, src_x, src_y;
|
||||
uint32_t cell_x_offset, cell_y_offset, num_cols, num_rows, effective_num_rows, effective_num_cols;
|
||||
int32_t z_index;
|
||||
int32_t start_row, start_column;
|
||||
uint32_t client_id;
|
||||
ImageRect src_rect;
|
||||
// Indicates whether this reference represents a cell image that should be
|
||||
// removed when the corresponding cells are modified.
|
||||
bool is_cell_image;
|
||||
// Virtual refs are not displayed but they can be used as prototypes for
|
||||
// refs placed using unicode placeholders.
|
||||
bool is_virtual_ref;
|
||||
} ImageRef;
|
||||
|
||||
typedef struct {
|
||||
@ -154,10 +161,13 @@ gl_pos_y(const unsigned int px_from_top_margin, const unsigned int viewport_size
|
||||
GraphicsManager* grman_alloc(void);
|
||||
void grman_clear(GraphicsManager*, bool, CellPixelSize fg);
|
||||
const char* grman_handle_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload, Cursor *c, bool *is_dirty, CellPixelSize fg);
|
||||
Image* grman_put_cell_image(GraphicsManager *self, uint32_t row, uint32_t col, uint32_t image_id, uint32_t placement_id, uint32_t x, uint32_t y, uint32_t w, uint32_t h, CellPixelSize cell);
|
||||
bool grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float screen_left, float screen_top, float dx, float dy, unsigned int num_cols, unsigned int num_rows, CellPixelSize);
|
||||
void grman_scroll_images(GraphicsManager *self, const ScrollData*, CellPixelSize fg);
|
||||
void grman_resize(GraphicsManager*, index_type, index_type, index_type, index_type);
|
||||
void grman_rescale(GraphicsManager *self, CellPixelSize fg);
|
||||
void grman_remove_cell_images(GraphicsManager *self, int32_t top, int32_t bottom);
|
||||
void grman_remove_all_cell_images(GraphicsManager *self);
|
||||
void gpu_data_for_image(ImageRenderData *ans, float left, float top, float right, float bottom);
|
||||
void gpu_data_for_centered_image(ImageRenderData *ans, unsigned int screen_width_px, unsigned int screen_height_px, unsigned int width, unsigned int height);
|
||||
bool png_path_to_bitmap(const char *path, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz);
|
||||
|
@ -201,6 +201,11 @@ historybuf_mark_line_dirty(HistoryBuf *self, index_type y) {
|
||||
attrptr(self, index_of(self, y))->has_dirty_text = true;
|
||||
}
|
||||
|
||||
void
|
||||
historybuf_set_line_has_image_placeholders(HistoryBuf *self, index_type y, bool val) {
|
||||
attrptr(self, index_of(self, y))->has_image_placeholders = val;
|
||||
}
|
||||
|
||||
void
|
||||
historybuf_clear(HistoryBuf *self) {
|
||||
pagerhist_clear(self);
|
||||
|
@ -52,6 +52,11 @@ linebuf_mark_line_clean(LineBuf *self, index_type y) {
|
||||
self->line_attrs[y].has_dirty_text = false;
|
||||
}
|
||||
|
||||
void
|
||||
linebuf_set_line_has_image_placeholders(LineBuf *self, index_type y, bool val) {
|
||||
self->line_attrs[y].has_image_placeholders = val;
|
||||
}
|
||||
|
||||
void
|
||||
linebuf_clear_attrs_and_dirty(LineBuf *self, index_type y) {
|
||||
self->line_attrs[y].val = 0;
|
||||
|
@ -112,6 +112,7 @@ void linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *, index_type *, H
|
||||
void linebuf_mark_line_dirty(LineBuf *self, index_type y);
|
||||
void linebuf_clear_attrs_and_dirty(LineBuf *self, index_type y);
|
||||
void linebuf_mark_line_clean(LineBuf *self, index_type y);
|
||||
void linebuf_set_line_has_image_placeholders(LineBuf *self, index_type y, bool val);
|
||||
unsigned int linebuf_char_width_at(LineBuf *self, index_type x, index_type y);
|
||||
void linebuf_set_last_char_as_continuation(LineBuf *self, index_type y, bool continued);
|
||||
bool linebuf_line_ends_with_continuation(LineBuf *self, index_type y);
|
||||
@ -124,6 +125,7 @@ bool history_buf_endswith_wrap(HistoryBuf *self);
|
||||
CPUCell* historybuf_cpu_cells(HistoryBuf *self, index_type num);
|
||||
void historybuf_mark_line_clean(HistoryBuf *self, index_type y);
|
||||
void historybuf_mark_line_dirty(HistoryBuf *self, index_type y);
|
||||
void historybuf_set_line_has_image_placeholders(HistoryBuf *self, index_type y, bool val);
|
||||
void historybuf_refresh_sprite_positions(HistoryBuf *self);
|
||||
void historybuf_clear(HistoryBuf *self);
|
||||
void mark_text_in_line(PyObject *marker, Line *line);
|
||||
|
14
kitty/parse-graphics-command.h
generated
14
kitty/parse-graphics-command.h
generated
@ -39,7 +39,8 @@ static inline void parse_graphics_code(Screen *screen,
|
||||
cell_x_offset = 'X',
|
||||
cell_y_offset = 'Y',
|
||||
z_index = 'z',
|
||||
cursor_movement = 'C'
|
||||
cursor_movement = 'C',
|
||||
unicode_placement = 'U'
|
||||
};
|
||||
|
||||
enum KEYS key = 'a';
|
||||
@ -124,6 +125,9 @@ static inline void parse_graphics_code(Screen *screen,
|
||||
case cursor_movement:
|
||||
value_state = UINT;
|
||||
break;
|
||||
case unicode_placement:
|
||||
value_state = UINT;
|
||||
break;
|
||||
default:
|
||||
REPORT_ERROR("Malformed GraphicsCommand control block, invalid key "
|
||||
"character: 0x%x",
|
||||
@ -268,6 +272,7 @@ static inline void parse_graphics_code(Screen *screen,
|
||||
U(cell_x_offset);
|
||||
U(cell_y_offset);
|
||||
U(cursor_movement);
|
||||
U(unicode_placement);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -327,7 +332,7 @@ static inline void parse_graphics_code(Screen *screen,
|
||||
|
||||
REPORT_VA_COMMAND(
|
||||
"s {sc sc sc sc sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI "
|
||||
"si sI} y#",
|
||||
"sI si sI} y#",
|
||||
"graphics_command", "action", g.action, "delete_action", g.delete_action,
|
||||
"transmission_type", g.transmission_type, "compressed", g.compressed,
|
||||
"format", (unsigned int)g.format, "more", (unsigned int)g.more, "id",
|
||||
@ -341,8 +346,9 @@ static inline void parse_graphics_code(Screen *screen,
|
||||
"num_cells", (unsigned int)g.num_cells, "num_lines",
|
||||
(unsigned int)g.num_lines, "cell_x_offset", (unsigned int)g.cell_x_offset,
|
||||
"cell_y_offset", (unsigned int)g.cell_y_offset, "cursor_movement",
|
||||
(unsigned int)g.cursor_movement, "z_index", (int)g.z_index, "payload_sz",
|
||||
g.payload_sz, payload, g.payload_sz);
|
||||
(unsigned int)g.cursor_movement, "unicode_placement",
|
||||
(unsigned int)g.unicode_placement, "z_index", (int)g.z_index,
|
||||
"payload_sz", g.payload_sz, payload, g.payload_sz);
|
||||
|
||||
screen_handle_graphics_command(screen, &g, payload);
|
||||
}
|
||||
|
187
kitty/rowcolumn-diacritics.c
generated
Normal file
187
kitty/rowcolumn-diacritics.c
generated
Normal file
@ -0,0 +1,187 @@
|
||||
// Unicode data, built from the Unicode Standard 15.0.0
|
||||
// Code generated by gen-wcwidth.py, DO NOT EDIT.
|
||||
|
||||
#include "data-types.h"
|
||||
|
||||
START_ALLOW_CASE_RANGE
|
||||
|
||||
#include "unicode-data.h"
|
||||
int diacritic_to_num(char_type code) {
|
||||
switch (code) {
|
||||
case 0x305 ... 0x306:
|
||||
return code - 0x305 + 1;
|
||||
case 0x30d ... 0x30f:
|
||||
return code - 0x30d + 2;
|
||||
case 0x310 ... 0x311:
|
||||
return code - 0x310 + 4;
|
||||
case 0x312 ... 0x313:
|
||||
return code - 0x312 + 5;
|
||||
case 0x33d ... 0x340:
|
||||
return code - 0x33d + 6;
|
||||
case 0x346 ... 0x347:
|
||||
return code - 0x346 + 9;
|
||||
case 0x34a ... 0x34d:
|
||||
return code - 0x34a + 10;
|
||||
case 0x350 ... 0x353:
|
||||
return code - 0x350 + 13;
|
||||
case 0x357 ... 0x358:
|
||||
return code - 0x357 + 16;
|
||||
case 0x35b ... 0x35c:
|
||||
return code - 0x35b + 17;
|
||||
case 0x363 ... 0x370:
|
||||
return code - 0x363 + 18;
|
||||
case 0x483 ... 0x488:
|
||||
return code - 0x483 + 31;
|
||||
case 0x592 ... 0x596:
|
||||
return code - 0x592 + 36;
|
||||
case 0x597 ... 0x59a:
|
||||
return code - 0x597 + 40;
|
||||
case 0x59c ... 0x5a2:
|
||||
return code - 0x59c + 43;
|
||||
case 0x5a8 ... 0x5aa:
|
||||
return code - 0x5a8 + 49;
|
||||
case 0x5ab ... 0x5ad:
|
||||
return code - 0x5ab + 51;
|
||||
case 0x5af ... 0x5b0:
|
||||
return code - 0x5af + 53;
|
||||
case 0x5c4 ... 0x5c5:
|
||||
return code - 0x5c4 + 54;
|
||||
case 0x610 ... 0x618:
|
||||
return code - 0x610 + 55;
|
||||
case 0x657 ... 0x65c:
|
||||
return code - 0x657 + 63;
|
||||
case 0x65d ... 0x65f:
|
||||
return code - 0x65d + 68;
|
||||
case 0x6d6 ... 0x6dd:
|
||||
return code - 0x6d6 + 70;
|
||||
case 0x6df ... 0x6e3:
|
||||
return code - 0x6df + 77;
|
||||
case 0x6e4 ... 0x6e5:
|
||||
return code - 0x6e4 + 81;
|
||||
case 0x6e7 ... 0x6e9:
|
||||
return code - 0x6e7 + 82;
|
||||
case 0x6eb ... 0x6ed:
|
||||
return code - 0x6eb + 84;
|
||||
case 0x730 ... 0x731:
|
||||
return code - 0x730 + 86;
|
||||
case 0x732 ... 0x734:
|
||||
return code - 0x732 + 87;
|
||||
case 0x735 ... 0x737:
|
||||
return code - 0x735 + 89;
|
||||
case 0x73a ... 0x73b:
|
||||
return code - 0x73a + 91;
|
||||
case 0x73d ... 0x73e:
|
||||
return code - 0x73d + 92;
|
||||
case 0x73f ... 0x742:
|
||||
return code - 0x73f + 93;
|
||||
case 0x743 ... 0x744:
|
||||
return code - 0x743 + 96;
|
||||
case 0x745 ... 0x746:
|
||||
return code - 0x745 + 97;
|
||||
case 0x747 ... 0x748:
|
||||
return code - 0x747 + 98;
|
||||
case 0x749 ... 0x74b:
|
||||
return code - 0x749 + 99;
|
||||
case 0x7eb ... 0x7f2:
|
||||
return code - 0x7eb + 101;
|
||||
case 0x7f3 ... 0x7f4:
|
||||
return code - 0x7f3 + 108;
|
||||
case 0x816 ... 0x81a:
|
||||
return code - 0x816 + 109;
|
||||
case 0x81b ... 0x824:
|
||||
return code - 0x81b + 113;
|
||||
case 0x825 ... 0x828:
|
||||
return code - 0x825 + 122;
|
||||
case 0x829 ... 0x82e:
|
||||
return code - 0x829 + 125;
|
||||
case 0x951 ... 0x952:
|
||||
return code - 0x951 + 130;
|
||||
case 0x953 ... 0x955:
|
||||
return code - 0x953 + 131;
|
||||
case 0xf82 ... 0xf84:
|
||||
return code - 0xf82 + 133;
|
||||
case 0xf86 ... 0xf88:
|
||||
return code - 0xf86 + 135;
|
||||
case 0x135d ... 0x1360:
|
||||
return code - 0x135d + 137;
|
||||
case 0x17dd ... 0x17de:
|
||||
return code - 0x17dd + 140;
|
||||
case 0x193a ... 0x193b:
|
||||
return code - 0x193a + 141;
|
||||
case 0x1a17 ... 0x1a18:
|
||||
return code - 0x1a17 + 142;
|
||||
case 0x1a75 ... 0x1a7d:
|
||||
return code - 0x1a75 + 143;
|
||||
case 0x1b6b ... 0x1b6c:
|
||||
return code - 0x1b6b + 151;
|
||||
case 0x1b6d ... 0x1b74:
|
||||
return code - 0x1b6d + 152;
|
||||
case 0x1cd0 ... 0x1cd3:
|
||||
return code - 0x1cd0 + 159;
|
||||
case 0x1cda ... 0x1cdc:
|
||||
return code - 0x1cda + 162;
|
||||
case 0x1ce0 ... 0x1ce1:
|
||||
return code - 0x1ce0 + 164;
|
||||
case 0x1dc0 ... 0x1dc2:
|
||||
return code - 0x1dc0 + 165;
|
||||
case 0x1dc3 ... 0x1dca:
|
||||
return code - 0x1dc3 + 167;
|
||||
case 0x1dcb ... 0x1dcd:
|
||||
return code - 0x1dcb + 174;
|
||||
case 0x1dd1 ... 0x1de7:
|
||||
return code - 0x1dd1 + 176;
|
||||
case 0x1dfe ... 0x1dff:
|
||||
return code - 0x1dfe + 198;
|
||||
case 0x20d0 ... 0x20d2:
|
||||
return code - 0x20d0 + 199;
|
||||
case 0x20d4 ... 0x20d8:
|
||||
return code - 0x20d4 + 201;
|
||||
case 0x20db ... 0x20dd:
|
||||
return code - 0x20db + 205;
|
||||
case 0x20e1 ... 0x20e2:
|
||||
return code - 0x20e1 + 207;
|
||||
case 0x20e7 ... 0x20e8:
|
||||
return code - 0x20e7 + 208;
|
||||
case 0x20e9 ... 0x20ea:
|
||||
return code - 0x20e9 + 209;
|
||||
case 0x20f0 ... 0x20f1:
|
||||
return code - 0x20f0 + 210;
|
||||
case 0x2cef ... 0x2cf2:
|
||||
return code - 0x2cef + 211;
|
||||
case 0x2de0 ... 0x2e00:
|
||||
return code - 0x2de0 + 214;
|
||||
case 0xa66f ... 0xa670:
|
||||
return code - 0xa66f + 246;
|
||||
case 0xa67c ... 0xa67e:
|
||||
return code - 0xa67c + 247;
|
||||
case 0xa6f0 ... 0xa6f2:
|
||||
return code - 0xa6f0 + 249;
|
||||
case 0xa8e0 ... 0xa8f2:
|
||||
return code - 0xa8e0 + 251;
|
||||
case 0xaab0 ... 0xaab1:
|
||||
return code - 0xaab0 + 269;
|
||||
case 0xaab2 ... 0xaab4:
|
||||
return code - 0xaab2 + 270;
|
||||
case 0xaab7 ... 0xaab9:
|
||||
return code - 0xaab7 + 272;
|
||||
case 0xaabe ... 0xaac0:
|
||||
return code - 0xaabe + 274;
|
||||
case 0xaac1 ... 0xaac2:
|
||||
return code - 0xaac1 + 276;
|
||||
case 0xfe20 ... 0xfe27:
|
||||
return code - 0xfe20 + 277;
|
||||
case 0x10a0f ... 0x10a10:
|
||||
return code - 0x10a0f + 284;
|
||||
case 0x10a38 ... 0x10a39:
|
||||
return code - 0x10a38 + 285;
|
||||
case 0x1d185 ... 0x1d18a:
|
||||
return code - 0x1d185 + 286;
|
||||
case 0x1d1aa ... 0x1d1ae:
|
||||
return code - 0x1d1aa + 291;
|
||||
case 0x1d242 ... 0x1d245:
|
||||
return code - 0x1d242 + 295;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
END_ALLOW_CASE_RANGE
|
163
kitty/screen.c
163
kitty/screen.c
@ -374,6 +374,7 @@ screen_resize(Screen *self, unsigned int lines, unsigned int columns) {
|
||||
if (is_main) setup_cursor(cursor);
|
||||
/* printf("old_cursor: (%u, %u) new_cursor: (%u, %u) beyond_content: %d\n", self->cursor->x, self->cursor->y, cursor.after.x, cursor.after.y, cursor.is_beyond_content); */
|
||||
setup_cursor(main_saved_cursor);
|
||||
grman_remove_all_cell_images(self->main_grman);
|
||||
grman_resize(self->main_grman, self->lines, lines, self->columns, columns);
|
||||
|
||||
// Resize alt linebuf
|
||||
@ -382,6 +383,7 @@ screen_resize(Screen *self, unsigned int lines, unsigned int columns) {
|
||||
Py_CLEAR(self->alt_linebuf); self->alt_linebuf = n;
|
||||
if (!is_main) setup_cursor(cursor);
|
||||
setup_cursor(alt_saved_cursor);
|
||||
grman_remove_all_cell_images(self->alt_grman);
|
||||
grman_resize(self->alt_grman, self->lines, lines, self->columns, columns);
|
||||
#undef setup_cursor
|
||||
|
||||
@ -432,6 +434,8 @@ screen_resize(Screen *self, unsigned int lines, unsigned int columns) {
|
||||
|
||||
void
|
||||
screen_rescale_images(Screen *self) {
|
||||
grman_remove_all_cell_images(self->main_grman);
|
||||
grman_remove_all_cell_images(self->alt_grman);
|
||||
grman_rescale(self->main_grman, self->cell_size);
|
||||
grman_rescale(self->alt_grman, self->cell_size);
|
||||
}
|
||||
@ -708,6 +712,9 @@ draw_codepoint(Screen *self, char_type och, bool from_input_stream) {
|
||||
line_set_char(self->linebuf->line, self->cursor->x, 0, 0, self->cursor, self->active_hyperlink_id);
|
||||
self->cursor->x++;
|
||||
}
|
||||
if (UNLIKELY(ch == IMAGE_PLACEHOLDER_CHAR)) {
|
||||
linebuf_set_line_has_image_placeholders(self->linebuf, self->cursor->y, true);
|
||||
}
|
||||
self->is_dirty = true;
|
||||
if (selection_has_screen_line(&self->selections, self->cursor->y)) clear_selection(&self->selections);
|
||||
linebuf_mark_line_dirty(self->linebuf, self->cursor->y);
|
||||
@ -1621,6 +1628,25 @@ screen_fake_move_cursor_to_position(Screen *self, index_type start_x, index_type
|
||||
|
||||
// Editing {{{
|
||||
|
||||
// Remove all cell images from a portion of the screen and mark lines that
|
||||
// contain image placeholders as dirty to make sure they are redrawn. This is
|
||||
// needed when we perform commands that may move some lines without marking them
|
||||
// as dirty (like screen_insert_lines) and at the same time don't move image
|
||||
// references (i.e. unlike screen_scroll, which moves everything).
|
||||
static void
|
||||
screen_dirty_line_graphics(Screen *self, unsigned int top, unsigned int bottom) {
|
||||
bool need_to_remove = false;
|
||||
for (unsigned int y = top; y <= bottom; y++) {
|
||||
if (self->linebuf->line_attrs[y].has_image_placeholders) {
|
||||
need_to_remove = true;
|
||||
linebuf_mark_line_dirty(self->linebuf, y);
|
||||
self->is_dirty = true;
|
||||
}
|
||||
}
|
||||
if (need_to_remove)
|
||||
grman_remove_cell_images(self->grman, top, bottom);
|
||||
}
|
||||
|
||||
void
|
||||
screen_erase_in_line(Screen *self, unsigned int how, bool private) {
|
||||
/*Erases a line in a specific way.
|
||||
@ -1651,6 +1677,7 @@ screen_erase_in_line(Screen *self, unsigned int how, bool private) {
|
||||
break;
|
||||
}
|
||||
if (n > 0) {
|
||||
screen_dirty_line_graphics(self, self->cursor->y, self->cursor->y);
|
||||
linebuf_init_line(self->linebuf, self->cursor->y);
|
||||
if (private) {
|
||||
line_clear_text(self->linebuf->line, s, n, BLANK_CHAR);
|
||||
@ -1701,6 +1728,7 @@ screen_erase_in_display(Screen *self, unsigned int how, bool private) {
|
||||
return;
|
||||
}
|
||||
if (b > a) {
|
||||
if (how != 3) screen_dirty_line_graphics(self, a, b);
|
||||
for (unsigned int i=a; i < b; i++) {
|
||||
linebuf_init_line(self->linebuf, i);
|
||||
if (private) {
|
||||
@ -1728,6 +1756,7 @@ screen_insert_lines(Screen *self, unsigned int count) {
|
||||
unsigned int top = self->margin_top, bottom = self->margin_bottom;
|
||||
if (count == 0) count = 1;
|
||||
if (top <= self->cursor->y && self->cursor->y <= bottom) {
|
||||
screen_dirty_line_graphics(self, top, bottom);
|
||||
linebuf_insert_lines(self->linebuf, count, self->cursor->y, bottom);
|
||||
self->is_dirty = true;
|
||||
clear_selection(&self->selections);
|
||||
@ -1751,6 +1780,7 @@ screen_delete_lines(Screen *self, unsigned int count) {
|
||||
unsigned int top = self->margin_top, bottom = self->margin_bottom;
|
||||
if (count == 0) count = 1;
|
||||
if (top <= self->cursor->y && self->cursor->y <= bottom) {
|
||||
screen_dirty_line_graphics(self, top, bottom);
|
||||
linebuf_delete_lines(self->linebuf, count, self->cursor->y, bottom);
|
||||
self->is_dirty = true;
|
||||
clear_selection(&self->selections);
|
||||
@ -2277,6 +2307,134 @@ screen_has_marker(Screen *self) {
|
||||
return self->marker != NULL;
|
||||
}
|
||||
|
||||
static uint32_t diacritic_to_rowcolumn(combining_type m) {
|
||||
char_type c = codepoint_for_mark(m);
|
||||
return diacritic_to_num(c);
|
||||
}
|
||||
|
||||
static uint32_t color_to_id(color_type c) {
|
||||
// Just take 24 most significant bits of the color. This works both for
|
||||
// 24-bit and 8-bit colors.
|
||||
return (c >> 8) & 0xffffff;
|
||||
}
|
||||
|
||||
// Scan the line and create cell images in place of unicode placeholders
|
||||
// reserved for image placement.
|
||||
static void
|
||||
screen_render_line_graphics(Screen *self, Line *line, int32_t row) {
|
||||
// If there are no image placeholders now, no need to rescan the line.
|
||||
if (!line->attrs.has_image_placeholders)
|
||||
return;
|
||||
// Remove existing images.
|
||||
grman_remove_cell_images(self->grman, row, row);
|
||||
// The placeholders might be erased. We will update the attribute.
|
||||
line->attrs.has_image_placeholders = false;
|
||||
index_type i;
|
||||
uint32_t run_length = 0;
|
||||
uint32_t prev_img_id_lower24bits = 0;
|
||||
uint32_t prev_placement_id = 0;
|
||||
// Note that the following values are 1-based, zero means unknown or incorrect.
|
||||
uint32_t prev_img_id_higher8bits = 0;
|
||||
uint32_t prev_img_row = 0;
|
||||
uint32_t prev_img_col = 0;
|
||||
for (i = 0; i < line->xnum; i++) {
|
||||
CPUCell *cpu_cell = line->cpu_cells + i;
|
||||
GPUCell *gpu_cell = line->gpu_cells + i;
|
||||
uint32_t cur_img_id_lower24bits = 0;
|
||||
uint32_t cur_placement_id = 0;
|
||||
uint32_t cur_img_id_higher8bits = 0;
|
||||
uint32_t cur_img_row = 0;
|
||||
uint32_t cur_img_col = 0;
|
||||
if (cpu_cell->ch == IMAGE_PLACEHOLDER_CHAR) {
|
||||
line->attrs.has_image_placeholders = true;
|
||||
// The lower 24 bits of the image id are encoded in the foreground
|
||||
// color, and the placement id is (optionally) in the underline color.
|
||||
cur_img_id_lower24bits = color_to_id(gpu_cell->fg);
|
||||
cur_placement_id = color_to_id(gpu_cell->decoration_fg);
|
||||
// If the char has diacritics, use them as row and column indices.
|
||||
if (cpu_cell->cc_idx[0])
|
||||
cur_img_row = diacritic_to_rowcolumn(cpu_cell->cc_idx[0]);
|
||||
if (cpu_cell->cc_idx[1])
|
||||
cur_img_col = diacritic_to_rowcolumn(cpu_cell->cc_idx[1]);
|
||||
// The third diacritic is used to encode the higher 8 bits of the
|
||||
// image id (optional).
|
||||
if (cpu_cell->cc_idx[2])
|
||||
cur_img_id_higher8bits = diacritic_to_rowcolumn(cpu_cell->cc_idx[2]);
|
||||
}
|
||||
// The current run is continued if the lower 24 bits of the image id and
|
||||
// the placement id are the same as in the previous cell and everything
|
||||
// else is unknown or compatible with the previous cell.
|
||||
if (run_length > 0 && cur_img_id_lower24bits == prev_img_id_lower24bits &&
|
||||
cur_placement_id == prev_placement_id &&
|
||||
(!cur_img_row || cur_img_row == prev_img_row) &&
|
||||
(!cur_img_col || cur_img_col == prev_img_col + 1) &&
|
||||
(!cur_img_id_higher8bits || cur_img_id_higher8bits == prev_img_id_higher8bits)) {
|
||||
// This cell continues the current run.
|
||||
run_length++;
|
||||
// If some values are unknown, infer them from the previous cell.
|
||||
cur_img_row = MAX(prev_img_row, 1u);
|
||||
cur_img_col = prev_img_col + 1;
|
||||
cur_img_id_higher8bits = MAX(prev_img_id_higher8bits, 1u);
|
||||
} else {
|
||||
// This cell breaks the current run. Render the current run if it
|
||||
// has a non-zero length.
|
||||
if (run_length > 0) {
|
||||
uint32_t img_id = prev_img_id_lower24bits | (prev_img_id_higher8bits - 1) << 24;
|
||||
grman_put_cell_image(
|
||||
self->grman, row, i - run_length, img_id,
|
||||
prev_placement_id, prev_img_col - run_length,
|
||||
prev_img_row - 1, run_length, 1, self->cell_size);
|
||||
}
|
||||
// Start a new run.
|
||||
if (cpu_cell->ch == IMAGE_PLACEHOLDER_CHAR) {
|
||||
run_length = 1;
|
||||
if (!cur_img_col) cur_img_col = 1;
|
||||
if (!cur_img_row) cur_img_row = 1;
|
||||
if (!cur_img_id_higher8bits) cur_img_id_higher8bits = 1;
|
||||
}
|
||||
}
|
||||
prev_img_id_lower24bits = cur_img_id_lower24bits;
|
||||
prev_img_id_higher8bits = cur_img_id_higher8bits;
|
||||
prev_placement_id = cur_placement_id;
|
||||
prev_img_row = cur_img_row;
|
||||
prev_img_col = cur_img_col;
|
||||
}
|
||||
if (run_length > 0) {
|
||||
// Render the last run.
|
||||
uint32_t img_id = prev_img_id_lower24bits | (prev_img_id_higher8bits - 1) << 24;
|
||||
grman_put_cell_image(self->grman, row, i - run_length, img_id,
|
||||
prev_placement_id, prev_img_col - run_length,
|
||||
prev_img_row - 1, run_length, 1, self->cell_size);
|
||||
}
|
||||
}
|
||||
|
||||
// This functions is similar to screen_update_cell_data, but it only updates
|
||||
// line graphics (cell images) and then marks lines as clean. It's used
|
||||
// exclusively for testing unicode placeholders.
|
||||
static void
|
||||
screen_update_only_line_graphics_data(Screen *self) {
|
||||
unsigned int history_line_added_count = self->history_line_added_count;
|
||||
index_type lnum;
|
||||
if (self->scrolled_by) self->scrolled_by = MIN(self->scrolled_by + history_line_added_count, self->historybuf->count);
|
||||
screen_reset_dirty(self);
|
||||
self->scroll_changed = false;
|
||||
for (index_type y = 0; y < MIN(self->lines, self->scrolled_by); y++) {
|
||||
lnum = self->scrolled_by - 1 - y;
|
||||
historybuf_init_line(self->historybuf, lnum, self->historybuf->line);
|
||||
if (self->historybuf->line->attrs.has_dirty_text) {
|
||||
screen_render_line_graphics(self, self->historybuf->line, y - self->scrolled_by);
|
||||
historybuf_mark_line_clean(self->historybuf, lnum);
|
||||
}
|
||||
}
|
||||
for (index_type y = self->scrolled_by; y < self->lines; y++) {
|
||||
lnum = y - self->scrolled_by;
|
||||
linebuf_init_line(self->linebuf, lnum);
|
||||
if (self->linebuf->line->attrs.has_dirty_text) {
|
||||
screen_render_line_graphics(self, self->linebuf->line, y - self->scrolled_by);
|
||||
linebuf_mark_line_clean(self->linebuf, lnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE fonts_data, bool cursor_has_moved) {
|
||||
@ -2291,6 +2449,7 @@ screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE fonts_dat
|
||||
historybuf_init_line(self->historybuf, lnum, self->historybuf->line);
|
||||
if (self->historybuf->line->attrs.has_dirty_text) {
|
||||
render_line(fonts_data, self->historybuf->line, lnum, self->cursor, self->disable_ligatures);
|
||||
screen_render_line_graphics(self, self->historybuf->line, y - self->scrolled_by);
|
||||
if (screen_has_marker(self)) mark_text_in_line(self->marker, self->historybuf->line);
|
||||
historybuf_mark_line_clean(self->historybuf, lnum);
|
||||
}
|
||||
@ -2302,6 +2461,7 @@ screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE fonts_dat
|
||||
if (self->linebuf->line->attrs.has_dirty_text ||
|
||||
(cursor_has_moved && (self->cursor->y == lnum || self->last_rendered.cursor_y == lnum))) {
|
||||
render_line(fonts_data, self->linebuf->line, lnum, self->cursor, self->disable_ligatures);
|
||||
screen_render_line_graphics(self, self->linebuf->line, y - self->scrolled_by);
|
||||
if (self->linebuf->line->attrs.has_dirty_text && screen_has_marker(self)) mark_text_in_line(self->marker, self->linebuf->line);
|
||||
|
||||
linebuf_mark_line_clean(self->linebuf, lnum);
|
||||
@ -4059,6 +4219,8 @@ line_edge_colors(Screen *self, PyObject *a UNUSED) {
|
||||
return Py_BuildValue("kk", (unsigned long)left, (unsigned long)right);
|
||||
}
|
||||
|
||||
WRAP0(update_only_line_graphics_data)
|
||||
|
||||
|
||||
#define MND(name, args) {#name, (PyCFunction)name, args, #name},
|
||||
#define MODEFUNC(name) MND(name, METH_NOARGS) MND(set_##name, METH_O)
|
||||
@ -4144,6 +4306,7 @@ static PyMethodDef methods[] = {
|
||||
MND(set_marker, METH_VARARGS)
|
||||
MND(marked_cells, METH_NOARGS)
|
||||
MND(scroll_to_next_mark, METH_VARARGS)
|
||||
MND(update_only_line_graphics_data, METH_NOARGS)
|
||||
{"select_graphic_rendition", (PyCFunction)_select_graphic_rendition, METH_VARARGS, ""},
|
||||
|
||||
{NULL} /* Sentinel */
|
||||
|
@ -5,6 +5,9 @@
|
||||
static const combining_type VS15 = 1364, VS16 = 1365;
|
||||
// END_KNOWN_MARKS
|
||||
|
||||
// Converts row/column diacritics to numbers.
|
||||
int diacritic_to_num(char_type ch);
|
||||
|
||||
bool is_combining_char(char_type ch);
|
||||
bool is_ignored_char(char_type ch);
|
||||
bool is_word_char(char_type ch);
|
||||
|
@ -114,21 +114,21 @@ def sl(payload, **kw):
|
||||
return s, g, pl, sl
|
||||
|
||||
|
||||
def put_helpers(self, cw, ch):
|
||||
def put_helpers(self, cw, ch, cols=10, lines=5):
|
||||
iid = 0
|
||||
|
||||
def create_screen():
|
||||
s = self.create_screen(10, 5, cell_width=cw, cell_height=ch)
|
||||
s = self.create_screen(cols, lines, cell_width=cw, cell_height=ch)
|
||||
return s, 2 / s.columns, 2 / s.lines
|
||||
|
||||
def put_cmd(
|
||||
z=0, num_cols=0, num_lines=0, x_off=0, y_off=0, width=0,
|
||||
height=0, cell_x_off=0, cell_y_off=0, placement_id=0,
|
||||
cursor_movement=0
|
||||
cursor_movement=0, unicode_placeholder=0
|
||||
):
|
||||
return 'z=%d,c=%d,r=%d,x=%d,y=%d,w=%d,h=%d,X=%d,Y=%d,p=%d,C=%d' % (
|
||||
return 'z=%d,c=%d,r=%d,x=%d,y=%d,w=%d,h=%d,X=%d,Y=%d,p=%d,C=%d,U=%d' % (
|
||||
z, num_cols, num_lines, x_off, y_off, width, height, cell_x_off,
|
||||
cell_y_off, placement_id, cursor_movement
|
||||
cell_y_off, placement_id, cursor_movement, unicode_placeholder
|
||||
)
|
||||
|
||||
def put_image(screen, w, h, **kw):
|
||||
@ -515,6 +515,232 @@ def test_image_put(self):
|
||||
s.reset()
|
||||
self.assertEqual(s.grman.disk_cache.total_size, 0)
|
||||
|
||||
def test_unicode_placeholders(self):
|
||||
# This test tests basic image placement using using unicode placeholders
|
||||
cw, ch = 10, 20
|
||||
s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch)
|
||||
# Upload two images.
|
||||
put_image(s, 20, 20, num_cols=4, num_lines=2, unicode_placeholder=1, id=42)
|
||||
put_image(s, 10, 20, num_cols=4, num_lines=2, unicode_placeholder=1, id=(42<<16) + (43<<8) + 44)
|
||||
# The references are virtual, so no visible refs yet.
|
||||
s.update_only_line_graphics_data()
|
||||
refs = layers(s)
|
||||
self.ae(len(refs), 0)
|
||||
# A reminder of row/column diacritics meaning (assuming 0-based):
|
||||
# \u0305 -> 0
|
||||
# \u030D -> 1
|
||||
# \u030E -> 2
|
||||
# \u0310 -> 3
|
||||
# Now print the placeholders for the first image.
|
||||
# Encode the id as an 8-bit color.
|
||||
s.apply_sgr("38;5;42")
|
||||
# These two characters will become one 2x1 ref.
|
||||
s.draw("\U0010EEEE\u0305\u0305\U0010EEEE\u0305\u030D")
|
||||
# These two characters will be two separate refs (not contiguous).
|
||||
s.draw("\U0010EEEE\u0305\u0305\U0010EEEE\u0305\u030E")
|
||||
s.cursor_back(4)
|
||||
s.update_only_line_graphics_data()
|
||||
refs = layers(s)
|
||||
self.ae(len(refs), 3)
|
||||
self.ae(refs[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 0.5, 'bottom': 0.5})
|
||||
self.ae(refs[1]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 0.25, 'bottom': 0.5})
|
||||
self.ae(refs[2]['src_rect'], {'left': 0.5, 'top': 0.0, 'right': 0.75, 'bottom': 0.5})
|
||||
# Erase the line.
|
||||
s.erase_in_line(2)
|
||||
# There must be 0 refs after the line is erased.
|
||||
s.update_only_line_graphics_data()
|
||||
refs = layers(s)
|
||||
self.ae(len(refs), 0)
|
||||
# Now test encoding IDs with the 24-bit color.
|
||||
# The first image, 1x1
|
||||
s.apply_sgr("38;2;0;0;42")
|
||||
s.draw("\U0010EEEE\u0305\u0305")
|
||||
# The second image, 2x1
|
||||
s.apply_sgr("38;2;42;43;44")
|
||||
s.draw("\U0010EEEE\u0305\u030D\U0010EEEE\u0305\u030E")
|
||||
s.cursor_back(2)
|
||||
s.update_only_line_graphics_data()
|
||||
refs = layers(s)
|
||||
self.ae(len(refs), 2)
|
||||
self.ae(refs[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 0.25, 'bottom': 0.5})
|
||||
# The second ref spans the whole widths of the second image because it's
|
||||
# fit to height and centered in a 4x2 box (specified in put_image).
|
||||
self.ae(refs[1]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 0.5})
|
||||
# Erase the line.
|
||||
s.erase_in_line(2)
|
||||
# Now test implicit column numbers.
|
||||
# We will mix implicit and explicit column/row specifications, but they
|
||||
# will be combine into just two references.
|
||||
s.apply_sgr("38;5;42")
|
||||
# full row 0 of the first image
|
||||
s.draw("\U0010EEEE\u0305\u0305\U0010EEEE\u0305\U0010EEEE\U0010EEEE\u0305")
|
||||
# full row 1 of the first image
|
||||
s.draw("\U0010EEEE\u030D\U0010EEEE\U0010EEEE\U0010EEEE\u030D\u0310")
|
||||
s.cursor_back(8)
|
||||
s.update_only_line_graphics_data()
|
||||
refs = layers(s)
|
||||
self.ae(len(refs), 2)
|
||||
self.ae(refs[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 0.5})
|
||||
self.ae(refs[1]['src_rect'], {'left': 0.0, 'top': 0.5, 'right': 1.0, 'bottom': 1.0})
|
||||
|
||||
def test_unicode_placeholders_3rd_combining_char(self):
|
||||
# This test tests that we can use the 3rd diacritic for the most
|
||||
# significant byte
|
||||
cw, ch = 10, 20
|
||||
s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch)
|
||||
# Upload two images.
|
||||
put_image(s, 20, 20, num_cols=4, num_lines=2, unicode_placeholder=1, id=42)
|
||||
put_image(s, 20, 10, num_cols=4, num_lines=1, unicode_placeholder=1, id=(42 << 24) + 43)
|
||||
# This one will have id=43, which does not exist.
|
||||
s.apply_sgr("38;2;0;0;43")
|
||||
s.draw("\U0010EEEE\u0305\U0010EEEE\U0010EEEE\U0010EEEE")
|
||||
s.cursor_back(4)
|
||||
s.update_only_line_graphics_data()
|
||||
refs = layers(s)
|
||||
self.ae(len(refs), 0)
|
||||
s.erase_in_line(2)
|
||||
# This one will have id=42. We explicitly specify that the most
|
||||
# significant byte is 0 (third \u305). Specifying the zero byte like
|
||||
# this is not necessary but is correct.
|
||||
s.apply_sgr("38;2;0;0;42")
|
||||
s.draw("\U0010EEEE\u0305\u0305\u0305\U0010EEEE\u0305\u030D\u0305")
|
||||
# This is the second image.
|
||||
# \u059C -> 42
|
||||
s.apply_sgr("38;2;0;0;43")
|
||||
s.draw("\U0010EEEE\u0305\u0305\u059C\U0010EEEE\u0305\u030D\u059C")
|
||||
# Check that we can continue by using implicit row/column specification.
|
||||
s.draw("\U0010EEEE\u0305\U0010EEEE")
|
||||
s.cursor_back(6)
|
||||
s.update_only_line_graphics_data()
|
||||
refs = layers(s)
|
||||
self.ae(len(refs), 2)
|
||||
self.ae(refs[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 0.5, 'bottom': 0.5})
|
||||
self.ae(refs[1]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 1.0})
|
||||
s.erase_in_line(2)
|
||||
# Now test the 8-bit color mode. Using the third diacritic, we can
|
||||
# specify 16 bits: the most significant byte and the least significant
|
||||
# byte.
|
||||
s.apply_sgr("38;5;42")
|
||||
s.draw("\U0010EEEE\u0305\u0305\u0305\U0010EEEE")
|
||||
s.apply_sgr("38;5;43")
|
||||
s.draw("\U0010EEEE\u0305\u0305\u059C\U0010EEEE\U0010EEEE\u0305\U0010EEEE")
|
||||
s.cursor_back(6)
|
||||
s.update_only_line_graphics_data()
|
||||
refs = layers(s)
|
||||
self.ae(len(refs), 2)
|
||||
self.ae(refs[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 0.5, 'bottom': 0.5})
|
||||
self.ae(refs[1]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 1.0})
|
||||
|
||||
def test_unicode_placeholders_multiple_placements(self):
|
||||
# Here we test placement specification via underline color.
|
||||
cw, ch = 10, 20
|
||||
s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch)
|
||||
put_image(s, 20, 20, num_cols=1, num_lines=1, placement_id=1, unicode_placeholder=1, id=42)
|
||||
put_ref(s, id=42, num_cols=2, num_lines=1, placement_id=22, unicode_placeholder=1)
|
||||
put_ref(s, id=42, num_cols=4, num_lines=2, placement_id=44, unicode_placeholder=1)
|
||||
# The references are virtual, so no visible refs yet.
|
||||
s.update_only_line_graphics_data()
|
||||
refs = layers(s)
|
||||
self.ae(len(refs), 0)
|
||||
# Draw the first row of each placement.
|
||||
s.apply_sgr("38;5;42")
|
||||
s.apply_sgr("58;5;1")
|
||||
s.draw("\U0010EEEE\u0305")
|
||||
s.apply_sgr("58;5;22")
|
||||
s.draw("\U0010EEEE\u0305\U0010EEEE\u0305")
|
||||
s.apply_sgr("58;5;44")
|
||||
s.draw("\U0010EEEE\u0305\U0010EEEE\u0305\U0010EEEE\u0305\U0010EEEE\u0305")
|
||||
s.update_only_line_graphics_data()
|
||||
refs = layers(s)
|
||||
self.ae(len(refs), 3)
|
||||
self.ae(refs[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 1.5})
|
||||
self.ae(refs[1]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 1.0})
|
||||
self.ae(refs[2]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 0.5})
|
||||
|
||||
def test_unicode_placeholders_scroll(self):
|
||||
# Here we test scrolling of a region. We'll draw an image spanning 8
|
||||
# rows and then scroll only the middle part of this image. Each
|
||||
# reference corresponds to one row.
|
||||
cw, ch = 5, 10
|
||||
s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch, lines=8)
|
||||
put_image(s, 5, 80, num_cols=1, num_lines=8, unicode_placeholder=1, id=42)
|
||||
s.apply_sgr("38;5;42")
|
||||
s.cursor_position(1, 0)
|
||||
s.draw("\U0010EEEE\u0305\n")
|
||||
s.cursor_position(2, 0)
|
||||
s.draw("\U0010EEEE\u030D\n")
|
||||
s.cursor_position(3, 0)
|
||||
s.draw("\U0010EEEE\u030E\n")
|
||||
s.cursor_position(4, 0)
|
||||
s.draw("\U0010EEEE\u0310\n")
|
||||
s.cursor_position(5, 0)
|
||||
s.draw("\U0010EEEE\u0312\n")
|
||||
s.cursor_position(6, 0)
|
||||
s.draw("\U0010EEEE\u033D\n")
|
||||
s.cursor_position(7, 0)
|
||||
s.draw("\U0010EEEE\u033E\n")
|
||||
s.cursor_position(8, 0)
|
||||
s.draw("\U0010EEEE\u033F")
|
||||
# Each line will contain a part of the image.
|
||||
s.update_only_line_graphics_data()
|
||||
refs = layers(s)
|
||||
refs = sorted(refs, key=lambda r: r['src_rect']['top'])
|
||||
self.ae(len(refs), 8)
|
||||
for i in range(8):
|
||||
self.ae(refs[i]['src_rect'], {'left': 0.0, 'top': 0.125*i, 'right': 1.0, 'bottom': 0.125*(i + 1)})
|
||||
self.ae(refs[i]['dest_rect']['top'], 1 - 0.25*i)
|
||||
# Now set margins to lines 3 and 6.
|
||||
s.set_margins(3, 6) # 1-based indexing
|
||||
# Scroll two lines down (i.e. move lines 3..6 up).
|
||||
# Lines 3 and 4 will be erased.
|
||||
s.cursor_position(6, 0)
|
||||
s.index()
|
||||
s.index()
|
||||
s.update_only_line_graphics_data()
|
||||
refs = layers(s)
|
||||
refs = sorted(refs, key=lambda r: r['src_rect']['top'])
|
||||
self.ae(len(refs), 6)
|
||||
# Lines 1 and 2 are outside of the region, not scrolled.
|
||||
self.ae(refs[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 0.125})
|
||||
self.ae(refs[0]['dest_rect']['top'], 1.0)
|
||||
self.ae(refs[1]['src_rect'], {'left': 0.0, 'top': 0.125*1, 'right': 1.0, 'bottom': 0.125*2})
|
||||
self.ae(refs[1]['dest_rect']['top'], 1.0 - 0.25*1)
|
||||
# Lines 3 and 4 are erased.
|
||||
# Lines 5 and 6 are now higher.
|
||||
self.ae(refs[2]['src_rect'], {'left': 0.0, 'top': 0.125*4, 'right': 1.0, 'bottom': 0.125*5})
|
||||
self.ae(refs[2]['dest_rect']['top'], 1.0 - 0.25*2)
|
||||
self.ae(refs[3]['src_rect'], {'left': 0.0, 'top': 0.125*5, 'right': 1.0, 'bottom': 0.125*6})
|
||||
self.ae(refs[3]['dest_rect']['top'], 1.0 - 0.25*3)
|
||||
# Lines 7 and 8 are outside of the region.
|
||||
self.ae(refs[4]['src_rect'], {'left': 0.0, 'top': 0.125*6, 'right': 1.0, 'bottom': 0.125*7})
|
||||
self.ae(refs[4]['dest_rect']['top'], 1.0 - 0.25*6)
|
||||
self.ae(refs[5]['src_rect'], {'left': 0.0, 'top': 0.125*7, 'right': 1.0, 'bottom': 0.125*8})
|
||||
self.ae(refs[5]['dest_rect']['top'], 1.0 - 0.25*7)
|
||||
# Now scroll three lines up (i.e. move lines 5..6 down).
|
||||
# Line 6 will be erased.
|
||||
s.cursor_position(3, 0)
|
||||
s.reverse_index()
|
||||
s.reverse_index()
|
||||
s.reverse_index()
|
||||
s.update_only_line_graphics_data()
|
||||
refs = layers(s)
|
||||
refs = sorted(refs, key=lambda r: r['src_rect']['top'])
|
||||
self.ae(len(refs), 5)
|
||||
# Lines 1 and 2 are outside of the region, not scrolled.
|
||||
self.ae(refs[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 0.125})
|
||||
self.ae(refs[0]['dest_rect']['top'], 1.0)
|
||||
self.ae(refs[1]['src_rect'], {'left': 0.0, 'top': 0.125*1, 'right': 1.0, 'bottom': 0.125*2})
|
||||
self.ae(refs[1]['dest_rect']['top'], 1.0 - 0.25*1)
|
||||
# Lines 3, 4 and 6 are erased.
|
||||
# Line 5 is now lower.
|
||||
self.ae(refs[2]['src_rect'], {'left': 0.0, 'top': 0.125*4, 'right': 1.0, 'bottom': 0.125*5})
|
||||
self.ae(refs[2]['dest_rect']['top'], 1.0 - 0.25*5)
|
||||
# Lines 7 and 8 are outside of the region.
|
||||
self.ae(refs[3]['src_rect'], {'left': 0.0, 'top': 0.125*6, 'right': 1.0, 'bottom': 0.125*7})
|
||||
self.ae(refs[3]['dest_rect']['top'], 1.0 - 0.25*6)
|
||||
self.ae(refs[4]['src_rect'], {'left': 0.0, 'top': 0.125*7, 'right': 1.0, 'bottom': 0.125*8})
|
||||
self.ae(refs[4]['dest_rect']['top'], 1.0 - 0.25*7)
|
||||
|
||||
def test_gr_scroll(self):
|
||||
cw, ch = 10, 20
|
||||
s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch)
|
||||
|
@ -410,7 +410,7 @@ def c(**k):
|
||||
for f in 'action delete_action transmission_type compressed'.split():
|
||||
k.setdefault(f, b'\0')
|
||||
for f in ('format more id data_sz data_offset width height x_offset y_offset data_height data_width cursor_movement'
|
||||
' num_cells num_lines cell_x_offset cell_y_offset z_index placement_id image_number quiet').split():
|
||||
' num_cells num_lines cell_x_offset cell_y_offset z_index placement_id image_number quiet unicode_placement').split():
|
||||
k.setdefault(f, 0)
|
||||
p = k.pop('payload', '').encode('utf-8')
|
||||
k['payload_sz'] = len(p)
|
||||
|
310
rowcolumn-diacritics.txt
Normal file
310
rowcolumn-diacritics.txt
Normal file
@ -0,0 +1,310 @@
|
||||
# This file lists the diacritics used to indicate row/column numbers for
|
||||
# Unicode terminal image placeholders. It is derived from UnicodeData.txt for
|
||||
# Unicode 6.0.0 (chosen somewhat arbitrarily: it's old enough, but still
|
||||
# contains more than 255 suitable combining chars) using the following
|
||||
# command:
|
||||
#
|
||||
# cat UnicodeData.txt | grep "Mn;230;NSM;;" | grep -v "0300\|0301\|0302\|0303\|0304\|0306\|0307\|0308\|0309\|030A\|030B\|030C\|030F\|0311\|0313\|0314\|0342\|0653\|0654"
|
||||
#
|
||||
# That is, we use combining chars of the same combining class 230 (above the
|
||||
# base character) that do not have decomposition mappings, and we also remove
|
||||
# some characters that may be fused with other characters during normalization,
|
||||
# like 0041 0300 -> 00C0 which is À (A with grave).
|
||||
#
|
||||
0305;COMBINING OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING OVERSCORE;;;;
|
||||
030D;COMBINING VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL LINE ABOVE;;;;
|
||||
030E;COMBINING DOUBLE VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE VERTICAL LINE ABOVE;;;;
|
||||
0310;COMBINING CANDRABINDU;Mn;230;NSM;;;;;N;NON-SPACING CANDRABINDU;;;;
|
||||
0312;COMBINING TURNED COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING TURNED COMMA ABOVE;;;;
|
||||
033D;COMBINING X ABOVE;Mn;230;NSM;;;;;N;NON-SPACING X ABOVE;;;;
|
||||
033E;COMBINING VERTICAL TILDE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL TILDE;;;;
|
||||
033F;COMBINING DOUBLE OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE OVERSCORE;;;;
|
||||
0346;COMBINING BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
034A;COMBINING NOT TILDE ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
034B;COMBINING HOMOTHETIC ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
034C;COMBINING ALMOST EQUAL TO ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
0350;COMBINING RIGHT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
0351;COMBINING LEFT HALF RING ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
0352;COMBINING FERMATA;Mn;230;NSM;;;;;N;;;;;
|
||||
0357;COMBINING RIGHT HALF RING ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
035B;COMBINING ZIGZAG ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
0363;COMBINING LATIN SMALL LETTER A;Mn;230;NSM;;;;;N;;;;;
|
||||
0364;COMBINING LATIN SMALL LETTER E;Mn;230;NSM;;;;;N;;;;;
|
||||
0365;COMBINING LATIN SMALL LETTER I;Mn;230;NSM;;;;;N;;;;;
|
||||
0366;COMBINING LATIN SMALL LETTER O;Mn;230;NSM;;;;;N;;;;;
|
||||
0367;COMBINING LATIN SMALL LETTER U;Mn;230;NSM;;;;;N;;;;;
|
||||
0368;COMBINING LATIN SMALL LETTER C;Mn;230;NSM;;;;;N;;;;;
|
||||
0369;COMBINING LATIN SMALL LETTER D;Mn;230;NSM;;;;;N;;;;;
|
||||
036A;COMBINING LATIN SMALL LETTER H;Mn;230;NSM;;;;;N;;;;;
|
||||
036B;COMBINING LATIN SMALL LETTER M;Mn;230;NSM;;;;;N;;;;;
|
||||
036C;COMBINING LATIN SMALL LETTER R;Mn;230;NSM;;;;;N;;;;;
|
||||
036D;COMBINING LATIN SMALL LETTER T;Mn;230;NSM;;;;;N;;;;;
|
||||
036E;COMBINING LATIN SMALL LETTER V;Mn;230;NSM;;;;;N;;;;;
|
||||
036F;COMBINING LATIN SMALL LETTER X;Mn;230;NSM;;;;;N;;;;;
|
||||
0483;COMBINING CYRILLIC TITLO;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING TITLO;;;;
|
||||
0484;COMBINING CYRILLIC PALATALIZATION;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PALATALIZATION;;;;
|
||||
0485;COMBINING CYRILLIC DASIA PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING DASIA PNEUMATA;;;;
|
||||
0486;COMBINING CYRILLIC PSILI PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PSILI PNEUMATA;;;;
|
||||
0487;COMBINING CYRILLIC POKRYTIE;Mn;230;NSM;;;;;N;;;;;
|
||||
0592;HEBREW ACCENT SEGOL;Mn;230;NSM;;;;;N;;;;;
|
||||
0593;HEBREW ACCENT SHALSHELET;Mn;230;NSM;;;;;N;;;;;
|
||||
0594;HEBREW ACCENT ZAQEF QATAN;Mn;230;NSM;;;;;N;;;;;
|
||||
0595;HEBREW ACCENT ZAQEF GADOL;Mn;230;NSM;;;;;N;;;;;
|
||||
0597;HEBREW ACCENT REVIA;Mn;230;NSM;;;;;N;;;;;
|
||||
0598;HEBREW ACCENT ZARQA;Mn;230;NSM;;;;;N;;;;;
|
||||
0599;HEBREW ACCENT PASHTA;Mn;230;NSM;;;;;N;;;;;
|
||||
059C;HEBREW ACCENT GERESH;Mn;230;NSM;;;;;N;;;;;
|
||||
059D;HEBREW ACCENT GERESH MUQDAM;Mn;230;NSM;;;;;N;;;;;
|
||||
059E;HEBREW ACCENT GERSHAYIM;Mn;230;NSM;;;;;N;;;;;
|
||||
059F;HEBREW ACCENT QARNEY PARA;Mn;230;NSM;;;;;N;;;;;
|
||||
05A0;HEBREW ACCENT TELISHA GEDOLA;Mn;230;NSM;;;;;N;;;;;
|
||||
05A1;HEBREW ACCENT PAZER;Mn;230;NSM;;;;;N;;;;;
|
||||
05A8;HEBREW ACCENT QADMA;Mn;230;NSM;;;;;N;;;;;
|
||||
05A9;HEBREW ACCENT TELISHA QETANA;Mn;230;NSM;;;;;N;;;;;
|
||||
05AB;HEBREW ACCENT OLE;Mn;230;NSM;;;;;N;;;;;
|
||||
05AC;HEBREW ACCENT ILUY;Mn;230;NSM;;;;;N;;;;;
|
||||
05AF;HEBREW MARK MASORA CIRCLE;Mn;230;NSM;;;;;N;;;;;
|
||||
05C4;HEBREW MARK UPPER DOT;Mn;230;NSM;;;;;N;;;;;
|
||||
0610;ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM;Mn;230;NSM;;;;;N;;;;;
|
||||
0611;ARABIC SIGN ALAYHE ASSALLAM;Mn;230;NSM;;;;;N;;;;;
|
||||
0612;ARABIC SIGN RAHMATULLAH ALAYHE;Mn;230;NSM;;;;;N;;;;;
|
||||
0613;ARABIC SIGN RADI ALLAHOU ANHU;Mn;230;NSM;;;;;N;;;;;
|
||||
0614;ARABIC SIGN TAKHALLUS;Mn;230;NSM;;;;;N;;;;;
|
||||
0615;ARABIC SMALL HIGH TAH;Mn;230;NSM;;;;;N;;;;;
|
||||
0616;ARABIC SMALL HIGH LIGATURE ALEF WITH LAM WITH YEH;Mn;230;NSM;;;;;N;;;;;
|
||||
0617;ARABIC SMALL HIGH ZAIN;Mn;230;NSM;;;;;N;;;;;
|
||||
0657;ARABIC INVERTED DAMMA;Mn;230;NSM;;;;;N;;;;;
|
||||
0658;ARABIC MARK NOON GHUNNA;Mn;230;NSM;;;;;N;;;;;
|
||||
0659;ARABIC ZWARAKAY;Mn;230;NSM;;;;;N;;;;;
|
||||
065A;ARABIC VOWEL SIGN SMALL V ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
065B;ARABIC VOWEL SIGN INVERTED SMALL V ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
065D;ARABIC REVERSED DAMMA;Mn;230;NSM;;;;;N;;;;;
|
||||
065E;ARABIC FATHA WITH TWO DOTS;Mn;230;NSM;;;;;N;;;;;
|
||||
06D6;ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;;
|
||||
06D7;ARABIC SMALL HIGH LIGATURE QAF WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;;
|
||||
06D8;ARABIC SMALL HIGH MEEM INITIAL FORM;Mn;230;NSM;;;;;N;;;;;
|
||||
06D9;ARABIC SMALL HIGH LAM ALEF;Mn;230;NSM;;;;;N;;;;;
|
||||
06DA;ARABIC SMALL HIGH JEEM;Mn;230;NSM;;;;;N;;;;;
|
||||
06DB;ARABIC SMALL HIGH THREE DOTS;Mn;230;NSM;;;;;N;;;;;
|
||||
06DC;ARABIC SMALL HIGH SEEN;Mn;230;NSM;;;;;N;;;;;
|
||||
06DF;ARABIC SMALL HIGH ROUNDED ZERO;Mn;230;NSM;;;;;N;;;;;
|
||||
06E0;ARABIC SMALL HIGH UPRIGHT RECTANGULAR ZERO;Mn;230;NSM;;;;;N;;;;;
|
||||
06E1;ARABIC SMALL HIGH DOTLESS HEAD OF KHAH;Mn;230;NSM;;;;;N;;;;;
|
||||
06E2;ARABIC SMALL HIGH MEEM ISOLATED FORM;Mn;230;NSM;;;;;N;;;;;
|
||||
06E4;ARABIC SMALL HIGH MADDA;Mn;230;NSM;;;;;N;;;;;
|
||||
06E7;ARABIC SMALL HIGH YEH;Mn;230;NSM;;;;;N;;;;;
|
||||
06E8;ARABIC SMALL HIGH NOON;Mn;230;NSM;;;;;N;;;;;
|
||||
06EB;ARABIC EMPTY CENTRE HIGH STOP;Mn;230;NSM;;;;;N;;;;;
|
||||
06EC;ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE;Mn;230;NSM;;;;;N;;;;;
|
||||
0730;SYRIAC PTHAHA ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
0732;SYRIAC PTHAHA DOTTED;Mn;230;NSM;;;;;N;;;;;
|
||||
0733;SYRIAC ZQAPHA ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
0735;SYRIAC ZQAPHA DOTTED;Mn;230;NSM;;;;;N;;;;;
|
||||
0736;SYRIAC RBASA ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
073A;SYRIAC HBASA ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
073D;SYRIAC ESASA ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
073F;SYRIAC RWAHA;Mn;230;NSM;;;;;N;;;;;
|
||||
0740;SYRIAC FEMININE DOT;Mn;230;NSM;;;;;N;;;;;
|
||||
0741;SYRIAC QUSHSHAYA;Mn;230;NSM;;;;;N;;;;;
|
||||
0743;SYRIAC TWO VERTICAL DOTS ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
0745;SYRIAC THREE DOTS ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
0747;SYRIAC OBLIQUE LINE ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
0749;SYRIAC MUSIC;Mn;230;NSM;;;;;N;;;;;
|
||||
074A;SYRIAC BARREKH;Mn;230;NSM;;;;;N;;;;;
|
||||
07EB;NKO COMBINING SHORT HIGH TONE;Mn;230;NSM;;;;;N;;;;;
|
||||
07EC;NKO COMBINING SHORT LOW TONE;Mn;230;NSM;;;;;N;;;;;
|
||||
07ED;NKO COMBINING SHORT RISING TONE;Mn;230;NSM;;;;;N;;;;;
|
||||
07EE;NKO COMBINING LONG DESCENDING TONE;Mn;230;NSM;;;;;N;;;;;
|
||||
07EF;NKO COMBINING LONG HIGH TONE;Mn;230;NSM;;;;;N;;;;;
|
||||
07F0;NKO COMBINING LONG LOW TONE;Mn;230;NSM;;;;;N;;;;;
|
||||
07F1;NKO COMBINING LONG RISING TONE;Mn;230;NSM;;;;;N;;;;;
|
||||
07F3;NKO COMBINING DOUBLE DOT ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
0816;SAMARITAN MARK IN;Mn;230;NSM;;;;;N;;;;;
|
||||
0817;SAMARITAN MARK IN-ALAF;Mn;230;NSM;;;;;N;;;;;
|
||||
0818;SAMARITAN MARK OCCLUSION;Mn;230;NSM;;;;;N;;;;;
|
||||
0819;SAMARITAN MARK DAGESH;Mn;230;NSM;;;;;N;;;;;
|
||||
081B;SAMARITAN MARK EPENTHETIC YUT;Mn;230;NSM;;;;;N;;;;;
|
||||
081C;SAMARITAN VOWEL SIGN LONG E;Mn;230;NSM;;;;;N;;;;;
|
||||
081D;SAMARITAN VOWEL SIGN E;Mn;230;NSM;;;;;N;;;;;
|
||||
081E;SAMARITAN VOWEL SIGN OVERLONG AA;Mn;230;NSM;;;;;N;;;;;
|
||||
081F;SAMARITAN VOWEL SIGN LONG AA;Mn;230;NSM;;;;;N;;;;;
|
||||
0820;SAMARITAN VOWEL SIGN AA;Mn;230;NSM;;;;;N;;;;;
|
||||
0821;SAMARITAN VOWEL SIGN OVERLONG A;Mn;230;NSM;;;;;N;;;;;
|
||||
0822;SAMARITAN VOWEL SIGN LONG A;Mn;230;NSM;;;;;N;;;;;
|
||||
0823;SAMARITAN VOWEL SIGN A;Mn;230;NSM;;;;;N;;;;;
|
||||
0825;SAMARITAN VOWEL SIGN SHORT A;Mn;230;NSM;;;;;N;;;;;
|
||||
0826;SAMARITAN VOWEL SIGN LONG U;Mn;230;NSM;;;;;N;;;;;
|
||||
0827;SAMARITAN VOWEL SIGN U;Mn;230;NSM;;;;;N;;;;;
|
||||
0829;SAMARITAN VOWEL SIGN LONG I;Mn;230;NSM;;;;;N;;;;;
|
||||
082A;SAMARITAN VOWEL SIGN I;Mn;230;NSM;;;;;N;;;;;
|
||||
082B;SAMARITAN VOWEL SIGN O;Mn;230;NSM;;;;;N;;;;;
|
||||
082C;SAMARITAN VOWEL SIGN SUKUN;Mn;230;NSM;;;;;N;;;;;
|
||||
082D;SAMARITAN MARK NEQUDAA;Mn;230;NSM;;;;;N;;;;;
|
||||
0951;DEVANAGARI STRESS SIGN UDATTA;Mn;230;NSM;;;;;N;;;;;
|
||||
0953;DEVANAGARI GRAVE ACCENT;Mn;230;NSM;;;;;N;;;;;
|
||||
0954;DEVANAGARI ACUTE ACCENT;Mn;230;NSM;;;;;N;;;;;
|
||||
0F82;TIBETAN SIGN NYI ZLA NAA DA;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU WITH ORNAMENT;;;;
|
||||
0F83;TIBETAN SIGN SNA LDAN;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU;;;;
|
||||
0F86;TIBETAN SIGN LCI RTAGS;Mn;230;NSM;;;;;N;;;;;
|
||||
0F87;TIBETAN SIGN YANG RTAGS;Mn;230;NSM;;;;;N;;;;;
|
||||
135D;ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK;Mn;230;NSM;;;;;N;;;;;
|
||||
135E;ETHIOPIC COMBINING VOWEL LENGTH MARK;Mn;230;NSM;;;;;N;;;;;
|
||||
135F;ETHIOPIC COMBINING GEMINATION MARK;Mn;230;NSM;;;;;N;;;;;
|
||||
17DD;KHMER SIGN ATTHACAN;Mn;230;NSM;;;;;N;;;;;
|
||||
193A;LIMBU SIGN KEMPHRENG;Mn;230;NSM;;;;;N;;;;;
|
||||
1A17;BUGINESE VOWEL SIGN I;Mn;230;NSM;;;;;N;;;;;
|
||||
1A75;TAI THAM SIGN TONE-1;Mn;230;NSM;;;;;N;;;;;
|
||||
1A76;TAI THAM SIGN TONE-2;Mn;230;NSM;;;;;N;;;;;
|
||||
1A77;TAI THAM SIGN KHUEN TONE-3;Mn;230;NSM;;;;;N;;;;;
|
||||
1A78;TAI THAM SIGN KHUEN TONE-4;Mn;230;NSM;;;;;N;;;;;
|
||||
1A79;TAI THAM SIGN KHUEN TONE-5;Mn;230;NSM;;;;;N;;;;;
|
||||
1A7A;TAI THAM SIGN RA HAAM;Mn;230;NSM;;;;;N;;;;;
|
||||
1A7B;TAI THAM SIGN MAI SAM;Mn;230;NSM;;;;;N;;;;;
|
||||
1A7C;TAI THAM SIGN KHUEN-LUE KARAN;Mn;230;NSM;;;;;N;;;;;
|
||||
1B6B;BALINESE MUSICAL SYMBOL COMBINING TEGEH;Mn;230;NSM;;;;;N;;;;;
|
||||
1B6D;BALINESE MUSICAL SYMBOL COMBINING KEMPUL;Mn;230;NSM;;;;;N;;;;;
|
||||
1B6E;BALINESE MUSICAL SYMBOL COMBINING KEMPLI;Mn;230;NSM;;;;;N;;;;;
|
||||
1B6F;BALINESE MUSICAL SYMBOL COMBINING JEGOGAN;Mn;230;NSM;;;;;N;;;;;
|
||||
1B70;BALINESE MUSICAL SYMBOL COMBINING KEMPUL WITH JEGOGAN;Mn;230;NSM;;;;;N;;;;;
|
||||
1B71;BALINESE MUSICAL SYMBOL COMBINING KEMPLI WITH JEGOGAN;Mn;230;NSM;;;;;N;;;;;
|
||||
1B72;BALINESE MUSICAL SYMBOL COMBINING BENDE;Mn;230;NSM;;;;;N;;;;;
|
||||
1B73;BALINESE MUSICAL SYMBOL COMBINING GONG;Mn;230;NSM;;;;;N;;;;;
|
||||
1CD0;VEDIC TONE KARSHANA;Mn;230;NSM;;;;;N;;;;;
|
||||
1CD1;VEDIC TONE SHARA;Mn;230;NSM;;;;;N;;;;;
|
||||
1CD2;VEDIC TONE PRENKHA;Mn;230;NSM;;;;;N;;;;;
|
||||
1CDA;VEDIC TONE DOUBLE SVARITA;Mn;230;NSM;;;;;N;;;;;
|
||||
1CDB;VEDIC TONE TRIPLE SVARITA;Mn;230;NSM;;;;;N;;;;;
|
||||
1CE0;VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA;Mn;230;NSM;;;;;N;;;;;
|
||||
1DC0;COMBINING DOTTED GRAVE ACCENT;Mn;230;NSM;;;;;N;;;;;
|
||||
1DC1;COMBINING DOTTED ACUTE ACCENT;Mn;230;NSM;;;;;N;;;;;
|
||||
1DC3;COMBINING SUSPENSION MARK;Mn;230;NSM;;;;;N;;;;;
|
||||
1DC4;COMBINING MACRON-ACUTE;Mn;230;NSM;;;;;N;;;;;
|
||||
1DC5;COMBINING GRAVE-MACRON;Mn;230;NSM;;;;;N;;;;;
|
||||
1DC6;COMBINING MACRON-GRAVE;Mn;230;NSM;;;;;N;;;;;
|
||||
1DC7;COMBINING ACUTE-MACRON;Mn;230;NSM;;;;;N;;;;;
|
||||
1DC8;COMBINING GRAVE-ACUTE-GRAVE;Mn;230;NSM;;;;;N;;;;;
|
||||
1DC9;COMBINING ACUTE-GRAVE-ACUTE;Mn;230;NSM;;;;;N;;;;;
|
||||
1DCB;COMBINING BREVE-MACRON;Mn;230;NSM;;;;;N;;;;;
|
||||
1DCC;COMBINING MACRON-BREVE;Mn;230;NSM;;;;;N;;;;;
|
||||
1DD1;COMBINING UR ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
1DD2;COMBINING US ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
1DD3;COMBINING LATIN SMALL LETTER FLATTENED OPEN A ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
1DD4;COMBINING LATIN SMALL LETTER AE;Mn;230;NSM;;;;;N;;;;;
|
||||
1DD5;COMBINING LATIN SMALL LETTER AO;Mn;230;NSM;;;;;N;;;;;
|
||||
1DD6;COMBINING LATIN SMALL LETTER AV;Mn;230;NSM;;;;;N;;;;;
|
||||
1DD7;COMBINING LATIN SMALL LETTER C CEDILLA;Mn;230;NSM;;;;;N;;;;;
|
||||
1DD8;COMBINING LATIN SMALL LETTER INSULAR D;Mn;230;NSM;;;;;N;;;;;
|
||||
1DD9;COMBINING LATIN SMALL LETTER ETH;Mn;230;NSM;;;;;N;;;;;
|
||||
1DDA;COMBINING LATIN SMALL LETTER G;Mn;230;NSM;;;;;N;;;;;
|
||||
1DDB;COMBINING LATIN LETTER SMALL CAPITAL G;Mn;230;NSM;;;;;N;;;;;
|
||||
1DDC;COMBINING LATIN SMALL LETTER K;Mn;230;NSM;;;;;N;;;;;
|
||||
1DDD;COMBINING LATIN SMALL LETTER L;Mn;230;NSM;;;;;N;;;;;
|
||||
1DDE;COMBINING LATIN LETTER SMALL CAPITAL L;Mn;230;NSM;;;;;N;;;;;
|
||||
1DDF;COMBINING LATIN LETTER SMALL CAPITAL M;Mn;230;NSM;;;;;N;;;;;
|
||||
1DE0;COMBINING LATIN SMALL LETTER N;Mn;230;NSM;;;;;N;;;;;
|
||||
1DE1;COMBINING LATIN LETTER SMALL CAPITAL N;Mn;230;NSM;;;;;N;;;;;
|
||||
1DE2;COMBINING LATIN LETTER SMALL CAPITAL R;Mn;230;NSM;;;;;N;;;;;
|
||||
1DE3;COMBINING LATIN SMALL LETTER R ROTUNDA;Mn;230;NSM;;;;;N;;;;;
|
||||
1DE4;COMBINING LATIN SMALL LETTER S;Mn;230;NSM;;;;;N;;;;;
|
||||
1DE5;COMBINING LATIN SMALL LETTER LONG S;Mn;230;NSM;;;;;N;;;;;
|
||||
1DE6;COMBINING LATIN SMALL LETTER Z;Mn;230;NSM;;;;;N;;;;;
|
||||
1DFE;COMBINING LEFT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
20D0;COMBINING LEFT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT HARPOON ABOVE;;;;
|
||||
20D1;COMBINING RIGHT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT HARPOON ABOVE;;;;
|
||||
20D4;COMBINING ANTICLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING ANTICLOCKWISE ARROW ABOVE;;;;
|
||||
20D5;COMBINING CLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING CLOCKWISE ARROW ABOVE;;;;
|
||||
20D6;COMBINING LEFT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT ARROW ABOVE;;;;
|
||||
20D7;COMBINING RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT ARROW ABOVE;;;;
|
||||
20DB;COMBINING THREE DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING THREE DOTS ABOVE;;;;
|
||||
20DC;COMBINING FOUR DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING FOUR DOTS ABOVE;;;;
|
||||
20E1;COMBINING LEFT RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT RIGHT ARROW ABOVE;;;;
|
||||
20E7;COMBINING ANNUITY SYMBOL;Mn;230;NSM;;;;;N;;;;;
|
||||
20E9;COMBINING WIDE BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
20F0;COMBINING ASTERISK ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
2CEF;COPTIC COMBINING NI ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
2CF0;COPTIC COMBINING SPIRITUS ASPER;Mn;230;NSM;;;;;N;;;;;
|
||||
2CF1;COPTIC COMBINING SPIRITUS LENIS;Mn;230;NSM;;;;;N;;;;;
|
||||
2DE0;COMBINING CYRILLIC LETTER BE;Mn;230;NSM;;;;;N;;;;;
|
||||
2DE1;COMBINING CYRILLIC LETTER VE;Mn;230;NSM;;;;;N;;;;;
|
||||
2DE2;COMBINING CYRILLIC LETTER GHE;Mn;230;NSM;;;;;N;;;;;
|
||||
2DE3;COMBINING CYRILLIC LETTER DE;Mn;230;NSM;;;;;N;;;;;
|
||||
2DE4;COMBINING CYRILLIC LETTER ZHE;Mn;230;NSM;;;;;N;;;;;
|
||||
2DE5;COMBINING CYRILLIC LETTER ZE;Mn;230;NSM;;;;;N;;;;;
|
||||
2DE6;COMBINING CYRILLIC LETTER KA;Mn;230;NSM;;;;;N;;;;;
|
||||
2DE7;COMBINING CYRILLIC LETTER EL;Mn;230;NSM;;;;;N;;;;;
|
||||
2DE8;COMBINING CYRILLIC LETTER EM;Mn;230;NSM;;;;;N;;;;;
|
||||
2DE9;COMBINING CYRILLIC LETTER EN;Mn;230;NSM;;;;;N;;;;;
|
||||
2DEA;COMBINING CYRILLIC LETTER O;Mn;230;NSM;;;;;N;;;;;
|
||||
2DEB;COMBINING CYRILLIC LETTER PE;Mn;230;NSM;;;;;N;;;;;
|
||||
2DEC;COMBINING CYRILLIC LETTER ER;Mn;230;NSM;;;;;N;;;;;
|
||||
2DED;COMBINING CYRILLIC LETTER ES;Mn;230;NSM;;;;;N;;;;;
|
||||
2DEE;COMBINING CYRILLIC LETTER TE;Mn;230;NSM;;;;;N;;;;;
|
||||
2DEF;COMBINING CYRILLIC LETTER HA;Mn;230;NSM;;;;;N;;;;;
|
||||
2DF0;COMBINING CYRILLIC LETTER TSE;Mn;230;NSM;;;;;N;;;;;
|
||||
2DF1;COMBINING CYRILLIC LETTER CHE;Mn;230;NSM;;;;;N;;;;;
|
||||
2DF2;COMBINING CYRILLIC LETTER SHA;Mn;230;NSM;;;;;N;;;;;
|
||||
2DF3;COMBINING CYRILLIC LETTER SHCHA;Mn;230;NSM;;;;;N;;;;;
|
||||
2DF4;COMBINING CYRILLIC LETTER FITA;Mn;230;NSM;;;;;N;;;;;
|
||||
2DF5;COMBINING CYRILLIC LETTER ES-TE;Mn;230;NSM;;;;;N;;;;;
|
||||
2DF6;COMBINING CYRILLIC LETTER A;Mn;230;NSM;;;;;N;;;;;
|
||||
2DF7;COMBINING CYRILLIC LETTER IE;Mn;230;NSM;;;;;N;;;;;
|
||||
2DF8;COMBINING CYRILLIC LETTER DJERV;Mn;230;NSM;;;;;N;;;;;
|
||||
2DF9;COMBINING CYRILLIC LETTER MONOGRAPH UK;Mn;230;NSM;;;;;N;;;;;
|
||||
2DFA;COMBINING CYRILLIC LETTER YAT;Mn;230;NSM;;;;;N;;;;;
|
||||
2DFB;COMBINING CYRILLIC LETTER YU;Mn;230;NSM;;;;;N;;;;;
|
||||
2DFC;COMBINING CYRILLIC LETTER IOTIFIED A;Mn;230;NSM;;;;;N;;;;;
|
||||
2DFD;COMBINING CYRILLIC LETTER LITTLE YUS;Mn;230;NSM;;;;;N;;;;;
|
||||
2DFE;COMBINING CYRILLIC LETTER BIG YUS;Mn;230;NSM;;;;;N;;;;;
|
||||
2DFF;COMBINING CYRILLIC LETTER IOTIFIED BIG YUS;Mn;230;NSM;;;;;N;;;;;
|
||||
A66F;COMBINING CYRILLIC VZMET;Mn;230;NSM;;;;;N;;;;;
|
||||
A67C;COMBINING CYRILLIC KAVYKA;Mn;230;NSM;;;;;N;;;;;
|
||||
A67D;COMBINING CYRILLIC PAYEROK;Mn;230;NSM;;;;;N;;;;;
|
||||
A6F0;BAMUM COMBINING MARK KOQNDON;Mn;230;NSM;;;;;N;;;;;
|
||||
A6F1;BAMUM COMBINING MARK TUKWENTIS;Mn;230;NSM;;;;;N;;;;;
|
||||
A8E0;COMBINING DEVANAGARI DIGIT ZERO;Mn;230;NSM;;;;;N;;;;;
|
||||
A8E1;COMBINING DEVANAGARI DIGIT ONE;Mn;230;NSM;;;;;N;;;;;
|
||||
A8E2;COMBINING DEVANAGARI DIGIT TWO;Mn;230;NSM;;;;;N;;;;;
|
||||
A8E3;COMBINING DEVANAGARI DIGIT THREE;Mn;230;NSM;;;;;N;;;;;
|
||||
A8E4;COMBINING DEVANAGARI DIGIT FOUR;Mn;230;NSM;;;;;N;;;;;
|
||||
A8E5;COMBINING DEVANAGARI DIGIT FIVE;Mn;230;NSM;;;;;N;;;;;
|
||||
A8E6;COMBINING DEVANAGARI DIGIT SIX;Mn;230;NSM;;;;;N;;;;;
|
||||
A8E7;COMBINING DEVANAGARI DIGIT SEVEN;Mn;230;NSM;;;;;N;;;;;
|
||||
A8E8;COMBINING DEVANAGARI DIGIT EIGHT;Mn;230;NSM;;;;;N;;;;;
|
||||
A8E9;COMBINING DEVANAGARI DIGIT NINE;Mn;230;NSM;;;;;N;;;;;
|
||||
A8EA;COMBINING DEVANAGARI LETTER A;Mn;230;NSM;;;;;N;;;;;
|
||||
A8EB;COMBINING DEVANAGARI LETTER U;Mn;230;NSM;;;;;N;;;;;
|
||||
A8EC;COMBINING DEVANAGARI LETTER KA;Mn;230;NSM;;;;;N;;;;;
|
||||
A8ED;COMBINING DEVANAGARI LETTER NA;Mn;230;NSM;;;;;N;;;;;
|
||||
A8EE;COMBINING DEVANAGARI LETTER PA;Mn;230;NSM;;;;;N;;;;;
|
||||
A8EF;COMBINING DEVANAGARI LETTER RA;Mn;230;NSM;;;;;N;;;;;
|
||||
A8F0;COMBINING DEVANAGARI LETTER VI;Mn;230;NSM;;;;;N;;;;;
|
||||
A8F1;COMBINING DEVANAGARI SIGN AVAGRAHA;Mn;230;NSM;;;;;N;;;;;
|
||||
AAB0;TAI VIET MAI KANG;Mn;230;NSM;;;;;N;;;;;
|
||||
AAB2;TAI VIET VOWEL I;Mn;230;NSM;;;;;N;;;;;
|
||||
AAB3;TAI VIET VOWEL UE;Mn;230;NSM;;;;;N;;;;;
|
||||
AAB7;TAI VIET MAI KHIT;Mn;230;NSM;;;;;N;;;;;
|
||||
AAB8;TAI VIET VOWEL IA;Mn;230;NSM;;;;;N;;;;;
|
||||
AABE;TAI VIET VOWEL AM;Mn;230;NSM;;;;;N;;;;;
|
||||
AABF;TAI VIET TONE MAI EK;Mn;230;NSM;;;;;N;;;;;
|
||||
AAC1;TAI VIET TONE MAI THO;Mn;230;NSM;;;;;N;;;;;
|
||||
FE20;COMBINING LIGATURE LEFT HALF;Mn;230;NSM;;;;;N;;;;;
|
||||
FE21;COMBINING LIGATURE RIGHT HALF;Mn;230;NSM;;;;;N;;;;;
|
||||
FE22;COMBINING DOUBLE TILDE LEFT HALF;Mn;230;NSM;;;;;N;;;;;
|
||||
FE23;COMBINING DOUBLE TILDE RIGHT HALF;Mn;230;NSM;;;;;N;;;;;
|
||||
FE24;COMBINING MACRON LEFT HALF;Mn;230;NSM;;;;;N;;;;;
|
||||
FE25;COMBINING MACRON RIGHT HALF;Mn;230;NSM;;;;;N;;;;;
|
||||
FE26;COMBINING CONJOINING MACRON;Mn;230;NSM;;;;;N;;;;;
|
||||
10A0F;KHAROSHTHI SIGN VISARGA;Mn;230;NSM;;;;;N;;;;;
|
||||
10A38;KHAROSHTHI SIGN BAR ABOVE;Mn;230;NSM;;;;;N;;;;;
|
||||
1D185;MUSICAL SYMBOL COMBINING DOIT;Mn;230;NSM;;;;;N;;;;;
|
||||
1D186;MUSICAL SYMBOL COMBINING RIP;Mn;230;NSM;;;;;N;;;;;
|
||||
1D187;MUSICAL SYMBOL COMBINING FLIP;Mn;230;NSM;;;;;N;;;;;
|
||||
1D188;MUSICAL SYMBOL COMBINING SMEAR;Mn;230;NSM;;;;;N;;;;;
|
||||
1D189;MUSICAL SYMBOL COMBINING BEND;Mn;230;NSM;;;;;N;;;;;
|
||||
1D1AA;MUSICAL SYMBOL COMBINING DOWN BOW;Mn;230;NSM;;;;;N;;;;;
|
||||
1D1AB;MUSICAL SYMBOL COMBINING UP BOW;Mn;230;NSM;;;;;N;;;;;
|
||||
1D1AC;MUSICAL SYMBOL COMBINING HARMONIC;Mn;230;NSM;;;;;N;;;;;
|
||||
1D1AD;MUSICAL SYMBOL COMBINING SNAP PIZZICATO;Mn;230;NSM;;;;;N;;;;;
|
||||
1D242;COMBINING GREEK MUSICAL TRISEME;Mn;230;NSM;;;;;N;;;;;
|
||||
1D243;COMBINING GREEK MUSICAL TETRASEME;Mn;230;NSM;;;;;N;;;;;
|
||||
1D244;COMBINING GREEK MUSICAL PENTASEME;Mn;230;NSM;;;;;N;;;;;
|
Loading…
Reference in New Issue
Block a user