mirror of
https://gitlab.freedesktop.org/wayland/wayland.git
synced 2025-10-29 05:40:16 -04:00
client: add wl_display_dispatch_pending_single
As well as wl_display_dispatch_queue_pending_single. The motivation is writing libwayland bindings for a dynamic language with exceptions/non-local returns. Since it is invalid for a wl_dispatcher_func_t callback provided to libwayland to not return, there is no way to prevent dispatching of further events in the case of an exception in the dynamic language event handler. Furthermore, since creating/destroying Wayland objects in an event handler affects the dispatching of subsequent events by libwayland, it is not possible to collect Wayland events in a queue outside libwayland and dispatch them one-by-one after wl_display_dispatch_pending() returns. Adding libwayland API to dispatch at most one pending event solves this problem cleanly. The bindings can have libwayland dispatch a single event, wait for wl_display_dispatch_pending_single() to return, run the dynamic language event handler (which may longjmp away), and continue the loop for as long as there are more events to dispatch. References: https://codeberg.org/ifreund/janet-wayland Signed-off-by: Isaac Freund <mail@isaacfreund.com>
This commit is contained in:
parent
4673ef7e9c
commit
d81525a235
3 changed files with 151 additions and 0 deletions
|
|
@ -268,9 +268,16 @@ int
|
|||
wl_display_dispatch_queue_pending(struct wl_display *display,
|
||||
struct wl_event_queue *queue);
|
||||
|
||||
int
|
||||
wl_display_dispatch_queue_pending_single(struct wl_display *display,
|
||||
struct wl_event_queue *queue);
|
||||
|
||||
int
|
||||
wl_display_dispatch_pending(struct wl_display *display);
|
||||
|
||||
int
|
||||
wl_display_dispatch_pending_single(struct wl_display *display);
|
||||
|
||||
int
|
||||
wl_display_get_error(struct wl_display *display);
|
||||
|
||||
|
|
|
|||
|
|
@ -1882,6 +1882,34 @@ err:
|
|||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
dispatch_queue_single(struct wl_display *display, struct wl_event_queue *queue)
|
||||
{
|
||||
if (display->last_error)
|
||||
goto err;
|
||||
|
||||
while (!wl_list_empty(&display->display_queue.event_list)) {
|
||||
dispatch_event(display, &display->display_queue);
|
||||
if (display->last_error)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!wl_list_empty(&queue->event_list)) {
|
||||
dispatch_event(display, queue);
|
||||
if (display->last_error)
|
||||
goto err;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
err:
|
||||
errno = display->last_error;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Prepare to read events from the display's file descriptor to a queue
|
||||
*
|
||||
* \param display The display context object
|
||||
|
|
@ -2212,6 +2240,34 @@ wl_display_dispatch_queue_pending(struct wl_display *display,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/** Dispatch at most one pending event in an event queue
|
||||
*
|
||||
* \param display The display context object
|
||||
* \param queue The event queue to dispatch
|
||||
* \return The number of dispatched events (0 or 1) on success or -1 on failure
|
||||
*
|
||||
* Dispatch at most one pending event for objects assigned to the given
|
||||
* event queue. On failure -1 is returned and errno set appropriately.
|
||||
* If there are no events queued, this function returns immediately.
|
||||
*
|
||||
* \memberof wl_display
|
||||
* \since 1.25.0
|
||||
*/
|
||||
WL_EXPORT int
|
||||
wl_display_dispatch_queue_pending_single(struct wl_display *display,
|
||||
struct wl_event_queue *queue)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pthread_mutex_lock(&display->mutex);
|
||||
|
||||
ret = dispatch_queue_single(display, queue);
|
||||
|
||||
pthread_mutex_unlock(&display->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Process incoming events
|
||||
*
|
||||
* \param display The display context object
|
||||
|
|
@ -2272,6 +2328,25 @@ wl_display_dispatch_pending(struct wl_display *display)
|
|||
&display->default_queue);
|
||||
}
|
||||
|
||||
/** Dispatch at most one pending event in the default event queue.
|
||||
*
|
||||
* \param display The display context object
|
||||
* \return The number of dispatched events (0 or 1) on success or -1 on failure
|
||||
*
|
||||
* Dispatch at most one pending event for objects assigned to the default
|
||||
* event queue. On failure -1 is returned and errno set appropriately.
|
||||
* If there are no events queued, this function returns immediately.
|
||||
*
|
||||
* \memberof wl_display
|
||||
* \since 1.25.0
|
||||
*/
|
||||
WL_EXPORT int
|
||||
wl_display_dispatch_pending_single(struct wl_display *display)
|
||||
{
|
||||
return wl_display_dispatch_queue_pending_single(display,
|
||||
&display->default_queue);
|
||||
}
|
||||
|
||||
/** Retrieve the last error that occurred on a display
|
||||
*
|
||||
* \param display The display context object
|
||||
|
|
|
|||
|
|
@ -1695,6 +1695,75 @@ TEST(global_remove)
|
|||
display_destroy(d);
|
||||
}
|
||||
|
||||
static void
|
||||
dispatch_single_read_events(struct wl_display *d)
|
||||
{
|
||||
if (wl_display_prepare_read(d) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
do {
|
||||
ret = wl_display_flush(d);
|
||||
} while (ret < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
assert(ret >= 0);
|
||||
|
||||
struct pollfd pfd[1];
|
||||
pfd[0].fd = wl_display_get_fd(d);
|
||||
pfd[0].events = POLLIN;
|
||||
|
||||
do {
|
||||
ret = poll(pfd, 1, -1);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
assert(ret > 0);
|
||||
|
||||
wl_display_read_events(d);
|
||||
}
|
||||
|
||||
static void
|
||||
dispatch_single_client(void)
|
||||
{
|
||||
struct client *c = client_connect();
|
||||
|
||||
assert(wl_display_dispatch_pending_single(c->wl_display) == 0);
|
||||
|
||||
struct wl_registry *registry = wl_display_get_registry(c->wl_display);
|
||||
|
||||
dispatch_single_read_events(c->wl_display);
|
||||
|
||||
// [1815110.061] {Default Queue} wl_registry#3.global(1, "test", 1)
|
||||
assert(wl_display_dispatch_pending_single(c->wl_display) == 1);
|
||||
|
||||
dispatch_single_read_events(c->wl_display);
|
||||
|
||||
// [1815110.067] {Default Queue} wl_registry#3.global(2, "wl_seat", 1)
|
||||
assert(wl_display_dispatch_pending_single(c->wl_display) == 1);
|
||||
|
||||
// No more events
|
||||
assert(wl_display_dispatch_pending_single(c->wl_display) == 0);
|
||||
|
||||
wl_registry_destroy(registry);
|
||||
|
||||
client_disconnect(c);
|
||||
}
|
||||
|
||||
TEST(dispatch_single)
|
||||
{
|
||||
struct display *d = display_create();
|
||||
|
||||
struct wl_global *global = wl_global_create(d->wl_display,
|
||||
&wl_seat_interface,
|
||||
1, d, bind_seat);
|
||||
|
||||
client_create_noarg(d, dispatch_single_client);
|
||||
|
||||
display_run(d);
|
||||
|
||||
wl_global_destroy(global);
|
||||
|
||||
display_destroy(d);
|
||||
}
|
||||
|
||||
static void
|
||||
terminate_display(void *arg)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue