summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cocoa/api.h1
-rw-r--r--src/cocoa/window.m83
-rw-r--r--src/common/backend.c12
-rw-r--r--src/common/backend.h1
-rw-r--r--src/common/loop.c173
-rw-r--r--src/common/timing.c9
-rw-r--r--src/common/utf8.c39
-rw-r--r--src/wayland/api.h1
-rw-r--r--src/wayland/backend.c6
-rw-r--r--src/wayland/buffer.c30
-rw-r--r--src/wayland/clipboard.c9
-rw-r--r--src/wayland/internal.h129
-rw-r--r--src/wayland/keyboard.c12
-rw-r--r--src/wayland/pointer.c124
-rw-r--r--src/wayland/shell.c29
-rw-r--r--src/wayland/surface.c117
-rw-r--r--src/x11/window.c51
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,