summaryrefslogtreecommitdiff
path: root/src/x11/window.c
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-12 00:09:38 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-12 00:09:38 +0300
commit624d1371304a5ae4b3429ad5e4267aac7cd03dfd (patch)
tree32de069f6eec83686521fc4bf39cc9874b123e93 /src/x11/window.c
parente813252e685d761235ad45e170d97c9375f51958 (diff)
missing impls and hardening
Diffstat (limited to 'src/x11/window.c')
-rw-r--r--src/x11/window.c109
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;
}