diff --git a/src/wayland-server.c b/src/wayland-server.c index 4778d85..6ae4689 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -105,6 +106,9 @@ struct wl_display { wl_display_global_filter_func_t global_filter; void *global_filter_data; + + int terminate_efd; + struct wl_event_source *term_source; }; struct wl_global { @@ -1030,6 +1034,16 @@ bind_display(struct wl_client *client, struct wl_display *display) return 0; } +static int +handle_display_terminate(int fd, uint32_t mask, void *data) { + uint64_t term_event; + + if (read(fd, &term_event, sizeof(term_event)) < 0 && errno != EAGAIN) + return -1; + + return 0; +} + /** Create Wayland display object. * * \return The Wayland display object. Null if failed to create @@ -1058,6 +1072,19 @@ wl_display_create(void) return NULL; } + display->terminate_efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (display->terminate_efd < 0) + goto err_eventfd; + + display->term_source = wl_event_loop_add_fd(display->loop, + display->terminate_efd, + WL_EVENT_READABLE, + handle_display_terminate, + NULL); + + if (display->term_source == NULL) + goto err_term_source; + wl_list_init(&display->global_list); wl_list_init(&display->socket_list); wl_list_init(&display->client_list); @@ -1076,6 +1103,13 @@ wl_display_create(void) wl_array_init(&display->additional_shm_formats); return display; + +err_term_source: + close(display->terminate_efd); +err_eventfd: + wl_event_loop_destroy(display->loop); + free(display); + return NULL; } static void @@ -1135,6 +1169,10 @@ wl_display_destroy(struct wl_display *display) wl_list_for_each_safe(s, next, &display->socket_list, link) { wl_socket_destroy(s); } + + close(display->terminate_efd); + wl_event_source_remove(display->term_source); + wl_event_loop_destroy(display->loop); wl_list_for_each_safe(global, gnext, &display->global_list, link) @@ -1351,7 +1389,13 @@ wl_display_get_event_loop(struct wl_display *display) WL_EXPORT void wl_display_terminate(struct wl_display *display) { + int ret; + uint64_t terminate = 1; + display->run = 0; + + ret = write(display->terminate_efd, &terminate, sizeof(terminate)); + assert (ret >= 0 || errno == EAGAIN); } WL_EXPORT void diff --git a/tests/display-test.c b/tests/display-test.c index 3db7c95..763adc9 100644 --- a/tests/display-test.c +++ b/tests/display-test.c @@ -1629,3 +1629,24 @@ TEST(global_remove) display_destroy(d); } + +static void +terminate_display(void *arg) +{ + struct wl_display *wl_display = arg; + wl_display_terminate(wl_display); +} + +TEST(no_source_terminate) +{ + struct display *d; + struct wl_event_loop *loop; + + d = display_create(); + loop = wl_display_get_event_loop(d->wl_display); + + wl_event_loop_add_idle(loop, terminate_display, d->wl_display); + + display_run(d); + display_destroy(d); +}