diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-10-13 23:41:31 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-10-13 23:41:31 +0300 |
| commit | 76073fe5fad8799e5850805f2f090af2cb62c741 (patch) | |
| tree | 9658019babe4760a3634bdab4da0731a10e42e68 /src/platform/x11/window.c | |
| parent | dd1775a9927922c14f8679ccbc4d3ca8d5917e20 (diff) | |
Simplify the public event and CPU API
Applications now need far less ceremony: hello.c is a 20-line loop that
handles REFRESH, CLOSE and QUIT and draws into the surface's own pixels.
- Fold GEOMETRY, STATE and FOCUS into one WINDOW event carrying size,
scale and state; unchanged values send nothing.
- Guarantee REFRESH once a window has a size and after every resize,
never at size zero.
- Merge DROP_HOVER into DROP with over/leave/data actions and give drag
and drop actions enums.
- Keep event strings valid until the next wait and drop
yasl_event_release; empty or failed waits report NONE.
- Replace copy-in presentation with a lent pixel buffer:
yasl_cpu_pixels and yasl_cpu_present.
- Remove yasl_poll_event, yasl_cpu_read_pixels, unused event fields and
comments that restated names.
- Wayland: resize floating windows in request_size instead of reporting
it unsupported, and stop marking new GPU surfaces as Vulkan so
yasl_vulkan_create_surface no longer fails with NATIVE_WINDOW_IN_USE.
Also includes the in-progress public API refactor that was uncommitted
in the tree (removal of the app/loop layer, new surface, GL and Vulkan
entry points).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'src/platform/x11/window.c')
| -rw-r--r-- | src/platform/x11/window.c | 1174 |
1 files changed, 877 insertions, 297 deletions
diff --git a/src/platform/x11/window.c b/src/platform/x11/window.c index 004d56c..8129dd5 100644 --- a/src/platform/x11/window.c +++ b/src/platform/x11/window.c @@ -16,6 +16,10 @@ #include <X11/keysym.h> #include <X11/extensions/XInput2.h> #include <X11/extensions/XShm.h> +#if YASL_HAS_EGL +#include <EGL/egl.h> +#include <EGL/eglext.h> +#endif #include <sys/ipc.h> #include <sys/shm.h> #if YASL_HAS_XRANDR @@ -35,7 +39,7 @@ #include <wchar.h> #define TEXT_LIMIT (16 * 1024 * 1024) -#define PASTE_TIMEOUT_NS YASL_MS_TO_NS(30000) +#define PASTE_TIMEOUT_NS YASL_PRIVATE_MS_TO_NS(30000) #ifndef likely #define likely(x) __builtin_expect(!!(x), 1) @@ -50,26 +54,33 @@ struct x11_surface { struct x11_surface *dead_next; Window window; GC gc; + Colormap colormap; XIC ic; +#if YASL_HAS_EGL + EGLContext gl_context; + EGLSurface gl_surface; + EGLConfig gl_config; +#endif XImage *image; uint32_t *pixels; bool shm_active; XShmSegmentInfo shm_info; - struct yasl_vector2i size; - const struct yasl_window_cbs *cbs; + struct yasl_private_vector2i size; + const struct yasl_private_window_cbs *cbs; void *ud; struct input_queue input; - struct yasl_frame frame; - struct yasl_damage_list damage; - struct yasl_frame_clock clock; + struct yasl_private_frame frame; + struct yasl_private_damage_list damage; + struct yasl_private_frame_clock clock; bool want_draw, drawing, presented, destroyed; + bool focused; struct x11_surface *parent; bool is_popup; - struct yasl_vector2i content_offset; + struct yasl_private_vector2i content_offset; char *ime_preedit; int ime_cursor_begin; int ime_cursor_end; - struct yasl_rect2i ime_caret; + struct yasl_private_rect2i ime_caret; }; struct paste_request { @@ -79,33 +90,45 @@ struct paste_request { Window window; Atom selection; uint64_t id; - yasl_timer_id timeout; + yasl_private_timer_id timeout; }; struct tablet_axis { int number; double min, max, value; + bool valid; +}; + +struct x11_scroll_axis { + int number; + double increment, value; + bool valid, initialized; + int type; }; struct x11_pointer { struct x11_pointer *next; int id; - enum yasl_pointer_device device; - struct tablet_axis pressure, tilt_x, tilt_y; + enum yasl_private_pointer_device device; + unsigned tool; + struct tablet_axis pressure, tilt_x, tilt_y, rotation, distance, tangential, wheel; + struct x11_scroll_axis scroll_x, scroll_y; + uint32_t buttons; + bool finger_scroll; }; struct x11_display { - struct yasl_display base; + struct yasl_private_display base; Display *native; int screen; - struct yasl_loop *loop; + struct yasl_private_loop *loop; int watch; struct x11_surface *surfaces, *dead; int depth; XIM im; Window selection_window; Atom protocols, delete_window, utf8, clipboard, targets, paste_property, incr; - Atom net_name, net_state, fullscreen, maximized_h, maximized_v; + Atom net_name, net_state, fullscreen, maximized_h, maximized_v, motif_hints; Atom xdnd_aware, xdnd_enter, xdnd_position, xdnd_status, xdnd_leave, xdnd_drop, xdnd_finished; Atom xdnd_selection, xdnd_action_copy, xdnd_type_list, uri_list; char *selection[2]; @@ -121,20 +144,26 @@ struct x11_display { int xkb_event; enum yasl_key keys[256]; Cursor cursors[16]; - Atom abs_pressure, abs_tilt_x, abs_tilt_y, tool_type, eraser; + Atom abs_pressure, abs_tilt_x, abs_tilt_y, abs_rotation, abs_distance, abs_tangential, abs_wheel; + Atom tool_type, eraser; + Atom brush, airbrush, pencil; struct x11_pointer *pointers; +#if YASL_HAS_EGL + EGLDisplay egl; + bool egl_ready; +#endif struct { bool active; struct x11_surface *origin; struct x11_surface *carried; - struct yasl_vector2i carry_offset; - struct yasl_drag_listener listener; + struct yasl_private_vector2i carry_offset; + struct yasl_private_drag_listener listener; void *ud; struct x11_surface *hover; - struct yasl_vector2i hover_pos; + struct yasl_private_vector2i hover_pos; Window icon_window; Pixmap icon_pixmap; - struct yasl_vector2i hotspot; + struct yasl_private_vector2i hotspot; } drag; struct { Window source; @@ -142,7 +171,7 @@ struct x11_display { bool accepts; bool has_uri_list; struct x11_surface *surface; - struct yasl_vector2i pos; + struct yasl_private_vector2i pos; Time time; } xdnd; struct { @@ -153,11 +182,11 @@ struct x11_display { } pending_drop; }; -static bool place(struct yasl_surface *base, struct yasl_vector2i at); -static void drag_end(struct x11_display *d, enum yasl_drag_end how); -static void drag_cancel(struct yasl_display *base); +static bool place(struct yasl_surface *base, struct yasl_private_vector2i at); +static void drag_end(struct x11_display *d, enum yasl_private_drag_end how); +static void drag_cancel(struct yasl_private_display *base); static void schedule_redraw(struct yasl_surface *base); -static uint64_t paste_timeout(void *ud, yasl_timer_id id, uint64_t interval_ns); +static uint64_t paste_timeout(void *ud, yasl_private_timer_id id, uint64_t interval_ns); static void leave(struct x11_display *d) { @@ -169,7 +198,7 @@ static void leave(struct x11_display *d) } } -static void push(struct x11_surface *s, struct yasl_event event, +static void push(struct x11_surface *s, struct yasl_private_event event, const char *text, size_t len) { if (s->destroyed) return; @@ -189,39 +218,39 @@ static unsigned long component(uint32_t color, unsigned long mask) return (((unsigned long)color * m + 127) / 255) << shift; } -static void drag_set_hover(struct x11_display *d, struct x11_surface *s, struct yasl_vector2i pos) +static void drag_set_hover(struct x11_display *d, struct x11_surface *s, struct yasl_private_vector2i pos) { if (!d->drag.active) return; if (d->drag.hover && d->drag.hover != s) { struct x11_surface *old = d->drag.hover; - struct yasl_vector2i old_pos = d->drag.hover_pos; + struct yasl_private_vector2i old_pos = d->drag.hover_pos; d->drag.hover = NULL; if (!old->destroyed) - yasl_surface_notify_drag(&old->base, YASL_DRAG_LEAVE, old_pos); + yasl_private_surface_notify_drag(&old->base, YASL_PRIVATE_DRAG_LEAVE, old_pos); if (!d->drag.active || (s && s->destroyed)) return; } if (s && s->destroyed) return; d->drag.hover = s; d->drag.hover_pos = pos; if (s && !s->destroyed) - yasl_surface_notify_drag(&s->base, YASL_DRAG_OVER, pos); + yasl_private_surface_notify_drag(&s->base, YASL_PRIVATE_DRAG_OVER, pos); } -static void drag_leave_hover(struct x11_display *d, struct x11_surface *s, struct yasl_vector2i pos) +static void drag_leave_hover(struct x11_display *d, struct x11_surface *s, struct yasl_private_vector2i pos) { if (!d->drag.active || d->drag.hover != s) return; d->drag.hover = NULL; if (s && !s->destroyed) - yasl_surface_notify_drag(&s->base, YASL_DRAG_LEAVE, pos); + yasl_private_surface_notify_drag(&s->base, YASL_PRIVATE_DRAG_LEAVE, pos); } static void drag_drop_hover(struct x11_display *d) { if (!d->drag.active) return; - drag_end(d, YASL_DRAG_DROPPED); + drag_end(d, YASL_PRIVATE_DRAG_DROPPED); } -static int yasl_x11_error_handler(Display *native, XErrorEvent *event) +static int yasl_private_x11_error_handler(Display *native, XErrorEvent *event) { (void)native; (void)event; @@ -230,7 +259,7 @@ static int yasl_x11_error_handler(Display *native, XErrorEvent *event) return 0; } -static struct x11_surface *surface_at_root_pos(struct x11_display *d, int rx, int ry, struct yasl_vector2i *out_pos) +static struct x11_surface *surface_at_root_pos(struct x11_display *d, int rx, int ry, struct yasl_private_vector2i *out_pos) { if (!d->native) return NULL; Window ignore_icon = d->drag.icon_window; @@ -243,7 +272,7 @@ static struct x11_surface *surface_at_root_pos(struct x11_display *d, int rx, in if (XTranslateCoordinates(d->native, RootWindow(d->native, d->screen), s->window, rx, ry, &tx, &ty, &sub)) { if (tx >= 0 && ty >= 0 && tx < s->size.x && ty < s->size.y) { - if (out_pos) *out_pos = (struct yasl_vector2i){tx, ty}; + if (out_pos) *out_pos = (struct yasl_private_vector2i){tx, ty}; return s; } } @@ -253,19 +282,31 @@ static struct x11_surface *surface_at_root_pos(struct x11_display *d, int rx, in static uint32_t modifiers(unsigned state) { - return ((state & ShiftMask) ? YASL_MOD_SHIFT : 0) | - ((state & ControlMask) ? YASL_MOD_CTRL : 0) | - ((state & Mod1Mask) ? YASL_MOD_ALT : 0) | - ((state & Mod4Mask) ? YASL_MOD_SUPER : 0) | - ((state & LockMask) ? YASL_MOD_CAPS : 0); + return ((state & ShiftMask) ? YASL_PRIVATE_MOD_SHIFT : 0) | + ((state & ControlMask) ? YASL_PRIVATE_MOD_CTRL : 0) | + ((state & Mod1Mask) ? YASL_PRIVATE_MOD_ALT : 0) | + ((state & Mod4Mask) ? YASL_PRIVATE_MOD_SUPER : 0) | + ((state & LockMask) ? YASL_PRIVATE_MOD_CAPS : 0); } static void pointer_info(struct x11_display *d, struct x11_pointer *p, const XIDeviceInfo *info) { p->id = info->deviceid; - p->device = YASL_PTR_DEV_MOUSE; + p->device = YASL_PRIVATE_PTR_DEV_MOUSE; + p->tool = 0; + p->buttons = 0; p->pressure.number = p->tilt_x.number = p->tilt_y.number = -1; + p->rotation.number = p->distance.number = p->tangential.number = p->wheel.number = -1; + p->scroll_x.number = p->scroll_y.number = -1; + if (info->name) { + for (const char *s = info->name; *s; s++) { + if (!strncasecmp(s, "touchpad", 8) || !strncasecmp(s, "trackpad", 8)) { + p->finger_scroll = true; + break; + } + } + } bool touch = false; for (int i = 0; i < info->num_classes; i++) { XIAnyClassInfo *c = info->classes[i]; @@ -275,17 +316,42 @@ static void pointer_info(struct x11_display *d, struct x11_pointer *p, if (v->mode != XIModeAbsolute) continue; struct tablet_axis *axis = v->label == d->abs_pressure ? &p->pressure : v->label == d->abs_tilt_x ? &p->tilt_x : - v->label == d->abs_tilt_y ? &p->tilt_y : NULL; - if (axis) *axis = (struct tablet_axis){v->number, v->min, v->max, v->value}; + v->label == d->abs_tilt_y ? &p->tilt_y : + v->label == d->abs_rotation ? &p->rotation : + v->label == d->abs_distance ? &p->distance : + v->label == d->abs_tangential ? &p->tangential : + v->label == d->abs_wheel ? &p->wheel : NULL; + if (axis) *axis = (struct tablet_axis){v->number, v->min, v->max, v->value, true}; + } + for (int i = 0; i < info->num_classes; i++) { + XIAnyClassInfo *c = info->classes[i]; + if (c->type != XIScrollClass) continue; + XIScrollClassInfo *scroll = (XIScrollClassInfo *)c; + struct x11_scroll_axis *axis = scroll->scroll_type == XIScrollTypeVertical ? + &p->scroll_y : scroll->scroll_type == XIScrollTypeHorizontal ? &p->scroll_x : NULL; + if (!axis) continue; + *axis = (struct x11_scroll_axis){.number = scroll->number, + .increment = scroll->increment, .type = scroll->scroll_type}; + for (int j = 0; j < info->num_classes; j++) { + XIAnyClassInfo *vclass = info->classes[j]; + if (vclass->type != XIValuatorClass) continue; + XIValuatorClassInfo *v = (XIValuatorClassInfo *)vclass; + if (v->number == scroll->number) { + axis->value = v->value; + axis->initialized = axis->valid = true; + break; + } + } } if (!touch && p->pressure.number >= 0) { - p->device = YASL_PTR_DEV_PEN; + p->device = YASL_PRIVATE_PTR_DEV_PEN; + p->tool = 1; /* Xorg's Wacom and libinput drivers expose separate eraser devices. * Use the tool property when available, with the device name as a * fallback for servers (including Xwayland) without that property. */ if (info->name) { for (const char *s = info->name; *s; s++) - if (!strncasecmp(s, "eraser", 6)) { p->device = YASL_PTR_DEV_ERASER; break; } + if (!strncasecmp(s, "eraser", 6)) { p->device = YASL_PRIVATE_PTR_DEV_ERASER; break; } } } } @@ -301,17 +367,21 @@ static struct x11_pointer *pointer_for(struct x11_display *d, int id) struct x11_pointer *p = count ? calloc(1, sizeof(*p)) : NULL; if (p) { pointer_info(d, p, info); - if (p->device == YASL_PTR_DEV_PEN || p->device == YASL_PTR_DEV_ERASER) { + if (p->device == YASL_PRIVATE_PTR_DEV_PEN || p->device == YASL_PRIVATE_PTR_DEV_ERASER) { Atom type; int format; unsigned long n, after; unsigned char *value = NULL; if (XIGetProperty(d->native, id, d->tool_type, 0, 1, False, XA_ATOM, &type, &format, &n, &after, &value) == Success && - type == XA_ATOM && format == 32 && n == 1 && value) + type == XA_ATOM && format == 32 && n == 1 && value) { /* XIGetProperty packs format-32 values as uint32_t, unlike * XGetWindowProperty's array of native-sized longs. */ - p->device = *(uint32_t *)value == d->eraser ? YASL_PTR_DEV_ERASER : p->device; + if (*(uint32_t *)value == d->eraser) p->device = YASL_PRIVATE_PTR_DEV_ERASER; + else if (d->brush && *(uint32_t *)value == d->brush) p->tool = 3; + else if (d->airbrush && *(uint32_t *)value == d->airbrush) p->tool = 4; + else if (d->pencil && *(uint32_t *)value == d->pencil) p->tool = 5; + } if (value) XFree(value); } p->next = d->pointers; @@ -330,21 +400,61 @@ static void clear_pointers(struct x11_display *d) } } -static void update_axes(struct x11_pointer *p, const XIValuatorState *state) +static uint32_t update_axes(struct x11_pointer *p, const XIValuatorState *state) { + uint32_t changed = 0; const double *value = state->values; for (int i = 0; i < state->mask_len * 8; i++) { if (!XIMaskIsSet(state->mask, i)) continue; - struct tablet_axis *axes[] = {&p->pressure, &p->tilt_x, &p->tilt_y}; + struct tablet_axis *axes[] = {&p->pressure, &p->tilt_x, &p->tilt_y, &p->rotation, + &p->distance, &p->tangential, &p->wheel}; + const uint32_t bits[] = {YASL_PRIVATE_PEN_PRESSURE, YASL_PRIVATE_PEN_TILT, + YASL_PRIVATE_PEN_TILT, YASL_PRIVATE_PEN_ROTATION, YASL_PRIVATE_PEN_DISTANCE, + YASL_PRIVATE_PEN_TANGENTIAL, YASL_PRIVATE_PEN_WHEEL}; for (unsigned a = 0; a < sizeof(axes) / sizeof(axes[0]); a++) - if (axes[a]->number == i && isfinite(*value)) axes[a]->value = *value; + if (axes[a]->number == i && isfinite(*value)) { + axes[a]->value = *value; + axes[a]->valid = true; + changed |= bits[a]; + } + value++; + } + return changed; +} + +static void update_scroll(struct x11_pointer *p, const XIValuatorState *state, + struct yasl_private_pointer_event *out) +{ + const double *value = state->values; + for (int i = 0; i < state->mask_len * 8; i++) { + if (!XIMaskIsSet(state->mask, i)) continue; + struct x11_scroll_axis *axes[] = {&p->scroll_x, &p->scroll_y}; + for (unsigned a = 0; a < 2; a++) { + struct x11_scroll_axis *axis = axes[a]; + if (axis->number != i || !isfinite(*value)) continue; + if (axis->initialized) { + double delta = *value - axis->value; + if (axis->increment != 0 && isfinite(delta)) { + double lines = delta / axis->increment; + if (a == 0) out->scroll.x += lines; + else out->scroll.y += lines; + if (lines != 0) { + out->scroll_valid |= 1u; + out->scroll_source = p->finger_scroll ? 2u : 1u; + out->scroll_phase = 2u; + } + } + } + axis->value = *value; + axis->initialized = true; + } value++; } } static uint16_t pressure_value(const struct tablet_axis *a) { - if (a->number < 0 || !(a->max > a->min) || !isfinite(a->value)) return 0; + if (a->number < 0 || !(a->max > a->min) || !a->valid || !isfinite(a->value)) return 0; double v = (a->value - a->min) / (a->max - a->min); return (uint16_t)lround(fmax(0, fmin(1, v)) * 65535); } @@ -352,50 +462,121 @@ static uint16_t pressure_value(const struct tablet_axis *a) static int8_t tilt_value(const struct tablet_axis *a) { /* Xorg tablet tilt valuators are already in degrees. */ - return a->number < 0 || !isfinite(a->value) ? 0 : + return a->number < 0 || !a->valid || !isfinite(a->value) ? 0 : (int8_t)lround(fmax(-90, fmin(90, a->value))); } -static enum yasl_pointer_button pointer_button(unsigned button) +static enum yasl_private_pointer_button pointer_button(unsigned button) +{ + return button == 1 ? YASL_PRIVATE_BUTTON_PRIMARY : + button == 2 ? YASL_PRIVATE_BUTTON_MIDDLE : + button == 3 ? YASL_PRIVATE_BUTTON_SECONDARY : + button >= 8 && button <= INT16_MAX ? (enum yasl_private_pointer_button)(button - 5) : + YASL_PRIVATE_BUTTON_BACK; +} + +static uint32_t pen_axes_valid(const struct x11_pointer *p) { - return button == 1 ? YASL_BUTTON_PRIMARY : - button == 2 ? YASL_BUTTON_MIDDLE : - button == 3 ? YASL_BUTTON_SECONDARY : - button >= 8 && button <= INT16_MAX ? (enum yasl_pointer_button)(button - 5) : - YASL_BUTTON_BACK; + return (p->pressure.valid ? YASL_PRIVATE_PEN_PRESSURE : 0) | + (p->tilt_x.valid || p->tilt_y.valid ? YASL_PRIVATE_PEN_TILT : 0) | + (p->rotation.valid ? YASL_PRIVATE_PEN_ROTATION : 0) | + (p->distance.valid ? YASL_PRIVATE_PEN_DISTANCE : 0) | + (p->tangential.valid ? YASL_PRIVATE_PEN_TANGENTIAL : 0) | + (p->wheel.valid ? YASL_PRIVATE_PEN_WHEEL : 0); +} + +static double axis_normalized(const struct tablet_axis *axis) +{ + return axis->valid && axis->max > axis->min && isfinite(axis->value) ? + fmax(0, fmin(1, (axis->value - axis->min) / (axis->max - axis->min))) : 0; +} + +static void push_pen_sample(struct x11_surface *s, struct x11_pointer *p, + unsigned action, double x, double y, uint32_t changed, unsigned changed_button) +{ + if (!p || (p->device != YASL_PRIVATE_PTR_DEV_PEN && p->device != YASL_PRIVATE_PTR_DEV_ERASER)) return; + push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_PEN, + .pen = { + .device_id = (uint64_t)(unsigned)p->id, + .tool_id = 0, /* XI2 exposes source IDs, but not a stable per-tool serial. */ + .x = x, .y = y, + .pressure = p->pressure.valid && p->pressure.max > p->pressure.min ? + fmax(0, fmin(1, (p->pressure.value - p->pressure.min) / + (p->pressure.max - p->pressure.min))) : 0, + .tilt_x = p->tilt_x.valid ? fmax(-90, fmin(90, p->tilt_x.value)) : 0, + .tilt_y = p->tilt_y.valid ? fmax(-90, fmin(90, p->tilt_y.value)) : 0, + .rotation = p->rotation.valid ? p->rotation.value : 0, + .distance = axis_normalized(&p->distance), + .tangential = p->tangential.valid && p->tangential.max > p->tangential.min ? + fmax(-1, fmin(1, 2 * axis_normalized(&p->tangential) - 1)) : 0, + .wheel_degrees = p->wheel.valid ? p->wheel.value : 0, + .axes_valid = pen_axes_valid(p), .axes_changed = changed, + .buttons = p->buttons, .changed_button = changed_button, + .in_contact = (p->buttons & YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY)) != 0, + .tool = p->device == YASL_PRIVATE_PTR_DEV_ERASER ? 2u : p->tool, + .action = action, + }}, NULL, 0); } static void xi_pointer_event(struct x11_surface *s, const XIDeviceEvent *ev) { struct x11_display *d = (struct x11_display *)s->base.display; struct x11_pointer *p = pointer_for(d, ev->sourceid); - struct yasl_event event = { + struct yasl_private_event event = { .pointer = { - .pos = {yasl_scale_to_device(ev->event_x, YASL_SCALE_UNIT), - yasl_scale_to_device(ev->event_y, YASL_SCALE_UNIT)}, - .device = p ? p->device : YASL_PTR_DEV_MOUSE, + .pos = {yasl_private_scale_to_device(ev->event_x, YASL_PRIVATE_SCALE_UNIT), + yasl_private_scale_to_device(ev->event_y, YASL_PRIVATE_SCALE_UNIT)}, + .device = p ? p->device : YASL_PRIVATE_PTR_DEV_MOUSE, .mods = modifiers(ev->mods.effective), }, }; - if (p && (p->device == YASL_PTR_DEV_PEN || p->device == YASL_PTR_DEV_ERASER)) { - update_axes(p, &ev->valuators); - if (ev->evtype == XI_ButtonRelease && ev->detail == 1) + uint32_t pen_changed = 0; + if (p && (p->device == YASL_PRIVATE_PTR_DEV_PEN || p->device == YASL_PRIVATE_PTR_DEV_ERASER)) { + pen_changed = update_axes(p, &ev->valuators); + if (ev->evtype == XI_ButtonRelease && ev->detail == 1) { p->pressure.value = p->pressure.min; - event.pointer.pen = (struct yasl_pointer_pen){ + pen_changed |= p->pressure.valid ? YASL_PRIVATE_PEN_PRESSURE : 0; + } + if (ev->evtype == XI_ButtonPress || ev->evtype == XI_ButtonRelease) { + enum yasl_private_pointer_button button = pointer_button(ev->detail); + uint32_t mask = YASL_PRIVATE_BUTTON_BIT(button); + if (ev->evtype == XI_ButtonPress) p->buttons |= mask; + else p->buttons &= ~mask; + } + event.pointer.pen = (struct yasl_private_pointer_pen){ pressure_value(&p->pressure), tilt_value(&p->tilt_x), tilt_value(&p->tilt_y)}; } - if (ev->evtype == XI_Motion) event.type = YASL_EVENT_POINTER_MOTION; + if (p) update_scroll(p, &ev->valuators, &event.pointer); + if (ev->evtype == XI_Motion) event.type = YASL_PRIVATE_EVENT_POINTER_MOTION; else if (ev->detail >= 4 && ev->detail <= 7) { if (ev->evtype == XI_ButtonRelease) return; - event.type = YASL_EVENT_POINTER_SCROLL; + event.type = YASL_PRIVATE_EVENT_POINTER_SCROLL; if (ev->detail <= 5) event.pointer.scroll.y = ev->detail == 4 ? -1 : 1; else event.pointer.scroll.x = ev->detail == 6 ? -1 : 1; + event.pointer.scroll_valid = 1u; + event.pointer.scroll_source = p && p->finger_scroll ? 2u : 1u; + event.pointer.scroll_phase = 2u; } else { - event.type = ev->evtype == XI_ButtonPress ? YASL_EVENT_POINTER_DOWN : YASL_EVENT_POINTER_UP; + event.type = ev->evtype == XI_ButtonPress ? YASL_PRIVATE_EVENT_POINTER_DOWN : YASL_PRIVATE_EVENT_POINTER_UP; event.pointer.button = pointer_button(ev->detail); } d->mods = event.pointer.mods; + if (p && (p->device == YASL_PRIVATE_PTR_DEV_PEN || p->device == YASL_PRIVATE_PTR_DEV_ERASER) && + ev->detail != 4 && ev->detail != 5 && ev->detail != 6 && ev->detail != 7) { + unsigned action = ev->evtype == XI_Motion ? YASL_PRIVATE_PEN_MOVE : + ev->evtype == XI_ButtonPress ? (ev->detail == 1 ? YASL_PRIVATE_PEN_DOWN : YASL_PRIVATE_PEN_BUTTON) : + (ev->detail == 1 ? YASL_PRIVATE_PEN_UP : YASL_PRIVATE_PEN_BUTTON); + push_pen_sample(s, p, action, ev->event_x, ev->event_y, pen_changed, + ev->evtype == XI_Motion ? 0u : (unsigned)ev->detail); + } push(s, event, NULL, 0); + if (event.type == YASL_PRIVATE_EVENT_POINTER_MOTION && event.pointer.scroll_valid) { + event.type = YASL_PRIVATE_EVENT_POINTER_SCROLL; + event.pointer.pos = (struct yasl_private_vector2i){ + yasl_private_scale_to_device(ev->event_x, YASL_PRIVATE_SCALE_UNIT), + yasl_private_scale_to_device(ev->event_y, YASL_PRIVATE_SCALE_UNIT)}; + push(s, event, NULL, 0); + } } static void xi_event(struct x11_display *d, XGenericEventCookie *cookie) @@ -403,15 +584,20 @@ static void xi_event(struct x11_display *d, XGenericEventCookie *cookie) if (cookie->evtype == XI_HierarchyChanged || cookie->evtype == XI_DeviceChanged) { if (cookie->evtype == XI_DeviceChanged && ((XIDeviceChangedEvent *)cookie->data)->reason == XISlaveSwitch) return; + for (struct x11_pointer *p = d->pointers; p; p = p->next) { + if (!p->buttons) continue; + for (struct x11_surface *s = d->surfaces; s; s = s->next) + push_pen_sample(s, p, YASL_PRIVATE_PEN_CANCEL, 0, 0, 0, 0); + } clear_pointers(d); if (cookie->evtype == XI_HierarchyChanged && (((XIHierarchyEvent *)cookie->data)->flags & (XISlaveRemoved | XIDeviceDisabled | XISlaveDetached | XIMasterRemoved))) { /* Removal/disable may interrupt a drag before a release arrives. */ if (d->drag.active) - drag_end(d, YASL_DRAG_CANCELLED); + drag_end(d, YASL_PRIVATE_DRAG_CANCELLED); for (struct x11_surface *s = d->surfaces; s; s = s->next) - push(s, (struct yasl_event){.type = YASL_EVENT_INPUT_RESET}, NULL, 0); + push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_INPUT_RESET}, NULL, 0); } return; } @@ -430,22 +616,22 @@ static void xi_event(struct x11_display *d, XGenericEventCookie *cookie) XMoveWindow(d->native, d->drag.icon_window, (int)round(mev->root_x) - d->drag.hotspot.x, (int)round(mev->root_y) - d->drag.hotspot.y); - struct yasl_vector2i hpos = {yasl_scale_to_device(mev->event_x, YASL_SCALE_UNIT), - yasl_scale_to_device(mev->event_y, YASL_SCALE_UNIT)}; + struct yasl_private_vector2i hpos = {yasl_private_scale_to_device(mev->event_x, YASL_PRIVATE_SCALE_UNIT), + yasl_private_scale_to_device(mev->event_y, YASL_PRIVATE_SCALE_UNIT)}; struct x11_surface *hsurf = d->native ? surface_at_root_pos(d, (int)round(mev->root_x), (int)round(mev->root_y), &hpos) : s; drag_set_hover(d, hsurf, hpos); } else if (cookie->evtype == XI_Enter && d->drag.active) { const XIEnterEvent *eev = cookie->data; - struct yasl_vector2i hpos = {yasl_scale_to_device(eev->event_x, YASL_SCALE_UNIT), - yasl_scale_to_device(eev->event_y, YASL_SCALE_UNIT)}; + struct yasl_private_vector2i hpos = {yasl_private_scale_to_device(eev->event_x, YASL_PRIVATE_SCALE_UNIT), + yasl_private_scale_to_device(eev->event_y, YASL_PRIVATE_SCALE_UNIT)}; struct x11_surface *hsurf = d->native ? surface_at_root_pos(d, (int)round(eev->root_x), (int)round(eev->root_y), &hpos) : s; drag_set_hover(d, hsurf, hpos); } else if (cookie->evtype == XI_Leave && d->drag.active) { const XIEnterEvent *lev = cookie->data; - struct yasl_vector2i lpos = {yasl_scale_to_device(lev->event_x, YASL_SCALE_UNIT), - yasl_scale_to_device(lev->event_y, YASL_SCALE_UNIT)}; + struct yasl_private_vector2i lpos = {yasl_private_scale_to_device(lev->event_x, YASL_PRIVATE_SCALE_UNIT), + yasl_private_scale_to_device(lev->event_y, YASL_PRIVATE_SCALE_UNIT)}; drag_leave_hover(d, s, lpos); } else if (cookie->evtype == XI_ButtonRelease && d->drag.active) { drag_drop_hover(d); @@ -455,24 +641,35 @@ static void xi_event(struct x11_display *d, XGenericEventCookie *cookie) else { const XIEnterEvent *ev = cookie->data; struct x11_pointer *p = pointer_for(d, ev->sourceid); - if (p) p->pressure.value = p->pressure.min; - push(s, (struct yasl_event){ - .type = cookie->evtype == XI_Leave ? YASL_EVENT_POINTER_LEAVE : YASL_EVENT_POINTER_MOTION, + if (p && cookie->evtype == XI_Leave) { + p->pressure.value = p->pressure.min; + if (p->pressure.valid) push_pen_sample(s, p, YASL_PRIVATE_PEN_LEAVE, + ev->event_x, ev->event_y, YASL_PRIVATE_PEN_PRESSURE, 0); + } else if (p && (p->device == YASL_PRIVATE_PTR_DEV_PEN || p->device == YASL_PRIVATE_PTR_DEV_ERASER)) { + push_pen_sample(s, p, YASL_PRIVATE_PEN_ENTER, ev->event_x, ev->event_y, 0, 0); + } + push(s, (struct yasl_private_event){ + .type = cookie->evtype == XI_Leave ? YASL_PRIVATE_EVENT_POINTER_LEAVE : YASL_PRIVATE_EVENT_POINTER_MOTION, .pointer = { - .pos = {yasl_scale_to_device(ev->event_x, YASL_SCALE_UNIT), - yasl_scale_to_device(ev->event_y, YASL_SCALE_UNIT)}, - .device = p ? p->device : YASL_PTR_DEV_MOUSE, + .pos = {yasl_private_scale_to_device(ev->event_x, YASL_PRIVATE_SCALE_UNIT), + yasl_private_scale_to_device(ev->event_y, YASL_PRIVATE_SCALE_UNIT)}, + .device = p ? p->device : YASL_PRIVATE_PTR_DEV_MOUSE, .mods = modifiers(ev->mods.effective), }, }, NULL, 0); } } -static bool resize(struct x11_surface *s, struct yasl_vector2i size) +static bool resize(struct x11_surface *s, struct yasl_private_vector2i size) { if (size.x <= 0 || size.y <= 0 || size.x > 32767 || size.y > 32767 || (size_t)size.x > SIZE_MAX / sizeof(uint32_t) / (size_t)size.y) return false; + if (s->base.presenter != YASL_PRIVATE_PRESENTER_CPU) { + s->size = size; + s->base.size = size; + return true; + } if (s->image && size.x == s->size.x && size.y == s->size.y) return true; struct x11_display *d = (struct x11_display *)s->base.display; if (!d->native) { @@ -480,7 +677,7 @@ static bool resize(struct x11_surface *s, struct yasl_vector2i size) s->want_draw = true; return true; } - uint64_t start = yasl_loop_now_ns(); + uint64_t start = yasl_private_loop_now_ns(); /* The presented frame goes with the old pixels. */ s->presented = false; @@ -557,9 +754,9 @@ static bool resize(struct x11_surface *s, struct yasl_vector2i size) s->pixels = pixels; s->size = size; s->base.size = size; - s->base.scale_120 = YASL_SCALE_UNIT; + s->base.scale_120 = YASL_PRIVATE_SCALE_UNIT; s->want_draw = true; - yasl_clock_note_resize(&s->clock, yasl_loop_now_ns() - start); + yasl_private_clock_note_resize(&s->clock, yasl_private_loop_now_ns() - start); return true; } @@ -575,7 +772,7 @@ static void cancel_pastes(struct x11_display *d, struct x11_surface *target) p = &r->next; } else if (!target) { *p = r->next; - if (r->timeout) yasl_loop_del_timer(d->loop, r->timeout); + if (r->timeout) yasl_private_loop_del_timer(d->loop, r->timeout); XDestroyWindow(d->native, r->window); free(r); } else { @@ -584,7 +781,7 @@ static void cancel_pastes(struct x11_display *d, struct x11_surface *target) } } -static uint64_t paste_timeout(void *ud, yasl_timer_id id, uint64_t interval_ns) +static uint64_t paste_timeout(void *ud, yasl_private_timer_id id, uint64_t interval_ns) { (void)id; (void)interval_ns; @@ -597,7 +794,7 @@ static uint64_t paste_timeout(void *ud, yasl_timer_id id, uint64_t interval_ns) r->timeout = 0; /* The loop retires this one-shot timer after the callback. */ XDestroyWindow(d->native, r->window); if (s && !s->destroyed) - yasl_surface_complete_paste(&s->base, r->id, YASL_TRANSFER_FAILED, NULL, 0); + yasl_private_surface_complete_paste(&s->base, r->id, YASL_PRIVATE_TRANSFER_FAILED, NULL, 0); free(r); return 0; } @@ -609,7 +806,7 @@ static void surface_destroy(struct yasl_surface *base) struct x11_display *d = (struct x11_display *)base->display; if (d->drag.active) { if (d->drag.origin == s || d->drag.carried == s) - drag_end(d, YASL_DRAG_CANCELLED); + drag_end(d, YASL_PRIVATE_DRAG_CANCELLED); else if (d->drag.hover == s) d->drag.hover = NULL; } @@ -627,6 +824,10 @@ static void surface_destroy(struct yasl_surface *base) free(s->ime_preedit); s->ime_preedit = NULL; if (s->ic) XDestroyIC(s->ic); +#if YASL_HAS_EGL + if (s->gl_context && d->egl_ready) eglDestroyContext(d->egl, s->gl_context); + if (s->gl_surface && d->egl_ready) eglDestroySurface(d->egl, s->gl_surface); +#endif if (s->shm_active) { XShmDetach(d->native, &s->shm_info); if (s->image) XDestroyImage(s->image); @@ -643,6 +844,7 @@ static void surface_destroy(struct yasl_surface *base) if (d->native) { if (s->gc) XFreeGC(d->native, s->gc); if (s->window) XDestroyWindow(d->native, s->window); + if (s->colormap) XFreeColormap(d->native, s->colormap); } input_queue_clear(&s->input); if (d->depth) { @@ -652,11 +854,11 @@ static void surface_destroy(struct yasl_surface *base) } static void set_callbacks(struct yasl_surface *base, - const struct yasl_window_cbs *cbs, void *ud) + const struct yasl_private_window_cbs *cbs, void *ud) { struct x11_surface *s = (struct x11_surface *)base; if (s->destroyed) return; - s->base.callbacks = cbs ? *cbs : (struct yasl_window_cbs){0}; + s->base.callbacks = cbs ? *cbs : (struct yasl_private_window_cbs){0}; s->cbs = &s->base.callbacks; s->base.ud = ud; s->ud = ud; @@ -685,7 +887,7 @@ static bool net_message(struct x11_surface *s, Atom type, SubstructureRedirectMask | SubstructureNotifyMask, &event) != 0; } -static bool surface_set_size(struct yasl_surface *base, struct yasl_vector2i size) +static bool surface_set_size(struct yasl_surface *base, struct yasl_private_vector2i size) { struct x11_surface *s = (struct x11_surface *)base; struct x11_display *d = (struct x11_display *)base->display; @@ -817,7 +1019,7 @@ static char *xim_text_dup(const XIMText *text, size_t *len) if (converted != text->length) { free(out); return NULL; } } out[used] = '\0'; - if (!yasl_utf8_validate(out, used)) { free(out); return NULL; } + if (!yasl_private_utf8_validate(out, used)) { free(out); return NULL; } *len = used; return out; } @@ -831,7 +1033,7 @@ static void xim_preedit_done(XIM im, XPointer client_data, XPointer call_data) free(s->ime_preedit); s->ime_preedit = NULL; s->ime_cursor_begin = s->ime_cursor_end = -1; - yasl_surface_notify_composition(&s->base, NULL, -1, -1); + yasl_private_surface_notify_composition(&s->base, NULL, -1, -1); } static int xim_preedit_start(XIM im, XPointer client_data, XPointer call_data) @@ -880,7 +1082,7 @@ static void xim_preedit_draw(XIM im, XPointer client_data, XPointer call_data) int caret = draw->caret < 0 ? -1 : (int)utf8_offset_for_chars( s->ime_preedit ? s->ime_preedit : "", new_len, draw->caret); s->ime_cursor_begin = s->ime_cursor_end = caret; - yasl_surface_notify_composition(&s->base, s->ime_preedit, caret, caret); + yasl_private_surface_notify_composition(&s->base, s->ime_preedit, caret, caret); } static void xim_preedit_caret(XIM im, XPointer client_data, XPointer call_data) @@ -892,7 +1094,7 @@ static void xim_preedit_caret(XIM im, XPointer client_data, XPointer call_data) int offset = caret->position < 0 ? -1 : (int)utf8_offset_for_chars( s->ime_preedit, strlen(s->ime_preedit), caret->position); s->ime_cursor_begin = s->ime_cursor_end = offset; - yasl_surface_notify_composition(&s->base, s->ime_preedit, offset, offset); + yasl_private_surface_notify_composition(&s->base, s->ime_preedit, offset, offset); } static XIC create_ic(struct x11_display *d, struct x11_surface *s) @@ -923,22 +1125,35 @@ static XIC create_ic(struct x11_display *d, struct x11_surface *s) return ic; } -static struct yasl_surface *create_toplevel(struct yasl_display *base, - const struct yasl_window_spec *spec, void *graphics_device) +static struct yasl_surface *create_toplevel_visual(struct yasl_private_display *base, + const struct yasl_private_window_spec *spec, Visual *visual, int depth, + enum yasl_private_presenter presenter) { - (void)graphics_device; if (!spec) return NULL; struct x11_display *d = (struct x11_display *)base; struct x11_surface *s = calloc(1, sizeof(*s)); if (!s) return NULL; s->base.display = base; - s->clock.refresh_ns = YASL_REFRESH_DEFAULT_NS; + s->base.creation_flags = spec->flags | (spec->external_gpu ? 0u : YASL_PRIVATE_SURFACE_CPU); + s->base.presenter = presenter; + s->clock.refresh_ns = YASL_PRIVATE_REFRESH_DEFAULT_NS; if (!resize(s, spec->size)) { free(s); return NULL; } - s->window = XCreateSimpleWindow(d->native, RootWindow(d->native, d->screen), - 0, 0, (unsigned)spec->size.x, (unsigned)spec->size.y, 0, 0, 0); + if (!visual) visual = DefaultVisual(d->native, d->screen); + if (!depth) depth = DefaultDepth(d->native, d->screen); + s->colormap = XCreateColormap(d->native, RootWindow(d->native, d->screen), visual, AllocNone); + XSetWindowAttributes attrs = {.colormap = s->colormap, .border_pixel = 0, .background_pixel = 0}; + s->window = XCreateWindow(d->native, RootWindow(d->native, d->screen), 0, 0, + (unsigned)spec->size.x, (unsigned)spec->size.y, 0, depth, InputOutput, + visual, CWBorderPixel | CWBackPixel | CWColormap, &attrs); + if (!s->window) { + XFreeColormap(d->native, s->colormap); + free(s); + errno = ENODEV; + return NULL; + } s->base.native_handle = (void *)(uintptr_t)s->window; s->base.size = s->size; - s->base.scale_120 = YASL_SCALE_UNIT; + s->base.scale_120 = YASL_PRIVATE_SCALE_UNIT; s->gc = XCreateGC(d->native, s->window, 0, NULL); XSelectInput(d->native, s->window, ExposureMask | StructureNotifyMask | FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask | @@ -955,6 +1170,19 @@ static struct yasl_surface *create_toplevel(struct yasl_display *base, XISelectEvents(d->native, s->window, &mask, 1); } XSetWMProtocols(d->native, s->window, &d->delete_window, 1); + if (spec->flags & YASL_PRIVATE_SURFACE_UNDECORATED) { + struct { unsigned long flags, functions, decorations; long input_mode; unsigned long status; } hints = { + .flags = 1, .decorations = 0, + }; + XChangeProperty(d->native, s->window, d->motif_hints, d->motif_hints, 32, + PropModeReplace, (unsigned char *)&hints, 5); + } + if (spec->flags & YASL_PRIVATE_SURFACE_FIXED_SIZE) { + XSizeHints size_hints = {.flags = PMinSize | PMaxSize, + .min_width = spec->size.x, .max_width = spec->size.x, + .min_height = spec->size.y, .max_height = spec->size.y}; + XSetWMNormalHints(d->native, s->window, &size_hints); + } XClassHint hint = { .res_name = d->app_id, .res_class = d->app_id }; XSetClassHint(d->native, s->window, &hint); Atom xdnd_version = 5; @@ -967,17 +1195,197 @@ static struct yasl_surface *create_toplevel(struct yasl_display *base, if (spec->fullscreen) { XChangeProperty(d->native, s->window, d->net_state, XA_ATOM, 32, PropModeReplace, (unsigned char *)&d->fullscreen, 1); - s->base.state |= YASL_STATE_FULLSCREEN; + s->base.state |= YASL_PRIVATE_STATE_FULLSCREEN; } - XMapWindow(d->native, s->window); + if (!(spec->flags & YASL_PRIVATE_SURFACE_HIDDEN)) XMapWindow(d->native, s->window); XFlush(d->native); return &s->base; } +static struct yasl_surface *create_toplevel(struct yasl_private_display *base, + const struct yasl_private_window_spec *spec, void *graphics_device) +{ + if (!spec) return NULL; + if (graphics_device) { errno = ENOTSUP; return NULL; } + return create_toplevel_visual(base, spec, NULL, 0, + spec->external_gpu ? YASL_PRIVATE_PRESENTER_NONE : YASL_PRIVATE_PRESENTER_CPU); +} + +#if YASL_HAS_EGL +static bool egl_open(struct x11_display *d) +{ + if (d->egl_ready) return true; + d->egl = eglGetDisplay((EGLNativeDisplayType)d->native); + EGLint major = 0, minor = 0; + if (d->egl == EGL_NO_DISPLAY || !eglInitialize(d->egl, &major, &minor)) { + errno = ENOTSUP; + return false; + } + d->egl_ready = true; + return true; +} + +static struct yasl_surface *create_gl_surface(struct yasl_private_display *base, + const struct yasl_private_window_spec *spec, const int *attrs, + struct yasl_surface *share) +{ + if (!spec) { errno = EINVAL; return NULL; } + struct x11_display *d = (struct x11_display *)base; + struct yasl_private_gl_options options; + if (!yasl_private_gl_parse_options(attrs, &options)) { errno = EINVAL; return NULL; } + if (!egl_open(d)) return NULL; + if (options.srgb) { + const char *extensions = eglQueryString(d->egl, EGL_EXTENSIONS); + if (!extensions || !strstr(extensions, "EGL_KHR_gl_colorspace")) { + errno = ENOTSUP; + return NULL; + } + } + + EGLint renderable = options.api == YASL_PRIVATE_GL_OPENGL_ES ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT; + EGLint config_attrs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, renderable, + EGL_RED_SIZE, options.red_bits, + EGL_GREEN_SIZE, options.green_bits, + EGL_BLUE_SIZE, options.blue_bits, + EGL_ALPHA_SIZE, options.alpha_bits, + EGL_DEPTH_SIZE, options.depth_bits, + EGL_STENCIL_SIZE, options.stencil_bits, + EGL_SAMPLE_BUFFERS, options.samples > 0 ? 1 : 0, + EGL_SAMPLES, options.samples, + EGL_NONE, + }; + EGLConfig config = NULL; + EGLint count = 0; + if (!eglChooseConfig(d->egl, config_attrs, &config, 1, &count) || !count) { + errno = ENOTSUP; + return NULL; + } + EGLint visual_id = 0, depth = 0; + if (!eglGetConfigAttrib(d->egl, config, EGL_NATIVE_VISUAL_ID, &visual_id) || + !eglGetConfigAttrib(d->egl, config, EGL_BUFFER_SIZE, &depth)) { + errno = ENODEV; + return NULL; + } + XVisualInfo visual_query = {.visualid = (VisualID)visual_id}; + int visual_count = 0; + XVisualInfo *visual_info = XGetVisualInfo(d->native, VisualIDMask, &visual_query, &visual_count); + if (!visual_info || !visual_count) { + if (visual_info) XFree(visual_info); + errno = ENODEV; + return NULL; + } + Visual *visual = visual_info[0].visual; + depth = visual_info[0].depth; + struct yasl_private_window_spec gl_spec = *spec; + gl_spec.external_gpu = true; + gl_spec.flags &= ~YASL_PRIVATE_SURFACE_CPU; + struct yasl_surface *base_surface = create_toplevel_visual(base, &gl_spec, visual, depth, + YASL_PRIVATE_PRESENTER_GL); + XFree(visual_info); + if (!base_surface) return NULL; + struct x11_surface *s = (struct x11_surface *)base_surface; + s->gl_config = config; + EGLint surface_attrs[] = {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, EGL_NONE}; + s->gl_surface = eglCreateWindowSurface(d->egl, config, + (EGLNativeWindowType)s->window, options.srgb ? surface_attrs : NULL); + if (s->gl_surface == EGL_NO_SURFACE) { + surface_destroy(base_surface); + errno = ENODEV; + return NULL; + } + if (options.api == YASL_PRIVATE_GL_OPENGL) { + if (!eglBindAPI(EGL_OPENGL_API)) { surface_destroy(base_surface); errno = ENOTSUP; return NULL; } + } else if (!eglBindAPI(EGL_OPENGL_ES_API)) { + surface_destroy(base_surface); errno = ENOTSUP; return NULL; + } + EGLContext share_context = EGL_NO_CONTEXT; + if (share) { + struct x11_surface *shared = (struct x11_surface *)share; + if (share->display != base || shared->base.presenter != YASL_PRIVATE_PRESENTER_GL || + shared->gl_context == EGL_NO_CONTEXT) { + surface_destroy(base_surface); errno = EINVAL; return NULL; + } + share_context = shared->gl_context; + } + EGLint context_attrs[12]; + EGLint n = 0; + context_attrs[n++] = EGL_CONTEXT_MAJOR_VERSION; + context_attrs[n++] = options.major; + context_attrs[n++] = EGL_CONTEXT_MINOR_VERSION; + context_attrs[n++] = options.minor; + if (options.api == YASL_PRIVATE_GL_OPENGL && options.profile != YASL_PRIVATE_GL_ANY) { + context_attrs[n++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR; + context_attrs[n++] = options.profile == YASL_PRIVATE_GL_CORE ? + EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR : EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; + } + EGLint context_flags = (options.debug ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0) | + (options.forward_compat ? EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR : 0); + if (context_flags) { + context_attrs[n++] = EGL_CONTEXT_FLAGS_KHR; + context_attrs[n++] = context_flags; + } + context_attrs[n] = EGL_NONE; + s->gl_context = eglCreateContext(d->egl, config, share_context, context_attrs); + if (s->gl_context == EGL_NO_CONTEXT) { + surface_destroy(base_surface); + errno = ENOTSUP; + return NULL; + } + return base_surface; +} + +static bool gl_make_current(struct yasl_private_display *base, struct yasl_surface *surface) +{ + struct x11_display *d = (struct x11_display *)base; + if (!d->egl_ready) { errno = ENOTSUP; return false; } + if (!surface) return eglMakeCurrent(d->egl, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + struct x11_surface *s = (struct x11_surface *)surface; + if (surface->display != base || s->base.presenter != YASL_PRIVATE_PRESENTER_GL || !s->gl_context) { + errno = EINVAL; + return false; + } + return eglMakeCurrent(d->egl, s->gl_surface, s->gl_surface, s->gl_context) == EGL_TRUE; +} + +static yasl_private_gl_proc gl_get_proc_address(struct yasl_private_display *base, const char *name) +{ + (void)base; + return name ? (yasl_private_gl_proc)eglGetProcAddress(name) : NULL; +} + +static bool gl_swap_interval(struct yasl_private_display *base, struct yasl_surface *surface, int interval) +{ + struct x11_display *d = (struct x11_display *)base; + if (!surface || surface->display != base || ((struct x11_surface *)surface)->base.presenter != YASL_PRIVATE_PRESENTER_GL) { + errno = EINVAL; + return false; + } + return d->egl_ready && eglSwapInterval(d->egl, interval) == EGL_TRUE; +} + +static bool gl_swap_buffers(struct yasl_private_display *base, struct yasl_surface *surface) +{ + struct x11_display *d = (struct x11_display *)base; + struct x11_surface *s = (struct x11_surface *)surface; + if (!surface || surface->display != base || s->base.presenter != YASL_PRIVATE_PRESENTER_GL || !d->egl_ready) { + errno = EINVAL; + return false; + } + if (!eglSwapBuffers(d->egl, s->gl_surface)) return false; + s->presented = true; + yasl_private_clock_presented(&s->clock, yasl_private_loop_now_ns(), false); + return true; +} +#endif + static bool popup_spec_valid(const struct x11_surface *parent, - const struct yasl_popup_spec *spec) + const struct yasl_private_popup_spec *spec) { if (!parent || !spec) return false; + if (spec->positioner_explicit && (spec->anchor_edges > 8 || spec->gravity > 8 || + (spec->constraints & ~63u))) return false; int parent_cx = parent->is_popup ? parent->content_offset.x : 0; int parent_cy = parent->is_popup ? parent->content_offset.y : 0; int64_t x = (int64_t)spec->anchor.x - parent_cx; @@ -992,10 +1400,87 @@ static bool popup_spec_valid(const struct x11_surface *parent, (int64_t)spec->content_offset.y + spec->content_size.y <= spec->size.y; } +enum { X11_ANCHOR_NONE, X11_ANCHOR_TOP, X11_ANCHOR_BOTTOM, X11_ANCHOR_LEFT, + X11_ANCHOR_RIGHT, X11_ANCHOR_TOP_LEFT, X11_ANCHOR_BOTTOM_LEFT, + X11_ANCHOR_TOP_RIGHT, X11_ANCHOR_BOTTOM_RIGHT }; +enum { X11_CONSTRAINT_SLIDE_X = 1u, X11_CONSTRAINT_SLIDE_Y = 2u, + X11_CONSTRAINT_FLIP_X = 4u, X11_CONSTRAINT_FLIP_Y = 8u, + X11_CONSTRAINT_RESIZE_X = 16u, X11_CONSTRAINT_RESIZE_Y = 32u }; + +static void popup_anchor_point(const struct yasl_private_popup_spec *spec, int *x, int *y) +{ + switch (spec->anchor_edges) { + case X11_ANCHOR_TOP: *x = spec->anchor.x + spec->anchor.width / 2; *y = spec->anchor.y; break; + case X11_ANCHOR_BOTTOM: *x = spec->anchor.x + spec->anchor.width / 2; *y = spec->anchor.y + spec->anchor.height; break; + case X11_ANCHOR_LEFT: *x = spec->anchor.x; *y = spec->anchor.y + spec->anchor.height / 2; break; + case X11_ANCHOR_RIGHT: *x = spec->anchor.x + spec->anchor.width; *y = spec->anchor.y + spec->anchor.height / 2; break; + case X11_ANCHOR_TOP_LEFT: *x = spec->anchor.x; *y = spec->anchor.y; break; + case X11_ANCHOR_BOTTOM_LEFT: *x = spec->anchor.x; *y = spec->anchor.y + spec->anchor.height; break; + case X11_ANCHOR_TOP_RIGHT: *x = spec->anchor.x + spec->anchor.width; *y = spec->anchor.y; break; + case X11_ANCHOR_BOTTOM_RIGHT: *x = spec->anchor.x + spec->anchor.width; *y = spec->anchor.y + spec->anchor.height; break; + default: *x = spec->anchor.x + spec->anchor.width / 2; *y = spec->anchor.y + spec->anchor.height / 2; break; + } +} + +static unsigned popup_flip_x(unsigned gravity) +{ + switch (gravity) { + case X11_ANCHOR_LEFT: return X11_ANCHOR_RIGHT; + case X11_ANCHOR_RIGHT: return X11_ANCHOR_LEFT; + case X11_ANCHOR_TOP_LEFT: return X11_ANCHOR_TOP_RIGHT; + case X11_ANCHOR_BOTTOM_LEFT: return X11_ANCHOR_BOTTOM_RIGHT; + case X11_ANCHOR_TOP_RIGHT: return X11_ANCHOR_TOP_LEFT; + case X11_ANCHOR_BOTTOM_RIGHT: return X11_ANCHOR_BOTTOM_LEFT; + default: return gravity; + } +} + +static unsigned popup_flip_y(unsigned gravity) +{ + switch (gravity) { + case X11_ANCHOR_TOP: return X11_ANCHOR_BOTTOM; + case X11_ANCHOR_BOTTOM: return X11_ANCHOR_TOP; + case X11_ANCHOR_TOP_LEFT: return X11_ANCHOR_BOTTOM_LEFT; + case X11_ANCHOR_TOP_RIGHT: return X11_ANCHOR_BOTTOM_RIGHT; + case X11_ANCHOR_BOTTOM_LEFT: return X11_ANCHOR_TOP_LEFT; + case X11_ANCHOR_BOTTOM_RIGHT: return X11_ANCHOR_TOP_RIGHT; + default: return gravity; + } +} + +static void popup_position_at(const struct yasl_private_popup_spec *spec, int ax, int ay, + unsigned gravity, struct yasl_private_vector2i size, int *x, int *y) +{ + int content_w = spec->content_size.x ? spec->content_size.x : size.x - spec->content_offset.x; + int content_h = spec->content_size.y ? spec->content_size.y : size.y - spec->content_offset.y; + switch (gravity) { + case X11_ANCHOR_LEFT: case X11_ANCHOR_TOP_LEFT: case X11_ANCHOR_BOTTOM_LEFT: + *x = ax - content_w - spec->content_offset.x; break; + case X11_ANCHOR_RIGHT: case X11_ANCHOR_TOP_RIGHT: case X11_ANCHOR_BOTTOM_RIGHT: + *x = ax - spec->content_offset.x; break; + default: *x = ax - content_w / 2 - spec->content_offset.x; break; + } + switch (gravity) { + case X11_ANCHOR_TOP: case X11_ANCHOR_TOP_LEFT: case X11_ANCHOR_TOP_RIGHT: + *y = ay - content_h - spec->content_offset.y; break; + case X11_ANCHOR_BOTTOM: case X11_ANCHOR_BOTTOM_LEFT: case X11_ANCHOR_BOTTOM_RIGHT: + *y = ay - spec->content_offset.y; break; + default: *y = ay - content_h / 2 - spec->content_offset.y; break; + } +} + +static int popup_overflow(int pos, int extent, int low, int high) +{ + int64_t end = (int64_t)pos + extent; + int64_t overflow = (pos < low ? (int64_t)low - pos : 0) + (end > high ? end - high : 0); + return overflow > INT_MAX ? INT_MAX : (int)overflow; +} + static void popup_calculate_position(struct x11_display *d, struct x11_surface *parent, - const struct yasl_popup_spec *spec, - int *out_x, int *out_y) + const struct yasl_private_popup_spec *spec, + int *out_x, int *out_y, + struct yasl_private_vector2i *out_size) { int parent_rx = 0, parent_ry = 0; if (d->native) { @@ -1007,16 +1492,23 @@ static void popup_calculate_position(struct x11_display *d, int parent_cx = parent->is_popup ? parent->content_offset.x : 0; int parent_cy = parent->is_popup ? parent->content_offset.y : 0; - int anchor_x = parent_rx + (spec->anchor.x - parent_cx); - int anchor_y = parent_ry + (spec->anchor.y - parent_cy); - - int root_x, root_y; - if (spec->beside) { - root_x = anchor_x + spec->anchor.width - spec->content_offset.x; - root_y = anchor_y - spec->content_offset.y; + struct yasl_private_vector2i size = spec->size; + int anchor_x, anchor_y, root_x, root_y; + if (spec->positioner_explicit) { + popup_anchor_point(spec, &anchor_x, &anchor_y); + anchor_x += parent_rx - parent_cx; + anchor_y += parent_ry - parent_cy; + popup_position_at(spec, anchor_x, anchor_y, spec->gravity, size, &root_x, &root_y); } else { - root_x = anchor_x - spec->content_offset.x; - root_y = anchor_y + spec->anchor.height - spec->content_offset.y; + anchor_x = parent_rx + (spec->anchor.x - parent_cx); + anchor_y = parent_ry + (spec->anchor.y - parent_cy); + if (spec->beside) { + root_x = anchor_x + spec->anchor.width - spec->content_offset.x; + root_y = anchor_y - spec->content_offset.y; + } else { + root_x = anchor_x - spec->content_offset.x; + root_y = anchor_y + spec->anchor.height - spec->content_offset.y; + } } int mon_x = 0, mon_y = 0; @@ -1043,29 +1535,69 @@ static void popup_calculate_position(struct x11_display *d, } #endif - if (spec->beside) { - if (root_x + spec->size.x > mon_x + mon_w) { - int flipped_x = anchor_x - spec->size.x + spec->content_offset.x; - if (flipped_x >= mon_x) root_x = flipped_x; + if (spec->positioner_explicit) { + if (spec->constraints & X11_CONSTRAINT_FLIP_X) { + int flip_x, flip_y; + /* Reflect the selected anchor edge across the anchor rectangle center. */ + int anchor_origin_x = parent_rx - parent_cx + spec->anchor.x; + int flipped_anchor_x = 2 * anchor_origin_x + spec->anchor.width - anchor_x; + popup_position_at(spec, flipped_anchor_x, anchor_y, popup_flip_x(spec->gravity), size, &flip_x, &flip_y); + if (popup_overflow(flip_x, size.x, mon_x, mon_x + mon_w) < + popup_overflow(root_x, size.x, mon_x, mon_x + mon_w)) root_x = flip_x; + } + if (spec->constraints & X11_CONSTRAINT_FLIP_Y) { + int flip_x, flip_y; + int anchor_origin_y = parent_ry - parent_cy + spec->anchor.y; + int flipped_anchor_y = 2 * anchor_origin_y + spec->anchor.height - anchor_y; + popup_position_at(spec, anchor_x, flipped_anchor_y, popup_flip_y(spec->gravity), size, &flip_x, &flip_y); + if (popup_overflow(flip_y, size.y, mon_y, mon_y + mon_h) < + popup_overflow(root_y, size.y, mon_y, mon_y + mon_h)) root_y = flip_y; + } + if (spec->constraints & X11_CONSTRAINT_SLIDE_X) { + if (root_x < mon_x) root_x = mon_x; + if (root_x + size.x > mon_x + mon_w) root_x = mon_x + mon_w - size.x; + } + if (spec->constraints & X11_CONSTRAINT_SLIDE_Y) { + if (root_y < mon_y) root_y = mon_y; + if (root_y + size.y > mon_y + mon_h) root_y = mon_y + mon_h - size.y; + } + if ((spec->constraints & X11_CONSTRAINT_RESIZE_X) && + popup_overflow(root_x, size.x, mon_x, mon_x + mon_w)) { + if (root_x >= mon_x + mon_w) root_x = mon_x + mon_w - 1; + if (root_x < mon_x) { size.x -= mon_x - root_x; root_x = mon_x; } + if (root_x + size.x > mon_x + mon_w) size.x = mon_x + mon_w - root_x; + if (size.x < 1) size.x = 1; + } + if ((spec->constraints & X11_CONSTRAINT_RESIZE_Y) && + popup_overflow(root_y, size.y, mon_y, mon_y + mon_h)) { + if (root_y >= mon_y + mon_h) root_y = mon_y + mon_h - 1; + if (root_y < mon_y) { size.y -= mon_y - root_y; root_y = mon_y; } + if (root_y + size.y > mon_y + mon_h) size.y = mon_y + mon_h - root_y; + if (size.y < 1) size.y = 1; } } else { - if (root_y + spec->size.y > mon_y + mon_h) { - int flipped_y = anchor_y - spec->size.y + spec->content_offset.y; + if (spec->beside) { + if (root_x + size.x > mon_x + mon_w) { + int flipped_x = anchor_x - size.x + spec->content_offset.x; + if (flipped_x >= mon_x) root_x = flipped_x; + } + } else if (root_y + size.y > mon_y + mon_h) { + int flipped_y = anchor_y - size.y + spec->content_offset.y; if (flipped_y >= mon_y) root_y = flipped_y; } + if (root_x + size.x > mon_x + mon_w) root_x = mon_x + mon_w - size.x; + if (root_x < mon_x) root_x = mon_x; + if (root_y + size.y > mon_y + mon_h) root_y = mon_y + mon_h - size.y; + if (root_y < mon_y) root_y = mon_y; } - if (root_x + spec->size.x > mon_x + mon_w) root_x = mon_x + mon_w - spec->size.x; - if (root_x < mon_x) root_x = mon_x; - if (root_y + spec->size.y > mon_y + mon_h) root_y = mon_y + mon_h - spec->size.y; - if (root_y < mon_y) root_y = mon_y; - *out_x = root_x; *out_y = root_y; + if (out_size) *out_size = size; } static struct yasl_surface *surface_create_popup(struct yasl_surface *parent_base, - const struct yasl_popup_spec *spec) + const struct yasl_private_popup_spec *spec) { struct x11_surface *parent = (struct x11_surface *)parent_base; if (!parent || parent->destroyed || !popup_spec_valid(parent, spec)) @@ -1074,14 +1606,17 @@ static struct yasl_surface *surface_create_popup(struct yasl_surface *parent_bas struct x11_surface *s = calloc(1, sizeof(*s)); if (!s) return NULL; s->base.display = parent_base->display; + s->base.creation_flags = parent_base->creation_flags; + s->base.presenter = (parent_base->creation_flags & YASL_PRIVATE_SURFACE_CPU) ? + YASL_PRIVATE_PRESENTER_CPU : YASL_PRIVATE_PRESENTER_NONE; s->parent = parent; s->is_popup = true; s->content_offset = spec->content_offset; s->clock.refresh_ns = parent->clock.refresh_ns; - if (!resize(s, spec->size)) { free(s); return NULL; } - int root_x = 0, root_y = 0; - popup_calculate_position(d, parent, spec, &root_x, &root_y); + struct yasl_private_vector2i actual_size; + popup_calculate_position(d, parent, spec, &root_x, &root_y, &actual_size); + if (!resize(s, actual_size)) { free(s); return NULL; } if (d->native) { XSetWindowAttributes attrs = { @@ -1089,7 +1624,7 @@ static struct yasl_surface *surface_create_popup(struct yasl_surface *parent_bas .save_under = True, }; s->window = XCreateWindow(d->native, RootWindow(d->native, d->screen), - root_x, root_y, (unsigned)spec->size.x, (unsigned)spec->size.y, + root_x, root_y, (unsigned)actual_size.x, (unsigned)actual_size.y, 0, CopyFromParent, InputOutput, CopyFromParent, CWOverrideRedirect | CWSaveUnder, &attrs); if (!s->window) { @@ -1098,7 +1633,7 @@ static struct yasl_surface *surface_create_popup(struct yasl_surface *parent_bas } s->base.native_handle = (void *)(uintptr_t)s->window; s->base.size = s->size; - s->base.scale_120 = YASL_SCALE_UNIT; + s->base.scale_120 = YASL_PRIVATE_SCALE_UNIT; s->gc = XCreateGC(d->native, s->window, 0, NULL); XSelectInput(d->native, s->window, ExposureMask | StructureNotifyMask | FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask | @@ -1127,25 +1662,27 @@ static struct yasl_surface *surface_create_popup(struct yasl_surface *parent_bas } static bool surface_popup_reposition(struct yasl_surface *popup_base, - const struct yasl_popup_spec *spec) + const struct yasl_private_popup_spec *spec) { struct x11_surface *s = (struct x11_surface *)popup_base; if (!s || s->destroyed || !s->is_popup || !s->parent || s->parent->destroyed || !popup_spec_valid(s->parent, spec)) return false; struct x11_display *d = (struct x11_display *)popup_base->display; - if (spec->size.x != s->size.x || spec->size.y != s->size.y) { - if (!resize(s, spec->size)) - return false; - } s->content_offset = spec->content_offset; int root_x = 0, root_y = 0; - popup_calculate_position(d, s->parent, spec, &root_x, &root_y); + struct yasl_private_vector2i actual_size; + popup_calculate_position(d, s->parent, spec, &root_x, &root_y, &actual_size); + bool changed_size = actual_size.x != s->size.x || actual_size.y != s->size.y; + if (changed_size && !resize(s, actual_size)) return false; if (d->native && s->window) { XMoveResizeWindow(d->native, s->window, root_x, root_y, - (unsigned)spec->size.x, (unsigned)spec->size.y); + (unsigned)actual_size.x, (unsigned)actual_size.y); XFlush(d->native); } + if (changed_size) + yasl_private_surface_notify_configure(&s->base, + (struct yasl_private_geometry){actual_size, YASL_PRIVATE_SCALE_UNIT}); schedule_redraw(&s->base); return true; } @@ -1157,22 +1694,22 @@ static void update_state(struct x11_surface *s) int format; unsigned long count, left; unsigned char *data = NULL; - uint32_t state = s->base.state & ~(YASL_STATE_FULLSCREEN | YASL_STATE_MAXIMIZED); + uint32_t state = s->base.state & ~(YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_MAXIMIZED); if (XGetWindowProperty(d->native, s->window, d->net_state, 0, 64, False, XA_ATOM, &actual, &format, &count, &left, &data) == Success && format == 32) { Atom *atoms = (Atom *)data; bool h = false, v = false; for (unsigned long i = 0; i < count; i++) { - if (atoms[i] == d->fullscreen) state |= YASL_STATE_FULLSCREEN; + if (atoms[i] == d->fullscreen) state |= YASL_PRIVATE_STATE_FULLSCREEN; if (atoms[i] == d->maximized_h) h = true; if (atoms[i] == d->maximized_v) v = true; } - if (h && v) state |= YASL_STATE_MAXIMIZED; + if (h && v) state |= YASL_PRIVATE_STATE_MAXIMIZED; } if (data) XFree(data); if (state == s->base.state) return; s->base.state = state; - yasl_surface_notify_state(&s->base, state); + yasl_private_surface_notify_state(&s->base, state); } static void selection_request(struct x11_display *d, XSelectionRequestEvent *request) @@ -1184,10 +1721,10 @@ static void selection_request(struct x11_display *d, XSelectionRequestEvent *req .target = request->target, .time = request->time, }; Atom property = request->property ? request->property : request->target; - int slot = request->selection == XA_PRIMARY ? YASL_PRIMARY : YASL_CLIPBOARD; + int slot = request->selection == XA_PRIMARY ? YASL_PRIVATE_PRIMARY : YASL_PRIVATE_CLIPBOARD; const char *text = d->selection[slot]; size_t latin1_len = 0; - char *latin1 = text ? yasl_utf8_to_latin1(text, &latin1_len) : NULL; + char *latin1 = text ? yasl_private_utf8_to_latin1(text, &latin1_len) : NULL; if (text && request->target == d->targets) { Atom targets[] = {d->targets, d->utf8, XA_STRING}; XChangeProperty(d->native, request->requestor, property, XA_ATOM, 32, @@ -1267,7 +1804,7 @@ static void xdnd_selection_notify(struct x11_display *d, XSelectionEvent *event) size_t out_len = 0; char *parsed = parse_file_uris((const char *)data, count, &out_len); if (parsed && out_len > 0) { - push(s, (struct yasl_event){.type = YASL_EVENT_DROP}, parsed, out_len); + push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_DROP}, parsed, out_len); free(parsed); success = true; } @@ -1304,20 +1841,20 @@ static void selection_notify(struct x11_display *d, XSelectionEvent *event) /* Surface destruction detached this accepted request. The reply means * the owner has finished writing the initial property; retire safely. */ *p = r->next; - if (r->timeout) yasl_loop_del_timer(d->loop, r->timeout); + if (r->timeout) yasl_private_loop_del_timer(d->loop, r->timeout); XDestroyWindow(d->native, r->window); free(r); return; } if (event->property == None) { *p = r->next; - if (r->timeout) yasl_loop_del_timer(d->loop, r->timeout); - enum yasl_transfer_status status = + if (r->timeout) yasl_private_loop_del_timer(d->loop, r->timeout); + enum yasl_private_transfer_status status = XGetSelectionOwner(d->native, r->selection) == None - ? YASL_TRANSFER_UNAVAILABLE : YASL_TRANSFER_FAILED; + ? YASL_PRIVATE_TRANSFER_UNAVAILABLE : YASL_PRIVATE_TRANSFER_FAILED; XDestroyWindow(d->native, r->window); if (s && !s->destroyed) - yasl_surface_complete_paste(&s->base, r->id, status, NULL, 0); + yasl_private_surface_complete_paste(&s->base, r->id, status, NULL, 0); free(r); return; } @@ -1333,14 +1870,14 @@ static void selection_notify(struct x11_display *d, XSelectionEvent *event) &probe_count, &probe_left, &probe_data); if (probe_data) XFree(probe_data); if (probe_result == Success && probe_type == d->incr) { - yasl_surface_complete_paste(&s->base, r->id, - YASL_TRANSFER_FAILED, NULL, 0); + yasl_private_surface_complete_paste(&s->base, r->id, + YASL_PRIVATE_TRANSFER_FAILED, NULL, 0); r->target = NULL; r->id = 0; return; } *p = r->next; - if (r->timeout) yasl_loop_del_timer(d->loop, r->timeout); + if (r->timeout) yasl_private_loop_del_timer(d->loop, r->timeout); Atom actual = None; int format = 0; unsigned long count = 0, left = 0; @@ -1389,17 +1926,17 @@ static void selection_notify(struct x11_display *d, XSelectionEvent *event) } if (complete && actual == XA_STRING) { size_t utf8_len; - char *utf8 = yasl_utf8_from_latin1(buf, total, &utf8_len); + char *utf8 = yasl_private_utf8_from_latin1(buf, total, &utf8_len); free(buf); buf = utf8; if (buf) total = utf8_len; } - enum yasl_transfer_status status = YASL_TRANSFER_FAILED; - if (complete && buf && yasl_utf8_validate(buf, total)) - status = YASL_TRANSFER_OK; - yasl_surface_complete_paste(&s->base, r->id, status, - status == YASL_TRANSFER_OK ? buf : NULL, - status == YASL_TRANSFER_OK ? total : 0); + enum yasl_private_transfer_status status = YASL_PRIVATE_TRANSFER_FAILED; + if (complete && buf && yasl_private_utf8_validate(buf, total)) + status = YASL_PRIVATE_TRANSFER_OK; + yasl_private_surface_complete_paste(&s->base, r->id, status, + status == YASL_PRIVATE_TRANSFER_OK ? buf : NULL, + status == YASL_PRIVATE_TRANSFER_OK ? total : 0); free(buf); XDestroyWindow(d->native, r->window); free(r); @@ -1413,7 +1950,7 @@ static void refresh_keyboard(struct x11_display *d) if (!keyboard) return; if (keyboard->names && keyboard->names->keys) { for (unsigned code = keyboard->min_key_code; code <= keyboard->max_key_code; code++) - d->keys[code] = yasl_key_from_xkb_name(keyboard->names->keys[code].name); + d->keys[code] = yasl_private_key_from_xkb_name(keyboard->names->keys[code].name); } XkbFreeKeyboard(keyboard, XkbAllComponentsMask, True); } @@ -1460,18 +1997,18 @@ static void key_event(struct x11_surface *s, XKeyEvent *key, bool repeat) } } uint32_t cp = xkb_keysym_to_utf32((xkb_keysym_t)base); - push(s, (struct yasl_event){ - .type = YASL_EVENT_KEY, + push(s, (struct yasl_private_event){ + .type = YASL_PRIVATE_EVENT_KEY, .key = {.key = key->keycode < 256 ? d->keys[key->keycode] : YASL_KEY_UNKNOWN, - .codepoint = yasl_key_label(cp), .native_code = key->keycode, - .shifted = yasl_key_label(xkb_keysym_to_utf32((xkb_keysym_t)shifted)), + .codepoint = yasl_private_key_label(cp), .native_code = key->keycode, + .shifted = yasl_private_key_label(xkb_keysym_to_utf32((xkb_keysym_t)shifted)), .mods = d->mods, .action = key->type == KeyRelease ? YASL_KEY_RELEASE : repeat ? YASL_KEY_REPEAT : YASL_KEY_PRESS}, }, NULL, 0); if (len > 0 && text && (unsigned char)text[0] >= 0x20 && text[0] != 0x7f && - !(d->mods & (YASL_MOD_CTRL | YASL_MOD_SUPER | YASL_MOD_ALT))) - push(s, (struct yasl_event){.type = YASL_EVENT_TEXT}, text, (size_t)len); + !(d->mods & (YASL_PRIVATE_MOD_CTRL | YASL_PRIVATE_MOD_SUPER | YASL_PRIVATE_MOD_ALT))) + push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_TEXT}, text, (size_t)len); if (text != local) free(text); } @@ -1480,7 +2017,7 @@ static void handle_client_message(struct x11_display *d, struct x11_surface *s, { if (ev->message_type == d->protocols && (Atom)ev->data.l[0] == d->delete_window) { - yasl_surface_notify_close(&s->base); + yasl_private_surface_notify_close(&s->base); } else if (ev->message_type == d->xdnd_enter) { Window source = (Window)ev->data.l[0]; int version = (int)((unsigned long)ev->data.l[1] >> 24); @@ -1521,19 +2058,19 @@ static void handle_client_message(struct x11_display *d, struct x11_surface *s, XTranslateCoordinates(d->native, RootWindow(d->native, d->screen), target, rx, ry, &wx, &wy, &child); } - struct yasl_vector2i pos = {wx, wy}; + struct yasl_private_vector2i pos = {wx, wy}; if (d->xdnd.surface && d->xdnd.surface != s) { struct x11_surface *old = d->xdnd.surface; - struct yasl_vector2i old_pos = d->xdnd.pos; + struct yasl_private_vector2i old_pos = d->xdnd.pos; d->xdnd.surface = NULL; if (d->xdnd.accepts && !old->destroyed) - yasl_surface_notify_drop(&old->base, YASL_DROP_LEAVE, old_pos); + yasl_private_surface_notify_drop(&old->base, YASL_PRIVATE_DROP_LEAVE, old_pos); } d->xdnd.surface = s; d->xdnd.pos = pos; d->xdnd.time = t; if (d->xdnd.accepts && !s->destroyed) - yasl_surface_notify_drop(&s->base, YASL_DROP_OVER, pos); + yasl_private_surface_notify_drop(&s->base, YASL_PRIVATE_DROP_OVER, pos); if (d->native) { XEvent reply = {0}; @@ -1556,7 +2093,7 @@ static void handle_client_message(struct x11_display *d, struct x11_surface *s, } else if (ev->message_type == d->xdnd_leave) { if (d->xdnd.surface == s) { if (d->xdnd.accepts && !s->destroyed) - yasl_surface_notify_drop(&s->base, YASL_DROP_LEAVE, d->xdnd.pos); + yasl_private_surface_notify_drop(&s->base, YASL_PRIVATE_DROP_LEAVE, d->xdnd.pos); } d->xdnd = (typeof(d->xdnd)){0}; } else if (ev->message_type == d->xdnd_drop) { @@ -1566,7 +2103,7 @@ static void handle_client_message(struct x11_display *d, struct x11_surface *s, bool was_accepting = d->xdnd.accepts && d->xdnd.has_uri_list && (d->xdnd.source == source); if (d->xdnd.surface == s) { if (was_accepting && !s->destroyed) - yasl_surface_notify_drop(&s->base, YASL_DROP_LEAVE, d->xdnd.pos); + yasl_private_surface_notify_drop(&s->base, YASL_PRIVATE_DROP_LEAVE, d->xdnd.pos); } if (was_accepting && !s->destroyed) { d->pending_drop.active = true; @@ -1593,7 +2130,7 @@ static void handle_client_message(struct x11_display *d, struct x11_surface *s, } } -static void dispatch(struct yasl_display *base) +static void dispatch(struct yasl_private_display *base) { struct x11_display *d = (struct x11_display *)base; d->depth++; @@ -1636,11 +2173,11 @@ static void dispatch(struct yasl_display *base) switch (event.type) { case Expose: s->want_draw = true; break; case ConfigureNotify: { - struct yasl_vector2i size = {event.xconfigure.width, event.xconfigure.height}; + struct yasl_private_vector2i size = {event.xconfigure.width, event.xconfigure.height}; if (size.x == s->size.x && size.y == s->size.y) break; if (resize(s, size)) - yasl_surface_notify_configure(&s->base, - (struct yasl_geometry){size, YASL_SCALE_UNIT}); + yasl_private_surface_notify_configure(&s->base, + (struct yasl_private_geometry){size, YASL_PRIVATE_SCALE_UNIT}); break; } case ClientMessage: @@ -1648,12 +2185,16 @@ static void dispatch(struct yasl_display *base) break; case FocusIn: case FocusOut: { bool focused = event.type == FocusIn; - if (s->ic) { if (focused) XSetICFocus(s->ic); else XUnsetICFocus(s->ic); } - if (focused) s->base.state |= YASL_STATE_ACTIVATED; - else s->base.state &= ~YASL_STATE_ACTIVATED; - push(s, (struct yasl_event){ - .type = YASL_EVENT_FOCUS, .focus.focused = focused}, NULL, 0); - yasl_surface_notify_state(&s->base, s->base.state); + s->focused = focused; + if (s->ic) { + if (focused && s->base.text_input_active) XSetICFocus(s->ic); + else XUnsetICFocus(s->ic); + } + if (focused) s->base.state |= YASL_PRIVATE_STATE_ACTIVATED; + else s->base.state &= ~YASL_PRIVATE_STATE_ACTIVATED; + push(s, (struct yasl_private_event){ + .type = YASL_PRIVATE_EVENT_FOCUS, .focus.focused = focused}, NULL, 0); + yasl_private_surface_notify_state(&s->base, s->base.state); break; } case PropertyNotify: @@ -1674,13 +2215,13 @@ static void dispatch(struct yasl_display *base) break; } case MotionNotify: case LeaveNotify: case EnterNotify: case ButtonPress: case ButtonRelease: { - struct yasl_event ev = { - .pointer = {.device = YASL_PTR_DEV_MOUSE}, + struct yasl_private_event ev = { + .pointer = {.device = YASL_PRIVATE_PTR_DEV_MOUSE}, }; unsigned state; if (event.type == MotionNotify) { - ev.pointer.pos = (struct yasl_vector2i){event.xmotion.x, event.xmotion.y}; - ev.type = YASL_EVENT_POINTER_MOTION; + ev.pointer.pos = (struct yasl_private_vector2i){event.xmotion.x, event.xmotion.y}; + ev.type = YASL_PRIVATE_EVENT_POINTER_MOTION; state = event.xmotion.state; if (d->drag.active) { if (d->drag.carried) @@ -1689,38 +2230,41 @@ static void dispatch(struct yasl_display *base) XMoveWindow(d->native, d->drag.icon_window, event.xmotion.x_root - d->drag.hotspot.x, event.xmotion.y_root - d->drag.hotspot.y); - struct yasl_vector2i hpos = ev.pointer.pos; + struct yasl_private_vector2i hpos = ev.pointer.pos; struct x11_surface *hsurf = d->native ? surface_at_root_pos(d, event.xmotion.x_root, event.xmotion.y_root, &hpos) : s; drag_set_hover(d, hsurf, hpos); } } else if (event.type == EnterNotify) { - ev.pointer.pos = (struct yasl_vector2i){event.xcrossing.x, event.xcrossing.y}; - ev.type = YASL_EVENT_POINTER_MOTION; + ev.pointer.pos = (struct yasl_private_vector2i){event.xcrossing.x, event.xcrossing.y}; + ev.type = YASL_PRIVATE_EVENT_POINTER_MOTION; state = event.xcrossing.state; if (d->drag.active) { - struct yasl_vector2i hpos = ev.pointer.pos; + struct yasl_private_vector2i hpos = ev.pointer.pos; struct x11_surface *hsurf = d->native ? surface_at_root_pos(d, event.xcrossing.x_root, event.xcrossing.y_root, &hpos) : s; drag_set_hover(d, hsurf, hpos); } } else if (event.type == LeaveNotify) { - ev.pointer.pos = (struct yasl_vector2i){event.xcrossing.x, event.xcrossing.y}; - ev.type = YASL_EVENT_POINTER_LEAVE; + ev.pointer.pos = (struct yasl_private_vector2i){event.xcrossing.x, event.xcrossing.y}; + ev.type = YASL_PRIVATE_EVENT_POINTER_LEAVE; state = event.xcrossing.state; if (d->drag.active) drag_leave_hover(d, s, ev.pointer.pos); } else { - ev.pointer.pos = (struct yasl_vector2i){event.xbutton.x, event.xbutton.y}; + ev.pointer.pos = (struct yasl_private_vector2i){event.xbutton.x, event.xbutton.y}; state = event.xbutton.state; unsigned button = event.xbutton.button; if (button >= 4 && button <= 7) { if (event.type == ButtonRelease) break; - ev.type = YASL_EVENT_POINTER_SCROLL; + ev.type = YASL_PRIVATE_EVENT_POINTER_SCROLL; if (button <= 5) ev.pointer.scroll.y = button == 4 ? -1 : 1; else ev.pointer.scroll.x = button == 6 ? -1 : 1; + ev.pointer.scroll_valid = 1u; + ev.pointer.scroll_source = 1u; + ev.pointer.scroll_phase = 2u; } else { - ev.type = event.type == ButtonPress ? YASL_EVENT_POINTER_DOWN : YASL_EVENT_POINTER_UP; + ev.type = event.type == ButtonPress ? YASL_PRIVATE_EVENT_POINTER_DOWN : YASL_PRIVATE_EVENT_POINTER_UP; ev.pointer.button = pointer_button(button); if (event.type == ButtonRelease && d->drag.active) drag_drop_hover(d); @@ -1735,13 +2279,13 @@ static void dispatch(struct yasl_display *base) leave(d); } -static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb, - const struct yasl_damage_list *damage) +static bool present(struct yasl_surface *base, const struct yasl_private_framebuffer *fb, + const struct yasl_private_damage_list *damage) { 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 || - damage->n < -1 || damage->n > YASL_DAMAGE_MAX) return false; + damage->n < -1 || damage->n > YASL_PRIVATE_DAMAGE_MAX) return false; struct x11_display *d = (struct x11_display *)base->display; XImage *im = s->image; if (s->shm_active) { @@ -1749,7 +2293,7 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb XShmPutImage(d->native, s->window, s->gc, im, 0, 0, 0, 0, (unsigned)s->size.x, (unsigned)s->size.y, False); else for (int i = 0; i < damage->n; i++) { - struct yasl_rect2i r = damage->rects[i]; + struct yasl_private_rect2i r = damage->rects[i]; int64_t right = (int64_t)r.x + r.width, bottom = (int64_t)r.y + r.height; int x = r.x > 0 ? r.x : 0, y = r.y > 0 ? r.y : 0; int w = (int)(right < s->size.x ? right : s->size.x) - x; @@ -1758,7 +2302,7 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb XShmPutImage(d->native, s->window, s->gc, im, x, y, x, y, (unsigned)w, (unsigned)h, False); } s->presented = true; - yasl_clock_presented(&s->clock, yasl_loop_now_ns(), false); + yasl_private_clock_presented(&s->clock, yasl_private_loop_now_ns(), false); return true; } bool direct = im->bits_per_pixel == 32 && im->red_mask == 0xff0000 && @@ -1769,7 +2313,7 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb y_start = s->size.y; y_end = 0; x_start = s->size.x; x_end = 0; for (int i = 0; i < damage->n; i++) { - struct yasl_rect2i r = damage->rects[i]; + struct yasl_private_rect2i r = damage->rects[i]; int y0 = r.y > 0 ? r.y : 0; int y1 = (r.y + r.height < s->size.y) ? r.y + r.height : s->size.y; int x0 = r.x > 0 ? r.x : 0; @@ -1798,7 +2342,7 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb XPutImage(d->native, s->window, s->gc, im, 0, 0, 0, 0, (unsigned)s->size.x, (unsigned)s->size.y); else for (int i = 0; i < damage->n; i++) { - struct yasl_rect2i r = damage->rects[i]; + struct yasl_private_rect2i r = damage->rects[i]; int64_t right = (int64_t)r.x + r.width, bottom = (int64_t)r.y + r.height; int x = r.x > 0 ? r.x : 0, y = r.y > 0 ? r.y : 0; int w = (int)(right < s->size.x ? right : s->size.x) - x; @@ -1807,27 +2351,33 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb XPutImage(d->native, s->window, s->gc, im, x, y, x, y, (unsigned)w, (unsigned)h); } s->presented = true; - yasl_clock_presented(&s->clock, yasl_loop_now_ns(), false); + yasl_private_clock_presented(&s->clock, yasl_private_loop_now_ns(), false); return true; } -static void flush(struct yasl_display *base) +static void flush(struct yasl_private_display *base) { struct x11_display *d = (struct x11_display *)base; d->depth++; for (struct x11_surface *s = d->surfaces, *next; s; s = next) { next = s->next; - if (s->destroyed || !s->want_draw || !s->cbs || !s->cbs->draw || s->drawing) continue; + if (s->destroyed || !s->want_draw || s->drawing) continue; + if (s->base.presenter != YASL_PRIVATE_PRESENTER_CPU) { + s->want_draw = false; + yasl_private_surface_notify_refresh(&s->base); + continue; + } + if (!s->cbs || !s->cbs->draw) continue; s->want_draw = false; s->drawing = true; s->presented = false; - s->damage = (struct yasl_damage_list){0}; - s->frame = (struct yasl_frame){ + s->damage = (struct yasl_private_damage_list){0}; + s->frame = (struct yasl_private_frame){ .surface = &s->base, - .target = {.generation = yasl_frame_begin(&s->base), + .target = {.generation = yasl_private_frame_begin(&s->base), .pixels = s->pixels, .size = s->size}, }; - yasl_surface_draw(&s->base, &s->frame); + yasl_private_surface_draw(&s->base, &s->frame); if (!s->destroyed) s->drawing = false; } XFlush(d->native); @@ -1837,11 +2387,11 @@ static void flush(struct yasl_display *base) static int ready(int fd, short events, void *ud) { (void)fd; - struct yasl_display *base = ud; + struct yasl_private_display *base = ud; struct x11_display *d = (struct x11_display *)base; if ((events & (POLLERR | POLLHUP)) && !d->lost) { d->lost = true; - yasl_display_notify_event(base, YASL_EVENT_DISPLAY_LOST); + yasl_private_display_notify_event(base, YASL_PRIVATE_EVENT_DISPLAY_LOST); return 0; } if ((events & POLLIN) && !d->lost) { @@ -1851,7 +2401,7 @@ static int ready(int fd, short events, void *ud) return 0; } -static int dispatch_timeout(struct yasl_display *base, int timeout_ms) +static int dispatch_timeout(struct yasl_private_display *base, int timeout_ms) { struct x11_display *d = (struct x11_display *)base; if (unlikely(!d || !d->native || d->lost)) return -1; @@ -1867,26 +2417,26 @@ static int dispatch_timeout(struct yasl_display *base, int timeout_ms) if (XEventsQueued(d->native, QueuedAlready) > 0) timeout_ms = 0; if (!XPending(d->native) && timeout_ms != 0) { - yasl_loop_step(d->loop, timeout_ms); + yasl_private_loop_step(d->loop, timeout_ms); dispatch(base); } else { - yasl_loop_step(d->loop, 0); + yasl_private_loop_step(d->loop, 0); dispatch(base); } } return 0; } -static struct yasl_display *display_open(struct yasl_loop *loop, - const struct yasl_display_spec *spec) +static struct yasl_private_display *display_open(struct yasl_private_loop *loop, + const struct yasl_private_display_spec *spec) { 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->base.ops = &yasl_private_x11_backend; + d->base.callbacks = spec && spec->cbs ? *spec->cbs : (struct yasl_private_display_cbs){0}; d->native = XOpenDisplay(NULL); if (!d->native) { free(d); errno = ENODEV; return NULL; } - XSetErrorHandler(yasl_x11_error_handler); + XSetErrorHandler(yasl_private_x11_error_handler); d->base.native_handle = d->native; d->screen = DefaultScreen(d->native); d->has_shm = XShmQueryExtension(d->native); @@ -1908,8 +2458,15 @@ static struct yasl_display *display_open(struct yasl_loop *loop, d->abs_pressure = XInternAtom(d->native, "Abs Pressure", False); d->abs_tilt_x = XInternAtom(d->native, "Abs Tilt X", False); d->abs_tilt_y = XInternAtom(d->native, "Abs Tilt Y", False); + d->abs_rotation = XInternAtom(d->native, "Abs Rotation", False); + d->abs_distance = XInternAtom(d->native, "Abs Distance", False); + d->abs_tangential = XInternAtom(d->native, "Abs TPressure", False); + d->abs_wheel = XInternAtom(d->native, "Abs Wheel", False); d->tool_type = XInternAtom(d->native, "Wacom Tool Type", False); d->eraser = XInternAtom(d->native, "ERASER", False); + d->brush = XInternAtom(d->native, "BRUSH", False); + d->airbrush = XInternAtom(d->native, "AIRBRUSH", False); + d->pencil = XInternAtom(d->native, "PENCIL", False); unsigned char bits[XIMaskLen(XI_LASTEVENT)] = {0}; XISetMask(bits, XI_HierarchyChanged); XISetMask(bits, XI_DeviceChanged); @@ -1917,7 +2474,7 @@ static struct yasl_display *display_open(struct yasl_loop *loop, XISelectEvents(d->native, RootWindow(d->native, d->screen), &mask, 1); } if (!loop) { - d->loop = yasl_loop_create(); + d->loop = yasl_private_loop_create(); d->owns_loop = true; } else { d->loop = loop; @@ -1926,7 +2483,7 @@ static struct yasl_display *display_open(struct yasl_loop *loop, d->app_id = strdup(spec && spec->app_id ? spec->app_id : "YASL"); d->base.app_id = d->app_id; if (!d->loop || !d->app_id) { - if (d->owns_loop) yasl_loop_destroy(d->loop); + if (d->owns_loop) yasl_private_loop_destroy(d->loop); free(d->app_id); XCloseDisplay(d->native); free(d); @@ -1941,10 +2498,11 @@ static struct yasl_display *display_open(struct yasl_loop *loop, ATOM(utf8, "UTF8_STRING"); ATOM(clipboard, "CLIPBOARD"); ATOM(targets, "TARGETS"); - ATOM(paste_property, "YASL_SELECTION"); + ATOM(paste_property, "YASL_PRIVATE_SELECTION"); ATOM(incr, "INCR"); ATOM(net_name, "_NET_WM_NAME"); ATOM(net_state, "_NET_WM_STATE"); + ATOM(motif_hints, "_MOTIF_WM_HINTS"); ATOM(fullscreen, "_NET_WM_STATE_FULLSCREEN"); ATOM(maximized_h, "_NET_WM_STATE_MAXIMIZED_HORZ"); ATOM(maximized_v, "_NET_WM_STATE_MAXIMIZED_VERT"); @@ -1961,13 +2519,13 @@ static struct yasl_display *display_open(struct yasl_loop *loop, ATOM(uri_list, "text/uri-list"); #undef ATOM if (d->loop) { - d->watch = yasl_loop_add_fd(d->loop, ConnectionNumber(d->native), POLLIN, ready, &d->base); + d->watch = yasl_private_loop_add_fd(d->loop, ConnectionNumber(d->native), POLLIN, ready, &d->base); if (d->watch < 0) { if (d->im) XCloseIM(d->im); XDestroyWindow(d->native, d->selection_window); XCloseDisplay(d->native); free(d->app_id); - if (d->owns_loop) yasl_loop_destroy(d->loop); + if (d->owns_loop) yasl_private_loop_destroy(d->loop); free(d); return NULL; } @@ -1975,21 +2533,24 @@ static struct yasl_display *display_open(struct yasl_loop *loop, return &d->base; } -static void display_close(struct yasl_display *base) +static void display_close(struct yasl_private_display *base) { struct x11_display *d = (struct x11_display *)base; assert(!d->surfaces && "destroy every surface before its display"); drag_cancel(base); cancel_pastes(d, NULL); d->pending_drop.active = false; - if (d->loop && d->watch > 0) yasl_loop_del_fd(d->loop, d->watch); + if (d->loop && d->watch > 0) yasl_private_loop_del_fd(d->loop, d->watch); if (d->owns_loop && d->loop) { - yasl_loop_destroy(d->loop); + yasl_private_loop_destroy(d->loop); d->loop = NULL; d->base.loop = NULL; } if (d->im) XCloseIM(d->im); clear_pointers(d); +#if YASL_HAS_EGL + if (d->egl_ready) eglTerminate(d->egl); +#endif for (size_t i = 0; i < sizeof(d->cursors) / sizeof(d->cursors[0]); i++) { if (d->cursors[i]) { XFreeCursor(d->native, d->cursors[i]); @@ -2004,16 +2565,16 @@ static void display_close(struct yasl_display *base) free(d); } -static uint32_t caps(const struct yasl_display *d) +static uint32_t caps(const struct yasl_private_display *d) { - return YASL_CAP_POPUPS | YASL_CAP_DRAG | YASL_CAP_DRAG_CARRY | YASL_CAP_PLACE_WINDOW | - YASL_CAP_PRIMARY_SELECTION | YASL_CAP_CSD | - (((const struct x11_display *)d)->xi2 ? YASL_CAP_TABLET : 0); + return YASL_PRIVATE_CAP_POPUPS | YASL_PRIVATE_CAP_DRAG | YASL_PRIVATE_CAP_DRAG_CARRY | YASL_PRIVATE_CAP_PLACE_WINDOW | + YASL_PRIVATE_CAP_PRIMARY_SELECTION | YASL_PRIVATE_CAP_CSD | YASL_PRIVATE_CAP_NATIVE_GRAPHICS | + (((const struct x11_display *)d)->xi2 ? YASL_PRIVATE_CAP_TABLET : 0); } -static bool copy_text(struct yasl_display *base, enum yasl_selection sel, const char *text) +static bool copy_text(struct yasl_private_display *base, enum yasl_private_selection sel, const char *text) { - if (!text || (sel != YASL_CLIPBOARD && sel != YASL_PRIMARY)) { + if (!text || (sel != YASL_PRIVATE_CLIPBOARD && sel != YASL_PRIVATE_PRIMARY)) { errno = EINVAL; return false; } @@ -2022,39 +2583,39 @@ static bool copy_text(struct yasl_display *base, enum yasl_selection sel, const if (!value) { errno = ENOMEM; return false; } free(d->selection[sel]); d->selection[sel] = value; - XSetSelectionOwner(d->native, sel == YASL_PRIMARY ? XA_PRIMARY : d->clipboard, + XSetSelectionOwner(d->native, sel == YASL_PRIVATE_PRIMARY ? XA_PRIMARY : d->clipboard, d->selection_window, CurrentTime); XFlush(d->native); return true; } -static void copy(struct yasl_display *base, enum yasl_selection sel, const char *text) +static void copy(struct yasl_private_display *base, enum yasl_private_selection sel, const char *text) { (void)copy_text(base, sel, text); } -static bool request_paste(struct yasl_display *base, enum yasl_selection sel, +static bool request_paste(struct yasl_private_display *base, enum yasl_private_selection sel, struct yasl_surface *target, uint64_t id) { - if (!target || (sel != YASL_CLIPBOARD && sel != YASL_PRIMARY)) { + if (!target || (sel != YASL_PRIVATE_CLIPBOARD && sel != YASL_PRIVATE_PRIMARY)) { errno = EINVAL; return false; } struct x11_display *d = (struct x11_display *)base; struct x11_surface *s = (struct x11_surface *)target; if (s->destroyed || target->display != base) { errno = EINVAL; return false; } - Atom selection = sel == YASL_PRIMARY ? XA_PRIMARY : d->clipboard; + Atom selection = sel == YASL_PRIVATE_PRIMARY ? XA_PRIMARY : d->clipboard; Window owner = XGetSelectionOwner(d->native, selection); if (owner == None) { - yasl_surface_complete_paste(target, id, YASL_TRANSFER_UNAVAILABLE, NULL, 0); + yasl_private_surface_complete_paste(target, id, YASL_PRIVATE_TRANSFER_UNAVAILABLE, NULL, 0); return true; } if (owner == d->selection_window) { const char *text = d->selection[sel]; if (text) - yasl_surface_complete_paste(target, id, YASL_TRANSFER_OK, text, strlen(text)); + yasl_private_surface_complete_paste(target, id, YASL_PRIVATE_TRANSFER_OK, text, strlen(text)); else - yasl_surface_complete_paste(target, id, YASL_TRANSFER_UNAVAILABLE, NULL, 0); + yasl_private_surface_complete_paste(target, id, YASL_PRIVATE_TRANSFER_UNAVAILABLE, NULL, 0); return true; } else { struct paste_request *r = calloc(1, sizeof(*r)); @@ -2067,7 +2628,7 @@ static bool request_paste(struct yasl_display *base, enum yasl_selection sel, r->selection = selection; r->id = id; errno = 0; - r->timeout = yasl_loop_add_timer(d->loop, PASTE_TIMEOUT_NS, + r->timeout = yasl_private_loop_add_timer(d->loop, PASTE_TIMEOUT_NS, paste_timeout, r); if (!r->timeout) { XDestroyWindow(d->native, r->window); @@ -2084,17 +2645,17 @@ static bool request_paste(struct yasl_display *base, enum yasl_selection sel, } } -static void paste(struct yasl_display *base, enum yasl_selection sel, +static void paste(struct yasl_private_display *base, enum yasl_private_selection sel, struct yasl_surface *target) { (void)request_paste(base, sel, target, 0); } -static bool input_poll(struct yasl_surface *base, struct yasl_event *event) +static bool input_poll(struct yasl_surface *base, struct yasl_private_event *event) { struct x11_surface *s = (struct x11_surface *)base; if (s->destroyed) return false; - if (base->display->events) return yasl_events_poll_surface(base, event); + if (base->display->events) return yasl_private_events_poll_surface(base, event); return input_queue_pop(&s->input, event); } @@ -2110,47 +2671,47 @@ static void schedule_redraw(struct yasl_surface *base) s->want_draw = true; struct x11_display *d = (struct x11_display *)base->display; if (d && d->loop) - yasl_loop_kick(d->loop); + yasl_private_loop_kick(d->loop); } } -static struct yasl_display *surface_display(const struct yasl_surface *s) { return s->display; } -static struct yasl_geometry geometry(const struct yasl_surface *s) +static struct yasl_private_display *surface_display(const struct yasl_surface *s) { return s->display; } +static struct yasl_private_geometry geometry(const struct yasl_surface *s) { - return (struct yasl_geometry){((const struct x11_surface *)s)->size, YASL_SCALE_UNIT}; + return (struct yasl_private_geometry){((const struct x11_surface *)s)->size, YASL_PRIVATE_SCALE_UNIT}; } -static int mods(const struct yasl_display *d) { return (int)((const struct x11_display *)d)->mods; } -static void *native_display(struct yasl_display *d) { return ((struct x11_display *)d)->native; } +static int mods(const struct yasl_private_display *d) { return (int)((const struct x11_display *)d)->mods; } +static void *native_display(struct yasl_private_display *d) { return ((struct x11_display *)d)->native; } static void *native_surface(struct yasl_surface *s) { return (void *)(uintptr_t)((struct x11_surface *)s)->window; } -static struct yasl_timing timing(const struct yasl_surface *base) +static struct yasl_private_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, + struct yasl_private_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_private_clock_next_present_ns(&s->clock), .source = s->clock.source}; - yasl_clock_resize_stats(&s->clock, &t.resize); + yasl_private_clock_resize_stats(&s->clock, &t.resize); return t; } static uint64_t frame_time(struct yasl_surface *s, uint64_t now) { - return yasl_clock_frame_timestamp_ns(&((struct x11_surface *)s)->clock, now); + return yasl_private_clock_frame_timestamp_ns(&((struct x11_surface *)s)->clock, now); } -static void cursor(struct yasl_surface *base, enum yasl_cursor cursor) +static void cursor(struct yasl_surface *base, enum yasl_private_cursor cursor) { static const unsigned shapes[] = {XC_left_ptr, XC_xterm, XC_hand2, XC_crosshair, XC_fleur, XC_bottom_right_corner, XC_bottom_left_corner, XC_sb_h_double_arrow, XC_sb_v_double_arrow}; struct x11_surface *s = (struct x11_surface *)base; - if (s->destroyed || (unsigned)cursor > YASL_CURSOR_HIDDEN) return; + if (s->destroyed || (unsigned)cursor > YASL_PRIVATE_CURSOR_HIDDEN) return; struct x11_display *d = (struct x11_display *)base->display; if (!d->native) return; if (!d->cursors[cursor]) { - if (cursor == YASL_CURSOR_HIDDEN) { + if (cursor == YASL_PRIVATE_CURSOR_HIDDEN) { char bits[] = {0}; Pixmap pixmap = XCreateBitmapFromData(d->native, s->window, bits, 1, 1); XColor color = {0}; @@ -2164,7 +2725,7 @@ static void cursor(struct yasl_surface *base, enum yasl_cursor cursor) XDefineCursor(d->native, s->window, d->cursors[cursor]); } -static bool place(struct yasl_surface *base, struct yasl_vector2i at) +static bool place(struct yasl_surface *base, struct yasl_private_vector2i at) { struct x11_surface *s = (struct x11_surface *)base; struct x11_display *d = (struct x11_display *)base->display; @@ -2227,7 +2788,7 @@ static bool xsettings_int(const unsigned char *p, size_t len, const char *name, return false; } -static int double_click_ms(const struct yasl_display *base) +static int double_click_ms(const struct yasl_private_display *base) { const struct x11_display *d = (const struct x11_display *)base; if (d->native) { @@ -2255,12 +2816,12 @@ static int double_click_ms(const struct yasl_display *base) } static bool read_pixels(struct yasl_surface *base, uint32_t *out, size_t cap, - struct yasl_vector2i *size) + struct yasl_private_vector2i *size) { struct x11_surface *s = (struct x11_surface *)base; /* presented stays set from the last draw until the next one starts. */ if (s->destroyed || s->drawing || !s->presented || !s->pixels) return false; - return yasl_copy_frame(s->pixels, s->size, out, cap, size); + return yasl_private_copy_frame(s->pixels, s->size, out, cap, size); } static bool bell(struct yasl_surface *base) @@ -2275,16 +2836,16 @@ static bool bell(struct yasl_surface *base) return true; } -static void drag_end(struct x11_display *d, enum yasl_drag_end how) +static void drag_end(struct x11_display *d, enum yasl_private_drag_end how) { if (!d || !d->drag.active) return; - struct yasl_drag_listener l = d->drag.listener; + struct yasl_private_drag_listener l = d->drag.listener; void *ud = d->drag.ud; struct x11_surface *hover = d->drag.hover; - struct yasl_vector2i hover_pos = d->drag.hover_pos; + struct yasl_private_vector2i hover_pos = d->drag.hover_pos; d->drag.active = false; - d->drag.listener = (struct yasl_drag_listener){0}; + d->drag.listener = (struct yasl_private_drag_listener){0}; d->drag.hover = NULL; d->drag.origin = NULL; d->drag.carried = NULL; @@ -2299,19 +2860,19 @@ static void drag_end(struct x11_display *d, enum yasl_drag_end how) } if (hover && !hover->destroyed) { - if (how == YASL_DRAG_DROPPED) - yasl_surface_notify_drag(&hover->base, YASL_DRAG_DROP, hover_pos); + if (how == YASL_PRIVATE_DRAG_DROPPED) + yasl_private_surface_notify_drag(&hover->base, YASL_PRIVATE_DRAG_DROP, hover_pos); else - yasl_surface_notify_drag(&hover->base, YASL_DRAG_LEAVE, hover_pos); + yasl_private_surface_notify_drag(&hover->base, YASL_PRIVATE_DRAG_LEAVE, hover_pos); } if (l.end) l.end(ud, how); } -static bool drag_start(struct yasl_display *base, struct yasl_surface *origin, - const struct yasl_drag_image *image, - const struct yasl_drag_listener *l, void *ud) +static bool drag_start(struct yasl_private_display *base, struct yasl_surface *origin, + const struct yasl_private_drag_image *image, + const struct yasl_private_drag_listener *l, void *ud) { struct x11_display *d = (struct x11_display *)base; if (!d || !origin || d->drag.active || !l) @@ -2383,12 +2944,12 @@ static bool drag_start(struct yasl_display *base, struct yasl_surface *origin, return true; } -static void drag_cancel(struct yasl_display *base) +static void drag_cancel(struct yasl_private_display *base) { - drag_end((struct x11_display *)base, YASL_DRAG_CANCELLED); + drag_end((struct x11_display *)base, YASL_PRIVATE_DRAG_CANCELLED); } -static bool drag_carry(struct yasl_surface *base, struct yasl_vector2i offset) +static bool drag_carry(struct yasl_surface *base, struct yasl_private_vector2i offset) { struct x11_display *d = (struct x11_display *)base->display; if (!d || !d->drag.active) @@ -2420,7 +2981,7 @@ static bool interactive_move(struct yasl_surface *base) return true; } -static bool interactive_resize(struct yasl_surface *base, enum yasl_resize_edge edge) +static bool interactive_resize(struct yasl_surface *base, enum yasl_private_resize_edge edge) { struct x11_surface *s = (struct x11_surface *)base; struct x11_display *d = (struct x11_display *)base->display; @@ -2432,14 +2993,14 @@ static bool interactive_resize(struct yasl_surface *base, enum yasl_resize_edge int dir; switch (edge) { - case YASL_EDGE_TOP_LEFT: dir = 0; break; - case YASL_EDGE_TOP: dir = 1; break; - case YASL_EDGE_TOP_RIGHT: dir = 2; break; - case YASL_EDGE_RIGHT: dir = 3; break; - case YASL_EDGE_BOTTOM_RIGHT: dir = 4; break; - case YASL_EDGE_BOTTOM: dir = 5; break; - case YASL_EDGE_BOTTOM_LEFT: dir = 6; break; - case YASL_EDGE_LEFT: dir = 7; break; + case YASL_PRIVATE_EDGE_TOP_LEFT: dir = 0; break; + case YASL_PRIVATE_EDGE_TOP: dir = 1; break; + case YASL_PRIVATE_EDGE_TOP_RIGHT: dir = 2; break; + case YASL_PRIVATE_EDGE_RIGHT: dir = 3; break; + case YASL_PRIVATE_EDGE_BOTTOM_RIGHT: dir = 4; break; + case YASL_PRIVATE_EDGE_BOTTOM: dir = 5; break; + case YASL_PRIVATE_EDGE_BOTTOM_LEFT: dir = 6; break; + case YASL_PRIVATE_EDGE_LEFT: dir = 7; break; default: return false; } @@ -2448,7 +3009,7 @@ static bool interactive_resize(struct yasl_surface *base, enum yasl_resize_edge return true; } -static bool show_window_menu(struct yasl_surface *base, struct yasl_vector2i pos) +static bool show_window_menu(struct yasl_surface *base, struct yasl_private_vector2i pos) { struct x11_surface *s = (struct x11_surface *)base; struct x11_display *d = (struct x11_display *)base->display; @@ -2458,7 +3019,18 @@ static bool show_window_menu(struct yasl_surface *base, struct yasl_vector2i pos return true; } -static void set_ime_caret(struct yasl_surface *base, struct yasl_rect2i caret) +static bool surface_update_text_input(struct yasl_surface *base) +{ + struct x11_surface *s = (struct x11_surface *)base; + if (!s || s->destroyed || !base->display) return false; + if (s->ic) { + if (s->focused && base->text_input_active) XSetICFocus(s->ic); + else XUnsetICFocus(s->ic); + } + return true; +} + +static void set_ime_caret(struct yasl_surface *base, struct yasl_private_rect2i caret) { struct x11_surface *s = (struct x11_surface *)base; if (!s) return; @@ -2489,8 +3061,15 @@ static bool ime_preedit_cursor(const struct yasl_surface *base, int *begin, int return true; } -const struct yasl_backend_ops yasl_x11_backend = { - .type = YASL_BACKEND_X11, +const struct yasl_private_backend_ops yasl_private_x11_backend = { +#if YASL_HAS_EGL + .surface_create_gl = create_gl_surface, + .gl_make_current = gl_make_current, + .gl_get_proc_address = gl_get_proc_address, + .gl_swap_interval = gl_swap_interval, + .gl_swap_buffers = gl_swap_buffers, +#endif + .type = YASL_PRIVATE_BACKEND_X11, .display_open = display_open, .display_close = display_close, .display_caps = caps, @@ -2530,6 +3109,7 @@ const struct yasl_backend_ops yasl_x11_backend = { .surface_start_interactive_move = interactive_move, .surface_start_interactive_resize = interactive_resize, .surface_show_window_menu = show_window_menu, + .surface_update_text_input = surface_update_text_input, .surface_set_ime_caret = set_ime_caret, .surface_ime_preedit = ime_preedit, .surface_ime_preedit_cursor = ime_preedit_cursor, |
