diff options
Diffstat (limited to 'src/x11')
| -rw-r--r-- | src/x11/window.c | 302 |
1 files changed, 201 insertions, 101 deletions
diff --git a/src/x11/window.c b/src/x11/window.c index 12fc954..4b6f6fd 100644 --- a/src/x11/window.c +++ b/src/x11/window.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 200809L +#define _DEFAULT_SOURCE #include "common/backend.h" #include "common/input.h" #include "common/timing.h" @@ -10,6 +11,12 @@ #include <X11/cursorfont.h> #include <X11/keysym.h> #include <X11/extensions/XInput2.h> +#include <X11/extensions/XShm.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#if YASL_HAS_XRANDR +#include <X11/extensions/Xrandr.h> +#endif #include <xkbcommon/xkbcommon.h> #include <assert.h> @@ -39,6 +46,8 @@ struct x11_surface { XIC ic; XImage *image; uint32_t *pixels; + bool shm_active; + XShmSegmentInfo shm_info; struct yasl_vector2i size; const struct yasl_surface_cbs *cbs; void *ud; @@ -92,6 +101,9 @@ struct x11_display { bool owns_loop; int xi_opcode; bool xi2; + bool has_shm; + bool has_xkb; + Cursor cursors[16]; Atom abs_pressure, abs_tilt_x, abs_tilt_y, tool_type, eraser; struct x11_pointer *pointers; struct { @@ -204,79 +216,30 @@ static void drag_drop_hover(struct x11_display *d) drag_end(d, YASL_DRAG_DROPPED); } +static int yasl_x11_error_handler(Display *native, XErrorEvent *event) +{ + (void)native; + (void)event; + /* Suppress asynchronous errors (BadWindow, BadDrawable, BadMatch) + * during window close, destruction races, or speculative property queries. */ + return 0; +} + static struct x11_surface *surface_at_root_pos(struct x11_display *d, int rx, int ry, struct yasl_vector2i *out_pos) { if (!d->native) return NULL; - Window child = None; - int dx, dy; - if (!XTranslateCoordinates(d->native, RootWindow(d->native, d->screen), - RootWindow(d->native, d->screen), - rx, ry, &dx, &dy, &child) || !child) - return NULL; - Window ignore_icon = d->drag.icon_window; Window ignore_carried = d->drag.carried ? d->drag.carried->window : None; - if (child == ignore_icon || child == ignore_carried) { - Window root, parent, *children = NULL; - unsigned int nchildren = 0; - child = None; - if (XQueryTree(d->native, RootWindow(d->native, d->screen), - &root, &parent, &children, &nchildren) && children) { - for (int i = (int)nchildren - 1; i >= 0; i--) { - Window w = children[i]; - if (w == ignore_icon || w == ignore_carried) continue; - for (struct x11_surface *s = d->surfaces; s; s = s->next) { - int tx = 0, ty = 0; - Window sub = None; - 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 (s->window == w) { - child = w; - break; - } - Window cur = s->window; - while (cur && cur != RootWindow(d->native, d->screen)) { - if (cur == w) { child = w; break; } - Window cr, cp, *cc = NULL; - unsigned int ncc = 0; - if (!XQueryTree(d->native, cur, &cr, &cp, &cc, &ncc)) break; - if (cc) XFree(cc); - cur = cp; - } - if (child) break; - } - } - } - if (child) break; - } - XFree(children); - } - } - for (struct x11_surface *s = d->surfaces; s; s = s->next) { + if (s->destroyed || s->window == ignore_icon || s->window == ignore_carried) continue; int tx = 0, ty = 0; Window sub = None; 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 (s->window == child) { - if (out_pos) *out_pos = (struct yasl_vector2i){tx, ty}; - return s; - } - Window cur = s->window; - while (cur && cur != RootWindow(d->native, d->screen)) { - if (cur == child) { - if (out_pos) *out_pos = (struct yasl_vector2i){tx, ty}; - return s; - } - Window cr, cp, *cc = NULL; - unsigned int ncc = 0; - if (!XQueryTree(d->native, cur, &cr, &cp, &cc, &ncc)) break; - if (cc) XFree(cc); - cur = cp; - } + if (out_pos) *out_pos = (struct yasl_vector2i){tx, ty}; + return s; } } } @@ -506,23 +469,76 @@ static bool resize(struct x11_surface *s, struct yasl_vector2i size) return true; } uint64_t start = yasl_loop_now_ns(); - XImage *image = XCreateImage(d->native, DefaultVisual(d->native, d->screen), - (unsigned)DefaultDepth(d->native, d->screen), ZPixmap, 0, NULL, - (unsigned)size.x, (unsigned)size.y, 32, 0); - uint32_t *pixels = calloc((size_t)size.x * (size_t)size.y, sizeof(*pixels)); - if (!image || !pixels) { - if (image) XDestroyImage(image); - free(pixels); - return false; + + if (s->shm_active) { + XShmDetach(d->native, &s->shm_info); + if (s->image) XDestroyImage(s->image); + if (s->shm_info.shmaddr) shmdt(s->shm_info.shmaddr); + s->image = NULL; + s->pixels = NULL; + s->shm_active = false; + } else { + if (s->image) XDestroyImage(s->image); + free(s->pixels); + s->image = NULL; + s->pixels = NULL; + } + + XImage *image = NULL; + uint32_t *pixels = NULL; + bool shm_ok = false; + if (d->has_shm) { + size_t bytes = (size_t)size.x * (size_t)size.y * sizeof(uint32_t); + s->shm_info.shmid = shmget(IPC_PRIVATE, bytes, IPC_CREAT | 0600); + if (s->shm_info.shmid >= 0) { + s->shm_info.shmaddr = (char *)shmat(s->shm_info.shmid, NULL, 0); + if (s->shm_info.shmaddr != (char *)-1) { + s->shm_info.readOnly = False; + image = XShmCreateImage(d->native, DefaultVisual(d->native, d->screen), + (unsigned)DefaultDepth(d->native, d->screen), ZPixmap, + s->shm_info.shmaddr, &s->shm_info, + (unsigned)size.x, (unsigned)size.y); + if (image) { + shmctl(s->shm_info.shmid, IPC_RMID, NULL); + if (XShmAttach(d->native, &s->shm_info)) { + pixels = (uint32_t *)s->shm_info.shmaddr; + memset(pixels, 0, bytes); + shm_ok = true; + } else { + XDestroyImage(image); + image = NULL; + shmdt(s->shm_info.shmaddr); + s->shm_info.shmaddr = NULL; + } + } else { + shmdt(s->shm_info.shmaddr); + s->shm_info.shmaddr = NULL; + shmctl(s->shm_info.shmid, IPC_RMID, NULL); + } + } else { + s->shm_info.shmaddr = NULL; + shmctl(s->shm_info.shmid, IPC_RMID, NULL); + } + } } - image->data = calloc((size_t)image->bytes_per_line, (size_t)size.y); - if (!image->data) { - XDestroyImage(image); - free(pixels); - return false; + if (!shm_ok) { + image = XCreateImage(d->native, DefaultVisual(d->native, d->screen), + (unsigned)DefaultDepth(d->native, d->screen), ZPixmap, 0, NULL, + (unsigned)size.x, (unsigned)size.y, 32, 0); + pixels = calloc((size_t)size.x * (size_t)size.y, sizeof(*pixels)); + if (!image || !pixels) { + if (image) XDestroyImage(image); + free(pixels); + return false; + } + image->data = calloc((size_t)image->bytes_per_line, (size_t)size.y); + if (!image->data) { + XDestroyImage(image); + free(pixels); + return false; + } } - if (s->image) XDestroyImage(s->image); - free(s->pixels); + s->shm_active = shm_ok; s->image = image; s->pixels = pixels; s->size = size; @@ -573,8 +589,19 @@ static void surface_destroy(struct yasl_surface *base) } cancel_pastes(d, s); if (s->ic) XDestroyIC(s->ic); - if (s->image) XDestroyImage(s->image); - free(s->pixels); + if (s->shm_active) { + XShmDetach(d->native, &s->shm_info); + if (s->image) XDestroyImage(s->image); + if (s->shm_info.shmaddr) shmdt(s->shm_info.shmaddr); + s->image = NULL; + s->pixels = NULL; + s->shm_active = false; + } else { + if (s->image) XDestroyImage(s->image); + free(s->pixels); + s->image = NULL; + s->pixels = NULL; + } if (d->native) { if (s->gc) XFreeGC(d->native, s->gc); if (s->window) XDestroyWindow(d->native, s->window); @@ -718,25 +745,46 @@ static void popup_calculate_position(struct x11_display *d, root_y = anchor_y + spec->anchor.height - spec->content_offset.y; } - int screen_w = d->native ? DisplayWidth(d->native, d->screen) : 1920; - int screen_h = d->native ? DisplayHeight(d->native, d->screen) : 1080; + int mon_x = 0, mon_y = 0; + int mon_w = d->native ? DisplayWidth(d->native, d->screen) : 1920; + int mon_h = d->native ? DisplayHeight(d->native, d->screen) : 1080; + +#if YASL_HAS_XRANDR + if (d->native) { + int nmonitors = 0; + XRRMonitorInfo *monitors = XRRGetMonitors(d->native, RootWindow(d->native, d->screen), True, &nmonitors); + if (monitors && nmonitors > 0) { + for (int i = 0; i < nmonitors; i++) { + if (anchor_x >= monitors[i].x && anchor_x < monitors[i].x + monitors[i].width && + anchor_y >= monitors[i].y && anchor_y < monitors[i].y + monitors[i].height) { + mon_x = monitors[i].x; + mon_y = monitors[i].y; + mon_w = monitors[i].width; + mon_h = monitors[i].height; + break; + } + } + XRRFreeMonitors(monitors); + } + } +#endif if (spec->beside) { - if (root_x + spec->size.x > screen_w) { + 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 >= 0) root_x = flipped_x; + if (flipped_x >= mon_x) root_x = flipped_x; } } else { - if (root_y + spec->size.y > screen_h) { + if (root_y + spec->size.y > mon_y + mon_h) { int flipped_y = anchor_y - spec->size.y + spec->content_offset.y; - if (flipped_y >= 0) root_y = flipped_y; + if (flipped_y >= mon_y) root_y = flipped_y; } } - if (root_x + spec->size.x > screen_w) root_x = screen_w - spec->size.x; - if (root_x < 0) root_x = 0; - if (root_y + spec->size.y > screen_h) root_y = screen_h - spec->size.y; - if (root_y < 0) root_y = 0; + 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; @@ -1003,8 +1051,21 @@ static void key_event(struct x11_surface *s, XKeyEvent *key, bool repeat) { struct x11_display *d = display_of(&s->base); d->mods = modifiers(key->state); - KeySym sym = XLookupKeysym(key, (key->state & ShiftMask) ? 1 : 0); - KeySym base = XLookupKeysym(key, 0), shifted = XLookupKeysym(key, 1); + unsigned clean_state = key->state & ~(Mod2Mask | LockMask); + KeySym sym = NoSymbol, base = NoSymbol, shifted = NoSymbol; + + if (d->has_xkb) { + sym = XkbKeycodeToKeysym(d->native, key->keycode, 0, (clean_state & ShiftMask) ? 1 : 0); + base = XkbKeycodeToKeysym(d->native, key->keycode, 0, 0); + shifted = XkbKeycodeToKeysym(d->native, key->keycode, 0, 1); + } + if (sym == NoSymbol) { + XKeyEvent clean_key = *key; + clean_key.state = clean_state; + sym = XLookupKeysym(&clean_key, (clean_state & ShiftMask) ? 1 : 0); + base = XLookupKeysym(&clean_key, 0); + shifted = XLookupKeysym(&clean_key, 1); + } char local[128]; char *text = local; int len = 0; @@ -1286,6 +1347,23 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb damage->n < -1 || damage->n > YASL_DAMAGE_MAX) return false; struct x11_display *d = display_of(base); XImage *im = s->image; + if (s->shm_active) { + if (damage->n == -1) + 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]; + 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; + int h = (int)(bottom < s->size.y ? bottom : s->size.y) - y; + if (w > 0 && h > 0) + 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); + return true; + } bool direct = im->bits_per_pixel == 32 && im->red_mask == 0xff0000 && im->green_mask == 0xff00 && im->blue_mask == 0xff; int y_start = 0, y_end = s->size.y; @@ -1363,7 +1441,10 @@ static int ready(int fd, short events, void *ud) { (void)fd; struct yasl_display *base = ud; - if (events & POLLIN) { dispatch(base); flush(base); } + if (events & (POLLIN | POLLERR | POLLHUP)) { + dispatch(base); + flush(base); + } return 0; } @@ -1379,11 +1460,15 @@ static int dispatch_timeout(struct yasl_display *base, int timeout_ms) } } if (d->loop) { + XFlush(d->native); + if (XEventsQueued(d->native, QueuedAlready) > 0) + timeout_ms = 0; if (!XPending(d->native) && timeout_ms != 0) { yasl_loop_step(d->loop, timeout_ms); dispatch(base); } else { yasl_loop_step(d->loop, 0); + dispatch(base); } } return 0; @@ -1398,8 +1483,12 @@ static struct yasl_display *display_open(struct yasl_loop *loop, d->base.callbacks = spec && spec->cbs ? *spec->cbs : (struct yasl_display_cbs){0}; d->native = XOpenDisplay(NULL); if (!d->native) { free(d); errno = ENODEV; return NULL; } + XSetErrorHandler(yasl_x11_error_handler); d->base.native_handle = d->native; d->screen = DefaultScreen(d->native); + d->has_shm = XShmQueryExtension(d->native); + int xkb_opcode, xkb_event, xkb_error, xkb_major = XkbMajorVersion, xkb_minor = XkbMinorVersion; + d->has_xkb = XkbQueryExtension(d->native, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor); int xi_event_base, xi_error_base; int major = 2, minor = 0; d->xi2 = XQueryExtension(d->native, "XInputExtension", &d->xi_opcode, @@ -1489,6 +1578,12 @@ static void display_close(struct yasl_display *base) } if (d->im) XCloseIM(d->im); clear_pointers(d); + for (size_t i = 0; i < sizeof(d->cursors) / sizeof(d->cursors[0]); i++) { + if (d->cursors[i]) { + XFreeCursor(d->native, d->cursors[i]); + d->cursors[i] = None; + } + } XDestroyWindow(d->native, d->selection_window); XCloseDisplay(d->native); free(d->selection[0]); @@ -1591,16 +1686,21 @@ static void cursor(struct yasl_surface *base, enum yasl_cursor cursor) struct x11_surface *s = (struct x11_surface *)base; if (s->destroyed || (unsigned)cursor > YASL_CURSOR_HIDDEN) return; struct x11_display *d = display_of(base); - Cursor native; - if (cursor == YASL_CURSOR_HIDDEN) { - char bits[] = {0}; - Pixmap pixmap = XCreateBitmapFromData(d->native, s->window, bits, 1, 1); - XColor color = {0}; - native = XCreatePixmapCursor(d->native, pixmap, pixmap, &color, &color, 0, 0); - XFreePixmap(d->native, pixmap); - } else native = XCreateFontCursor(d->native, shapes[cursor]); - XDefineCursor(d->native, s->window, native); - XFreeCursor(d->native, native); + if (!d->native) return; + + if (!d->cursors[cursor]) { + if (cursor == YASL_CURSOR_HIDDEN) { + char bits[] = {0}; + Pixmap pixmap = XCreateBitmapFromData(d->native, s->window, bits, 1, 1); + XColor color = {0}; + d->cursors[cursor] = XCreatePixmapCursor(d->native, pixmap, pixmap, &color, &color, 0, 0); + XFreePixmap(d->native, pixmap); + } else if ((unsigned)cursor < sizeof(shapes) / sizeof(shapes[0])) { + d->cursors[cursor] = XCreateFontCursor(d->native, shapes[cursor]); + } + } + if (d->cursors[cursor]) + XDefineCursor(d->native, s->window, d->cursors[cursor]); } static bool place(struct yasl_surface *base, struct yasl_vector2i at) |
