Merge branch 'feat/10090-graphics-cache-dir' of https://github.com/Mekann2904/kitty
This commit is contained in:
commit
a2104d3755
10 changed files with 92 additions and 21 deletions
|
|
@ -1051,6 +1051,16 @@ Key Value Default Description
|
||||||
``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.
|
||||||
|
``N`` bitmask ``0`` Usage hints from the client to the terminal about the intended use of
|
||||||
|
the image. Only one hint is currently defined, the ``1`` bit which means
|
||||||
|
*transient*. The terminal is free to assume that an image with this hint
|
||||||
|
will be used for only a short time, and so may, for example, evict its
|
||||||
|
data before other images when the image is soft deleted, has no visible
|
||||||
|
placements and the terminal is under storage pressure, or skip writing
|
||||||
|
its data to disk. The terminal is also free to ignore the hint. If an
|
||||||
|
animation frame with the *transient* hint is composited onto another
|
||||||
|
frame, and any of the involved frames have the hint, the resulting
|
||||||
|
composited frame also has the hint.
|
||||||
|
|
||||||
**Keys for image display**
|
**Keys for image display**
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -313,6 +313,7 @@ def parsers() -> None:
|
||||||
'U': ('unicode_placement', 'uint'),
|
'U': ('unicode_placement', 'uint'),
|
||||||
'P': ('parent_id', 'uint'),
|
'P': ('parent_id', 'uint'),
|
||||||
'Q': ('parent_placement_id', 'uint'),
|
'Q': ('parent_placement_id', 'uint'),
|
||||||
|
'N': ('usage_hints', 'uint'),
|
||||||
'H': ('offset_from_parent_x', 'int'),
|
'H': ('offset_from_parent_x', 'int'),
|
||||||
'V': ('offset_from_parent_y', 'int'),
|
'V': ('offset_from_parent_y', 'int'),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ typedef struct CacheKey {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
size_t data_sz;
|
size_t data_sz;
|
||||||
bool written_to_disk, uses_encryption;
|
bool written_to_disk, uses_encryption, memory_only;
|
||||||
off_t pos_in_cache_file;
|
off_t pos_in_cache_file;
|
||||||
uint8_t encryption_key[64];
|
uint8_t encryption_key[64];
|
||||||
} CacheValue;
|
} CacheValue;
|
||||||
|
|
@ -593,7 +593,7 @@ create_cache_entry(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
add_to_disk_cache(PyObject *self_, const void *key, size_t key_sz, const void *data, size_t data_sz) {
|
add_to_disk_cache(PyObject *self_, const void *key, size_t key_sz, const void *data, size_t data_sz, bool memory_only) {
|
||||||
DiskCache *self = (DiskCache*)self_;
|
DiskCache *self = (DiskCache*)self_;
|
||||||
if (!ensure_state(self)) return false;
|
if (!ensure_state(self)) return false;
|
||||||
if (key_sz > MAX_KEY_SIZE) { PyErr_SetString(PyExc_KeyError, "cache key is too long"); return false; }
|
if (key_sz > MAX_KEY_SIZE) { PyErr_SetString(PyExc_KeyError, "cache key is too long"); return false; }
|
||||||
|
|
@ -618,11 +618,14 @@ add_to_disk_cache(PyObject *self_, const void *key, size_t key_sz, const void *d
|
||||||
if (s->data) free(s->data);
|
if (s->data) free(s->data);
|
||||||
}
|
}
|
||||||
s->data = copied_data; s->data_sz = data_sz; copied_data = NULL;
|
s->data = copied_data; s->data_sz = data_sz; copied_data = NULL;
|
||||||
|
s->memory_only = memory_only;
|
||||||
|
s->written_to_disk = memory_only;
|
||||||
|
if (memory_only) s->pos_in_cache_file = -1;
|
||||||
self->total_size += s->data_sz;
|
self->total_size += s->data_sz;
|
||||||
end:
|
end:
|
||||||
mutex(unlock);
|
mutex(unlock);
|
||||||
if (PyErr_Occurred()) return false;
|
if (PyErr_Occurred()) return false;
|
||||||
wakeup_write_loop(self);
|
if (!memory_only) wakeup_write_loop(self);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -740,7 +743,7 @@ disk_cache_clear_from_ram(PyObject *self_, bool(matches)(void*, void *key, unsig
|
||||||
mutex(lock);
|
mutex(lock);
|
||||||
cache_map_for_loop(i) {
|
cache_map_for_loop(i) {
|
||||||
CacheValue *s = i.data->val;
|
CacheValue *s = i.data->val;
|
||||||
if (s->written_to_disk && s->data && matches(data, i.data->key.hash_key, i.data->key.hash_keylen)) {
|
if (s->written_to_disk && !s->memory_only && s->data && matches(data, i.data->key.hash_key, i.data->key.hash_keylen)) {
|
||||||
free(s->data); s->data = NULL;
|
free(s->data); s->data = NULL;
|
||||||
ans++;
|
ans++;
|
||||||
}
|
}
|
||||||
|
|
@ -848,7 +851,7 @@ add(PyObject *self, PyObject *args) {
|
||||||
const char *key, *data;
|
const char *key, *data;
|
||||||
Py_ssize_t keylen, datalen;
|
Py_ssize_t keylen, datalen;
|
||||||
PA("y#y#", &key, &keylen, &data, &datalen);
|
PA("y#y#", &key, &keylen, &data, &datalen);
|
||||||
if (!add_to_disk_cache(self, key, keylen, data, datalen)) return NULL;
|
if (!add_to_disk_cache(self, key, keylen, data, datalen, false)) return NULL;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
#include "data-types.h"
|
#include "data-types.h"
|
||||||
|
|
||||||
PyObject* create_disk_cache(void);
|
PyObject* create_disk_cache(void);
|
||||||
bool add_to_disk_cache(PyObject *self, const void *key, size_t key_sz, const void *data, size_t data_sz);
|
bool add_to_disk_cache(PyObject *self, const void *key, size_t key_sz, const void *data, size_t data_sz, bool memory_only);
|
||||||
bool remove_from_disk_cache(PyObject *self_, const void *key, size_t key_sz);
|
bool remove_from_disk_cache(PyObject *self_, const void *key, size_t key_sz);
|
||||||
void* read_from_disk_cache(PyObject *self_, const void *key, size_t key_sz, void*(allocator)(void*, size_t), void*, bool);
|
void* read_from_disk_cache(PyObject *self_, const void *key, size_t key_sz, void*(allocator)(void*, size_t), void*, bool);
|
||||||
PyObject* read_from_disk_cache_python(PyObject *self_, const void *key, size_t key_sz, bool);
|
PyObject* read_from_disk_cache_python(PyObject *self_, const void *key, size_t key_sz, bool);
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,9 @@ cache_key(const ImageAndFrame x, char *key) {
|
||||||
#define CK(x) key, cache_key(x, key)
|
#define CK(x) key, cache_key(x, key)
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
add_to_cache(GraphicsManager *self, const ImageAndFrame x, const void *data, const size_t sz) {
|
add_to_cache(GraphicsManager *self, const ImageAndFrame x, const void *data, const size_t sz, bool memory_only) {
|
||||||
char key[CACHE_KEY_BUFFER_SIZE];
|
char key[CACHE_KEY_BUFFER_SIZE];
|
||||||
return add_to_disk_cache(self->disk_cache, CK(x), data, sz);
|
return add_to_disk_cache(self->disk_cache, CK(x), data, sz, memory_only);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -768,11 +768,12 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
|
||||||
.is_opaque = self->currently_loading.is_opaque,
|
.is_opaque = self->currently_loading.is_opaque,
|
||||||
.is_4byte_aligned = self->currently_loading.is_4byte_aligned,
|
.is_4byte_aligned = self->currently_loading.is_4byte_aligned,
|
||||||
.width = img->width, .height = img->height,
|
.width = img->width, .height = img->height,
|
||||||
|
.transient = (g->usage_hints & GRAPHICS_USAGE_HINT_TRANSIENT) != 0,
|
||||||
};
|
};
|
||||||
if (!is_query) {
|
if (!is_query) {
|
||||||
if (!add_to_cache(self, (const ImageAndFrame){.image_id = img->internal_id, .frame_id=img->root_frame.id}, self->currently_loading.data, self->currently_loading.data_sz)) {
|
if (!add_to_cache(self, (const ImageAndFrame){.image_id = img->internal_id, .frame_id=img->root_frame.id}, self->currently_loading.data, self->currently_loading.data_sz, img->root_frame.transient)) {
|
||||||
if (PyErr_Occurred()) PyErr_Print();
|
if (PyErr_Occurred()) PyErr_Print();
|
||||||
ABRT("ENOSPC", "Failed to store image data in disk cache");
|
ABRT("ENOSPC", "Failed to store image data in cache");
|
||||||
}
|
}
|
||||||
upload_to_gpu(self, img, img->root_frame.is_opaque, img->root_frame.is_4byte_aligned, self->currently_loading.data);
|
upload_to_gpu(self, img, img->root_frame.is_opaque, img->root_frame.is_4byte_aligned, self->currently_loading.data);
|
||||||
self->used_storage += required_sz;
|
self->used_storage += required_sz;
|
||||||
|
|
@ -1354,7 +1355,7 @@ change_gap(Image *img, Frame *f, int32_t gap) {
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
bool is_4byte_aligned, is_opaque;
|
bool is_4byte_aligned, is_opaque, transient;
|
||||||
} CoalescedFrameData;
|
} CoalescedFrameData;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -1450,6 +1451,7 @@ compose(const ComposeData d, uint8_t *under_data, const uint8_t *over_data) {
|
||||||
static CoalescedFrameData
|
static CoalescedFrameData
|
||||||
get_coalesced_frame_data_standalone(const Image *img, const Frame *f, uint8_t *frame_data) {
|
get_coalesced_frame_data_standalone(const Image *img, const Frame *f, uint8_t *frame_data) {
|
||||||
CoalescedFrameData ans = {0};
|
CoalescedFrameData ans = {0};
|
||||||
|
ans.transient = f->transient;
|
||||||
bool is_full_frame = f->width == img->width && f->height == img->height && !f->x && !f->y;
|
bool is_full_frame = f->width == img->width && f->height == img->height && !f->x && !f->y;
|
||||||
if (is_full_frame) {
|
if (is_full_frame) {
|
||||||
ans.buf = frame_data;
|
ans.buf = frame_data;
|
||||||
|
|
@ -1513,6 +1515,7 @@ get_coalesced_frame_data_impl(GraphicsManager *self, Image *img, const Frame *f,
|
||||||
};
|
};
|
||||||
compose(d, base_data.buf, frame_data);
|
compose(d, base_data.buf, frame_data);
|
||||||
free(frame_data);
|
free(frame_data);
|
||||||
|
base_data.transient = base_data.transient || f->transient;
|
||||||
return base_data;
|
return base_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1553,6 +1556,17 @@ reference_chain_too_large(Image *img, const Frame *frame) {
|
||||||
return num >= 5 || drawn_area >= limit;
|
return num >= 5 || drawn_area >= limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
frame_chain_is_transient(Image *img, const Frame *frame) {
|
||||||
|
// matches the recursion depth limit in get_coalesced_frame_data_impl
|
||||||
|
unsigned num = 0;
|
||||||
|
while (frame) {
|
||||||
|
if (frame->transient) return true;
|
||||||
|
if (!frame->base_frame_id || ++num > 32 || !(frame = frame_for_id(img, frame->base_frame_id))) break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static Image*
|
static Image*
|
||||||
handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, Image *img, const uint8_t *payload, bool *is_dirty) {
|
handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, Image *img, const uint8_t *payload, bool *is_dirty) {
|
||||||
uint32_t frame_number = g->frame_number, fmt = g->format ? g->format : RGBA;
|
uint32_t frame_number = g->frame_number, fmt = g->format ? g->format : RGBA;
|
||||||
|
|
@ -1595,6 +1609,7 @@ handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, I
|
||||||
.alpha_blend = g->compose_mode != 1 && !load_data->is_opaque,
|
.alpha_blend = g->compose_mode != 1 && !load_data->is_opaque,
|
||||||
.gap = g->gap > 0 ? g->gap : (g->gap < 0) ? 0 : DEFAULT_GAP,
|
.gap = g->gap > 0 ? g->gap : (g->gap < 0) ? 0 : DEFAULT_GAP,
|
||||||
.bgcolor = g->bgcolor,
|
.bgcolor = g->bgcolor,
|
||||||
|
.transient = (g->usage_hints & GRAPHICS_USAGE_HINT_TRANSIENT) != 0,
|
||||||
};
|
};
|
||||||
Frame *frame;
|
Frame *frame;
|
||||||
if (is_new_frame) {
|
if (is_new_frame) {
|
||||||
|
|
@ -1630,12 +1645,14 @@ handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, I
|
||||||
transmitted_frame.x = 0; transmitted_frame.y = 0;
|
transmitted_frame.x = 0; transmitted_frame.y = 0;
|
||||||
transmitted_frame.is_4byte_aligned = cfd.is_4byte_aligned;
|
transmitted_frame.is_4byte_aligned = cfd.is_4byte_aligned;
|
||||||
transmitted_frame.is_opaque = cfd.is_opaque;
|
transmitted_frame.is_opaque = cfd.is_opaque;
|
||||||
|
transmitted_frame.transient = transmitted_frame.transient || cfd.transient;
|
||||||
} else {
|
} else {
|
||||||
transmitted_frame.base_frame_id = other_frame->id;
|
transmitted_frame.base_frame_id = other_frame->id;
|
||||||
|
transmitted_frame.transient = transmitted_frame.transient || frame_chain_is_transient(img, other_frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*frame = transmitted_frame;
|
*frame = transmitted_frame;
|
||||||
if (!add_to_cache(self, key, load_data->data, load_data->data_sz)) {
|
if (!add_to_cache(self, key, load_data->data, load_data->data_sz, frame->transient)) {
|
||||||
img->extra_framecnt--;
|
img->extra_framecnt--;
|
||||||
if (PyErr_Occurred()) PyErr_Print();
|
if (PyErr_Occurred()) PyErr_Print();
|
||||||
ABRT("ENOSPC", "Failed to cache data for image frame");
|
ABRT("ENOSPC", "Failed to cache data for image frame");
|
||||||
|
|
@ -1651,6 +1668,7 @@ handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, I
|
||||||
if (g->gap != 0) change_gap(img, frame, transmitted_frame.gap);
|
if (g->gap != 0) change_gap(img, frame, transmitted_frame.gap);
|
||||||
CoalescedFrameData cfd = get_coalesced_frame_data(self, img, frame);
|
CoalescedFrameData cfd = get_coalesced_frame_data(self, img, frame);
|
||||||
if (!cfd.buf) ABRT("EINVAL", "No data associated with frame number: %u", frame_number);
|
if (!cfd.buf) ABRT("EINVAL", "No data associated with frame number: %u", frame_number);
|
||||||
|
frame->transient = cfd.transient || transmitted_frame.transient;
|
||||||
frame->alpha_blend = false; frame->base_frame_id = 0; frame->bgcolor = 0;
|
frame->alpha_blend = false; frame->base_frame_id = 0; frame->bgcolor = 0;
|
||||||
frame->is_opaque = cfd.is_opaque; frame->is_4byte_aligned = cfd.is_4byte_aligned;
|
frame->is_opaque = cfd.is_opaque; frame->is_4byte_aligned = cfd.is_4byte_aligned;
|
||||||
frame->x = 0; frame->y = 0; frame->width = img->width; frame->height = img->height;
|
frame->x = 0; frame->y = 0; frame->width = img->width; frame->height = img->height;
|
||||||
|
|
@ -1664,7 +1682,7 @@ handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, I
|
||||||
};
|
};
|
||||||
compose(d, cfd.buf, load_data->data);
|
compose(d, cfd.buf, load_data->data);
|
||||||
const ImageAndFrame key = { .image_id = img->internal_id, .frame_id = frame->id };
|
const ImageAndFrame key = { .image_id = img->internal_id, .frame_id = frame->id };
|
||||||
bool added = add_to_cache(self, key, cfd.buf, (size_t)bytes_per_pixel * frame->width * frame->height);
|
bool added = add_to_cache(self, key, cfd.buf, (size_t)bytes_per_pixel * frame->width * frame->height, frame->transient);
|
||||||
if (added && frame == current_frame(img)) {
|
if (added && frame == current_frame(img)) {
|
||||||
update_current_frame(self, img, &cfd);
|
update_current_frame(self, img, &cfd);
|
||||||
*is_dirty = true;
|
*is_dirty = true;
|
||||||
|
|
@ -1867,10 +1885,13 @@ handle_compose_command(GraphicsManager *self, bool *is_dirty, const GraphicsComm
|
||||||
.stride = img->width
|
.stride = img->width
|
||||||
};
|
};
|
||||||
compose_rectangles(d, dest_data.buf, src_data.buf);
|
compose_rectangles(d, dest_data.buf, src_data.buf);
|
||||||
|
bool transient = src_data.transient || dest_data.transient;
|
||||||
const ImageAndFrame key = { .image_id = img->internal_id, .frame_id = dest_frame->id };
|
const ImageAndFrame key = { .image_id = img->internal_id, .frame_id = dest_frame->id };
|
||||||
if (!add_to_cache(self, key, dest_data.buf, ((size_t)(dest_data.is_opaque ? 3 : 4)) * img->width * img->height)) {
|
if (!add_to_cache(self, key, dest_data.buf, ((size_t)(dest_data.is_opaque ? 3 : 4)) * img->width * img->height, transient)) {
|
||||||
if (PyErr_Occurred()) PyErr_Print();
|
if (PyErr_Occurred()) PyErr_Print();
|
||||||
set_command_failed_response("ENOSPC", "Failed to store image data in disk cache");
|
set_command_failed_response("ENOSPC", "Failed to store image data in cache");
|
||||||
|
} else {
|
||||||
|
dest_frame->transient = transient;
|
||||||
}
|
}
|
||||||
// frame is now a fully coalesced frame
|
// frame is now a fully coalesced frame
|
||||||
dest_frame->x = 0; dest_frame->y = 0; dest_frame->width = img->width; dest_frame->height = img->height;
|
dest_frame->x = 0; dest_frame->y = 0; dest_frame->width = img->width; dest_frame->height = img->height;
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,12 @@
|
||||||
#include "data-types.h"
|
#include "data-types.h"
|
||||||
#include "monotonic.h"
|
#include "monotonic.h"
|
||||||
|
|
||||||
|
// Bitmask values for GraphicsCommand.usage_hints
|
||||||
|
#define GRAPHICS_USAGE_HINT_TRANSIENT 1u
|
||||||
|
|
||||||
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, parent_id, parent_placement_id;
|
uint32_t format, more, id, image_number, data_sz, data_offset, placement_id, quiet, parent_id, parent_placement_id, usage_hints;
|
||||||
uint32_t width, height, x_offset, y_offset;
|
uint32_t width, height, x_offset, y_offset;
|
||||||
union { uint32_t cursor_movement, compose_mode; };
|
union { uint32_t cursor_movement, compose_mode; };
|
||||||
union { uint32_t cell_x_offset; };
|
union { uint32_t cell_x_offset; };
|
||||||
|
|
@ -71,7 +74,7 @@ typedef struct {
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t gap, id, width, height, x, y, base_frame_id, bgcolor;
|
uint32_t gap, id, width, height, x, y, base_frame_id, bgcolor;
|
||||||
bool is_opaque, is_4byte_aligned, alpha_blend;
|
bool is_opaque, is_4byte_aligned, alpha_blend, transient;
|
||||||
} Frame;
|
} Frame;
|
||||||
|
|
||||||
typedef enum { ANIMATION_STOPPED = 0, ANIMATION_LOADING = 1, ANIMATION_RUNNING = 2} AnimationState;
|
typedef enum { ANIMATION_STOPPED = 0, ANIMATION_LOADING = 1, ANIMATION_RUNNING = 2} AnimationState;
|
||||||
|
|
|
||||||
10
kitty/parse-graphics-command.h
generated
10
kitty/parse-graphics-command.h
generated
|
|
@ -46,6 +46,7 @@ static inline void parse_graphics_code(PS *self, uint8_t *parser_buf,
|
||||||
unicode_placement = 'U',
|
unicode_placement = 'U',
|
||||||
parent_id = 'P',
|
parent_id = 'P',
|
||||||
parent_placement_id = 'Q',
|
parent_placement_id = 'Q',
|
||||||
|
usage_hints = 'N',
|
||||||
offset_from_parent_x = 'H',
|
offset_from_parent_x = 'H',
|
||||||
offset_from_parent_y = 'V'
|
offset_from_parent_y = 'V'
|
||||||
};
|
};
|
||||||
|
|
@ -141,6 +142,9 @@ static inline void parse_graphics_code(PS *self, uint8_t *parser_buf,
|
||||||
case parent_placement_id:
|
case parent_placement_id:
|
||||||
value_state = UINT;
|
value_state = UINT;
|
||||||
break;
|
break;
|
||||||
|
case usage_hints:
|
||||||
|
value_state = UINT;
|
||||||
|
break;
|
||||||
case offset_from_parent_x:
|
case offset_from_parent_x:
|
||||||
value_state = INT;
|
value_state = INT;
|
||||||
break;
|
break;
|
||||||
|
|
@ -299,6 +303,7 @@ static inline void parse_graphics_code(PS *self, uint8_t *parser_buf,
|
||||||
U(unicode_placement);
|
U(unicode_placement);
|
||||||
U(parent_id);
|
U(parent_id);
|
||||||
U(parent_placement_id);
|
U(parent_placement_id);
|
||||||
|
U(usage_hints);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -359,7 +364,7 @@ static inline void parse_graphics_code(PS *self, uint8_t *parser_buf,
|
||||||
|
|
||||||
REPORT_VA_COMMAND(
|
REPORT_VA_COMMAND(
|
||||||
"K s {sc sc sc sc sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI "
|
"K 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 sI si si si ss#}",
|
"sI sI sI sI sI si si si ss#}",
|
||||||
self->window_id, "graphics_command",
|
self->window_id, "graphics_command",
|
||||||
|
|
||||||
"action", g.action, "delete_action", g.delete_action, "transmission_type",
|
"action", g.action, "delete_action", g.delete_action, "transmission_type",
|
||||||
|
|
@ -378,7 +383,8 @@ static inline void parse_graphics_code(PS *self, uint8_t *parser_buf,
|
||||||
"cell_y_offset", (unsigned int)g.cell_y_offset, "cursor_movement",
|
"cell_y_offset", (unsigned int)g.cell_y_offset, "cursor_movement",
|
||||||
(unsigned int)g.cursor_movement, "unicode_placement",
|
(unsigned int)g.cursor_movement, "unicode_placement",
|
||||||
(unsigned int)g.unicode_placement, "parent_id", (unsigned int)g.parent_id,
|
(unsigned int)g.unicode_placement, "parent_id", (unsigned int)g.parent_id,
|
||||||
"parent_placement_id", (unsigned int)g.parent_placement_id,
|
"parent_placement_id", (unsigned int)g.parent_placement_id, "usage_hints",
|
||||||
|
(unsigned int)g.usage_hints,
|
||||||
|
|
||||||
"z_index", (int)g.z_index, "offset_from_parent_x",
|
"z_index", (int)g.z_index, "offset_from_parent_x",
|
||||||
(int)g.offset_from_parent_x, "offset_from_parent_y",
|
(int)g.offset_from_parent_x, "offset_from_parent_y",
|
||||||
|
|
|
||||||
|
|
@ -386,6 +386,20 @@ def test_suppressing_gr_command_responses(self):
|
||||||
self.assertIsNone(li(payload='2' * 12, z=77, m=1, q=2))
|
self.assertIsNone(li(payload='2' * 12, z=77, m=1, q=2))
|
||||||
self.assertIsNone(li(payload='2' * 12))
|
self.assertIsNone(li(payload='2' * 12))
|
||||||
|
|
||||||
|
def test_transient_graphics_image(self):
|
||||||
|
s, g, pl, sl = load_helpers(self)
|
||||||
|
self.assertEqual(g.disk_cache.end_of_data_offset(), 0)
|
||||||
|
self.ae(pl('abc', s=1, v=1, f=24, N=1), 'OK')
|
||||||
|
self.assertTrue(g.disk_cache.wait_for_write())
|
||||||
|
self.assertEqual(g.disk_cache.end_of_data_offset(), 0)
|
||||||
|
img = g.image_for_client_id(1)
|
||||||
|
self.assertIsNotNone(img)
|
||||||
|
self.ae(img['data'], b'abc')
|
||||||
|
|
||||||
|
self.ae(pl('def', s=1, v=1, f=24, i=2), 'OK')
|
||||||
|
self.assertTrue(g.disk_cache.wait_for_write())
|
||||||
|
self.assertGreater(g.disk_cache.end_of_data_offset(), 0)
|
||||||
|
|
||||||
def test_load_images(self):
|
def test_load_images(self):
|
||||||
s, g, pl, sl = load_helpers(self)
|
s, g, pl, sl = load_helpers(self)
|
||||||
self.assertEqual(g.disk_cache.total_size, 0)
|
self.assertEqual(g.disk_cache.total_size, 0)
|
||||||
|
|
|
||||||
|
|
@ -920,7 +920,7 @@ def c(**k):
|
||||||
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 cursor_movement'
|
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 unicode_placement'
|
' num_cells num_lines cell_x_offset cell_y_offset z_index placement_id image_number quiet unicode_placement'
|
||||||
' parent_id parent_placement_id offset_from_parent_x offset_from_parent_y'
|
' parent_id parent_placement_id usage_hints offset_from_parent_x offset_from_parent_y'
|
||||||
).split():
|
).split():
|
||||||
k.setdefault(f, 0)
|
k.setdefault(f, 0)
|
||||||
p = k.pop('payload', '')
|
p = k.pop('payload', '')
|
||||||
|
|
@ -943,6 +943,7 @@ def e(cmd, err):
|
||||||
t('a=t,t=d,s=100,z=-9', payload='X', action='t', transmission_type='d', data_width=100, z_index=-9)
|
t('a=t,t=d,s=100,z=-9', payload='X', action='t', transmission_type='d', data_width=100, z_index=-9)
|
||||||
t('a=t,t=d,s=100,z=9', payload='payload', action='t', transmission_type='d', data_width=100, z_index=9)
|
t('a=t,t=d,s=100,z=9', payload='payload', action='t', transmission_type='d', data_width=100, z_index=9)
|
||||||
t('a=t,t=d,s=100,z=9,q=2', action='t', transmission_type='d', data_width=100, z_index=9, quiet=2)
|
t('a=t,t=d,s=100,z=9,q=2', action='t', transmission_type='d', data_width=100, z_index=9, quiet=2)
|
||||||
|
t('N=1', usage_hints=1)
|
||||||
e(',s=1', 'Malformed GraphicsCommand control block, invalid key character: 0x2c')
|
e(',s=1', 'Malformed GraphicsCommand control block, invalid key character: 0x2c')
|
||||||
e('W=1', 'Malformed GraphicsCommand control block, invalid key character: 0x57')
|
e('W=1', 'Malformed GraphicsCommand control block, invalid key character: 0x57')
|
||||||
e('1=1', 'Malformed GraphicsCommand control block, invalid key character: 0x31')
|
e('1=1', 'Malformed GraphicsCommand control block, invalid key character: 0x31')
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ type GraphicsCommand struct {
|
||||||
d GRT_d
|
d GRT_d
|
||||||
U GRT_U
|
U GRT_U
|
||||||
|
|
||||||
s, v, S, O, x, y, w, h, X, Y, c, r uint64
|
s, v, S, O, x, y, w, h, X, Y, c, r, N uint64
|
||||||
|
|
||||||
i, I, p uint32
|
i, I, p uint32
|
||||||
|
|
||||||
|
|
@ -176,6 +176,7 @@ func (self *GraphicsCommand) serialize_non_default_fields() (ans []string) {
|
||||||
write_key('U', self.U, null.U)
|
write_key('U', self.U, null.U)
|
||||||
write_key('d', self.d, null.d)
|
write_key('d', self.d, null.d)
|
||||||
|
|
||||||
|
write_key('N', self.N, null.N)
|
||||||
write_key('s', self.s, null.s)
|
write_key('s', self.s, null.s)
|
||||||
write_key('v', self.v, null.v)
|
write_key('v', self.v, null.v)
|
||||||
write_key('S', self.S, null.S)
|
write_key('S', self.S, null.S)
|
||||||
|
|
@ -376,6 +377,8 @@ func (self *GraphicsCommand) SetString(key byte, value string) (err error) {
|
||||||
err = set_val(&self.U, GRT_U_from_string, value)
|
err = set_val(&self.U, GRT_U_from_string, value)
|
||||||
case 'd':
|
case 'd':
|
||||||
err = set_val(&self.d, GRT_d_from_string, value)
|
err = set_val(&self.d, GRT_d_from_string, value)
|
||||||
|
case 'N':
|
||||||
|
err = set_uval(&self.N, value)
|
||||||
case 's':
|
case 's':
|
||||||
err = set_uval(&self.s, value)
|
err = set_uval(&self.s, value)
|
||||||
case 'v':
|
case 'v':
|
||||||
|
|
@ -753,6 +756,15 @@ func (self *GraphicsCommand) SetFrameToMakeCurrent(c uint64) *GraphicsCommand {
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *GraphicsCommand) UsageHints() uint64 {
|
||||||
|
return self.N
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GraphicsCommand) SetUsageHints(hints uint64) *GraphicsCommand {
|
||||||
|
self.N = hints
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
func (self *GraphicsCommand) ImageId() uint32 {
|
func (self *GraphicsCommand) ImageId() uint32 {
|
||||||
return self.i
|
return self.i
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue