diff --git a/kitty/graphics.c b/kitty/graphics.c index efc17e7ef..a307e4cc2 100644 --- a/kitty/graphics.c +++ b/kitty/graphics.c @@ -134,13 +134,16 @@ set_add_response(const char *code, const char *fmt, ...) { #define ABRT(code, ...) { set_add_response(#code, __VA_ARGS__); goto err; } static inline bool -mmap_img_file(GraphicsManager UNUSED *self, Image *img) { - struct stat s; - if (fstat(img->load_data.fd, &s) != 0) ABRT(EBADF, "Failed to fstat() SHM file with error: [%d] %s", errno, strerror(errno)); - void *addr = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, img->load_data.fd, 0); +mmap_img_file(GraphicsManager UNUSED *self, Image *img, size_t sz, off_t offset) { + if (!sz) { + struct stat s; + if (fstat(img->load_data.fd, &s) != 0) ABRT(EBADF, "Failed to fstat() file with error: [%d] %s", errno, strerror(errno)); + sz = s.st_size; + } + void *addr = mmap(0, sz, PROT_READ, MAP_PRIVATE, img->load_data.fd, offset); if (addr == MAP_FAILED) ABRT(EBADF, "Failed to map image file with error: [%d] %s", errno, strerror(errno)); img->load_data.mapped_file = addr; - img->load_data.mapped_file_sz = s.st_size; + img->load_data.mapped_file_sz = sz; return true; err: return false; @@ -332,11 +335,10 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_ snprintf(fname, sizeof(fname)/sizeof(fname[0]), "%.*s", (int)g->payload_sz, payload); if (tt == 's') fd = shm_open(fname, O_RDONLY, 0); else fd = open(fname, O_CLOEXEC | O_RDONLY); - if (fd == -1) { - ABRT(EBADF, "Failed to open file %s for graphics transmission with error: [%d] %s", fname, errno, strerror(errno)); - } + if (fd == -1) ABRT(EBADF, "Failed to open file %s for graphics transmission with error: [%d] %s", fname, errno, strerror(errno)); img->load_data.fd = fd; - img->data_loaded = mmap_img_file(self, img); + if (tt == 's' && !g->data_sz) ABRT(EINVAL, "data size required for shared memory object"); + img->data_loaded = mmap_img_file(self, img, g->data_sz, g->data_offset); if (tt == 't') unlink(fname); else if (tt == 's') shm_unlink(fname); break; diff --git a/kitty/graphics.h b/kitty/graphics.h index 4f2da19d4..9a41c5d6e 100644 --- a/kitty/graphics.h +++ b/kitty/graphics.h @@ -9,7 +9,7 @@ typedef struct { unsigned char action, transmission_type, compressed; - uint32_t format, more, id, data_sz; + uint32_t format, more, id, data_sz, data_offset; uint32_t width, height, x_offset, y_offset, data_height, data_width, num_cells, num_lines; int32_t z_index; size_t payload_sz; diff --git a/kitty/parser.c b/kitty/parser.c index 478bf6cb2..cef74be00 100644 --- a/kitty/parser.c +++ b/kitty/parser.c @@ -552,6 +552,7 @@ parse_graphics_code(Screen *screen, PyObject UNUSED *dump_callback) { data_height = 'v', data_width = 's', data_sz = 'S', + data_offset = 'O', num_cells = 'c', num_lines = 'r', z_index = 'z' @@ -575,7 +576,7 @@ parse_graphics_code(Screen *screen, PyObject UNUSED *dump_callback) { #define KS(n, vs) case n: state = EQUAL; value_state = vs; break #define U(x) KS(x, UINT) KS(action, FLAG); KS(transmission_type, FLAG); KS(compressed, FLAG); KS(z_index, INT); - U(format); U(more); U(id); U(data_sz); U(width); U(height); U(x_offset); U(y_offset); U(data_height); U(data_width); U(num_cells); U(num_lines); + U(format); U(more); U(id); U(data_sz); U(data_offset); U(width); U(height); U(x_offset); U(y_offset); U(data_height); U(data_width); U(num_cells); U(num_lines); #undef U #undef KS default: @@ -628,7 +629,7 @@ parse_graphics_code(Screen *screen, PyObject UNUSED *dump_callback) { READ_UINT; #define U(x) case x: g.x = code; break switch(key) { - U(format); U(more); U(id); U(data_sz); U(width); U(height); U(x_offset); U(y_offset); U(data_height); U(data_width); U(num_cells); U(num_lines); + U(format); U(more); U(id); U(data_sz); U(data_offset); U(width); U(height); U(x_offset); U(y_offset); U(data_height); U(data_width); U(num_cells); U(num_lines); default: break; } state = AFTER_VALUE; @@ -672,9 +673,9 @@ parse_graphics_code(Screen *screen, PyObject UNUSED *dump_callback) { #define A(x) #x, g.x #define U(x) #x, (unsigned int)(g.x) #define I(x) #x, (int)(g.x) - REPORT_VA_COMMAND("s {sc sc sc sI sI sI sI sI sI sI sI sI sI sI sI sI si} y#", "graphics_command", + REPORT_VA_COMMAND("s {sc sc sc sI sI sI sI sI sI sI sI sI sI sI sI sI sI si} y#", "graphics_command", A(action), A(transmission_type), A(compressed), - U(format), U(more), U(id), U(data_sz), + U(format), U(more), U(id), U(data_sz), U(data_offset), U(width), U(height), U(x_offset), U(y_offset), U(data_height), U(data_width), U(num_cells), U(num_lines), U(payload_sz), I(z_index), payload, g.payload_sz diff --git a/kitty_tests/graphics.py b/kitty_tests/graphics.py index d684ebbd7..62830cd18 100644 --- a/kitty_tests/graphics.py +++ b/kitty_tests/graphics.py @@ -43,7 +43,7 @@ class TestGraphics(BaseTest): res = send_command(s, cmd, payload) if not res: return - return res.decode('ascii').partition(';')[2].partition(':')[0].partition('\033')[0] + return res.decode('ascii').partition(';')[2].partition('\033')[0] def sl(payload, **kw): if isinstance(payload, str): @@ -90,5 +90,5 @@ class TestGraphics(BaseTest): # Test loading from POSIX SHM name = '/kitty-test-shm' g.shm_write(name, random_data) - sl(name, s=24, v=32, t='s', expecting_data=random_data) + sl(name, s=24, v=32, t='s', S=len(random_data), expecting_data=random_data) self.assertRaises(FileNotFoundError, g.shm_unlink, name) # check that file was deleted diff --git a/kitty_tests/parser.py b/kitty_tests/parser.py index 780222761..9334ae8e6 100644 --- a/kitty_tests/parser.py +++ b/kitty_tests/parser.py @@ -204,7 +204,7 @@ class TestParser(BaseTest): k[p] = v.encode('ascii') for f in 'action transmission_type compressed'.split(): k.setdefault(f, b'\0') - for f in 'format more id data_sz width height x_offset y_offset data_height data_width num_cells num_lines z_index'.split(): + for f in 'format more id data_sz data_offset width height x_offset y_offset data_height data_width num_cells num_lines z_index'.split(): k.setdefault(f, 0) p = k.pop('payload', '').encode('utf-8') k['payload_sz'] = len(p)