summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md27
-rw-r--r--examples/simple_window.c117
-rw-r--r--include/yasl/yasl.h2
-rw-r--r--meson.build1
-rw-r--r--src/cocoa/window.m51
-rw-r--r--src/common/loop.c168
-rw-r--r--src/common/timing.c9
-rw-r--r--src/common/utf8.c28
-rw-r--r--src/wayland/buffer.c31
-rw-r--r--src/wayland/internal.h129
-rw-r--r--src/wayland/keyboard.c12
-rw-r--r--src/wayland/pointer.c73
-rw-r--r--src/wayland/shell.c29
-rw-r--r--src/wayland/surface.c118
-rw-r--r--tests/test_lifecycle.c57
15 files changed, 663 insertions, 189 deletions
diff --git a/README.md b/README.md
index 23f4811..ee061b9 100644
--- a/README.md
+++ b/README.md
@@ -39,6 +39,8 @@ ninja -C build test
```c
#include <yasl/yasl.h>
+static bool running = true;
+
static void on_draw(struct yasl_surface *s, struct yasl_frame *frame, void *ud)
{
/* Present frame target */
@@ -46,29 +48,44 @@ static void on_draw(struct yasl_surface *s, struct yasl_frame *frame, void *ud)
yasl_surface_present(s, &frame->target, &dmg);
}
+static void on_close(struct yasl_surface *s, void *ud)
+{
+ (void)s; (void)ud;
+ running = false;
+}
+
int main(void)
{
- struct yasl_loop *loop = yasl_loop_create();
struct yasl_display_spec dspec = { .app_id = "org.example.app" };
- struct yasl_display *d = yasl_display_open(loop, &dspec);
+ struct yasl_display *d = yasl_display_open(NULL, &dspec);
struct yasl_surface_spec sspec = {
.title = "YASL Application",
.size = { .x = 800, .y = 600 },
};
struct yasl_surface *s = yasl_surface_create_toplevel(d, &sspec);
- struct yasl_surface_cbs cbs = { .draw = on_draw };
+ struct yasl_surface_cbs cbs = {
+ .draw = on_draw,
+ .close = on_close,
+ };
yasl_surface_set_callbacks(s, &cbs, NULL);
yasl_surface_schedule_redraw(s);
- while (true) {
+ while (running) {
yasl_display_dispatch(d);
struct yasl_input_event ev;
while (yasl_surface_input_poll(s, &ev)) {
- /* Handle input */
+ if (ev.kind == YASL_INPUT_KEY && ev.key.action == YASL_KEY_PRESS) {
+ if (ev.key.key == 0xff1b) /* Escape */
+ running = false;
+ }
}
yasl_display_flush(d);
}
+
+ yasl_surface_destroy(s);
+ yasl_display_close(d);
+ return 0;
}
```
diff --git a/examples/simple_window.c b/examples/simple_window.c
index 41a5375..a8a2861 100644
--- a/examples/simple_window.c
+++ b/examples/simple_window.c
@@ -1,8 +1,13 @@
#include "yasl/yasl.h"
#include <stdio.h>
#include <stdlib.h>
+#include <stdbool.h>
static bool running = true;
+static int mouse_x = -1;
+static int mouse_y = -1;
+static bool mouse_down = false;
+static int color_theme = 0;
static void on_draw(struct yasl_surface *s, struct yasl_frame *frame, void *ud)
{
@@ -10,25 +15,67 @@ static void on_draw(struct yasl_surface *s, struct yasl_frame *frame, void *ud)
int w = frame->target.size.x;
int h = frame->target.size.y;
uint32_t *pixels = frame->target.pixels;
- if (pixels) {
- for (int y = 0; y < h; y++) {
- for (int x = 0; x < w; x++) {
- uint8_t r = (uint8_t)((x * 255) / (w > 0 ? w : 1));
- uint8_t g = (uint8_t)((y * 255) / (h > 0 ? h : 1));
- uint8_t b = 128;
- pixels[y * w + x] = (255u << 24) | (r << 16) | (g << 8) | b;
+ if (!pixels || w <= 0 || h <= 0)
+ return;
+
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ uint8_t r, g, b;
+ uint8_t u = (uint8_t)((x * 255) / (w > 0 ? w : 1));
+ uint8_t v = (uint8_t)((y * 255) / (h > 0 ? h : 1));
+
+ switch (color_theme % 3) {
+ case 0:
+ r = u;
+ g = v;
+ b = 160;
+ break;
+ case 1:
+ r = 40;
+ g = u;
+ b = v;
+ break;
+ default:
+ r = v;
+ g = 60;
+ b = u;
+ break;
}
+
+ /* Draw interactive pointer indicator */
+ if (mouse_x >= 0 && mouse_y >= 0) {
+ int dx = x - mouse_x;
+ int dy = y - mouse_y;
+ int dist_sq = dx * dx + dy * dy;
+ int radius = mouse_down ? 36 : 20;
+ if (dist_sq < radius * radius) {
+ r = (uint8_t)(255 - r / 2);
+ g = (uint8_t)(255 - g / 2);
+ b = (uint8_t)(255 - b / 2);
+ }
+ }
+
+ pixels[y * w + x] = (255u << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
}
+
struct yasl_damage_list dmg = { .n = -1 };
yasl_surface_present(s, &frame->target, &dmg);
}
+static void on_configure(struct yasl_surface *s, struct yasl_geometry geo, void *ud)
+{
+ (void)ud;
+ printf("[YASL] Resized to %dx%d (scale: %.2fx)\n",
+ geo.size.x, geo.size.y, (double)geo.scale_120 / YASL_SCALE_UNIT);
+ yasl_surface_schedule_redraw(s);
+}
+
static void on_close(struct yasl_surface *s, void *ud)
{
(void)s;
(void)ud;
- printf("Window close requested\n");
+ printf("[YASL] Window close requested\n");
running = false;
}
@@ -40,58 +87,82 @@ static void on_quit(void *ud)
int main(void)
{
- struct yasl_loop *loop = yasl_loop_create();
- if (!loop) {
- fprintf(stderr, "Failed to create event loop\n");
- return 1;
- }
-
+ /* YASL manages its event loop internally when NULL is passed */
struct yasl_display_cbs disp_cbs = { .quit = on_quit };
struct yasl_display_spec disp_spec = {
.app_id = "org.yasl.example",
.cbs = &disp_cbs,
};
- struct yasl_display *d = yasl_display_open(loop, &disp_spec);
+ struct yasl_display *d = yasl_display_open(NULL, &disp_spec);
if (!d) {
fprintf(stderr, "Failed to open display\n");
- yasl_loop_destroy(loop);
return 1;
}
struct yasl_surface_spec surf_spec = {
- .title = "YASL Simple Window",
+ .title = "YASL Interactive Window",
.size = { .x = 640, .y = 480 },
};
struct yasl_surface *s = yasl_surface_create_toplevel(d, &surf_spec);
if (!s) {
fprintf(stderr, "Failed to create surface\n");
yasl_display_close(d);
- yasl_loop_destroy(loop);
return 1;
}
struct yasl_surface_cbs surf_cbs = {
.draw = on_draw,
+ .configure = on_configure,
.close = on_close,
};
yasl_surface_set_callbacks(s, &surf_cbs, NULL);
yasl_surface_schedule_redraw(s);
+ printf("=== YASL Demo Running ===\n");
+ printf(" - Move or Click mouse inside window to interact\n");
+ printf(" - Press Space to cycle background themes\n");
+ printf(" - Press Escape or close window to exit\n\n");
+
while (running) {
yasl_display_dispatch(d);
+
struct yasl_input_event ev;
while (yasl_surface_input_poll(s, &ev)) {
- if (ev.kind == YASL_INPUT_KEY && ev.key.action == YASL_KEY_PRESS) {
- printf("Key pressed: keysym=%u\n", ev.key.key);
- if (ev.key.key == 0xff1b) /* Escape */
- running = false;
+ if (ev.kind == YASL_INPUT_KEY) {
+ if (ev.key.action == YASL_KEY_PRESS) {
+ if (ev.key.key == 0xff1b) { /* Escape */
+ running = false;
+ } else if (ev.key.key == ' ' || ev.key.key == 0xff0d) { /* Space or Return */
+ color_theme++;
+ yasl_surface_schedule_redraw(s);
+ }
+ }
+ } else if (ev.kind == YASL_INPUT_MOUSE) {
+ if (ev.mouse.kind == YASL_PTR_MOTION) {
+ mouse_x = ev.mouse.pos.x;
+ mouse_y = ev.mouse.pos.y;
+ yasl_surface_schedule_redraw(s);
+ } else if (ev.mouse.kind == YASL_PTR_PRESS) {
+ mouse_down = true;
+ mouse_x = ev.mouse.pos.x;
+ mouse_y = ev.mouse.pos.y;
+ yasl_surface_schedule_redraw(s);
+ } else if (ev.mouse.kind == YASL_PTR_RELEASE) {
+ mouse_down = false;
+ yasl_surface_schedule_redraw(s);
+ } else if (ev.mouse.kind == YASL_PTR_LEAVE) {
+ mouse_x = -1;
+ mouse_y = -1;
+ mouse_down = false;
+ yasl_surface_schedule_redraw(s);
+ }
}
}
+
yasl_display_flush(d);
}
yasl_surface_destroy(s);
yasl_display_close(d);
- yasl_loop_destroy(loop);
return 0;
}
diff --git a/include/yasl/yasl.h b/include/yasl/yasl.h
index 0c2235a..081e1f0 100644
--- a/include/yasl/yasl.h
+++ b/include/yasl/yasl.h
@@ -269,6 +269,7 @@ YASL_API struct yasl_display *yasl_display_open(struct yasl_loop *loop,
YASL_API void yasl_display_close(struct yasl_display *d);
YASL_API uint32_t yasl_display_caps(const struct yasl_display *d);
YASL_API void yasl_display_dispatch(struct yasl_display *d);
+YASL_API int yasl_display_dispatch_timeout(struct yasl_display *d, int timeout_ms);
YASL_API void yasl_display_flush(struct yasl_display *d);
YASL_API int yasl_display_modifiers(const struct yasl_display *d);
YASL_API void yasl_display_copy(struct yasl_display *d, enum yasl_selection sel, const char *text);
@@ -343,6 +344,7 @@ YASL_API void *yasl_surface_get_native_handle(struct yasl_surface *s);
YASL_API struct yasl_loop *yasl_loop_create(void);
YASL_API void yasl_loop_destroy(struct yasl_loop *l);
YASL_API int yasl_loop_run(struct yasl_loop *l);
+YASL_API int yasl_loop_step(struct yasl_loop *l, int timeout_ms);
YASL_API void yasl_loop_quit(struct yasl_loop *l);
YASL_API void yasl_loop_kick(struct yasl_loop *l);
YASL_API uint64_t yasl_loop_now_ns(void);
diff --git a/meson.build b/meson.build
index e4396b2..912b7c9 100644
--- a/meson.build
+++ b/meson.build
@@ -108,6 +108,7 @@ test_exe = executable(
'tests/test_lifecycle.c',
include_directories: inc,
link_with: yasl_lib.get_static_lib(),
+ dependencies: deps,
)
add_test_setup('asan')
diff --git a/src/cocoa/window.m b/src/cocoa/window.m
index 0edf694..6c28f3f 100644
--- a/src/cocoa/window.m
+++ b/src/cocoa/window.m
@@ -843,29 +843,62 @@ static void pump_window(struct yasl_surface *w)
@autoreleasepool { render_and_commit(w); }
}
-void yasl_display_dispatch(struct yasl_display *d)
+int yasl_display_dispatch_timeout(struct yasl_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 yasl_display_dispatch(struct yasl_display *d)
+{
+ if (!d) return;
+ bool immediate = false;
+ for (int i = 0; i < d->count; i++) {
+ struct yasl_surface *w = d->windows[i];
+ if (w->input.count > 0 || (w->want_draw && (!w->link || w->frame_due || w->unpaced))) {
+ immediate = true;
+ break;
+ }
+ }
+ yasl_display_dispatch_timeout(d, immediate ? 0 : -1);
}
void yasl_display_flush(struct yasl_display *d)
diff --git a/src/common/loop.c b/src/common/loop.c
index 4c2da0d..de29c85 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;
}
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..7f2e6f1 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 {
diff --git a/src/wayland/buffer.c b/src/wayland/buffer.c
index 492cfde..0d639e6 100644
--- a/src/wayland/buffer.c
+++ b/src/wayland/buffer.c
@@ -305,13 +305,20 @@ static void arm_frame(struct yasl_surface *w)
}
}
+static const int8_t free_buf_lut[3][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 },
+};
+
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);
+ unsigned c = (unsigned)cur;
+ if (unlikely(c >= 3)) c = 0;
+ return free_buf_lut[c][busy];
}
struct latency_feedback {
@@ -523,14 +530,12 @@ void yasl_surface_set_title(struct yasl_surface *w, const char *title)
uint32_t yasl_surface_state(const struct yasl_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 yasl_surface_timing(const struct yasl_surface *w)
diff --git a/src/wayland/internal.h b/src/wayland/internal.h
index dc85943..79fd88d 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 yasl_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 yasl_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 yasl_surface *pointer_focus;
struct yasl_surface *keyboard_focus;
struct yasl_surface *tablet_focus;
struct yasl_surface *ime_focus;
struct yasl_surface *focus_leaving;
- uint32_t input_serial;
- uint32_t press_serial;
struct yasl_surface *press_surface;
+ struct yasl_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 yasl_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;
}
@@ -211,6 +224,7 @@ static inline void input_queue_clear(struct input_queue *q)
}
struct yasl_display {
+ /* 8-byte pointers & 64-bit masks */
struct wl_display *wl;
struct wl_registry *registry;
struct wl_compositor *compositor;
@@ -228,7 +242,6 @@ struct yasl_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;
@@ -240,20 +253,28 @@ struct yasl_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 yasl_surface *surfaces;
struct yasl_surface *paste_target;
+ struct yasl_surface *dead;
+
+ /* 4-byte integers */
+ int cursor_size;
+ int cursor_theme_scale;
+ clockid_t pres_clock;
int watch;
int depth;
- struct yasl_surface *dead;
+
+ /* 1-byte booleans */
+ bool owns_loop;
+ bool pres_clock_known;
bool reading;
bool lost;
};
struct yasl_surface {
+ /* 8-byte pointers & 64-bit values */
struct yasl_display *conn;
struct yasl_surface *conn_next;
struct yasl_surface *dead_next;
@@ -263,16 +284,11 @@ struct yasl_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;
@@ -282,18 +298,23 @@ struct yasl_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;
@@ -302,9 +323,11 @@ struct yasl_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 7283d58..6045962 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 yasl_display_modifiers(const struct yasl_display *c)
diff --git a/src/wayland/pointer.c b/src/wayland/pointer.c
index 37e72f3..89dacde 100644
--- a/src/wayland/pointer.c
+++ b/src/wayland/pointer.c
@@ -39,12 +39,44 @@ static struct yasl_vector2i device_from_fixed(const struct yasl_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)
@@ -333,18 +365,25 @@ static void pointer_button(void *data, struct wl_pointer *pointer,
(void)pointer; (void)time;
struct seat *s = data;
struct yasl_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;
diff --git a/src/wayland/shell.c b/src/wayland/shell.c
index ef72512..bc8083a 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 yasl_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 da334e4..5138201 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,92 @@ static int display_ready(int fd, short revents, void *ud)
return 0;
}
-void yasl_display_dispatch(struct yasl_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 yasl_display *c)
+static bool display_arm(struct yasl_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 bool display_has_immediate_work(const struct yasl_display *c)
+{
+ if (!c || c->lost) return false;
+ for (struct yasl_surface *w = c->surfaces; w; w = w->conn_next) {
+ if (w->closed) continue;
+ if (w->input.count > 0)
+ return true;
+ if (w->want_draw && !w->frame_cb && w->configured)
+ return true;
+ }
+ return false;
+}
+
+int yasl_display_dispatch_timeout(struct yasl_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 yasl_display_dispatch(struct yasl_display *c)
+{
+ if (!c || c->lost)
+ return;
+ int timeout = display_has_immediate_work(c) ? 0 : -1;
+ yasl_display_dispatch_timeout(c, timeout);
}
void yasl_display_flush(struct yasl_display *c)
@@ -130,8 +195,10 @@ void yasl_display_flush(struct yasl_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 +230,10 @@ static void display_free(struct yasl_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);
}
@@ -171,7 +242,13 @@ struct yasl_display *yasl_display_open(struct yasl_loop *loop, const struct yasl
{
struct yasl_display *c = calloc(1, sizeof(*c));
if (!c) return NULL;
- 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;
@@ -189,7 +266,7 @@ struct yasl_display *yasl_display_open(struct yasl_loop *loop, const struct yasl
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)
@@ -371,7 +448,10 @@ struct yasl_display *yasl_surface_display(const struct yasl_surface *w)
void yasl_surface_schedule_redraw(struct yasl_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 yasl_surface_size(const struct yasl_surface *w)
diff --git a/tests/test_lifecycle.c b/tests/test_lifecycle.c
index a57e6b5..681e5fc 100644
--- a/tests/test_lifecycle.c
+++ b/tests/test_lifecycle.c
@@ -1,6 +1,8 @@
#include "yasl/yasl.h"
#include <assert.h>
+#include <poll.h>
#include <stdio.h>
+#include <unistd.h>
static void test_scaling(void)
{
@@ -24,10 +26,65 @@ static void test_timing_clock(void)
assert(stats.max_us == 500.0);
}
+static int dummy_cb(int fd, short revents, void *ud)
+{
+ (void)fd; (void)revents; (void)ud;
+ return 0;
+}
+
+static void test_loop(void)
+{
+ struct yasl_loop *loop = yasl_loop_create();
+ assert(loop != NULL);
+
+ int p[2];
+ assert(pipe(p) == 0);
+ int watch = yasl_loop_add_fd(loop, p[0], POLLIN, dummy_cb, NULL);
+ assert(watch > 0);
+ assert(yasl_loop_set_fd_events(loop, watch, POLLIN | POLLOUT) == 0);
+
+ yasl_loop_kick(loop);
+ int n = yasl_loop_step(loop, 10);
+ assert(n >= 1);
+
+ assert(yasl_loop_del_fd(loop, watch) == 0);
+ close(p[0]);
+ close(p[1]);
+
+ yasl_loop_destroy(loop);
+}
+
+static void test_display_lifecycle_standalone(void)
+{
+ struct yasl_display_spec spec = { .app_id = "org.yasl.test" };
+ struct yasl_display *d = yasl_display_open(NULL, &spec);
+ if (!d) return; /* Headless CI without Wayland */
+
+ struct yasl_surface_spec sspec = {
+ .title = "Test Window",
+ .size = { .x = 320, .y = 240 },
+ };
+ struct yasl_surface *s = yasl_surface_create_toplevel(d, &sspec);
+ assert(s != NULL);
+ assert(yasl_surface_display(s) == d);
+
+ struct yasl_vector2i sz = yasl_surface_size(s);
+ assert(sz.x > 0 && sz.y > 0);
+
+ yasl_surface_schedule_redraw(s);
+ yasl_display_flush(d);
+ yasl_display_dispatch_timeout(d, 0);
+
+ yasl_surface_destroy(s);
+ yasl_display_close(d);
+}
+
int main(void)
{
test_scaling();
test_timing_clock();
+ test_loop();
+ test_display_lifecycle_standalone();
printf("All YASL basic tests passed.\n");
return 0;
}