Graphics protocol: Add support for giving individual image placements their

Fixes #3133
This commit is contained in:
Kovid Goyal 2020-12-02 05:23:08 +05:30
parent 0173959e64
commit b5e704a934
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
8 changed files with 116 additions and 53 deletions

View File

@ -13,6 +13,9 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
- Add a new mappable `select_tab` action to choose a tab to switch to even - Add a new mappable `select_tab` action to choose a tab to switch to even
when the tab bar is hidden (:iss:`3115`) when the tab bar is hidden (:iss:`3115`)
- Graphics protocol: Add support for giving individual image placements their
own ids. This is a backwards compatible protocol extension. (:iss:`3133`)
- Distribute extra pixels among all eight-blocks rather than adding them - Distribute extra pixels among all eight-blocks rather than adding them
all to the last block (:iss:`3097`) all to the last block (:iss:`3097`)

View File

@ -344,6 +344,25 @@ scheme described above for querying available transmission media, except that
here we are querying if the image with the specified id is available or needs to here we are querying if the image with the specified id is available or needs to
be re-transmitted. be re-transmitted.
Since there can be many placements per image, you can also give placements an
id. To do so add the ``p`` key with a number between ``1`` and ``4294967295``.
When you specify a placement id, it will be added to the acknowledgement code
above. Every placement is uniquely identified by the pair of the ``image id``
and the ``placement id``. If you specify a placement id for an image that does
not have an id, it will be ignored. An example response::
<ESC>_Gi=<image id>,p=<placement id>;OK<ESC>\
If you send two placements with the same ``image id`` and ``placement id`` the
second one will replace the first. This can be used to resize or move
placements around the screen, without flicker.
.. note:: Support for specifying placement ids was added to kitty in
versions after 0.19.2. You can use the protocol documented in the
:doc:`kittens/query_terminal` to query kitty version.
Controlling displayed image layout Controlling displayed image layout
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -386,24 +405,30 @@ scrollback buffer. The values of the ``x`` and ``y`` keys are the same as cursor
================= ============ ================= ============
Value of ``d`` Meaning Value of ``d`` Meaning
================= ============ ================= ============
``a`` or ``A`` Delete all images visible on screen ``a`` or ``A`` Delete all placements visible on screen
``i`` or ``I`` Delete all images with the specified id, specified using the ``i`` key. ``i`` or ``I`` Delete all images with the specified id, specified using the ``i`` key. If you specify a ``p`` key for the placement id as well, then only the placement with the specified image id and placement id will be deleted.
``c`` or ``C`` Delete all images that intersect with the current cursor position. placement id
``p`` or ``P`` Delete all images that intersect a specific cell, the cell is specified using the ``x`` and ``y`` keys ``c`` or ``C`` Delete all placements that intersect with the current cursor position.
``q`` or ``Q`` Delete all images that intersect a specific cell having a specific z-index. The cell and z-index is specified using the ``x``, ``y`` and ``z`` keys. ``p`` or ``P`` Delete all placements that intersect a specific cell, the cell is specified using the ``x`` and ``y`` keys
``x`` or ``X`` Delete all images that intersect the specified column, specified using the ``x`` key. ``q`` or ``Q`` Delete all placements that intersect a specific cell having a specific z-index. The cell and z-index is specified using the ``x``, ``y`` and ``z`` keys.
``y`` or ``Y`` Delete all images that intersect the specified row, specified using the ``y`` key. ``x`` or ``X`` Delete all placements that intersect the specified column, specified using the ``x`` key.
``z`` or ``Z`` Delete all images that have the specified z-index, specified using the ``z`` key. ``y`` or ``Y`` Delete all placements that intersect the specified row, specified using the ``y`` key.
``z`` or ``Z`` Delete all placements that have the specified z-index, specified using the ``z`` key.
================= ============ ================= ============
Note when all placements for an image have been deleted, the image is also
deleted, if the capital letter form above is specified. Also, when the terminal
is running out of quota space for image, images without placements will be
preferentially deleted.
Some examples:: Some examples::
<ESC>_Ga=d<ESC>\ # delete all visible images <ESC>_Ga=d<ESC>\ # delete all visible placements
<ESC>_Ga=d,d=i,i=10<ESC>\ # delete the image with id=10, without freeing data <ESC>_Ga=d,d=i,i=10<ESC>\ # delete the image with id=10, without freeing data
<ESC>_Ga=d,d=Z,z=-1<ESC>\ # delete the images with z-index -1, also freeing up image data <ESC>_Ga=d,d=i,i=10,p=7<ESC>\ # delete the image with id=10 and placement id=7, without freeing data
<ESC>_Ga=d,d=p,x=3,y=4<ESC>\ # delete all images that intersect the cell at (3, 4), without freeing data <ESC>_Ga=d,d=Z,z=-1<ESC>\ # delete the placements with z-index -1, also freeing up image data
<ESC>_Ga=d,d=p,x=3,y=4<ESC>\ # delete all placements that intersect the cell at (3, 4), without freeing data
Image persistence and storage quotas Image persistence and storage quotas
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -438,6 +463,8 @@ Key Value Default Description
``O`` Positive integer. ``0`` The offset from which to read data from a file. ``O`` Positive integer. ``0`` The offset from which to read data from a file.
``i`` Positive integer. ``i`` Positive integer.
``(0 - 4294967295)`` ``0`` The image id ``(0 - 4294967295)`` ``0`` The image id
``p`` Positive integer.
``(0 - 4294967295)`` ``0`` The placement id
``o`` Single character. ``null`` The type of data compression. ``o`` Single character. ``null`` The type of data compression.
``only z`` ``only z``
``m`` zero or one ``0`` Whether there is more chunked data available. ``m`` zero or one ``0`` Whether there is more chunked data available.

