Code to read image data
This commit is contained in:
parent
c567acb4e5
commit
66bce4b8cd
141
kitty/graphics.c
141
kitty/graphics.c
@ -8,8 +8,25 @@
|
||||
#include "graphics.h"
|
||||
#include "state.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define REPORT_ERROR(fmt, ...) { fprintf(stderr, fmt, __VA_ARGS__); fprintf(stderr, "\n"); }
|
||||
|
||||
static inline bool
|
||||
mmap_img_file(GraphicsManager UNUSED *self, Image *img) {
|
||||
off_t file_sz = lseek(img->load_data.fd, 0, SEEK_END);
|
||||
if (file_sz == -1) { REPORT_ERROR("Failed to seek in image file with error: [%d] %s", errno, strerror(errno)); return false; }
|
||||
lseek(img->load_data.fd, 0, SEEK_SET);
|
||||
void *addr = mmap(0, file_sz, PROT_READ, MAP_PRIVATE, img->load_data.fd, 0);
|
||||
if (addr == MAP_FAILED) { REPORT_ERROR("Failed to map image file with error: [%d] %s", errno, strerror(errno)); return false; }
|
||||
img->load_data.mapped_file = addr;
|
||||
img->load_data.mapped_file_sz = file_sz;
|
||||
return true;
|
||||
}
|
||||
|
||||
GraphicsManager*
|
||||
grman_realloc(GraphicsManager *old, index_type lines, index_type columns) {
|
||||
GraphicsManager *self = (GraphicsManager *)GraphicsManager_Type.tp_alloc(&GraphicsManager_Type, 0);
|
||||
@ -28,8 +45,20 @@ grman_realloc(GraphicsManager *old, index_type lines, index_type columns) {
|
||||
return self;
|
||||
}
|
||||
|
||||
static inline void
|
||||
free_load_data(LoadData *ld) {
|
||||
free(ld->buf); ld->buf_used = 0; ld->buf_capacity = 0;
|
||||
ld->buf = NULL;
|
||||
|
||||
if (ld->mapped_file) munmap(ld->mapped_file, ld->mapped_file_sz);
|
||||
ld->mapped_file = NULL; ld->mapped_file_sz = 0;
|
||||
if (ld->fd > 0) close(ld->fd);
|
||||
ld->fd = -1;
|
||||
}
|
||||
|
||||
GraphicsManager*
|
||||
grman_free(GraphicsManager* self) {
|
||||
for (size_t i = 0; i < self->image_count; i++) free_load_data(&(self->images[i].load_data));
|
||||
free(self->images);
|
||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||
return NULL;
|
||||
@ -64,17 +93,121 @@ find_or_create_image(GraphicsManager *self, uint32_t id, bool *existing) {
|
||||
return self->images + self->image_count++;
|
||||
}
|
||||
|
||||
static inline void
|
||||
remove_from_array(void *array, size_t item_size, size_t idx, size_t array_count) {
|
||||
size_t num_to_right = array_count - 1 - idx;
|
||||
uint8_t *p = (uint8_t*)array;
|
||||
if (num_to_right > 0) memmove(p + (idx * item_size), p + ((idx + 1) * item_size), num_to_right * item_size);
|
||||
memset(p + (item_size * (array_count - 1)), 0, item_size);
|
||||
}
|
||||
|
||||
static inline void
|
||||
free_image(Image *img) {
|
||||
img->data_loaded = false;
|
||||
free_load_data(&(img->load_data));
|
||||
}
|
||||
|
||||
static inline void
|
||||
remove_images(GraphicsManager *self, bool(*predicate)(Image*)) {
|
||||
for (size_t i = self->image_count; i-- > 0;) {
|
||||
if (predicate(self->images + i)) {
|
||||
free_image(self->images + i);
|
||||
remove_from_array(self->images, sizeof(Image), i, self->image_count--);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline Image*
|
||||
img_by_internal_id(GraphicsManager *self, size_t id) {
|
||||
for (size_t i = 0; i < self->image_count; i++) {
|
||||
if (self->images[i].internal_id == id) return self->images + i;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
add_trim_predicate(Image *img) {
|
||||
return !img->data_loaded || (!img->client_id && !img->refcnt);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t UNUSED *payload) {
|
||||
bool existing;
|
||||
Image *img = find_or_create_image(self, g->id, &existing);
|
||||
handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload) {
|
||||
bool existing, init_img = true;
|
||||
Image *img;
|
||||
unsigned char tt = g->transmission_type ? g->transmission_type : 'd';
|
||||
if (tt == 'd' && (g->more && self->loading_image)) init_img = false;
|
||||
if (init_img) {
|
||||
remove_images(self, add_trim_predicate);
|
||||
img = find_or_create_image(self, g->id, &existing);
|
||||
if (existing) {
|
||||
free(img->load_buf); img->load_buf = NULL;
|
||||
free_load_data(&img->load_data);
|
||||
img->data_loaded = false;
|
||||
} else {
|
||||
img->internal_id = internal_id_counter++;
|
||||
img->client_id = g->id;
|
||||
}
|
||||
img->width = g->data_width; img->height = g->data_height;
|
||||
size_t sz = img->width * img->height;
|
||||
switch(g->format) {
|
||||
case 100: // PNG
|
||||
sz = sz * 4 + 1024;
|
||||
break;
|
||||
case 8:
|
||||
case 24:
|
||||
case 32:
|
||||
sz *= g->format / 8;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
if (g->compressed) sz += 1024; // compression header
|
||||
img->load_data.max_data_sz = sz + 10;
|
||||
if (tt == 'd') {
|
||||
if (g->more) self->loading_image = img->internal_id;
|
||||
img->load_data.buf = malloc(img->load_data.max_data_sz + 4);
|
||||
if (img->load_data.buf == NULL) fatal("Out of memory while allocating image load data buffer");
|
||||
img->load_data.buf_capacity = img->load_data.max_data_sz;
|
||||
img->load_data.buf_used = 0;
|
||||
}
|
||||
} else {
|
||||
img = img_by_internal_id(self, self->loading_image);
|
||||
if (img == NULL) {
|
||||
self->loading_image = 0;
|
||||
REPORT_ERROR("%s", "More payload loading refers to non-existent image");
|
||||
return;
|
||||
}
|
||||
}
|
||||
int fd;
|
||||
switch(tt) {
|
||||
case 'd': // direct
|
||||
if (g->payload_sz >= img->load_data.buf_capacity - img->load_data.buf_used) {
|
||||
REPORT_ERROR("%s", "Too much data transmitted");
|
||||
return;
|
||||
}
|
||||
memcpy(img->load_data.buf + img->load_data.buf_used, payload, g->payload_sz);
|
||||
img->load_data.buf_used += g->payload_sz;
|
||||
if (!g->more) { img->data_loaded = true; self->loading_image = 0; }
|
||||
break;
|
||||
case 'f': // file
|
||||
case 't': // temporary file
|
||||
case 's': // POSIX shared memory
|
||||
if (tt == 's') fd = shm_open((const char*)payload, O_RDONLY, 0);
|
||||
else fd = open((const char*)payload, O_CLOEXEC | O_RDONLY);
|
||||
if (fd == -1) {
|
||||
REPORT_ERROR("Failed to open file for graphics transmission with error: [%d] %s", errno, strerror(errno));
|
||||
return;
|
||||
}
|
||||
img->load_data.fd = fd;
|
||||
img->data_loaded = mmap_img_file(self, img);
|
||||
if (tt == 't') unlink((const char*)payload);
|
||||
else if (tt == 's') shm_unlink((const char*)payload);
|
||||
break;
|
||||
default:
|
||||
REPORT_ERROR("Unknown transmission type: %c", g->transmission_type);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@ -8,18 +8,30 @@
|
||||
#include "data-types.h"
|
||||
|
||||
typedef struct {
|
||||
unsigned char action, transmission_type;
|
||||
unsigned char action, transmission_type, compressed;
|
||||
uint32_t format, more, id;
|
||||
uint32_t width, height, x_offset, y_offset, data_height, data_width, num_cells, num_lines;
|
||||
int32_t z_index;
|
||||
size_t payload_sz;
|
||||
} GraphicsCommand;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *buf;
|
||||
size_t buf_capacity, buf_used;
|
||||
|
||||
int fd;
|
||||
uint8_t *mapped_file;
|
||||
size_t mapped_file_sz;
|
||||
|
||||
size_t max_data_sz;
|
||||
} LoadData;
|
||||
|
||||
typedef struct {
|
||||
uint32_t gl_id, client_id, width, height;
|
||||
size_t internal_id, refcnt;
|
||||
uint8_t *load_buf;
|
||||
|
||||
bool data_loaded;
|
||||
LoadData load_data;
|
||||
} Image;
|
||||
|
||||
|
||||
@ -27,7 +39,7 @@ typedef struct {
|
||||
PyObject_HEAD
|
||||
|
||||
index_type lines, columns;
|
||||
size_t image_count, images_capacity;
|
||||
size_t image_count, images_capacity, loading_image;
|
||||
Image *images;
|
||||
} GraphicsManager;
|
||||
PyTypeObject GraphicsManager_Type;
|
||||
|
||||
@ -541,6 +541,7 @@ parse_graphics_code(Screen *screen, PyObject UNUSED *dump_callback) {
|
||||
enum KEYS {
|
||||
action='a',
|
||||
transmission_type='t',
|
||||
compressed='o',
|
||||
format = 'f',
|
||||
more = 'm',
|
||||
id = 'i',
|
||||
@ -671,8 +672,8 @@ 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 sI sI sI sI sI sI sI sI sI sI sI sI si} y#", "graphics_command",
|
||||
A(action), A(transmission_type),
|
||||
REPORT_VA_COMMAND("s {sc sc sc 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(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),
|
||||
|
||||
@ -202,7 +202,7 @@ class TestParser(BaseTest):
|
||||
for p, v in tuple(k.items()):
|
||||
if isinstance(v, str) and p != 'payload':
|
||||
k[p] = v.encode('ascii')
|
||||
for f in 'action transmission_type'.split():
|
||||
for f in 'action transmission_type compressed'.split():
|
||||
k.setdefault(f, b'\0')
|
||||
for f in 'format more id width height x_offset y_offset data_height data_width num_cells num_lines z_index'.split():
|
||||
k.setdefault(f, 0)
|
||||
@ -219,9 +219,9 @@ class TestParser(BaseTest):
|
||||
s = self.create_screen()
|
||||
pb = partial(self.parse_bytes_dump, s)
|
||||
pb('\033_Gi=12\033\\', c(id=12))
|
||||
t('a=t,t=f,s=100,z=-9', payload='X', action='t', transmission_type='f', data_width=100, z_index=-9, payload_sz=1)
|
||||
t('a=t,t=f,s=100,z=9', payload='payload', action='t', transmission_type='f', data_width=100, z_index=9, payload_sz=7)
|
||||
t('a=t,t=f,s=100,z=9', action='t', transmission_type='f', 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, payload_sz=1)
|
||||
t('a=t,t=d,s=100,z=9', payload='payload', action='t', transmission_type='d', data_width=100, z_index=9, payload_sz=7)
|
||||
t('a=t,t=d,s=100,z=9', action='t', transmission_type='d', data_width=100, z_index=9)
|
||||
e(',s=1', 'Malformed graphics control block, invalid key character: 0x2c')
|
||||
e('W=1', 'Malformed graphics control block, invalid key character: 0x57')
|
||||
e('1=1', 'Malformed graphics control block, invalid key character: 0x31')
|
||||
|
||||
2
setup.py
2
setup.py
@ -170,7 +170,7 @@ def init_env(debug=False, sanitize=False, native_optimizations=True, profile=Fal
|
||||
else:
|
||||
glfw_ldflags = pkg_config('glfw3', '--libs')
|
||||
glew_libs = pkg_config('glew', '--libs')
|
||||
ldpaths = pylib + glew_libs + font_libs + glfw_ldflags + ['-lunistring']
|
||||
ldpaths = pylib + glew_libs + font_libs + glfw_ldflags + ['-lunistring', '-lrt']
|
||||
|
||||
try:
|
||||
os.mkdir(build_dir)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user