mirror of
https://github.com/kovidgoyal/kitty.git
synced 2024-11-11 01:28:19 +03:00
Implement animation control escape codes
This commit is contained in:
parent
b35084062d
commit
5eba754c60
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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},
|
||||
))
|
||||
|
Loading…
Reference in New Issue
Block a user