Implement animation control escape codes

This commit is contained in:
Kovid Goyal 2021-01-28 17:32:13 +05:30
parent b35084062d
commit 5eba754c60
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 57 additions and 9 deletions

View File

@ -554,6 +554,15 @@ Key Value Default Description
``z`` 32-bit integer ``0`` The gap (in milliseconds) of this frame from the previous one. Values less than
one are ignored, new frames are given a default gap of ``40ms`` if not specified.
**Keys for animation control**
-----------------------------------------------------------
``s`` Positive integer ``0`` ``1`` - start animation, ``>1`` - stop animation
``r`` Positive integer ``0`` The 1-based frame number of the frame that is being affected
``z`` 32-bit integer ``0`` The gap (in milliseconds) of this frame from the previous one. Values less than
one are ignored
``c`` Positive integer ``0`` The 1-based frame number of the frame that should be made the current frame
**Keys for deleting images**
-----------------------------------------------------------
``d`` Single character. ``a`` What to delete.

View File

@ -778,6 +778,7 @@ grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float scree
#define _frame_number num_lines
#define _other_frame_number num_cells
#define _gap z_index
#define _animation_enabled data_width
static Image*
handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, Image *img, const uint8_t *payload) {
@ -923,6 +924,27 @@ handle_delete_frame_command(GraphicsManager *self, const GraphicsCommand *g, boo
img->extra_framecnt--;
return NULL;
}
static void
handle_animation_control_command(bool *is_dirty, const GraphicsCommand *g, Image *img) {
if (g->_frame_number) {
uint32_t frame_idx = g->_frame_number - 1;
if (frame_idx <= img->extra_framecnt) {
Frame *f = frame_idx ? img->extra_frames + frame_idx - 1 : &img->root_frame;
if (g->_gap > 0) f->gap = g->_gap;
}
}
if (g->_other_frame_number) {
uint32_t frame_idx = g->_other_frame_number - 1;
if (frame_idx != img->current_frame_index && frame_idx <= img->extra_framecnt) {
img->current_frame_index = frame_idx;
*is_dirty = true;
}
}
if (g->_animation_enabled) {
img->animation_enabled = g->_animation_enabled == 1;
}
}
// }}}
// Image lifetime/scrolling {{{
@ -1181,6 +1203,8 @@ grman_handle_command(GraphicsManager *self, const GraphicsCommand *g, const uint
if (ag.action == 'f') {
img = handle_animation_frame_load_command(self, &ag, img, payload);
ret = finish_command_response(&ag, img != NULL);
} else if (ag.action == 'a') {
handle_animation_control_command(is_dirty, &ag, img);
}
}
break;
@ -1217,24 +1241,23 @@ new(PyTypeObject UNUSED *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
static inline PyObject*
image_as_dict(GraphicsManager *self, Image *img) {
#define U(x) #x, img->x
#define B(x) #x, img->x ? Py_True : Py_False
ImageAndFrame key = {.image_id = img->internal_id};
PyObject *frames = PyTuple_New(img->extra_framecnt);
for (unsigned i = 0; i < img->extra_framecnt; i++) {
key.frame_id = img->extra_frames[i].id;
PyTuple_SET_ITEM(frames, i, Py_BuildValue(
"{sI sI sN}", "gap", img->extra_frames[i].gap, "id", key.frame_id, "data", read_from_cache_python(self, key)));
if (PyErr_Occurred()) return NULL;
if (PyErr_Occurred()) { Py_CLEAR(frames); return NULL; }
}
key.frame_id = img->root_frame.id;
return Py_BuildValue("{sI sI sI sI sK sI sI sO sO sI sI sO sN sN}",
return Py_BuildValue("{sI sI sI sI sK sI sI sO sO sO sI sI sI sN sN}",
U(texture_id), U(client_id), U(width), U(height), U(internal_id), U(refcnt), U(client_number),
"data_loaded", img->data_loaded ? Py_True : Py_False,
"is_4byte_aligned", img->is_4byte_aligned ? Py_True : Py_False,
U(current_frame_index), "root_frame_gap", img->root_frame.gap,
"animation_enabled", img->animation_enabled ? Py_True : Py_False,
"data", read_from_cache_python(self, key),
"extra_frames", frames
B(data_loaded), B(is_4byte_aligned), B(animation_enabled),
U(current_frame_index), "root_frame_gap", img->root_frame.gap, U(current_frame_index),
"data", read_from_cache_python(self, key), "extra_frames", frames
);
#undef B
#undef U
}

View File

@ -635,11 +635,27 @@ class TestGraphics(BaseTest):
{'gap': 40, 'id': 2, 'data': b'3' * 36},
{'gap': 101, 'id': 3, 'data': b'444444333333444444333333333333333333'},
))
# test changing gaps
img = g.image_for_client_id(1)
self.assertEqual(img['root_frame_gap'], 40)
self.assertIsNone(li(a='a', i=1, r=1, z=13))
img = g.image_for_client_id(1)
self.assertEqual(img['root_frame_gap'], 13)
self.assertIsNone(li(a='a', i=1, r=2, z=43))
img = g.image_for_client_id(1)
self.assertEqual(img['extra_frames'][0]['gap'], 43)
# test changing current frame
img = g.image_for_client_id(1)
self.assertEqual(img['current_frame_index'], 0)
self.assertIsNone(li(a='a', i=1, c=2))
img = g.image_for_client_id(1)
self.assertEqual(img['current_frame_index'], 1)
# test delete of frames
t(payload='5' * 36, frame_number=4)
img = g.image_for_client_id(1)
self.assertEqual(img['extra_frames'], (
{'gap': 40, 'id': 2, 'data': b'3' * 36},
{'gap': 43, 'id': 2, 'data': b'3' * 36},
{'gap': 101, 'id': 3, 'data': b'444444333333444444333333333333333333'},
{'gap': 40, 'id': 4, 'data': b'5' * 36},
))