diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/cocoa/api.h | 1 | ||||
| -rw-r--r-- | src/cocoa/window.m | 83 | ||||
| -rw-r--r-- | src/common/backend.c | 12 | ||||
| -rw-r--r-- | src/common/backend.h | 1 | ||||
| -rw-r--r-- | src/common/loop.c | 173 | ||||
| -rw-r--r-- | src/common/timing.c | 9 | ||||
| -rw-r--r-- | src/common/utf8.c | 39 | ||||
| -rw-r--r-- | src/wayland/api.h | 1 | ||||
| -rw-r--r-- | src/wayland/backend.c | 6 | ||||
| -rw-r--r-- | src/wayland/buffer.c | 30 | ||||
| -rw-r--r-- | src/wayland/clipboard.c | 9 | ||||
| -rw-r--r-- | src/wayland/internal.h | 129 | ||||
| -rw-r--r-- | src/wayland/keyboard.c | 12 | ||||
| -rw-r--r-- | src/wayland/pointer.c | 124 | ||||
| -rw-r--r-- | src/wayland/shell.c | 29 | ||||
| -rw-r--r-- | src/wayland/surface.c | 117 | ||||
| -rw-r--r-- | src/x11/window.c | 51 |
17 files changed, 618 insertions, 208 deletions
diff --git a/src/cocoa/api.h b/src/cocoa/api.h index ad259df..2fea415 100644 --- a/src/cocoa/api.h +++ b/src/cocoa/api.h @@ -10,6 +10,7 @@ struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct ya void cocoa_display_close(struct cocoa_display *d); uint32_t cocoa_display_caps(const struct cocoa_display *d); void cocoa_display_dispatch(struct cocoa_display *d); +int cocoa_display_dispatch_timeout(struct cocoa_display *d, int timeout_ms); void cocoa_display_flush(struct cocoa_display *d); int cocoa_display_modifiers(const struct cocoa_display *d); void cocoa_display_copy(struct cocoa_display *d, enum yasl_selection sel, const char *text); diff --git a/src/cocoa/window.m b/src/cocoa/window.m index bc6060f..8712ce8 100644 --- a/src/cocoa/window.m +++ b/src/cocoa/window.m @@ -320,14 +320,36 @@ static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_pointer w->mods = cocoa_mods(ev.modifierFlags); if (kind != YASL_PTR_LEAVE) w->pointer = event_pos(w, ev); + + enum yasl_pointer_device device = YASL_PTR_DEV_MOUSE; + struct yasl_pointer_pen pen = {0}; + + float press = (float)ev.pressure; + if (ev.subtype == NSEventSubtypeTabletPoint || press > 0.0f) { + if (ev.pointingDeviceType == NSPointingDeviceTypeEraser) + device = YASL_PTR_DEV_ERASER; + else if (ev.pointingDeviceType == NSPointingDeviceTypePen) + device = YASL_PTR_DEV_PEN; + + if (press > 1.0f) press = 1.0f; + if (press > 0.0f) + pen.pressure = (uint16_t)(press * 65535.0f); + + NSPoint tilt = ev.tilt; + pen.tilt_x = (int8_t)(tilt.x * 90.0); + pen.tilt_y = (int8_t)(tilt.y * 90.0); + } + struct yasl_input_event input_ev = { - .kind = YASL_INPUT_MOUSE, .surface = &w->base, + .kind = YASL_INPUT_MOUSE, .mouse = { .pos = w->pointer, - .button = button, + .button = (int16_t)button, .kind = kind, + .device = device, .mods = w->mods, + .pen = pen, }, }; input_queue_push(&w->input, &input_ev); @@ -871,29 +893,62 @@ static void pump_window(struct cocoa_surface *w) @autoreleasepool { render_and_commit(w); } } -void cocoa_display_dispatch(struct cocoa_display *d) +int cocoa_display_dispatch_timeout(struct cocoa_display *d, int timeout_ms) { assert([NSThread isMainThread]); if (!d || d->pumping) - return; + return -1; d->pumping = true; d->depth++; + int count = 0; @try { - for (;;) { - @autoreleasepool { - NSEvent *ev = [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:[NSDate distantPast] - inMode:NSDefaultRunLoopMode - dequeue:YES]; - if (!ev) break; + NSDate *limitDate; + if (timeout_ms == 0) + limitDate = [NSDate distantPast]; + else if (timeout_ms < 0) + limitDate = [NSDate distantFuture]; + else + limitDate = [NSDate dateWithTimeIntervalSinceNow:(double)timeout_ms / 1000.0]; + + @autoreleasepool { + NSEvent *ev = [NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:limitDate + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (ev) { + count++; [NSApp sendEvent:ev]; [NSApp updateWindows]; + + while ((ev = [NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES])) { + count++; + [NSApp sendEvent:ev]; + [NSApp updateWindows]; + } } } } @finally { d->depth--; d->pumping = false; } + return count; +} + +void cocoa_display_dispatch(struct cocoa_display *d) +{ + if (!d) return; + bool immediate = false; + for (int i = 0; i < d->count; i++) { + struct cocoa_surface *w = d->windows[i]; + if (w->input.count > 0 || (w->want_draw && (!w->link || w->frame_due || w->unpaced))) { + immediate = true; + break; + } + } + cocoa_display_dispatch_timeout(d, immediate ? 0 : -1); } void cocoa_display_flush(struct cocoa_display *d) @@ -1388,6 +1443,11 @@ static void impl_display_dispatch(struct yasl_display *d) cocoa_display_dispatch((struct cocoa_display *)d); } +static int impl_display_dispatch_timeout(struct yasl_display *d, int timeout_ms) +{ + return cocoa_display_dispatch_timeout((struct cocoa_display *)d, timeout_ms); +} + static void impl_display_flush(struct yasl_display *d) { cocoa_display_flush((struct cocoa_display *)d); @@ -1562,6 +1622,7 @@ const struct yasl_backend_ops yasl_cocoa_backend = { .display_close = impl_display_close, .display_caps = impl_display_caps, .display_dispatch = impl_display_dispatch, + .display_dispatch_timeout = impl_display_dispatch_timeout, .display_flush = impl_display_flush, .display_modifiers = impl_display_modifiers, .display_copy = impl_display_copy, diff --git a/src/common/backend.c b/src/common/backend.c index a6fa6f2..2220f1f 100644 --- a/src/common/backend.c +++ b/src/common/backend.c @@ -85,6 +85,18 @@ void yasl_display_dispatch(struct yasl_display *d) d->ops->display_dispatch(d); } +int yasl_display_dispatch_timeout(struct yasl_display *d, int timeout_ms) +{ + if (!d) return -1; + if (d->ops->display_dispatch_timeout) + return d->ops->display_dispatch_timeout(d, timeout_ms); + if (d->ops->display_dispatch) { + d->ops->display_dispatch(d); + return 0; + } + return -1; +} + void yasl_display_flush(struct yasl_display *d) { if (!d || !d->ops->display_flush) diff --git a/src/common/backend.h b/src/common/backend.h index 0294aed..c1d878b 100644 --- a/src/common/backend.h +++ b/src/common/backend.h @@ -19,6 +19,7 @@ struct yasl_backend_ops { void (*display_close)(struct yasl_display *d); uint32_t (*display_caps)(const struct yasl_display *d); void (*display_dispatch)(struct yasl_display *d); + int (*display_dispatch_timeout)(struct yasl_display *d, int timeout_ms); void (*display_flush)(struct yasl_display *d); int (*display_modifiers)(const struct yasl_display *d); void (*display_copy)(struct yasl_display *d, enum yasl_selection sel, const char *text); diff --git a/src/common/loop.c b/src/common/loop.c index 4c2da0d..ea9bee3 100644 --- a/src/common/loop.c +++ b/src/common/loop.c @@ -1,13 +1,16 @@ +#define _GNU_SOURCE #define _POSIX_C_SOURCE 200809L #include "yasl/yasl.h" #include <errno.h> +#include <fcntl.h> #include <poll.h> #include <stdlib.h> +#include <string.h> #include <time.h> #include <unistd.h> -#define YASL_MAX_FDS 64 +#define YASL_LOOP_INIT_CAP 32 typedef int (*yasl_loop_fd_fn)(int fd, short revents, void *ud); @@ -20,11 +23,15 @@ struct yasl_fd_source { }; struct yasl_loop { - struct yasl_fd_source fds[YASL_MAX_FDS]; - struct pollfd pollfds[YASL_MAX_FDS]; + struct yasl_fd_source *fds; + struct pollfd *pollfds; int count; + int capacity; int next_id; bool running; + int wake_rfd; + int wake_wfd; + int wake_watch; }; uint64_t yasl_loop_now_ns(void) @@ -34,25 +41,95 @@ uint64_t yasl_loop_now_ns(void) return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec; } +static int loop_wake_cb(int fd, short revents, void *ud) +{ + (void)revents; + (void)ud; + char buf[128]; + ssize_t n; + while ((n = read(fd, buf, sizeof(buf))) > 0 || (n < 0 && errno == EINTR)) {} + return 0; +} + struct yasl_loop *yasl_loop_create(void) { struct yasl_loop *l = calloc(1, sizeof(*l)); if (!l) return NULL; + + l->capacity = YASL_LOOP_INIT_CAP; + l->fds = malloc((size_t)l->capacity * sizeof(*l->fds)); + l->pollfds = malloc((size_t)l->capacity * sizeof(*l->pollfds)); + if (!l->fds || !l->pollfds) { + free(l->fds); + free(l->pollfds); + free(l); + return NULL; + } + l->next_id = 1; + l->wake_rfd = -1; + l->wake_wfd = -1; + + int p[2]; +#if defined(__linux__) && defined(O_CLOEXEC) + if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == 0) { + l->wake_rfd = p[0]; + l->wake_wfd = p[1]; + } +#else + if (pipe(p) == 0) { + int f0 = fcntl(p[0], F_GETFL, 0); + int f1 = fcntl(p[1], F_GETFL, 0); + if (f0 >= 0) fcntl(p[0], F_SETFL, f0 | O_NONBLOCK); + if (f1 >= 0) fcntl(p[1], F_SETFL, f1 | O_NONBLOCK); + fcntl(p[0], F_SETFD, FD_CLOEXEC); + fcntl(p[1], F_SETFD, FD_CLOEXEC); + l->wake_rfd = p[0]; + l->wake_wfd = p[1]; + } +#endif + + if (l->wake_rfd >= 0) { + l->wake_watch = yasl_loop_add_fd(l, l->wake_rfd, POLLIN, loop_wake_cb, l); + } return l; } void yasl_loop_destroy(struct yasl_loop *l) { + if (!l) return; + if (l->wake_watch > 0) + yasl_loop_del_fd(l, l->wake_watch); + if (l->wake_rfd >= 0) + close(l->wake_rfd); + if (l->wake_wfd >= 0) + close(l->wake_wfd); + free(l->fds); + free(l->pollfds); free(l); } int yasl_loop_add_fd(struct yasl_loop *l, int fd, short events, int (*fn)(int fd, short revents, void *ud), void *ud) { - if (!l || l->count >= YASL_MAX_FDS || fd < 0) + if (!l || fd < 0) return -1; + + if (l->count >= l->capacity) { + int new_cap = l->capacity ? l->capacity * 2 : YASL_LOOP_INIT_CAP; + struct yasl_fd_source *new_fds = realloc(l->fds, (size_t)new_cap * sizeof(*new_fds)); + if (!new_fds) return -1; + l->fds = new_fds; + + struct pollfd *new_pollfds = realloc(l->pollfds, (size_t)new_cap * sizeof(*new_pollfds)); + if (!new_pollfds) return -1; + l->pollfds = new_pollfds; + l->capacity = new_cap; + } + int id = l->next_id++; + if (l->next_id <= 0) l->next_id = 1; + l->fds[l->count] = (struct yasl_fd_source){ .fn = fn, .ud = ud, .id = id, .fd = fd, .events = events, }; @@ -92,7 +169,74 @@ int yasl_loop_set_fd_events(struct yasl_loop *l, int id, short events) void yasl_loop_kick(struct yasl_loop *l) { - (void)l; + if (!l || l->wake_wfd < 0) return; + char b = 1; + ssize_t n; + do { + n = write(l->wake_wfd, &b, 1); + } while (n < 0 && errno == EINTR); +} + +int yasl_loop_step(struct yasl_loop *l, int timeout_ms) +{ + if (!l) return -1; + if (l->count <= 0) { + if (timeout_ms > 0) { + struct timespec req = { + .tv_sec = timeout_ms / 1000, + .tv_nsec = (long)(timeout_ms % 1000) * 1000000L, + }; + nanosleep(&req, NULL); + } + return 0; + } + + int n; + do { + n = poll(l->pollfds, (nfds_t)l->count, timeout_ms); + } while (n < 0 && errno == EINTR); + + if (n < 0) return -1; + if (n == 0) return 0; + + int handled = 0; + int count = l->count; + + struct yasl_fd_source sstack[32]; + struct pollfd pstack[32]; + struct yasl_fd_source *scopy = (count <= 32) ? sstack : malloc((size_t)count * sizeof(*scopy)); + struct pollfd *pcopy = (count <= 32) ? pstack : malloc((size_t)count * sizeof(*pcopy)); + if (!scopy || !pcopy) { + if (scopy && scopy != sstack) free(scopy); + if (pcopy && pcopy != pstack) free(pcopy); + return -1; + } + + memcpy(scopy, l->fds, (size_t)count * sizeof(*scopy)); + memcpy(pcopy, l->pollfds, (size_t)count * sizeof(*pcopy)); + + for (int i = 0; i < count; i++) { + if (pcopy[i].revents) { + short revents = pcopy[i].revents; + // Verify that the source was not deleted by an earlier callback in this step + bool valid = false; + for (int j = 0; j < l->count; j++) { + if (l->fds[j].id == scopy[i].id) { + l->pollfds[j].revents = 0; + valid = true; + break; + } + } + if (valid && scopy[i].fn) { + scopy[i].fn(scopy[i].fd, revents, scopy[i].ud); + handled++; + } + } + } + + if (scopy != sstack) free(scopy); + if (pcopy != pstack) free(pcopy); + return handled; } int yasl_loop_run(struct yasl_loop *l) @@ -100,20 +244,8 @@ int yasl_loop_run(struct yasl_loop *l) if (!l) return -1; l->running = true; while (l->running) { - int n = poll(l->pollfds, (nfds_t)l->count, -1); - if (n < 0) { - if (errno == EINTR) continue; + if (yasl_loop_step(l, -1) < 0) return -1; - } - for (int i = 0; i < l->count && l->running; i++) { - if (l->pollfds[i].revents) { - struct yasl_fd_source src = l->fds[i]; - short revents = l->pollfds[i].revents; - l->pollfds[i].revents = 0; - if (src.fn) - src.fn(src.fd, revents, src.ud); - } - } } return 0; } @@ -122,3 +254,8 @@ void yasl_loop_quit(struct yasl_loop *l) { if (l) l->running = false; } + +bool yasl_loop_is_running(const struct yasl_loop *l) +{ + return l ? l->running : false; +} diff --git a/src/common/timing.c b/src/common/timing.c index b5e342f..3e3a705 100644 --- a/src/common/timing.c +++ b/src/common/timing.c @@ -5,8 +5,8 @@ uint64_t yasl_clock_extend_ms(uint32_t ms, uint64_t now_ns) uint64_t now_ms = now_ns / 1000000; int64_t ahead = (int32_t)(ms - (uint32_t)now_ms); if (ahead < 0 && (uint64_t)-ahead > now_ms) - return (uint64_t)ms * 1000000; - return (now_ms + (uint64_t)ahead) * 1000000; + return (uint64_t)ms * 1000000ull; + return (uint64_t)((int64_t)now_ms + ahead) * 1000000ull; } void yasl_clock_presented(struct yasl_frame_clock *c, uint64_t now, bool sample) @@ -45,9 +45,8 @@ double yasl_clock_refresh_ms(const struct yasl_frame_clock *c) uint64_t yasl_clock_next_present_ns(const struct yasl_frame_clock *c) { - return c->last_present_ns - ? c->last_present_ns + (uint64_t)c->refresh_ns - : 0; + uint64_t next = c->last_present_ns + (uint64_t)c->refresh_ns; + return (uint64_t)-(c->last_present_ns != 0) & next; } uint64_t yasl_clock_frame_timestamp_ns(struct yasl_frame_clock *c, uint64_t now) diff --git a/src/common/utf8.c b/src/common/utf8.c index 089fd89..1d4cf13 100644 --- a/src/common/utf8.c +++ b/src/common/utf8.c @@ -3,30 +3,46 @@ #include <stdbool.h> #include <stdint.h> +#ifndef likely +#define likely(x) __builtin_expect(!!(x), 1) +#endif +#ifndef unlikely +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif + bool yasl_utf8_validate(const char *str, size_t len) { const unsigned char *p = (const unsigned char *)str; const unsigned char *end = p + len; while (p < end) { - if (*p < 0x80) { + while (p + 8 <= end) { + uint64_t v; + memcpy(&v, p, 8); + if (v & 0x8080808080808080ull) + break; + p += 8; + } + if (p >= end) break; + + if (likely(*p < 0x80)) { p++; } else if ((*p & 0xe0) == 0xc0) { - if (p + 1 >= end || (p[1] & 0xc0) != 0x80 || (*p & 0x1e) == 0) + if (unlikely(p + 1 >= end || (p[1] & 0xc0) != 0x80 || (*p & 0x1e) == 0)) return false; p += 2; } else if ((*p & 0xf0) == 0xe0) { - if (p + 2 >= end || (p[1] & 0xc0) != 0x80 || (p[2] & 0xc0) != 0x80) + if (unlikely(p + 2 >= end || (p[1] & 0xc0) != 0x80 || (p[2] & 0xc0) != 0x80)) return false; uint32_t cp = ((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f); - if (cp < 0x800 || (cp >= 0xd800 && cp <= 0xdfff)) + if (unlikely(cp < 0x800 || (cp >= 0xd800 && cp <= 0xdfff))) return false; p += 3; } else if ((*p & 0xf8) == 0xf0) { - if (p + 3 >= end || (p[1] & 0xc0) != 0x80 || (p[2] & 0xc0) != 0x80 || (p[3] & 0xc0) != 0x80) + if (unlikely(p + 3 >= end || (p[1] & 0xc0) != 0x80 || (p[2] & 0xc0) != 0x80 || (p[3] & 0xc0) != 0x80)) return false; uint32_t cp = ((p[0] & 0x07) << 18) | ((p[1] & 0x3f) << 12) | ((p[2] & 0x3f) << 6) | (p[3] & 0x3f); - if (cp < 0x10000 || cp > 0x10ffff) + if (unlikely(cp < 0x10000 || cp > 0x10ffff)) return false; p += 4; } else { @@ -38,10 +54,17 @@ bool yasl_utf8_validate(const char *str, size_t len) char *yasl_utf8_sanitize_dup(const char *str) { - if (!str) return NULL; + if (unlikely(!str)) return NULL; size_t len = strlen(str); + if (likely(yasl_utf8_validate(str, len))) { + char *out = malloc(len + 1); + if (unlikely(!out)) return NULL; + memcpy(out, str, len + 1); + return out; + } + char *out = malloc(len + 1); - if (!out) return NULL; + if (unlikely(!out)) return NULL; const unsigned char *p = (const unsigned char *)str; size_t o = 0; diff --git a/src/wayland/api.h b/src/wayland/api.h index e4473cb..8252041 100644 --- a/src/wayland/api.h +++ b/src/wayland/api.h @@ -10,6 +10,7 @@ struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struc void wayland_display_close(struct wayland_display *d); uint32_t wayland_display_caps(const struct wayland_display *d); void wayland_display_dispatch(struct wayland_display *d); +int wayland_display_dispatch_timeout(struct wayland_display *d, int timeout_ms); void wayland_display_flush(struct wayland_display *d); int wayland_display_modifiers(const struct wayland_display *d); void wayland_display_copy(struct wayland_display *d, enum yasl_selection sel, const char *text); diff --git a/src/wayland/backend.c b/src/wayland/backend.c index da7f1a3..31e412f 100644 --- a/src/wayland/backend.c +++ b/src/wayland/backend.c @@ -21,6 +21,11 @@ static void impl_display_dispatch(struct yasl_display *d) wayland_display_dispatch((struct wayland_display *)d); } +static int impl_display_dispatch_timeout(struct yasl_display *d, int timeout_ms) +{ + return wayland_display_dispatch_timeout((struct wayland_display *)d, timeout_ms); +} + static void impl_display_flush(struct yasl_display *d) { wayland_display_flush((struct wayland_display *)d); @@ -195,6 +200,7 @@ const struct yasl_backend_ops yasl_wayland_backend = { .display_close = impl_display_close, .display_caps = impl_display_caps, .display_dispatch = impl_display_dispatch, + .display_dispatch_timeout = impl_display_dispatch_timeout, .display_flush = impl_display_flush, .display_modifiers = impl_display_modifiers, .display_copy = impl_display_copy, diff --git a/src/wayland/buffer.c b/src/wayland/buffer.c index 9776f13..76f6c0e 100644 --- a/src/wayland/buffer.c +++ b/src/wayland/buffer.c @@ -305,13 +305,19 @@ static void arm_frame(struct wayland_surface *w) } } +static const int8_t free_buf_lut[4][8] = { + [0] = { 1, 1, 2, 2, 1, 1, 0, -1 }, + [1] = { 0, 2, 0, 2, 0, 1, 0, -1 }, + [2] = { 0, 1, 0, 2, 0, 1, 0, -1 }, + [3] = { 0, 1, 0, 2, 0, 1, 0, -1 }, +}; + int buffer_pick_free_index(const struct buffer *bufs, int cur) { - if (!bufs[cur ^ 1].busy) return cur ^ 1; - for (int i = 0; i < NBUF; i++) - if (i != cur && i != (cur ^ 1) && !bufs[i].busy) return i; - if (!bufs[cur].busy) return cur; - return -1; + unsigned busy = ((unsigned)bufs[0].busy) | + (((unsigned)bufs[1].busy) << 1) | + (((unsigned)bufs[2].busy) << 2); + return free_buf_lut[(unsigned)cur & 3][busy]; } struct latency_feedback { @@ -523,14 +529,12 @@ void wayland_surface_set_title(struct wayland_surface *w, const char *title) uint32_t wayland_surface_state(const struct wayland_surface *w) { - if (!w) return 0; - uint32_t s = 0; - if (w->fullscreen) s |= YASL_STATE_FULLSCREEN; - if (w->maximized) s |= YASL_STATE_MAXIMIZED; - if (w->activated) s |= YASL_STATE_ACTIVATED; - if (w->suspended) s |= YASL_STATE_SUSPENDED; - if (!w->server_decorated) s |= YASL_STATE_CSD; - return s; + if (unlikely(!w)) return 0; + return ((uint32_t)w->fullscreen * YASL_STATE_FULLSCREEN) | + ((uint32_t)w->maximized * YASL_STATE_MAXIMIZED) | + ((uint32_t)w->activated * YASL_STATE_ACTIVATED) | + ((uint32_t)w->suspended * YASL_STATE_SUSPENDED) | + ((uint32_t)!w->server_decorated * YASL_STATE_CSD); } struct yasl_timing wayland_surface_timing(const struct wayland_surface *w) diff --git a/src/wayland/clipboard.c b/src/wayland/clipboard.c index 324644f..0e6ce33 100644 --- a/src/wayland/clipboard.c +++ b/src/wayland/clipboard.c @@ -170,9 +170,12 @@ static struct transfer *start_transfer(struct clipboard *c, int fd, bool sending static const char *match_text_mime(const char *mime) { - if (!strcasecmp(mime, "text/plain;charset=utf-8")) return "text/plain;charset=utf-8"; - if (!strcasecmp(mime, "text/plain;charset=utf8")) return "text/plain;charset=utf8"; - if (!strcasecmp(mime, "text/plain")) return "text/plain"; + if (unlikely(!mime)) return NULL; + if (likely(!strncasecmp(mime, "text/plain", 10))) { + if (!strcasecmp(mime, "text/plain;charset=utf-8")) return "text/plain;charset=utf-8"; + if (!strcasecmp(mime, "text/plain;charset=utf8")) return "text/plain;charset=utf8"; + if (!strcasecmp(mime, "text/plain")) return "text/plain"; + } if (!strcasecmp(mime, "UTF8_STRING")) return "UTF8_STRING"; if (!strcasecmp(mime, "STRING")) return "STRING"; if (!strcasecmp(mime, "TEXT")) return "TEXT"; diff --git a/src/wayland/internal.h b/src/wayland/internal.h index 6b9001f..cba5096 100644 --- a/src/wayland/internal.h +++ b/src/wayland/internal.h @@ -89,14 +89,21 @@ struct drag_icon { int fd; }; +#ifndef likely +#define likely(x) __builtin_expect(!!(x), 1) +#endif +#ifndef unlikely +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif + struct conn_drag { struct wl_data_source *source; struct xdg_toplevel_drag_v1 *toplevel; struct drag_icon icon; const struct yasl_drag_listener *listener; void *ud; - uint32_t scale_120; struct seat *seat; + uint32_t scale_120; bool dropped; bool carrying; }; @@ -105,17 +112,19 @@ struct conn_drag { struct touch_point { struct wayland_surface *surface; - int32_t id; double x, y; + int32_t id; bool active; - bool down, moved, up; + bool down; + bool moved; + bool up; }; struct seat { + /* 8-byte aligned pointers and 64-bit values */ struct wayland_display *conn; struct seat *next; struct wl_seat *proxy; - uint32_t name; struct wl_keyboard *keyboard; struct wl_pointer *pointer; struct wl_touch *touch; @@ -128,59 +137,63 @@ struct seat { struct tablet_pad *tablet_pads; struct zwp_text_input_v3 *text_input; struct clipboard *clipboard; - struct xkb_keymap *xkb_keymap; struct xkb_state *xkb_state; - xkb_mod_mask_t input_mod_masks[8]; - xkb_mod_index_t mod_shift, mod_alt, mod_ctrl, mod_super; - bool input_mods_ready; uint64_t repeat_started_ns; - int repeat_fd, repeat_watch; - int32_t repeat_rate, repeat_delay; - uint32_t repeat_key; - bool repeat_held; - struct wayland_surface *pointer_focus; struct wayland_surface *keyboard_focus; struct wayland_surface *tablet_focus; struct wayland_surface *ime_focus; struct wayland_surface *focus_leaving; - uint32_t input_serial; - uint32_t press_serial; struct wayland_surface *press_surface; + struct wayland_surface *ptr_left; + struct tablet_tool *tool; + struct touch_point *touch_primary; + uint64_t pointer_buttons; + uint64_t tool_buttons; + double axis_v, axis_h, frame_v; + double touch_scroll_y, touch_scroll_rest; + + /* Embedded structs */ + xkb_mod_mask_t input_mod_masks[8]; + struct yasl_vector2i pointer_pos; + struct yasl_vector2i ptr_left_pos; + struct yasl_vector2i tool_pos; + struct touch_point touch_points[SEAT_TOUCH_POINTS]; + struct press_queue ptr_presses; + struct press_queue tool_presses; + /* 4-byte integers */ + uint32_t name; + xkb_mod_index_t mod_shift, mod_alt, mod_ctrl, mod_super; + int repeat_fd, repeat_watch; + int32_t repeat_rate, repeat_delay; + uint32_t repeat_key; + uint32_t input_serial; + uint32_t press_serial; uint32_t pointer_serial; int cursor_shape; - struct yasl_vector2i pointer_pos; - uint64_t pointer_buttons; - double axis_v, axis_h, frame_v; int frame_v120; int axis_discrete_v, axis_discrete_h; int axis_value120_v, axis_value120_h; - bool axis_has_v120_v, axis_has_v120_h; + struct yasl_pointer_pen pen; + int tool_wheel; + + /* 1-byte booleans grouped tightly */ + bool input_mods_ready; + bool repeat_held; + bool axis_has_v120_v; + bool axis_has_v120_h; bool axis_smooth; bool motion_pending; - struct press_queue ptr_presses; - struct wayland_surface *ptr_left; - struct yasl_vector2i ptr_left_pos; bool ptr_left_motion; - - struct tablet_tool *tool; - struct press_queue tool_presses; - struct yasl_vector2i tool_pos; - uint64_t tool_buttons; - struct yasl_pointer_pen pen; - int tool_wheel; bool tool_motion; - - struct touch_point touch_points[SEAT_TOUCH_POINTS]; - struct touch_point *touch_primary; bool touch_pressed; bool touch_scrolling; - double touch_scroll_y, touch_scroll_rest; }; #define YASL_QUEUE_CAP 256 +#define YASL_QUEUE_MASK (YASL_QUEUE_CAP - 1) struct input_queue { struct yasl_input_event events[YASL_QUEUE_CAP]; int head; @@ -190,17 +203,17 @@ struct input_queue { static inline void input_queue_push(struct input_queue *q, const struct yasl_input_event *ev) { - if (q->count >= YASL_QUEUE_CAP) return; + if (unlikely(q->count >= YASL_QUEUE_CAP)) return; q->events[q->tail] = *ev; - q->tail = (q->tail + 1) % YASL_QUEUE_CAP; + q->tail = (q->tail + 1) & YASL_QUEUE_MASK; q->count++; } static inline bool input_queue_pop(struct input_queue *q, struct yasl_input_event *ev) { - if (q->count <= 0) return false; + if (unlikely(q->count <= 0)) return false; *ev = q->events[q->head]; - q->head = (q->head + 1) % YASL_QUEUE_CAP; + q->head = (q->head + 1) & YASL_QUEUE_MASK; q->count--; return true; } @@ -212,6 +225,7 @@ static inline void input_queue_clear(struct input_queue *q) struct wayland_display { struct yasl_display base; + /* 8-byte pointers & 64-bit masks */ struct wl_display *wl; struct wl_registry *registry; struct wl_compositor *compositor; @@ -229,7 +243,6 @@ struct wayland_display { struct wp_cursor_shape_manager_v1 *cursor_shape_mgr; struct wl_cursor_theme *cursor_theme; const char *cursor_theme_name; - int cursor_size, cursor_theme_scale; struct zwp_tablet_manager_v2 *tablet_mgr; struct zwp_text_input_manager_v3 *text_input_mgr; struct wp_tearing_control_manager_v1 *tearing_mgr; @@ -241,21 +254,29 @@ struct wayland_display { const struct yasl_display_cbs *cbs; void *ud; char *app_id; - clockid_t pres_clock; - bool pres_clock_known; struct xdg_toplevel_drag_manager_v1 *toplevel_drag_mgr; struct conn_drag drag; struct wayland_surface *surfaces; struct wayland_surface *paste_target; + struct wayland_surface *dead; + + /* 4-byte integers */ + int cursor_size; + int cursor_theme_scale; + clockid_t pres_clock; int watch; int depth; - struct wayland_surface *dead; + + /* 1-byte booleans */ + bool owns_loop; + bool pres_clock_known; bool reading; bool lost; }; struct wayland_surface { struct yasl_surface base; + /* 8-byte pointers & 64-bit values */ struct wayland_display *conn; struct wayland_surface *conn_next; struct wayland_surface *dead_next; @@ -265,16 +286,11 @@ struct wayland_surface { struct xdg_toplevel *toplevel; struct xdg_popup *popup; struct zxdg_toplevel_decoration_v1 *toplevel_decor; - bool server_decorated; struct wp_fractional_scale_v1 *frac_scale; struct wp_viewport *viewport; - char *ime_preedit; char *ime_commit; char *ime_shown; - int ime_cursor_begin, ime_cursor_end; - int ime_shown_begin, ime_shown_end; - struct yasl_rect2i ime_caret; struct seat *ime_seat; struct wp_tearing_control_v1 *tearing_control; struct latency_feedback *latency_feedbacks; @@ -284,18 +300,23 @@ struct wayland_surface { const struct yasl_surface_cbs *cbs; void *ud; uint64_t frames_skipped; + uint64_t outputs; + double scale_factor; + /* Sub-structures */ + struct yasl_rect2i ime_caret; + struct yasl_rect2i content; struct yasl_frame_clock clock; - struct input_queue input; - struct buffer buffers[NBUF]; struct yasl_frame frame; struct yasl_damage_list damage; - bool frame_active; - bool frame_presented; + int ime_cursor_begin; + struct input_queue input; + /* 4-byte integers */ + int ime_cursor_end; + int ime_shown_begin, ime_shown_end; int floating_width, floating_height; - struct yasl_rect2i content; int cur; int width, height; int logical_width, logical_height; @@ -304,9 +325,11 @@ struct wayland_surface { uint32_t scale_120; uint32_t pending_scale_120; uint32_t pending_serial; - double scale_factor; - uint64_t outputs; + /* 1-byte booleans packed tightly */ + bool server_decorated; + bool frame_active; + bool frame_presented; bool tiled; bool size_pending; bool sample_refresh; diff --git a/src/wayland/keyboard.c b/src/wayland/keyboard.c index 1746ac6..6bdd58c 100644 --- a/src/wayland/keyboard.c +++ b/src/wayland/keyboard.c @@ -346,13 +346,11 @@ static bool mod_index_active(const struct seat *s, xkb_mod_index_t mod) int seat_modifiers(const struct seat *s) { - if (!s || !s->xkb_state) return 0; - int mods = 0; - if (mod_index_active(s, s->mod_shift)) mods |= YASL_MOD_SHIFT; - if (mod_index_active(s, s->mod_alt)) mods |= YASL_MOD_ALT; - if (mod_index_active(s, s->mod_ctrl)) mods |= YASL_MOD_CTRL; - if (mod_index_active(s, s->mod_super)) mods |= YASL_MOD_SUPER; - return mods; + if (unlikely(!s || !s->xkb_state)) return 0; + return (mod_index_active(s, s->mod_shift) * YASL_MOD_SHIFT) | + (mod_index_active(s, s->mod_alt) * YASL_MOD_ALT) | + (mod_index_active(s, s->mod_ctrl) * YASL_MOD_CTRL) | + (mod_index_active(s, s->mod_super) * YASL_MOD_SUPER); } int wayland_display_modifiers(const struct wayland_display *c) diff --git a/src/wayland/pointer.c b/src/wayland/pointer.c index f2902b4..d434047 100644 --- a/src/wayland/pointer.c +++ b/src/wayland/pointer.c @@ -25,9 +25,9 @@ struct tablet_pad { struct zwp_tablet_pad_v2 *proxy; }; -static int device_px_round(double v) +static inline int device_px_round(double v) { - return (int)(v < 0 ? v - 0.5 : v + 0.5); + return (int)lrint(v); } static struct yasl_vector2i device_from_fixed(const struct wayland_surface *w, @@ -39,12 +39,44 @@ static struct yasl_vector2i device_from_fixed(const struct wayland_surface *w, }; } -static int motion_button(uint64_t buttons) -{ - for (int b = 0; b <= 7; b++) - if (buttons & (1ull << b)) - return b; - return 3; +static inline int motion_button(uint64_t buttons) +{ + static const uint8_t motion_btn_lut[256] = { + [0] = 3, + [1] = 0, [2] = 1, [3] = 0, [4] = 2, [5] = 0, [6] = 1, [7] = 0, + [8] = 3, [9] = 0, [10] = 1, [11] = 0, [12] = 2, [13] = 0, [14] = 1, [15] = 0, + [16] = 4, [17] = 0, [18] = 1, [19] = 0, [20] = 2, [21] = 0, [22] = 1, [23] = 0, + [24] = 3, [25] = 0, [26] = 1, [27] = 0, [28] = 2, [29] = 0, [30] = 1, [31] = 0, + [32] = 5, [33] = 0, [34] = 1, [35] = 0, [36] = 2, [37] = 0, [38] = 1, [39] = 0, + [40] = 3, [41] = 0, [42] = 1, [43] = 0, [44] = 2, [45] = 0, [46] = 1, [47] = 0, + [48] = 4, [49] = 0, [50] = 1, [51] = 0, [52] = 2, [53] = 0, [54] = 1, [55] = 0, + [56] = 3, [57] = 0, [58] = 1, [59] = 0, [60] = 2, [61] = 0, [62] = 1, [63] = 0, + [64] = 6, [65] = 0, [66] = 1, [67] = 0, [68] = 2, [69] = 0, [70] = 1, [71] = 0, + [72] = 3, [73] = 0, [74] = 1, [75] = 0, [76] = 2, [77] = 0, [78] = 1, [79] = 0, + [80] = 4, [81] = 0, [82] = 1, [83] = 0, [84] = 2, [85] = 0, [86] = 1, [87] = 0, + [88] = 3, [89] = 0, [90] = 1, [91] = 0, [92] = 2, [93] = 0, [94] = 1, [95] = 0, + [96] = 5, [97] = 0, [98] = 1, [99] = 0, [100] = 2, [101] = 0, [102] = 1, [103] = 0, + [104] = 3, [105] = 0, [106] = 1, [107] = 0, [108] = 2, [109] = 0, [110] = 1, [111] = 0, + [112] = 4, [113] = 0, [114] = 1, [115] = 0, [116] = 2, [117] = 0, [118] = 1, [119] = 0, + [120] = 3, [121] = 0, [122] = 1, [123] = 0, [124] = 2, [125] = 0, [126] = 1, [127] = 0, + [128] = 7, [129] = 0, [130] = 1, [131] = 0, [132] = 2, [133] = 0, [134] = 1, [135] = 0, + [136] = 3, [137] = 0, [138] = 1, [139] = 0, [140] = 2, [141] = 0, [142] = 1, [143] = 0, + [144] = 4, [145] = 0, [146] = 1, [147] = 0, [148] = 2, [149] = 0, [150] = 1, [151] = 0, + [152] = 3, [153] = 0, [154] = 1, [155] = 0, [156] = 2, [157] = 0, [158] = 1, [159] = 0, + [160] = 5, [161] = 0, [162] = 1, [163] = 0, [164] = 2, [165] = 0, [166] = 1, [167] = 0, + [168] = 3, [169] = 0, [170] = 1, [171] = 0, [172] = 2, [173] = 0, [174] = 1, [175] = 0, + [176] = 4, [177] = 0, [178] = 1, [179] = 0, [180] = 2, [181] = 0, [182] = 1, [183] = 0, + [184] = 3, [185] = 0, [186] = 1, [187] = 0, [188] = 2, [189] = 0, [190] = 1, [191] = 0, + [192] = 6, [193] = 0, [194] = 1, [195] = 0, [196] = 2, [197] = 0, [198] = 1, [199] = 0, + [200] = 3, [201] = 0, [202] = 1, [203] = 0, [204] = 2, [205] = 0, [206] = 1, [207] = 0, + [208] = 4, [209] = 0, [210] = 1, [211] = 0, [212] = 2, [213] = 0, [214] = 1, [215] = 0, + [216] = 3, [217] = 0, [218] = 1, [219] = 0, [220] = 2, [221] = 0, [222] = 1, [223] = 0, + [224] = 5, [225] = 0, [226] = 1, [227] = 0, [228] = 2, [229] = 0, [230] = 1, [231] = 0, + [232] = 3, [233] = 0, [234] = 1, [235] = 0, [236] = 2, [237] = 0, [238] = 1, [239] = 0, + [240] = 4, [241] = 0, [242] = 1, [243] = 0, [244] = 2, [245] = 0, [246] = 1, [247] = 0, + [248] = 3, [249] = 0, [250] = 1, [251] = 0, [252] = 2, [253] = 0, [254] = 1, [255] = 0, + }; + return motion_btn_lut[(uint8_t)buttons]; } void seat_note_input(struct seat *s, uint32_t serial) @@ -222,12 +254,12 @@ static void press_queue_flush(struct seat *s, struct press_queue *q, continue; } if (!p.surface) continue; - if (p.release) *buttons &= ~(1ull << p.button); - else *buttons |= 1ull << p.button; + uint64_t mask = 1ull << p.button; + *buttons = (*buttons & ~mask) | ((uint64_t)(!p.release) << p.button); struct yasl_mouse_event ev = like; ev.pos = p.pos; ev.button = p.button; - ev.kind = p.release ? YASL_PTR_RELEASE : YASL_PTR_PRESS; + ev.kind = (enum yasl_pointer_kind)p.release; push_mouse(s, p.surface, ev); } q->n = kept; @@ -333,18 +365,24 @@ static void pointer_button(void *data, struct wl_pointer *pointer, (void)pointer; (void)time; struct seat *s = data; struct wayland_surface *w = s->pointer_focus; - if (!w) return; - int b; - switch (button) { - case BTN_LEFT: b = 0; break; - case BTN_MIDDLE: b = 1; break; - case BTN_RIGHT: b = 2; break; - case BTN_BACK: b = 3; break; - case BTN_FORWARD: b = 4; break; - default: return; - } + if (unlikely(!w)) return; + + static const int8_t btn_map[8] = { + [0] = 0, /* BTN_LEFT (0x110) */ + [1] = 2, /* BTN_RIGHT (0x111) */ + [2] = 1, /* BTN_MIDDLE (0x112) */ + [3] = 3, /* BTN_SIDE (0x113) */ + [4] = 4, /* BTN_EXTRA (0x114) */ + [5] = 4, /* BTN_FORWARD (0x115) */ + [6] = 3, /* BTN_BACK (0x116) */ + [7] = -1, + }; + unsigned idx = button - 0x110; + if (unlikely(idx >= 7)) return; + int b = btn_map[idx]; + if (unlikely(b < 0)) return; seat_note_input(s, serial); - bool release = state == WL_POINTER_BUTTON_STATE_RELEASED; + bool release = (state == WL_POINTER_BUTTON_STATE_RELEASED); if (!release) { s->press_serial = serial; s->press_surface = w; @@ -358,13 +396,11 @@ static void pointer_axis(void *data, struct wl_pointer *pointer, { (void)pointer; (void)time; struct seat *s = data; - if (!s->pointer_focus) return; + if (unlikely(!s->pointer_focus)) return; double v = wl_fixed_to_double(value); - if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { - s->axis_v += v; - s->frame_v += v; - } else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { - s->axis_h += v; + if (likely(axis < 2)) { + (&s->axis_v)[axis] += v; + s->frame_v += v * (!axis); } pointer_event_done(s); } @@ -373,21 +409,17 @@ static void pointer_axis_source(void *data, struct wl_pointer *pointer, uint32_t { (void)pointer; struct seat *s = data; - if (axis_source == WL_POINTER_AXIS_SOURCE_FINGER || - axis_source == WL_POINTER_AXIS_SOURCE_CONTINUOUS) - s->axis_smooth = true; + s->axis_smooth = (axis_source == WL_POINTER_AXIS_SOURCE_FINGER || + axis_source == WL_POINTER_AXIS_SOURCE_CONTINUOUS); } static void pointer_axis_stop(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis) { (void)pointer; (void)time; struct seat *s = data; - if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { - s->axis_value120_v = 0; - s->axis_has_v120_v = false; - } else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { - s->axis_value120_h = 0; - s->axis_has_v120_h = false; + if (likely(axis < 2)) { + (&s->axis_value120_v)[axis] = 0; + (&s->axis_has_v120_v)[axis] = false; } } @@ -395,23 +427,18 @@ static void pointer_axis_discrete(void *data, struct wl_pointer *pointer, uint32 { (void)pointer; struct seat *s = data; - if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) - s->axis_discrete_v += discrete; - else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) - s->axis_discrete_h += discrete; + if (likely(axis < 2)) + (&s->axis_discrete_v)[axis] += discrete; } static void pointer_axis_value120(void *data, struct wl_pointer *pointer, uint32_t axis, int32_t value120) { (void)pointer; struct seat *s = data; - if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { - s->axis_value120_v += value120; - s->frame_v120 += value120; - s->axis_has_v120_v = true; - } else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { - s->axis_value120_h += value120; - s->axis_has_v120_h = true; + if (likely(axis < 2)) { + (&s->axis_value120_v)[axis] += value120; + (&s->axis_has_v120_v)[axis] = true; + s->frame_v120 += value120 * (!axis); } } @@ -757,6 +784,7 @@ static void tablet_tool_up(void *data, struct zwp_tablet_tool_v2 *tool) struct seat *s = ((struct tablet_tool *)data)->seat; struct wayland_surface *w = s->tablet_focus; if (!w) return; + s->pen.pressure = 0; press_queue_push(&s->tool_presses, w, s->tool_pos, 0, true); } diff --git a/src/wayland/shell.c b/src/wayland/shell.c index f068f48..10af712 100644 --- a/src/wayland/shell.c +++ b/src/wayland/shell.c @@ -277,24 +277,25 @@ static void toplevel_configure(void *data, struct xdg_toplevel *toplevel, (void)toplevel; struct wayland_surface *w = data; - bool fs = false, max = false, act = false, susp = false, tiled = false; + uint32_t state_mask = 0; uint32_t *s; wl_array_for_each(s, states) { - switch (*s) { - case XDG_TOPLEVEL_STATE_FULLSCREEN: fs = true; break; - case XDG_TOPLEVEL_STATE_MAXIMIZED: max = true; break; - case XDG_TOPLEVEL_STATE_ACTIVATED: act = true; break; - case XDG_TOPLEVEL_STATE_SUSPENDED: susp = true; break; - case XDG_TOPLEVEL_STATE_TILED_LEFT: - case XDG_TOPLEVEL_STATE_TILED_RIGHT: - case XDG_TOPLEVEL_STATE_TILED_TOP: - case XDG_TOPLEVEL_STATE_TILED_BOTTOM: tiled = true; break; - default: break; - } + if (*s < 32) + state_mask |= (1u << *s); } - if (fs != w->fullscreen || max != w->maximized || - act != w->activated || susp != w->suspended) { + bool max = (state_mask & (1u << XDG_TOPLEVEL_STATE_MAXIMIZED)) != 0; + bool fs = (state_mask & (1u << XDG_TOPLEVEL_STATE_FULLSCREEN)) != 0; + bool act = (state_mask & (1u << XDG_TOPLEVEL_STATE_ACTIVATED)) != 0; + bool susp = (state_mask & (1u << XDG_TOPLEVEL_STATE_SUSPENDED)) != 0; + bool tiled = (state_mask & ( + (1u << XDG_TOPLEVEL_STATE_TILED_LEFT) | + (1u << XDG_TOPLEVEL_STATE_TILED_RIGHT) | + (1u << XDG_TOPLEVEL_STATE_TILED_TOP) | + (1u << XDG_TOPLEVEL_STATE_TILED_BOTTOM))) != 0; + + if ((fs ^ w->fullscreen) | (max ^ w->maximized) | + (act ^ w->activated) | (susp ^ w->suspended)) { w->fullscreen = fs; w->maximized = max; w->activated = act; diff --git a/src/wayland/surface.c b/src/wayland/surface.c index ced7d89..8de4992 100644 --- a/src/wayland/surface.c +++ b/src/wayland/surface.c @@ -83,6 +83,14 @@ static int display_ready(int fd, short revents, void *ud) else if (wl_display_read_events(c->wl) < 0) revents |= POLLERR; } + if (revents & POLLOUT) { + if (wl_display_flush(c->wl) >= 0) { + if (c->loop && c->watch > 0) + yasl_loop_set_fd_events(c->loop, c->watch, POLLIN); + } else if (errno != EAGAIN) { + display_lose(c); + } + } if (wl_display_dispatch_pending(c->wl) < 0 || (revents & (POLLERR | POLLHUP | POLLNVAL))) display_lose(c); @@ -90,35 +98,91 @@ static int display_ready(int fd, short revents, void *ud) return 0; } -void wayland_display_dispatch(struct wayland_display *c) -{ - if (!c || c->lost || c->reading) - return; - display_enter(c); - if (wl_display_dispatch_pending(c->wl) < 0) - display_lose(c); - display_leave(c); -} - -static void display_arm(struct wayland_display *c) +static bool display_arm(struct wayland_display *c) { + bool had_pending = false; while (!c->reading && wl_display_prepare_read(c->wl) != 0) { + had_pending = true; if (wl_display_dispatch_pending(c->wl) < 0) { display_lose(c); - return; + return false; } } c->reading = true; short events = POLLIN; if (wl_display_flush(c->wl) < 0) { if (errno != EAGAIN) { + display_cancel_read(c); display_lose(c); - return; + return false; } events |= POLLOUT; } - if (c->loop) + if (c->loop && c->watch > 0) yasl_loop_set_fd_events(c->loop, c->watch, events); + return had_pending; +} + +static inline bool display_has_immediate_work(const struct wayland_display *c) +{ + if (unlikely(!c || c->lost)) return false; + for (struct wayland_surface *w = c->surfaces; w; w = w->conn_next) { + bool ready_draw = w->want_draw & (w->frame_cb == NULL) & w->configured; + bool has_input = (w->input.count > 0); + if (unlikely((ready_draw | has_input) & !w->closed)) + return true; + } + return false; +} + +int wayland_display_dispatch_timeout(struct wayland_display *c, int timeout_ms) +{ + if (!c || c->lost) + return -1; + + display_enter(c); + + if (wl_display_dispatch_pending(c->wl) < 0) { + display_lose(c); + display_leave(c); + return -1; + } + + bool had_pending = display_arm(c); + if (c->lost) { + display_leave(c); + return -1; + } + + if (had_pending && timeout_ms != 0 && display_has_immediate_work(c)) { + timeout_ms = 0; + } + + int n = 0; + if (c->loop) { + n = yasl_loop_step(c->loop, timeout_ms); + } + + if (c->reading) { + display_cancel_read(c); + } + + if (wl_display_dispatch_pending(c->wl) < 0) { + display_lose(c); + display_leave(c); + return -1; + } + + display_leave(c); + return n; +} + +void wayland_display_dispatch(struct wayland_display *c) +{ + if (unlikely(!c || c->lost)) + return; + int timeout = (display_has_immediate_work(c) || yasl_loop_is_running(c->loop)) ? 0 : -1; + wayland_display_dispatch_timeout(c, timeout); } void wayland_display_flush(struct wayland_display *c) @@ -130,8 +194,10 @@ void wayland_display_flush(struct wayland_display *c) next = w->conn_next; surface_draw_frame(w); } - if (!c->lost) - display_arm(c); + if (!c->lost && c->wl) { + if (wl_display_flush(c->wl) < 0 && errno != EAGAIN) + display_lose(c); + } display_leave(c); } @@ -163,6 +229,10 @@ static void display_free(struct wayland_display *c) if (c->compositor) wl_compositor_destroy(c->compositor); if (c->registry) wl_registry_destroy(c->registry); if (c->wl) wl_display_disconnect(c->wl); + if (c->owns_loop && c->loop) { + yasl_loop_destroy(c->loop); + c->loop = NULL; + } free(c->app_id); free(c); } @@ -172,7 +242,13 @@ struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struc struct wayland_display *c = calloc(1, sizeof(*c)); if (!c) return NULL; c->base.ops = &yasl_wayland_backend; - c->loop = loop; + if (!loop) { + c->loop = yasl_loop_create(); + c->owns_loop = true; + } else { + c->loop = loop; + } + if (!c->loop) goto err; if (spec) { c->cbs = spec->cbs; c->ud = spec->ud; @@ -190,7 +266,7 @@ struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struc surface_init_cursor(c); if (c->loop) { - c->watch = yasl_loop_add_fd(loop, wl_display_get_fd(c->wl), POLLIN, display_ready, c); + c->watch = yasl_loop_add_fd(c->loop, wl_display_get_fd(c->wl), POLLIN, display_ready, c); if (c->watch <= 0) { c->watch = 0; goto err; } } if (display_input_init(c) < 0) @@ -374,7 +450,10 @@ struct wayland_display *wayland_surface_display(const struct wayland_surface *w) void wayland_surface_schedule_redraw(struct wayland_surface *w) { - if (w) w->want_draw = true; + if (!w || w->closed) return; + w->want_draw = true; + if (w->conn && w->conn->loop) + yasl_loop_kick(w->conn->loop); } struct yasl_vector2i wayland_surface_size(const struct wayland_surface *w) diff --git a/src/x11/window.c b/src/x11/window.c index b80bfe0..4680d3f 100644 --- a/src/x11/window.c +++ b/src/x11/window.c @@ -19,6 +19,13 @@ #define INPUT_CAP 256 #define TEXT_LIMIT (16 * 1024 * 1024) +#ifndef likely +#define likely(x) __builtin_expect(!!(x), 1) +#endif +#ifndef unlikely +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif + struct x11_surface { struct yasl_surface base; struct x11_surface *next; @@ -58,6 +65,7 @@ struct x11_display { struct x11_surface *paste_target; uint32_t mods; char *app_id; + bool owns_loop; }; static struct x11_display *display_of(const struct yasl_surface *s) @@ -438,10 +446,10 @@ static void dispatch(struct yasl_display *base) static unsigned long component(uint32_t color, unsigned long mask) { - if (!mask) return 0; - unsigned shift = 0; - while (!(mask & 1)) { mask >>= 1; shift++; } - return (((unsigned long)color * mask + 127) / 255) << shift; + if (unlikely(!mask)) return 0; + unsigned shift = (unsigned)__builtin_ctzl(mask); + unsigned long m = mask >> shift; + return (((unsigned long)color * m + 127) / 255) << shift; } static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb, @@ -511,6 +519,18 @@ static int ready(int fd, short events, void *ud) return 0; } +static int dispatch_timeout(struct yasl_display *base, int timeout_ms) +{ + struct x11_display *d = (struct x11_display *)base; + if (unlikely(!d || !d->native)) return -1; + dispatch(base); + if (!XPending(d->native) && d->loop && timeout_ms != 0) { + yasl_loop_step(d->loop, timeout_ms); + dispatch(base); + } + return 0; +} + static struct yasl_display *display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec) { @@ -520,7 +540,12 @@ static struct yasl_display *display_open(struct yasl_loop *loop, d->native = XOpenDisplay(NULL); if (!d->native) { free(d); errno = ENODEV; return NULL; } d->screen = DefaultScreen(d->native); - d->loop = loop; + if (!loop) { + d->loop = yasl_loop_create(); + d->owns_loop = true; + } else { + d->loop = loop; + } d->app_id = strdup(spec && spec->app_id ? spec->app_id : "YASL"); if (!d->app_id) { XCloseDisplay(d->native); free(d); return NULL; } d->im = XOpenIM(d->native, NULL, NULL, NULL); @@ -539,13 +564,14 @@ static struct yasl_display *display_open(struct yasl_loop *loop, ATOM(maximized_h, "_NET_WM_STATE_MAXIMIZED_HORZ"); ATOM(maximized_v, "_NET_WM_STATE_MAXIMIZED_VERT"); #undef ATOM - if (loop) { - d->watch = yasl_loop_add_fd(loop, ConnectionNumber(d->native), POLLIN, ready, &d->base); + if (d->loop) { + d->watch = yasl_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); free(d); return NULL; } @@ -557,7 +583,8 @@ static void display_close(struct yasl_display *base) { struct x11_display *d = (struct x11_display *)base; assert(!d->surfaces && "destroy every surface before its display"); - if (d->loop) yasl_loop_del_fd(d->loop, d->watch); + if (d->loop && d->watch > 0) yasl_loop_del_fd(d->loop, d->watch); + if (d->owns_loop && d->loop) yasl_loop_destroy(d->loop); if (d->im) XCloseIM(d->im); XDestroyWindow(d->native, d->selection_window); XCloseDisplay(d->native); @@ -619,7 +646,12 @@ static bool input_poll(struct yasl_surface *base, struct yasl_input_event *event static void schedule_redraw(struct yasl_surface *base) { struct x11_surface *s = (struct x11_surface *)base; - if (!s->destroyed) s->want_draw = true; + if (!s->destroyed) { + s->want_draw = true; + struct x11_display *d = display_of(base); + if (d && d->loop) + yasl_loop_kick(d->loop); + } } static struct yasl_display *surface_display(const struct yasl_surface *s) { return s->display; } @@ -698,6 +730,7 @@ const struct yasl_backend_ops yasl_x11_backend = { .display_close = display_close, .display_caps = caps, .display_dispatch = dispatch, + .display_dispatch_timeout = dispatch_timeout, .display_flush = flush, .display_modifiers = mods, .display_copy = copy, |
