Graphics protocol: Add a control to allow clients to specify that the cursor should not move when displaying an image
Fixes #3411
This commit is contained in:
parent
ba32e481ca
commit
83bbcf0aa1
@ -102,6 +102,9 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
|
|||||||
- Improve rendering of rounded corners by using a rectircle equation rather
|
- Improve rendering of rounded corners by using a rectircle equation rather
|
||||||
than a cubic bezier (:iss:`3409`)
|
than a cubic bezier (:iss:`3409`)
|
||||||
|
|
||||||
|
- Graphics protocol: Add a control to allow clients to specify that the cursor
|
||||||
|
should not move when displaying an image (:iss:`3411`)
|
||||||
|
|
||||||
|
|
||||||
0.19.3 [2020-12-19]
|
0.19.3 [2020-12-19]
|
||||||
-------------------
|
-------------------
|
||||||
|
|||||||
@ -401,6 +401,12 @@ colors.
|
|||||||
number of rows in the image placement rectangle. If either of these cause
|
number of rows in the image placement rectangle. If either of these cause
|
||||||
the cursor to leave either the screen or the scroll area, the exact
|
the cursor to leave either the screen or the scroll area, the exact
|
||||||
positioning of the cursor is undefined, and up to implementations.
|
positioning of the cursor is undefined, and up to implementations.
|
||||||
|
The client can ask the terminal emulator to not move the cursor at all
|
||||||
|
by specifying ``C=1`` in the command, which sets the cursor movement policy
|
||||||
|
to no movement for placing the current image.
|
||||||
|
|
||||||
|
.. versionadded:: 0.20.0
|
||||||
|
Support for the C=1 cursor movement policy
|
||||||
|
|
||||||
|
|
||||||
Deleting images
|
Deleting images
|
||||||
@ -680,6 +686,8 @@ Key Value Default Description
|
|||||||
``Y`` Positive integer ``0`` The y-offset within the first cell at which to start displaying the image
|
``Y`` Positive integer ``0`` The y-offset within the first cell at which to start displaying the image
|
||||||
``c`` Positive integer ``0`` The number of columns to display the image over
|
``c`` Positive integer ``0`` The number of columns to display the image over
|
||||||
``r`` Positive integer ``0`` The number of rows to display the image over
|
``r`` Positive integer ``0`` The number of rows to display the image over
|
||||||
|
``C`` Positive integer ``0`` Cursor movement policy. ``0`` is the default, to move the cursor after the image.
|
||||||
|
``1`` is to not move the cursor at all when placing images.
|
||||||
``z`` 32-bit integer ``0`` The *z-index* vertical stacking order of the image
|
``z`` 32-bit integer ``0`` The *z-index* vertical stacking order of the image
|
||||||
|
|
||||||
**Keys for animation frame loading**
|
**Keys for animation frame loading**
|
||||||
|
|||||||
@ -273,6 +273,7 @@ def graphics_parser() -> None:
|
|||||||
'X': ('cell_x_offset', 'uint'),
|
'X': ('cell_x_offset', 'uint'),
|
||||||
'Y': ('cell_y_offset', 'uint'),
|
'Y': ('cell_y_offset', 'uint'),
|
||||||
'z': ('z_index', 'int'),
|
'z': ('z_index', 'int'),
|
||||||
|
'C': ('cursor_movement', 'uint'),
|
||||||
}
|
}
|
||||||
text = generate('parse_graphics_code', 'screen_handle_graphics_command', 'graphics_command', keymap, 'GraphicsCommand')
|
text = generate('parse_graphics_code', 'screen_handle_graphics_command', 'graphics_command', keymap, 'GraphicsCommand')
|
||||||
write_header(text, 'kitty/parse-graphics-command.h')
|
write_header(text, 'kitty/parse-graphics-command.h')
|
||||||
|
|||||||
@ -699,7 +699,9 @@ handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, b
|
|||||||
update_src_rect(ref, img);
|
update_src_rect(ref, img);
|
||||||
update_dest_rect(ref, g->num_cells, g->num_lines, cell);
|
update_dest_rect(ref, g->num_cells, g->num_lines, cell);
|
||||||
// Move the cursor, the screen will take care of ensuring it is in bounds
|
// Move the cursor, the screen will take care of ensuring it is in bounds
|
||||||
c->x += ref->effective_num_cols; c->y += ref->effective_num_rows - 1;
|
if (g->cursor_movement != 1) {
|
||||||
|
c->x += ref->effective_num_cols; c->y += ref->effective_num_rows - 1;
|
||||||
|
}
|
||||||
return img->client_id;
|
return img->client_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned char action, transmission_type, compressed, delete_action;
|
unsigned char action, transmission_type, compressed, delete_action;
|
||||||
uint32_t format, more, id, image_number, data_sz, data_offset, placement_id, quiet;
|
uint32_t format, more, id, image_number, data_sz, data_offset, placement_id, quiet, cursor_movement;
|
||||||
uint32_t width, height, x_offset, y_offset, data_height, data_width, num_cells, num_lines, cell_x_offset, cell_y_offset;
|
uint32_t width, height, x_offset, y_offset, data_height, data_width, num_cells, num_lines, cell_x_offset, cell_y_offset;
|
||||||
int32_t z_index;
|
int32_t z_index;
|
||||||
size_t payload_sz;
|
size_t payload_sz;
|
||||||
|
|||||||
46
kitty/parse-graphics-command.h
generated
46
kitty/parse-graphics-command.h
generated
@ -39,7 +39,8 @@ static inline void parse_graphics_code(Screen *screen,
|
|||||||
num_lines = 'r',
|
num_lines = 'r',
|
||||||
cell_x_offset = 'X',
|
cell_x_offset = 'X',
|
||||||
cell_y_offset = 'Y',
|
cell_y_offset = 'Y',
|
||||||
z_index = 'z'
|
z_index = 'z',
|
||||||
|
cursor_movement = 'C'
|
||||||
};
|
};
|
||||||
|
|
||||||
enum KEYS key = 'a';
|
enum KEYS key = 'a';
|
||||||
@ -121,6 +122,9 @@ static inline void parse_graphics_code(Screen *screen,
|
|||||||
case z_index:
|
case z_index:
|
||||||
value_state = INT;
|
value_state = INT;
|
||||||
break;
|
break;
|
||||||
|
case cursor_movement:
|
||||||
|
value_state = UINT;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
REPORT_ERROR("Malformed GraphicsCommand control block, invalid key "
|
REPORT_ERROR("Malformed GraphicsCommand control block, invalid key "
|
||||||
"character: 0x%x",
|
"character: 0x%x",
|
||||||
@ -144,9 +148,9 @@ static inline void parse_graphics_code(Screen *screen,
|
|||||||
|
|
||||||
case action: {
|
case action: {
|
||||||
g.action = screen->parser_buf[pos++] & 0xff;
|
g.action = screen->parser_buf[pos++] & 0xff;
|
||||||
if (g.action != 'q' && g.action != 'd' && g.action != 'p' &&
|
if (g.action != 't' && g.action != 'a' && g.action != 'T' &&
|
||||||
g.action != 't' && g.action != 'T' && g.action != 'f' &&
|
g.action != 'f' && g.action != 'd' && g.action != 'p' &&
|
||||||
g.action != 'a') {
|
g.action != 'q') {
|
||||||
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
|
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
|
||||||
"value for action: 0x%x",
|
"value for action: 0x%x",
|
||||||
g.action);
|
g.action);
|
||||||
@ -156,16 +160,16 @@ static inline void parse_graphics_code(Screen *screen,
|
|||||||
|
|
||||||
case delete_action: {
|
case delete_action: {
|
||||||
g.delete_action = screen->parser_buf[pos++] & 0xff;
|
g.delete_action = screen->parser_buf[pos++] & 0xff;
|
||||||
if (g.delete_action != 'F' && g.delete_action != 'n' &&
|
if (g.delete_action != 'Q' && g.delete_action != 'N' &&
|
||||||
g.delete_action != 'x' && g.delete_action != 'I' &&
|
g.delete_action != 'z' && g.delete_action != 'p' &&
|
||||||
g.delete_action != 'p' && g.delete_action != 'i' &&
|
g.delete_action != 'X' && g.delete_action != 'a' &&
|
||||||
g.delete_action != 'C' && g.delete_action != 'Q' &&
|
g.delete_action != 'y' && g.delete_action != 'f' &&
|
||||||
g.delete_action != 'A' && g.delete_action != 'z' &&
|
g.delete_action != 'Z' && g.delete_action != 'F' &&
|
||||||
g.delete_action != 'y' && g.delete_action != 'c' &&
|
g.delete_action != 'Y' && g.delete_action != 'n' &&
|
||||||
g.delete_action != 'Z' && g.delete_action != 'N' &&
|
g.delete_action != 'C' && g.delete_action != 'q' &&
|
||||||
g.delete_action != 'X' && g.delete_action != 'q' &&
|
g.delete_action != 'c' && g.delete_action != 'i' &&
|
||||||
g.delete_action != 'P' && g.delete_action != 'f' &&
|
g.delete_action != 'A' && g.delete_action != 'P' &&
|
||||||
g.delete_action != 'Y' && g.delete_action != 'a') {
|
g.delete_action != 'I' && g.delete_action != 'x') {
|
||||||
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
|
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
|
||||||
"value for delete_action: 0x%x",
|
"value for delete_action: 0x%x",
|
||||||
g.delete_action);
|
g.delete_action);
|
||||||
@ -175,8 +179,8 @@ static inline void parse_graphics_code(Screen *screen,
|
|||||||
|
|
||||||
case transmission_type: {
|
case transmission_type: {
|
||||||
g.transmission_type = screen->parser_buf[pos++] & 0xff;
|
g.transmission_type = screen->parser_buf[pos++] & 0xff;
|
||||||
if (g.transmission_type != 's' && g.transmission_type != 'f' &&
|
if (g.transmission_type != 's' && g.transmission_type != 't' &&
|
||||||
g.transmission_type != 'd' && g.transmission_type != 't') {
|
g.transmission_type != 'd' && g.transmission_type != 'f') {
|
||||||
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
|
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
|
||||||
"value for transmission_type: 0x%x",
|
"value for transmission_type: 0x%x",
|
||||||
g.transmission_type);
|
g.transmission_type);
|
||||||
@ -264,6 +268,7 @@ static inline void parse_graphics_code(Screen *screen,
|
|||||||
U(num_lines);
|
U(num_lines);
|
||||||
U(cell_x_offset);
|
U(cell_x_offset);
|
||||||
U(cell_y_offset);
|
U(cell_y_offset);
|
||||||
|
U(cursor_movement);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -322,8 +327,8 @@ static inline void parse_graphics_code(Screen *screen,
|
|||||||
}
|
}
|
||||||
|
|
||||||
REPORT_VA_COMMAND(
|
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 "
|
"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} y#",
|
"si sI} y#",
|
||||||
"graphics_command", "action", g.action, "delete_action", g.delete_action,
|
"graphics_command", "action", g.action, "delete_action", g.delete_action,
|
||||||
"transmission_type", g.transmission_type, "compressed", g.compressed,
|
"transmission_type", g.transmission_type, "compressed", g.compressed,
|
||||||
"format", (unsigned int)g.format, "more", (unsigned int)g.more, "id",
|
"format", (unsigned int)g.format, "more", (unsigned int)g.more, "id",
|
||||||
@ -336,8 +341,9 @@ static inline void parse_graphics_code(Screen *screen,
|
|||||||
(unsigned int)g.data_sz, "data_offset", (unsigned int)g.data_offset,
|
(unsigned int)g.data_sz, "data_offset", (unsigned int)g.data_offset,
|
||||||
"num_cells", (unsigned int)g.num_cells, "num_lines",
|
"num_cells", (unsigned int)g.num_cells, "num_lines",
|
||||||
(unsigned int)g.num_lines, "cell_x_offset", (unsigned int)g.cell_x_offset,
|
(unsigned int)g.num_lines, "cell_x_offset", (unsigned int)g.cell_x_offset,
|
||||||
"cell_y_offset", (unsigned int)g.cell_y_offset, "z_index", (int)g.z_index,
|
"cell_y_offset", (unsigned int)g.cell_y_offset, "cursor_movement",
|
||||||
"payload_sz", g.payload_sz, payload, g.payload_sz);
|
(unsigned int)g.cursor_movement, "z_index", (int)g.z_index, "payload_sz",
|
||||||
|
g.payload_sz, payload, g.payload_sz);
|
||||||
|
|
||||||
screen_handle_graphics_command(screen, &g, payload);
|
screen_handle_graphics_command(screen, &g, payload);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -124,9 +124,15 @@ def put_helpers(self, cw, ch):
|
|||||||
s = self.create_screen(10, 5, cell_width=cw, cell_height=ch)
|
s = self.create_screen(10, 5, cell_width=cw, cell_height=ch)
|
||||||
return s, 2 / s.columns, 2 / s.lines
|
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):
|
def put_cmd(
|
||||||
return 'z=%d,c=%d,r=%d,x=%d,y=%d,w=%d,h=%d,X=%d,Y=%d,p=%d' % (
|
z=0, num_cols=0, num_lines=0, x_off=0, y_off=0, width=0,
|
||||||
z, num_cols, num_lines, x_off, y_off, width, height, cell_x_off, cell_y_off, placement_id)
|
height=0, cell_x_off=0, cell_y_off=0, placement_id=0,
|
||||||
|
cursor_movement=0
|
||||||
|
):
|
||||||
|
return 'z=%d,c=%d,r=%d,x=%d,y=%d,w=%d,h=%d,X=%d,Y=%d,p=%d,C=%d' % (
|
||||||
|
z, num_cols, num_lines, x_off, y_off, width, height, cell_x_off,
|
||||||
|
cell_y_off, placement_id, cursor_movement
|
||||||
|
)
|
||||||
|
|
||||||
def put_image(screen, w, h, **kw):
|
def put_image(screen, w, h, **kw):
|
||||||
nonlocal iid
|
nonlocal iid
|
||||||
@ -510,6 +516,8 @@ class TestGraphics(BaseTest):
|
|||||||
rect_eq(l2[1]['dest_rect'], -1, 1, -1 + dx, 1 - dy)
|
rect_eq(l2[1]['dest_rect'], -1, 1, -1 + dx, 1 - dy)
|
||||||
self.ae(l2[0]['group_count'], 1), self.ae(l2[1]['group_count'], 1)
|
self.ae(l2[0]['group_count'], 1), self.ae(l2[1]['group_count'], 1)
|
||||||
self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 1)
|
self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 1)
|
||||||
|
self.ae(put_image(s, 10, 20, cursor_movement=1)[1], 'OK')
|
||||||
|
self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 1)
|
||||||
s.reset()
|
s.reset()
|
||||||
self.assertEqual(s.grman.disk_cache.total_size, 0)
|
self.assertEqual(s.grman.disk_cache.total_size, 0)
|
||||||
|
|
||||||
|
|||||||
@ -381,7 +381,7 @@ class TestParser(BaseTest):
|
|||||||
k[p] = v.encode('ascii')
|
k[p] = v.encode('ascii')
|
||||||
for f in 'action delete_action transmission_type compressed'.split():
|
for f in 'action delete_action transmission_type compressed'.split():
|
||||||
k.setdefault(f, b'\0')
|
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'
|
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').split():
|
||||||
k.setdefault(f, 0)
|
k.setdefault(f, 0)
|
||||||
p = k.pop('payload', '').encode('utf-8')
|
p = k.pop('payload', '').encode('utf-8')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user