Wayland: dispatch socket data with function libdecor_dispatch() as required by libdecor.

Many thanks to Jonas Ådahl for pointing out FLTK didn't follow this requirement
and suggesting code changes (#1390).
This commit is contained in:
ManoloFLTK
2026-04-02 09:17:23 +02:00
parent 1120eb96cd
commit 8e9e2b7818
2 changed files with 25 additions and 47 deletions
@@ -1422,31 +1422,12 @@ static const struct wl_registry_listener registry_listener = {
};
static void wayland_socket_callback(int fd, struct wl_display *display)
static void libdecor_fd_callback(int fd, struct libdecor *libdecor_context)
{
if (wl_display_prepare_read(display) == -1) {
wl_display_dispatch_pending(display);
return;
}
wl_display_flush(display);
struct pollfd fds = (struct pollfd) { fd, POLLIN, 0 };
if (poll(&fds, 1, 0) <= 0) {
wl_display_cancel_read(display);
return;
}
if (fds.revents & (POLLERR | POLLHUP)) {
wl_display_cancel_read(display);
goto fatal;
}
if (wl_display_read_events(display) == -1)
goto fatal;
if (wl_display_dispatch_pending(display) == -1)
goto fatal;
return;
fatal:
if (wl_display_get_error(display) == EPROTO) {
if (libdecor_dispatch(libdecor_context, 0) >= 0) return;
if (wl_display_get_error(Fl_Wayland_Screen_Driver::wl_display) == EPROTO) {
const struct wl_interface *interface;
int code = wl_display_get_protocol_error(display, &interface, NULL);
int code = wl_display_get_protocol_error(Fl_Wayland_Screen_Driver::wl_display, &interface, NULL);
Fl::fatal("Fatal error %d in Wayland protocol: %s",
code, (interface ? interface->name : "unknown") );
} else {
@@ -1511,6 +1492,16 @@ static void do_atexit() {
}
static void handle_error(struct libdecor *libdecor_context, enum libdecor_error error, const char *message)
{
Fl::fatal("Caught error (%d): %s\n", error, message);
}
static struct libdecor_interface libdecor_iface = {
.error = handle_error,
};
void Fl_Wayland_Screen_Driver::open_display_platform() {
static bool beenHereDoneThat = false;
if (beenHereDoneThat)
@@ -1533,8 +1524,9 @@ void Fl_Wayland_Screen_Driver::open_display_platform() {
struct wl_callback *registry_cb = wl_display_sync(wl_display);
wl_callback_add_listener(registry_cb, &sync_listener, &registry_cb);
while (registry_cb) wl_display_dispatch(wl_display);
Fl::add_fd(wl_display_get_fd(wl_display), FL_READ, (Fl_FD_Handler)wayland_socket_callback,
wl_display);
libdecor_context = libdecor_new(wl_display, &libdecor_iface);
Fl::add_fd(libdecor_get_fd(libdecor_context), FL_READ, (Fl_FD_Handler)libdecor_fd_callback,
libdecor_context);
fl_create_print_window();
/* This is useful to avoid crash of the Wayland compositor after
FLTK apps terminate in certain situations:
@@ -1621,10 +1613,6 @@ void Fl_Wayland_Screen_Driver::close_display() {
wl_seat_destroy(seat->wl_seat); seat->wl_seat = NULL;
if (seat->name) free(seat->name);
free(seat); seat = NULL;
if (libdecor_context) {
libdecor_unref(libdecor_context);
libdecor_context = NULL;
}
xdg_wm_base_destroy(xdg_wm_base); xdg_wm_base = NULL;
Fl_Wayland_Plugin *plugin = Fl_Wayland_Window_Driver::gl_plugin();
if (plugin) plugin->terminate();
@@ -1645,7 +1633,11 @@ void Fl_Wayland_Screen_Driver::close_display() {
}
#endif // HAVE_CURSOR_SHAPE
Fl::remove_fd(wl_display_get_fd(Fl_Wayland_Screen_Driver::wl_display));
if (libdecor_context) {
Fl::remove_fd(libdecor_get_fd(libdecor_context));
libdecor_unref(libdecor_context);
libdecor_context = NULL;
}
wl_registry_destroy(wl_registry); wl_registry = NULL;
wl_display_disconnect(Fl_Wayland_Screen_Driver::wl_display);
Fl_Wayland_Screen_Driver::wl_display = NULL;
@@ -659,18 +659,6 @@ int Fl_Wayland_Window_Driver::scroll(int src_x, int src_y, int src_w, int src_h,
}
static void handle_error(struct libdecor *libdecor_context, enum libdecor_error error, const char *message)
{
Fl::fatal("Caught error (%d): %s\n", error, message);
}
static struct libdecor_interface libdecor_iface = {
.error = handle_error,
};
static void delayed_rescale(Fl_Window *win) {
Fl_Window_Driver::driver(win)->is_a_rescale(true);
win->size(win->w(), win->h());
@@ -1054,11 +1042,12 @@ void Fl_Wayland_Window_Driver::wait_for_expose()
Fl_Window_Driver::wait_for_expose();
struct wld_window * xid = fl_wl_xid(pWindow);
if (!xid) return;
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
if (pWindow->fullscreen_active()) {
if (xid->kind == DECORATED) {
while (!(xid->state & LIBDECOR_WINDOW_STATE_FULLSCREEN) ||
!(xid->state & LIBDECOR_WINDOW_STATE_ACTIVE)) {
wl_display_dispatch(Fl_Wayland_Screen_Driver::wl_display);
libdecor_dispatch(scr_driver->libdecor_context, 0);
}
} else if (xid->kind == UNFRAMED) {
wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display);
@@ -1066,7 +1055,7 @@ void Fl_Wayland_Window_Driver::wait_for_expose()
} else if (xid->kind == DECORATED) {
// necessary for the windowfocus demo program with recent Wayland versions
if (!(xid->state & LIBDECOR_WINDOW_STATE_ACTIVE)) {
wl_display_dispatch(Fl_Wayland_Screen_Driver::wl_display);
libdecor_dispatch(scr_driver->libdecor_context, 0);
}
}
}
@@ -1527,9 +1516,6 @@ void Fl_Wayland_Window_Driver::makeWindow()
} else if (pWindow->border() && !pWindow->parent() ) { // a decorated window
new_window->kind = DECORATED;
if (!scr_driver->libdecor_context)
scr_driver->libdecor_context = libdecor_new(Fl_Wayland_Screen_Driver::wl_display,
&libdecor_iface);
new_window->frame = libdecor_decorate(scr_driver->libdecor_context, new_window->wl_surface,
&libdecor_frame_iface, new_window);
// appears in the Gnome desktop menu bar