View File

@ -257,6 +257,7 @@ def graphics_parser() -> None:
'f': ('format', 'uint'), 'f': ('format', 'uint'),
'm': ('more', 'uint'), 'm': ('more', 'uint'),
'i': ('id', 'uint'), 'i': ('id', 'uint'),
'p': ('placement_id', 'uint'),
'w': ('width', 'uint'), 'w': ('width', 'uint'),
'h': ('height', 'uint'), 'h': ('height', 'uint'),
'x': ('x_offset', 'uint'), 'x': ('x_offset', 'uint'),

View File

@ -475,14 +475,15 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
} }
static inline const char* static inline const char*
create_add_response(GraphicsManager UNUSED *self, bool data_loaded, uint32_t iid) { create_add_response(GraphicsManager UNUSED *self, bool data_loaded, uint32_t iid, uint32_t placement_id) {
static char rbuf[sizeof(add_response)/sizeof(add_response[0]) + 64]; static char rbuf[sizeof(add_response)/sizeof(add_response[0]) + 128];
if (iid) { if (iid) {
if (!has_add_respose) { if (!has_add_respose) {
if (!data_loaded) return NULL; if (!data_loaded) return NULL;
snprintf(add_response, 10, "OK"); snprintf(add_response, 10, "OK");
} }
snprintf(rbuf, sizeof(rbuf)/sizeof(rbuf[0]) - 1, "Gi=%u;%s", iid, add_response); if (placement_id) snprintf(rbuf, sizeof(rbuf)/sizeof(rbuf[0]) - 1, "Gi=%u,p=%u;%s", iid, placement_id, add_response);
else snprintf(rbuf, sizeof(rbuf)/sizeof(rbuf[0]) - 1, "Gi=%u;%s", iid, add_response);
return rbuf; return rbuf;
} }
return NULL; return NULL;
@ -529,12 +530,14 @@ handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, b
*is_dirty = true; *is_dirty = true;
self->layers_dirty = true; self->layers_dirty = true;
ImageRef *ref = NULL; ImageRef *ref = NULL;
if (g->placement_id && img->client_id) {
for (size_t i=0; i < img->refcnt; i++) { for (size_t i=0; i < img->refcnt; i++) {
if ((unsigned)img->refs[i].start_row == c->x && (unsigned)img->refs[i].start_column == c->y) { if (img->refs[i].client_id == g->placement_id) {
ref = img->refs + i; ref = img->refs + i;
break; break;
} }
} }
}
if (ref == NULL) { if (ref == NULL) {
ref = img->refs + img->refcnt++; ref = img->refs + img->refcnt++;
zero_at_ptr(ref); zero_at_ptr(ref);
@ -548,6 +551,7 @@ handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, b
ref->cell_x_offset = MIN(g->cell_x_offset, cell.width - 1); ref->cell_x_offset = MIN(g->cell_x_offset, cell.width - 1);
ref->cell_y_offset = MIN(g->cell_y_offset, cell.height - 1); ref->cell_y_offset = MIN(g->cell_y_offset, cell.height - 1);
ref->num_cols = g->num_cells; ref->num_rows = g->num_lines; ref->num_cols = g->num_cells; ref->num_rows = g->num_lines;
if (img->client_id) ref->client_id = g->placement_id;
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
@ -743,9 +747,10 @@ grman_clear(GraphicsManager *self, bool all, CellPixelSize cell) {
} }
static inline bool static inline bool
id_filter_func(const ImageRef UNUSED *ref, Image *img, const void *data, CellPixelSize cell UNUSED) { id_filter_func(const ImageRef *ref, Image *img, const void *data, CellPixelSize cell UNUSED) {
uint32_t iid = *(uint32_t*)data; const GraphicsCommand *g = data;
return img->client_id == iid; if (img->client_id == g->id) return !g->placement_id || ref->client_id == g->placement_id;
return false;
} }
static inline bool static inline bool
@ -787,7 +792,7 @@ handle_delete_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c
#define G(l, u, func) D(l, u, g, func) #define G(l, u, func) D(l, u, g, func)
case 0: case 0:
D('a', 'A', NULL, clear_filter_func); D('a', 'A', NULL, clear_filter_func);
D('i', 'I', &g->id, id_filter_func); G('i', 'I', id_filter_func);
G('p', 'P', point_filter_func); G('p', 'P', point_filter_func);
G('q', 'Q', point3d_filter_func); G('q', 'Q', point3d_filter_func);
G('x', 'X', x_filter_func); G('x', 'X', x_filter_func);
@ -839,9 +844,11 @@ grman_handle_command(GraphicsManager *self, const GraphicsCommand *g, const uint
case 'T': case 'T':
case 'q': { case 'q': {
uint32_t iid = g->id, q_iid = iid; uint32_t iid = g->id, q_iid = iid;
if (g->action == 'q') { iid = 0; if (!q_iid) { REPORT_ERROR("Query graphics command without image id"); break; } } bool is_query = g->action == 'q';
if (is_query) { iid = 0; if (!q_iid) { REPORT_ERROR("Query graphics command without image id"); break; } }
Image *image = handle_add_command(self, g, payload, is_dirty, iid); Image *image = handle_add_command(self, g, payload, is_dirty, iid);
ret = create_add_response(self, image != NULL, g->action == 'q' ? q_iid: self->last_init_graphics_command.id); if (is_query) ret = create_add_response(self, image != NULL, q_iid, 0);
else ret = create_add_response(self, image != NULL, self->last_init_graphics_command.id, self->last_init_graphics_command.placement_id);
if (self->last_init_graphics_command.action == 'T' && image && image->data_loaded) handle_put_command(self, &self->last_init_graphics_command, c, is_dirty, image, cell); if (self->last_init_graphics_command.action == 'T' && image && image->data_loaded) handle_put_command(self, &self->last_init_graphics_command, c, is_dirty, image, cell);
id_type added_image_id = image ? image->internal_id : 0; id_type added_image_id = image ? image->internal_id : 0;
if (g->action == 'q') remove_images(self, add_trim_predicate, 0); if (g->action == 'q') remove_images(self, add_trim_predicate, 0);
@ -854,7 +861,7 @@ grman_handle_command(GraphicsManager *self, const GraphicsCommand *g, const uint
break; break;
} }
handle_put_command(self, g, c, is_dirty, NULL, cell); handle_put_command(self, g, c, is_dirty, NULL, cell);
ret = create_add_response(self, true, g->id); ret = create_add_response(self, true, g->id, g->placement_id);
break; break;
case 'd': case 'd':
handle_delete_command(self, g, c, is_dirty, cell); handle_delete_command(self, g, c, is_dirty, cell);

View File

@ -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, data_sz, data_offset; uint32_t format, more, id, data_sz, data_offset, placement_id;
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;
@ -38,6 +38,7 @@ typedef struct {
uint32_t cell_x_offset, cell_y_offset, num_cols, num_rows, effective_num_rows, effective_num_cols; uint32_t cell_x_offset, cell_y_offset, num_cols, num_rows, effective_num_rows, effective_num_cols;
int32_t z_index; int32_t z_index;
int32_t start_row, start_column; int32_t start_row, start_column;
uint32_t client_id;
ImageRect src_rect; ImageRect src_rect;
} ImageRef; } ImageRef;

View File

@ -23,6 +23,7 @@ static inline void parse_graphics_code(Screen *screen,
format = 'f', format = 'f',
more = 'm', more = 'm',
id = 'i', id = 'i',
placement_id = 'p',
width = 'w', width = 'w',
height = 'h', height = 'h',
x_offset = 'x', x_offset = 'x',
@ -69,6 +70,9 @@ static inline void parse_graphics_code(Screen *screen,
case id: case id:
value_state = UINT; value_state = UINT;
break; break;
case placement_id:
value_state = UINT;
break;
case width: case width:
value_state = UINT; value_state = UINT;
break; break;
@ -131,8 +135,8 @@ 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 != 't' && g.action != 'd' && g.action != 'p' && if (g.action != 'p' && g.action != 'q' && g.action != 't' &&
g.action != 'q' && g.action != 'T') { g.action != 'T' && g.action != 'd') {
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);
@ -142,14 +146,14 @@ 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 != 'X' && g.delete_action != 'y' && if (g.delete_action != 'p' && g.delete_action != 'q' &&
g.delete_action != 'Z' && g.delete_action != 'x' &&
g.delete_action != 'Q' && g.delete_action != 'C' &&
g.delete_action != 'y' && g.delete_action != 'Y' &&
g.delete_action != 'i' && g.delete_action != 'I' && g.delete_action != 'i' && g.delete_action != 'I' &&
g.delete_action != 'A' && g.delete_action != 'p' && g.delete_action != 'a' && g.delete_action != 'z' &&
g.delete_action != 'Y' && g.delete_action != 'z' && g.delete_action != 'A' && g.delete_action != 'X' &&
g.delete_action != 'a' && g.delete_action != 'P' && g.delete_action != 'P' && g.delete_action != 'c') {
g.delete_action != 'x' && g.delete_action != 'q' &&
g.delete_action != 'Z' && g.delete_action != 'Q' &&
g.delete_action != 'c' && g.delete_action != 'C') {
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);
@ -159,8 +163,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 != 'f' && g.transmission_type != 'd' && if (g.transmission_type != 'd' && g.transmission_type != 'f' &&
g.transmission_type != 's' && g.transmission_type != 't') { g.transmission_type != 't' && g.transmission_type != 's') {
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);
@ -233,6 +237,7 @@ static inline void parse_graphics_code(Screen *screen,
U(format); U(format);
U(more); U(more);
U(id); U(id);
U(placement_id);
U(width); U(width);
U(height); U(height);
U(x_offset); U(x_offset);
@ -303,19 +308,21 @@ 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} y#", "s {sc sc sc sc sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI 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",
(unsigned int)g.id, "width", (unsigned int)g.width, "height", (unsigned int)g.id, "placement_id", (unsigned int)g.placement_id, "width",
(unsigned int)g.height, "x_offset", (unsigned int)g.x_offset, "y_offset", (unsigned int)g.width, "height", (unsigned int)g.height, "x_offset",
(unsigned int)g.y_offset, "data_height", (unsigned int)g.data_height, (unsigned int)g.x_offset, "y_offset", (unsigned int)g.y_offset,
"data_width", (unsigned int)g.data_width, "data_sz", "data_height", (unsigned int)g.data_height, "data_width",
(unsigned int)g.data_sz, "data_offset", (unsigned int)g.data_offset, (unsigned int)g.data_width, "data_sz", (unsigned int)g.data_sz,
"num_cells", (unsigned int)g.num_cells, "num_lines", "data_offset", (unsigned int)g.data_offset, "num_cells",
(unsigned int)g.num_lines, "cell_x_offset", (unsigned int)g.cell_x_offset, (unsigned int)g.num_cells, "num_lines", (unsigned int)g.num_lines,
"cell_y_offset", (unsigned int)g.cell_y_offset, "z_index", (int)g.z_index, "cell_x_offset", (unsigned int)g.cell_x_offset, "cell_y_offset",
"payload_sz", g.payload_sz, payload, g.payload_sz); (unsigned int)g.cell_y_offset, "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);
} }

View File

@ -48,6 +48,15 @@ def parse_response(res):
return res.decode('ascii').partition(';')[2].partition('\033')[0] return res.decode('ascii').partition(';')[2].partition('\033')[0]
def parse_response_with_ids(res):
if not res:
return
a, b = res.decode('ascii').split(';', 1)
code = b.partition('\033')[0].split(':', 1)[0]
a = a.split('G', 1)[1]
return code, a
all_bytes = bytes(bytearray(range(256))) all_bytes = bytes(bytearray(range(256)))
@ -90,8 +99,9 @@ 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): 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):
return 'z=%d,c=%d,r=%d,x=%d,y=%d,w=%d,h=%d,X=%d,Y=%d' % (z, num_cols, num_lines, x_off, y_off, width, height, cell_x_off, cell_y_off) return 'z=%d,c=%d,r=%d,x=%d,y=%d,w=%d,h=%d,X=%d,Y=%d,p=%d' % (
z, num_cols, num_lines, x_off, y_off, width, height, cell_x_off, cell_y_off, placement_id)
def put_image(screen, w, h, **kw): def put_image(screen, w, h, **kw):
nonlocal iid nonlocal iid
@ -103,7 +113,7 @@ def put_helpers(self, cw, ch):
def put_ref(screen, **kw): def put_ref(screen, **kw):
cmd = 'a=p,i=%d,%s' % (iid, put_cmd(**kw)) cmd = 'a=p,i=%d,%s' % (iid, put_cmd(**kw))
send_command(screen, cmd) return iid, parse_response_with_ids(send_command(screen, cmd))
def layers(screen, scrolled_by=0, xstart=-1, ystart=1): def layers(screen, scrolled_by=0, xstart=-1, ystart=1):
return screen.grman.update_layers(scrolled_by, xstart, ystart, dx, dy, screen.columns, screen.lines, cw, ch) return screen.grman.update_layers(scrolled_by, xstart, ystart, dx, dy, screen.columns, screen.lines, cw, ch)
@ -226,7 +236,8 @@ class TestGraphics(BaseTest):
rect_eq(l0[0]['dest_rect'], -1, 1, -1 + dx, 1 - dy) rect_eq(l0[0]['dest_rect'], -1, 1, -1 + dx, 1 - dy)
self.ae(l0[0]['group_count'], 1) self.ae(l0[0]['group_count'], 1)
self.ae(s.cursor.x, 1), self.ae(s.cursor.y, 0) self.ae(s.cursor.x, 1), self.ae(s.cursor.y, 0)
put_ref(s, num_cols=s.columns, x_off=2, y_off=1, width=3, height=5, cell_x_off=3, cell_y_off=1, z=-1) iid, (code, idstr) = put_ref(s, num_cols=s.columns, x_off=2, y_off=1, width=3, height=5, cell_x_off=3, cell_y_off=1, z=-1, placement_id=17)
self.ae(idstr, f'i={iid},p=17')
l2 = layers(s) l2 = layers(s)
self.ae(len(l2), 2) self.ae(len(l2), 2)
rect_eq(l2[0]['src_rect'], 2 / 10, 1 / 20, (2 + 3) / 10, (1 + 5)/20) rect_eq(l2[0]['src_rect'], 2 / 10, 1 / 20, (2 + 3) / 10, (1 + 5)/20)
@ -320,8 +331,13 @@ class TestGraphics(BaseTest):
delete('A') delete('A')
self.ae(s.grman.image_count, 0) self.ae(s.grman.image_count, 0)
iid = put_image(s, cw, ch)[0] iid = put_image(s, cw, ch)[0]
delete('I', i=iid, p=7)
self.ae(s.grman.image_count, 1)
delete('I', i=iid) delete('I', i=iid)
self.ae(s.grman.image_count, 0) self.ae(s.grman.image_count, 0)
iid = put_image(s, cw, ch, placement_id=9)[0]
delete('I', i=iid, p=9)
self.ae(s.grman.image_count, 0)
s.reset() s.reset()
put_image(s, cw, ch) put_image(s, cw, ch)
put_image(s, cw, ch) put_image(s, cw, ch)

View File

@ -379,7 +379,7 @@ class TestParser(BaseTest):
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'
' num_cells num_lines cell_x_offset cell_y_offset z_index').split(): ' num_cells num_lines cell_x_offset cell_y_offset z_index placement_id').split():
k.setdefault(f, 0) k.setdefault(f, 0)
p = k.pop('payload', '').encode('utf-8') p = k.pop('payload', '').encode('utf-8')
k['payload_sz'] = len(p) k['payload_sz'] = len(p)
@ -395,6 +395,7 @@ class TestParser(BaseTest):
pb = partial(self.parse_bytes_dump, s) pb = partial(self.parse_bytes_dump, s)
uint32_max = 2**32 - 1 uint32_max = 2**32 - 1
t('i=%d' % uint32_max, id=uint32_max) t('i=%d' % uint32_max, id=uint32_max)
t('i=3,p=4', id=3, placement_id=4)
e('i=%d' % (uint32_max + 1), 'Malformed GraphicsCommand control block, number is too large') e('i=%d' % (uint32_max + 1), 'Malformed GraphicsCommand control block, number is too large')
pb('\033_Gi=12\033\\', c(id=12)) pb('\033_Gi=12\033\\', c(id=12))
t('a=t,t=d,s=100,z=-9', payload='X', action='t', transmission_type='d', data_width=100, z_index=-9, payload_sz=1) t('a=t,t=d,s=100,z=-9', payload='X', action='t', transmission_type='d', data_width=100, z_index=-9, payload_sz=1)