summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cocoa/window.m26
-rw-r--r--src/common/backend.c106
-rw-r--r--src/common/backend.h6
-rw-r--r--src/common/loop.c267
-rw-r--r--src/wayland/surface.c2
-rw-r--r--src/x11/window.c35
6 files changed, 397 insertions, 45 deletions
diff --git a/src/cocoa/window.m b/src/cocoa/window.m
index 4454be3..fc69412 100644
--- a/src/cocoa/window.m
+++ b/src/cocoa/window.m
@@ -110,6 +110,7 @@ struct cocoa_display {
bool open;
bool launched;
bool pumping;
+ bool owns_loop;
};
static struct cocoa_display darwin_ctx;
@@ -867,6 +868,13 @@ int cocoa_display_dispatch_timeout(struct cocoa_display *d, int timeout_ms)
d->depth++;
int count = 0;
@try {
+ if (d->loop) {
+ int timer_to = yasl_loop_next_timeout_ms(d->loop);
+ if (timer_to >= 0 && (timeout_ms < 0 || timer_to < timeout_ms)) {
+ timeout_ms = timer_to;
+ }
+ }
+
NSDate *limitDate;
if (timeout_ms == 0)
limitDate = [NSDate distantPast];
@@ -895,6 +903,10 @@ int cocoa_display_dispatch_timeout(struct cocoa_display *d, int timeout_ms)
}
}
}
+
+ if (d->loop) {
+ yasl_loop_step(d->loop, 0);
+ }
} @finally {
d->depth--;
d->pumping = false;
@@ -1003,7 +1015,14 @@ struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct ya
@autoreleasepool { app_init(name); }
darwin_ctx.base.ops = &yasl_cocoa_backend;
darwin_ctx.open = true;
- darwin_ctx.loop = loop;
+ if (!loop) {
+ darwin_ctx.loop = yasl_loop_create();
+ darwin_ctx.owns_loop = true;
+ } else {
+ darwin_ctx.loop = loop;
+ darwin_ctx.owns_loop = false;
+ }
+ darwin_ctx.base.loop = darwin_ctx.loop;
if (spec) {
darwin_ctx.base.callbacks = spec->cbs ? *spec->cbs : (struct yasl_display_cbs){0};
darwin_ctx.cbs = &darwin_ctx.base.callbacks;
@@ -1022,6 +1041,11 @@ void cocoa_display_close(struct cocoa_display *d)
[NSEvent removeMonitor:d->key_up_monitor];
d->key_up_monitor = nil;
}
+ if (d->owns_loop && d->loop) {
+ yasl_loop_destroy(d->loop);
+ d->loop = NULL;
+ d->base.loop = NULL;
+ }
NSApp.delegate = nil;
[d->delegate release];
d->delegate = nil;
diff --git a/src/common/backend.c b/src/common/backend.c
index 214854b..27ab297 100644
--- a/src/common/backend.c
+++ b/src/common/backend.c
@@ -181,6 +181,7 @@ void yasl_surface_destroy(struct yasl_surface *s)
{
if (!s || !s->display->ops->surface_destroy)
return;
+ yasl_surface_clear_timers(s);
s->display->ops->surface_destroy(s);
}
@@ -368,3 +369,108 @@ void *yasl_surface_get_native_handle(struct yasl_surface *s)
return 0;
return s->display->ops->surface_get_native_handle(s);
}
+
+void yasl_surface_clear_timers(struct yasl_surface *s)
+{
+ if (!s || !s->timers) return;
+ struct yasl_loop *loop = s->display ? s->display->loop : NULL;
+ for (int i = 0; i < s->timer_count; i++) {
+ if (loop && s->timers[i]) {
+ yasl_loop_del_timer(loop, s->timers[i]);
+ }
+ }
+ free(s->timers);
+ s->timers = NULL;
+ s->timer_count = 0;
+ s->timer_capacity = 0;
+}
+
+yasl_timer_id yasl_surface_add_timer(struct yasl_surface *s, uint64_t interval_ns,
+ yasl_timer_cb cb, void *ud)
+{
+ if (!s || !s->display || !s->display->loop || !cb || interval_ns == 0)
+ return 0;
+
+ yasl_timer_id id = yasl_loop_add_timer(s->display->loop, interval_ns, cb, ud);
+ if (!id) return 0;
+
+ if (s->timer_count >= s->timer_capacity) {
+ int new_cap = s->timer_capacity ? s->timer_capacity * 2 : 4;
+ yasl_timer_id *nt = realloc(s->timers, (size_t)new_cap * sizeof(*nt));
+ if (!nt) {
+ yasl_loop_del_timer(s->display->loop, id);
+ return 0;
+ }
+ s->timers = nt;
+ s->timer_capacity = new_cap;
+ }
+ s->timers[s->timer_count++] = id;
+ return id;
+}
+
+yasl_timer_id yasl_surface_add_timer_ms(struct yasl_surface *s, uint32_t interval_ms,
+ yasl_timer_ms_cb cb, void *ud)
+{
+ if (!s || !s->display || !s->display->loop || !cb || interval_ms == 0)
+ return 0;
+
+ yasl_timer_id id = yasl_loop_add_timer_ms(s->display->loop, interval_ms, cb, ud);
+ if (!id) return 0;
+
+ if (s->timer_count >= s->timer_capacity) {
+ int new_cap = s->timer_capacity ? s->timer_capacity * 2 : 4;
+ yasl_timer_id *nt = realloc(s->timers, (size_t)new_cap * sizeof(*nt));
+ if (!nt) {
+ yasl_loop_del_timer(s->display->loop, id);
+ return 0;
+ }
+ s->timers = nt;
+ s->timer_capacity = new_cap;
+ }
+ s->timers[s->timer_count++] = id;
+ return id;
+}
+
+bool yasl_surface_del_timer(struct yasl_surface *s, yasl_timer_id id)
+{
+ if (!s || id == 0) return false;
+ for (int i = 0; i < s->timer_count; i++) {
+ if (s->timers[i] == id) {
+ s->timers[i] = 0;
+ if (s->display && s->display->loop)
+ yasl_loop_del_timer(s->display->loop, id);
+ return true;
+ }
+ }
+ return false;
+}
+
+yasl_timer_id yasl_display_add_timer(struct yasl_display *d, uint64_t interval_ns,
+ yasl_timer_cb cb, void *ud)
+{
+ if (!d || !d->loop) return 0;
+ return yasl_loop_add_timer(d->loop, interval_ns, cb, ud);
+}
+
+yasl_timer_id yasl_display_add_timer_ms(struct yasl_display *d, uint32_t interval_ms,
+ yasl_timer_ms_cb cb, void *ud)
+{
+ if (!d || !d->loop) return 0;
+ return yasl_loop_add_timer_ms(d->loop, interval_ms, cb, ud);
+}
+
+bool yasl_display_del_timer(struct yasl_display *d, yasl_timer_id id)
+{
+ if (!d || !d->loop) return false;
+ return yasl_loop_del_timer(d->loop, id);
+}
+
+struct yasl_loop *yasl_display_loop(struct yasl_display *d)
+{
+ return d ? d->loop : NULL;
+}
+
+struct yasl_loop *yasl_surface_loop(struct yasl_surface *s)
+{
+ return (s && s->display) ? s->display->loop : NULL;
+}
diff --git a/src/common/backend.h b/src/common/backend.h
index c6691eb..b52a147 100644
--- a/src/common/backend.h
+++ b/src/common/backend.h
@@ -8,14 +8,20 @@ struct yasl_backend_ops;
struct yasl_display {
const struct yasl_backend_ops *ops;
struct yasl_display_cbs callbacks;
+ struct yasl_loop *loop;
};
struct yasl_surface {
struct yasl_display *display;
struct yasl_surface_cbs callbacks;
uint64_t frame_generation;
+ yasl_timer_id *timers;
+ int timer_count;
+ int timer_capacity;
};
+void yasl_surface_clear_timers(struct yasl_surface *s);
+
static inline uint64_t yasl_frame_begin(struct yasl_surface *s)
{
if (++s->frame_generation == 0) ++s->frame_generation;
diff --git a/src/common/loop.c b/src/common/loop.c
index ea9bee3..a1f2434 100644
--- a/src/common/loop.c
+++ b/src/common/loop.c
@@ -22,6 +22,20 @@ struct yasl_fd_source {
short events;
};
+struct yasl_timer_source {
+ union {
+ yasl_timer_cb ns;
+ yasl_timer_ms_cb ms;
+ void *raw;
+ } fn;
+ void *ud;
+ yasl_timer_id id;
+ uint64_t interval_ns;
+ uint64_t deadline_ns;
+ bool ms_mode;
+ bool deleted;
+};
+
struct yasl_loop {
struct yasl_fd_source *fds;
struct pollfd *pollfds;
@@ -32,6 +46,12 @@ struct yasl_loop {
int wake_rfd;
int wake_wfd;
int wake_watch;
+
+ struct yasl_timer_source *timers;
+ int timer_count;
+ int timer_capacity;
+ uint32_t next_timer_id;
+ int timer_firing_depth;
};
uint64_t yasl_loop_now_ns(void)
@@ -67,6 +87,7 @@ struct yasl_loop *yasl_loop_create(void)
}
l->next_id = 1;
+ l->next_timer_id = 1;
l->wake_rfd = -1;
l->wake_wfd = -1;
@@ -106,6 +127,7 @@ void yasl_loop_destroy(struct yasl_loop *l)
close(l->wake_wfd);
free(l->fds);
free(l->pollfds);
+ free(l->timers);
free(l);
}
@@ -177,65 +199,232 @@ void yasl_loop_kick(struct yasl_loop *l)
} while (n < 0 && errno == EINTR);
}
+static void timer_cleanup_deleted(struct yasl_loop *l)
+{
+ if (!l || l->timer_firing_depth > 0) return;
+ int dest = 0;
+ for (int src = 0; src < l->timer_count; src++) {
+ if (!l->timers[src].deleted) {
+ if (dest != src)
+ l->timers[dest] = l->timers[src];
+ dest++;
+ }
+ }
+ l->timer_count = dest;
+}
+
+int yasl_loop_next_timeout_ms(const struct yasl_loop *l)
+{
+ if (!l || l->timer_count <= 0) return -1;
+ uint64_t now = yasl_loop_now_ns();
+ uint64_t min_deadline = UINT64_MAX;
+ bool has_active = false;
+ for (int i = 0; i < l->timer_count; i++) {
+ if (!l->timers[i].deleted) {
+ has_active = true;
+ if (l->timers[i].deadline_ns < min_deadline)
+ min_deadline = l->timers[i].deadline_ns;
+ }
+ }
+ if (!has_active) return -1;
+ if (min_deadline <= now) return 0;
+ uint64_t diff = min_deadline - now;
+ uint64_t ms = (diff + 999999ull) / 1000000ull;
+ return (ms > (uint64_t)INT_MAX) ? INT_MAX : (int)ms;
+}
+
+yasl_timer_id yasl_loop_add_timer(struct yasl_loop *l, uint64_t interval_ns,
+ yasl_timer_cb cb, void *ud)
+{
+ if (!l || !cb || interval_ns == 0) return 0;
+
+ if (l->timer_count >= l->timer_capacity) {
+ int new_cap = l->timer_capacity ? l->timer_capacity * 2 : 16;
+ struct yasl_timer_source *new_timers = realloc(l->timers, (size_t)new_cap * sizeof(*new_timers));
+ if (!new_timers) return 0;
+ l->timers = new_timers;
+ l->timer_capacity = new_cap;
+ }
+
+ uint32_t id = l->next_timer_id++;
+ if (l->next_timer_id == 0) l->next_timer_id = 1;
+
+ uint64_t now = yasl_loop_now_ns();
+ l->timers[l->timer_count++] = (struct yasl_timer_source){
+ .fn.ns = cb,
+ .ud = ud,
+ .id = id,
+ .interval_ns = interval_ns,
+ .deadline_ns = now + interval_ns,
+ .ms_mode = false,
+ .deleted = false,
+ };
+
+ yasl_loop_kick(l);
+ return id;
+}
+
+yasl_timer_id yasl_loop_add_timer_ms(struct yasl_loop *l, uint32_t interval_ms,
+ yasl_timer_ms_cb cb, void *ud)
+{
+ if (!l || !cb || interval_ms == 0) return 0;
+
+ if (l->timer_count >= l->timer_capacity) {
+ int new_cap = l->timer_capacity ? l->timer_capacity * 2 : 16;
+ struct yasl_timer_source *new_timers = realloc(l->timers, (size_t)new_cap * sizeof(*new_timers));
+ if (!new_timers) return 0;
+ l->timers = new_timers;
+ l->timer_capacity = new_cap;
+ }
+
+ uint32_t id = l->next_timer_id++;
+ if (l->next_timer_id == 0) l->next_timer_id = 1;
+
+ uint64_t now = yasl_loop_now_ns();
+ uint64_t interval_ns = (uint64_t)interval_ms * 1000000ull;
+ l->timers[l->timer_count++] = (struct yasl_timer_source){
+ .fn.ms = cb,
+ .ud = ud,
+ .id = id,
+ .interval_ns = interval_ns,
+ .deadline_ns = now + interval_ns,
+ .ms_mode = true,
+ .deleted = false,
+ };
+
+ yasl_loop_kick(l);
+ return id;
+}
+
+bool yasl_loop_del_timer(struct yasl_loop *l, yasl_timer_id id)
+{
+ if (!l || id == 0) return false;
+ for (int i = 0; i < l->timer_count; i++) {
+ if (l->timers[i].id == id && !l->timers[i].deleted) {
+ l->timers[i].deleted = true;
+ if (l->timer_firing_depth == 0) {
+ timer_cleanup_deleted(l);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
int yasl_loop_step(struct yasl_loop *l, int timeout_ms)
{
if (!l) return -1;
+
+ int timer_to = yasl_loop_next_timeout_ms(l);
+ int effective_timeout = timeout_ms;
+ if (timer_to >= 0) {
+ if (effective_timeout < 0 || timer_to < effective_timeout)
+ effective_timeout = timer_to;
+ }
+
if (l->count <= 0) {
- if (timeout_ms > 0) {
+ if (effective_timeout > 0) {
struct timespec req = {
- .tv_sec = timeout_ms / 1000,
- .tv_nsec = (long)(timeout_ms % 1000) * 1000000L,
+ .tv_sec = effective_timeout / 1000,
+ .tv_nsec = (long)(effective_timeout % 1000) * 1000000L,
};
nanosleep(&req, NULL);
}
- return 0;
+ } else {
+ int n;
+ do {
+ n = poll(l->pollfds, (nfds_t)l->count, effective_timeout);
+ } while (n < 0 && errno == EINTR);
+
+ if (n < 0) return -1;
}
- int n;
- do {
- n = poll(l->pollfds, (nfds_t)l->count, timeout_ms);
- } while (n < 0 && errno == EINTR);
+ int handled = 0;
- if (n < 0) return -1;
- if (n == 0) return 0;
+ if (l->count > 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;
+ }
- 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);
}
- 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;
+ if (l->timer_count > 0) {
+ uint64_t now = yasl_loop_now_ns();
+ l->timer_firing_depth++;
+ int initial_count = l->timer_count;
+ for (int i = 0; i < initial_count; i++) {
+ if (i >= l->timer_count) break;
+ if (l->timers[i].deleted || l->timers[i].deadline_ns > now)
+ continue;
+
+ yasl_timer_id tid = l->timers[i].id;
+ uint64_t interval = l->timers[i].interval_ns;
+ bool is_ms = l->timers[i].ms_mode;
+ void *ud = l->timers[i].ud;
+ struct yasl_timer_source timer_src = l->timers[i];
+
+ uint64_t next_interval = 0;
+ if (is_ms) {
+ uint32_t old_ms = (uint32_t)(interval / 1000000ull);
+ uint32_t res_ms = timer_src.fn.ms(ud, tid, old_ms);
+ next_interval = (uint64_t)res_ms * 1000000ull;
+ } else {
+ next_interval = timer_src.fn.ns(ud, tid, interval);
+ }
+ handled++;
+
+ for (int j = 0; j < l->timer_count; j++) {
+ if (l->timers[j].id == tid) {
+ if (next_interval == 0 || l->timers[j].deleted) {
+ l->timers[j].deleted = true;
+ } else {
+ l->timers[j].interval_ns = next_interval;
+ if (l->timers[j].deadline_ns + next_interval > now)
+ l->timers[j].deadline_ns += next_interval;
+ else
+ l->timers[j].deadline_ns = now + next_interval;
+ }
break;
}
}
- if (valid && scopy[i].fn) {
- scopy[i].fn(scopy[i].fd, revents, scopy[i].ud);
- handled++;
- }
+ }
+ l->timer_firing_depth--;
+ if (l->timer_firing_depth == 0) {
+ timer_cleanup_deleted(l);
}
}
- if (scopy != sstack) free(scopy);
- if (pcopy != pstack) free(pcopy);
return handled;
}
diff --git a/src/wayland/surface.c b/src/wayland/surface.c
index ef355ab..4f064e2 100644
--- a/src/wayland/surface.c
+++ b/src/wayland/surface.c
@@ -232,6 +232,7 @@ static void display_free(struct wayland_display *c)
if (c->owns_loop && c->loop) {
yasl_loop_destroy(c->loop);
c->loop = NULL;
+ c->base.loop = NULL;
}
free(c->app_id);
free(c);
@@ -248,6 +249,7 @@ struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struc
} else {
c->loop = loop;
}
+ c->base.loop = c->loop;
if (!c->loop) goto err;
if (spec) {
c->base.callbacks = spec->cbs ? *spec->cbs : (struct yasl_display_cbs){0};
diff --git a/src/x11/window.c b/src/x11/window.c
index e40db6e..2d9aff3 100644
--- a/src/x11/window.c
+++ b/src/x11/window.c
@@ -549,9 +549,13 @@ 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);
+ if (d->loop) {
+ if (!XPending(d->native) && timeout_ms != 0) {
+ yasl_loop_step(d->loop, timeout_ms);
+ dispatch(base);
+ } else {
+ yasl_loop_step(d->loop, 0);
+ }
}
return 0;
}
@@ -572,6 +576,7 @@ static struct yasl_display *display_open(struct yasl_loop *loop,
} else {
d->loop = loop;
}
+ d->base.loop = d->loop;
d->app_id = strdup(spec && spec->app_id ? spec->app_id : "YASL");
if (!d->loop || !d->app_id) {
if (d->owns_loop) yasl_loop_destroy(d->loop);
@@ -617,7 +622,11 @@ static void display_close(struct yasl_display *base)
assert(!d->surfaces && "destroy every surface before its display");
cancel_pastes(d, NULL);
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->owns_loop && d->loop) {
+ yasl_loop_destroy(d->loop);
+ d->loop = NULL;
+ d->base.loop = NULL;
+ }
if (d->im) XCloseIM(d->im);
XDestroyWindow(d->native, d->selection_window);
XCloseDisplay(d->native);
@@ -757,12 +766,28 @@ static bool interactive_move(struct yasl_surface *base)
return true;
}
+static void display_dispatch(struct yasl_display *base)
+{
+ struct x11_display *d = (struct x11_display *)base;
+ if (unlikely(!d || !d->native)) return;
+ bool immediate = false;
+ for (struct x11_surface *s = d->surfaces; s; s = s->next) {
+ if (s->want_draw || s->input.count > 0 || s->input.reset_pending) {
+ immediate = true;
+ break;
+ }
+ }
+ if (d->loop && yasl_loop_is_running(d->loop))
+ immediate = true;
+ dispatch_timeout(base, immediate ? 0 : -1);
+}
+
const struct yasl_backend_ops yasl_x11_backend = {
.type = YASL_BACKEND_X11,
.display_open = display_open,
.display_close = display_close,
.display_caps = caps,
- .display_dispatch = dispatch,
+ .display_dispatch = display_dispatch,
.display_dispatch_timeout = dispatch_timeout,
.display_flush = flush,
.display_modifiers = mods,