diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-10-12 00:09:38 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-10-12 00:09:38 +0300 |
| commit | 624d1371304a5ae4b3429ad5e4267aac7cd03dfd (patch) | |
| tree | 32de069f6eec83686521fc4bf39cc9874b123e93 /src/x11 | |
| parent | e813252e685d761235ad45e170d97c9375f51958 (diff) | |
missing impls and hardening
Diffstat (limited to 'src/x11')
| -rw-r--r-- | src/x11/window.c | 109 |
1 files changed, 71 insertions, 38 deletions
diff --git a/src/x11/window.c b/src/x11/window.c index 4680d3f..e40db6e 100644 --- a/src/x11/window.c +++ b/src/x11/window.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include "common/backend.h" +#include "common/input.h" #include <X11/Xatom.h> #include <X11/XKBlib.h> @@ -16,7 +17,6 @@ #include <stdlib.h> #include <string.h> -#define INPUT_CAP 256 #define TEXT_LIMIT (16 * 1024 * 1024) #ifndef likely @@ -38,10 +38,7 @@ struct x11_surface { struct yasl_vector2i size; const struct yasl_surface_cbs *cbs; void *ud; - struct yasl_input_event events[INPUT_CAP]; - char *event_text[INPUT_CAP]; - char *last_text; - unsigned head, count; + struct input_queue input; struct yasl_frame frame; struct yasl_damage_list damage; struct yasl_frame_clock clock; @@ -49,6 +46,12 @@ struct x11_surface { bool want_draw, drawing, presented, destroyed; }; +struct paste_request { + struct paste_request *next; + struct x11_surface *target; + Window window; +}; + struct x11_display { struct yasl_display base; Display *native; @@ -62,7 +65,7 @@ struct x11_display { Atom protocols, delete_window, utf8, clipboard, targets, paste_property; Atom net_name, net_state, fullscreen, maximized_h, maximized_v; char *selection[2]; - struct x11_surface *paste_target; + struct paste_request *pastes; uint32_t mods; char *app_id; bool owns_loop; @@ -86,25 +89,18 @@ static void leave(struct x11_display *d) static void push(struct x11_surface *s, struct yasl_input_event event, const char *text, size_t len) { - if (s->destroyed || s->count == INPUT_CAP) return; - unsigned slot = (s->head + s->count) % INPUT_CAP; + if (s->destroyed) return; if (text) { - char *copy = malloc(len + 1); - if (!copy) return; - memcpy(copy, text, len); - copy[len] = 0; - s->event_text[slot] = copy; if (event.kind == YASL_INPUT_TEXT) { - event.text.text = copy; + event.text.text = text; event.text.len = len; } else { - event.paste.text = copy; + event.paste.text = text; event.paste.len = len; } } event.surface = &s->base; - s->events[slot] = event; - s->count++; + input_queue_push(&s->input, &event); } static uint32_t modifiers(unsigned state) @@ -149,6 +145,23 @@ static bool resize(struct x11_surface *s, struct yasl_vector2i size) return true; } +static void cancel_pastes(struct x11_display *d, struct x11_surface *target) +{ + for (struct paste_request **p = &d->pastes; *p;) { + struct paste_request *r = *p; + if (!target) { + *p = r->next; + XDestroyWindow(d->native, r->window); + free(r); + } else { + /* Keep the requestor alive until SelectionNotify: the owner may + * still be writing to its property. Never retain a dead target. */ + if (r->target == target) r->target = NULL; + p = &r->next; + } + } +} + static void surface_destroy(struct yasl_surface *base) { struct x11_surface *s = (struct x11_surface *)base; @@ -158,14 +171,13 @@ static void surface_destroy(struct yasl_surface *base) for (struct x11_surface **p = &d->surfaces; *p; p = &(*p)->next) { if (*p == s) { *p = s->next; break; } } - if (d->paste_target == s) d->paste_target = NULL; + cancel_pastes(d, s); if (s->ic) XDestroyIC(s->ic); if (s->image) XDestroyImage(s->image); free(s->pixels); if (s->gc) XFreeGC(d->native, s->gc); if (s->window) XDestroyWindow(d->native, s->window); - for (unsigned i = 0; i < INPUT_CAP; i++) free(s->event_text[i]); - free(s->last_text); + input_queue_clear(&s->input); if (d->depth) { s->dead_next = d->dead; d->dead = s; @@ -177,7 +189,8 @@ static void set_callbacks(struct yasl_surface *base, { struct x11_surface *s = (struct x11_surface *)base; if (s->destroyed) return; - s->cbs = cbs; + s->base.callbacks = cbs ? *cbs : (struct yasl_surface_cbs){0}; + s->cbs = &s->base.callbacks; s->ud = ud; } @@ -295,14 +308,22 @@ static void selection_request(struct x11_display *d, XSelectionRequestEvent *req static void selection_notify(struct x11_display *d, XSelectionEvent *event) { - struct x11_surface *s = d->paste_target; - d->paste_target = NULL; - if (!s || event->property == None) return; + struct paste_request **p = &d->pastes; + while (*p && (*p)->window != event->requestor) p = &(*p)->next; + if (!*p) return; + struct paste_request *r = *p; + *p = r->next; + struct x11_surface *s = r->target; + if (!s || event->property == None) { + XDestroyWindow(d->native, r->window); + free(r); + return; + } Atom actual; int format; unsigned long count, left; unsigned char *data = NULL; - if (XGetWindowProperty(d->native, d->selection_window, event->property, + if (XGetWindowProperty(d->native, r->window, event->property, 0, TEXT_LIMIT / 4, True, AnyPropertyType, &actual, &format, &count, &left, &data) == Success && (actual == d->utf8 || actual == XA_STRING) && format == 8 && !left) { @@ -310,6 +331,8 @@ static void selection_notify(struct x11_display *d, XSelectionEvent *event) (const char *)data, count); } if (data) XFree(data); + XDestroyWindow(d->native, r->window); + free(r); } static void key_event(struct x11_surface *s, XKeyEvent *key, bool repeat) @@ -458,6 +481,7 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb struct x11_surface *s = (struct x11_surface *)base; if (s->destroyed || !s->drawing || s->presented || !fb || !damage || fb->pixels != s->pixels || fb->size.x != s->size.x || fb->size.y != s->size.y || + fb->buffer != s->frame.target.buffer || fb->native_drawable || fb->transaction || damage->n < -1 || damage->n > YASL_DAMAGE_MAX) return false; struct x11_display *d = display_of(base); XImage *im = s->image; @@ -502,7 +526,8 @@ static void flush(struct yasl_display *base) s->damage = (struct yasl_damage_list){0}; s->frame = (struct yasl_frame){ .surface = &s->base, - .target = {.pixels = s->pixels, .size = s->size, .damage = &s->damage}, + .target = {.generation = yasl_frame_begin(&s->base), + .pixels = s->pixels, .size = s->size, .damage = &s->damage}, }; s->cbs->draw(&s->base, &s->frame, s->ud); s->drawing = false; @@ -537,6 +562,7 @@ static struct yasl_display *display_open(struct yasl_loop *loop, struct x11_display *d = calloc(1, sizeof(*d)); if (!d) return NULL; d->base.ops = &yasl_x11_backend; + d->base.callbacks = spec && spec->cbs ? *spec->cbs : (struct yasl_display_cbs){0}; d->native = XOpenDisplay(NULL); if (!d->native) { free(d); errno = ENODEV; return NULL; } d->screen = DefaultScreen(d->native); @@ -547,7 +573,13 @@ static struct yasl_display *display_open(struct yasl_loop *loop, d->loop = loop; } d->app_id = strdup(spec && spec->app_id ? spec->app_id : "YASL"); - if (!d->app_id) { XCloseDisplay(d->native); free(d); return NULL; } + if (!d->loop || !d->app_id) { + if (d->owns_loop) yasl_loop_destroy(d->loop); + free(d->app_id); + XCloseDisplay(d->native); + free(d); + return NULL; + } d->im = XOpenIM(d->native, NULL, NULL, NULL); d->selection_window = XCreateSimpleWindow(d->native, RootWindow(d->native, d->screen), 0, 0, 1, 1, 0, 0, 0); @@ -583,6 +615,7 @@ static void display_close(struct yasl_display *base) { struct x11_display *d = (struct x11_display *)base; assert(!d->surfaces && "destroy every surface before its display"); + cancel_pastes(d, NULL); if (d->loop && d->watch > 0) yasl_loop_del_fd(d->loop, d->watch); if (d->owns_loop && d->loop) yasl_loop_destroy(d->loop); if (d->im) XCloseIM(d->im); @@ -623,9 +656,15 @@ static void paste(struct yasl_display *base, enum yasl_selection sel, struct yas const char *text = d->selection[sel]; if (text) push(s, (struct yasl_input_event){.kind = YASL_INPUT_PASTE}, text, strlen(text)); } else { - d->paste_target = s; + struct paste_request *r = calloc(1, sizeof(*r)); + if (!r) return; + r->window = XCreateSimpleWindow(d->native, RootWindow(d->native, d->screen), + 0, 0, 1, 1, 0, 0, 0); + r->target = s; + r->next = d->pastes; + d->pastes = r; XConvertSelection(d->native, selection, d->utf8, d->paste_property, - d->selection_window, CurrentTime); + r->window, CurrentTime); XFlush(d->native); } } @@ -633,14 +672,7 @@ static void paste(struct yasl_display *base, enum yasl_selection sel, struct yas static bool input_poll(struct yasl_surface *base, struct yasl_input_event *event) { struct x11_surface *s = (struct x11_surface *)base; - if (!event || s->destroyed || !s->count) return false; - free(s->last_text); - s->last_text = s->event_text[s->head]; - s->event_text[s->head] = NULL; - *event = s->events[s->head]; - s->head = (s->head + 1) % INPUT_CAP; - s->count--; - return true; + return !s->destroyed && input_queue_pop(&s->input, event); } static void schedule_redraw(struct yasl_surface *base) @@ -667,7 +699,8 @@ static struct yasl_timing timing(const struct yasl_surface *base) const struct x11_surface *s = (const struct x11_surface *)base; struct yasl_timing t = {.refresh_ns = s->clock.refresh_ns, .last_present_ns = s->clock.last_present_ns, - .next_present_ns = yasl_clock_next_present_ns(&s->clock)}; + .next_present_ns = yasl_clock_next_present_ns(&s->clock), + .source = s->clock.source}; yasl_clock_resize_stats(&s->clock, &t.resize); return t; } |
