mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-30 15:31:34 +00:00
2893526a48
This is required for proper integration with any other library/application that may perform wayland API calls and poll() the wayland fd from multiple threads. Using wl_display_dispatch{_queue}() is thread-safe if not mixed with custom poll() usage, which GSource/GMainContext does. Essentially, the problem is that multiple threads polling and reading the same fd is extremely racy. Use the wayland provided API for allowing concurrent access to the wayland display fd. See the wayland man pages for wl_display_prepare_read(), wl_display_cancel_read() and wl_display_read_events() for more details. https://bugzilla.gnome.org/show_bug.cgi?id=763852
209 lines
5.8 KiB
C
209 lines
5.8 KiB
C
/* GDK - The GIMP Drawing Kit
|
|
* Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gdkinternals.h"
|
|
#include "gdkprivate-wayland.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
|
|
typedef struct _GdkWaylandEventSource {
|
|
GSource source;
|
|
GPollFD pfd;
|
|
uint32_t mask;
|
|
GdkDisplay *display;
|
|
gboolean reading;
|
|
} GdkWaylandEventSource;
|
|
|
|
static gboolean
|
|
gdk_event_source_prepare (GSource *base,
|
|
gint *timeout)
|
|
{
|
|
GdkWaylandEventSource *source = (GdkWaylandEventSource *) base;
|
|
GdkWaylandDisplay *display = (GdkWaylandDisplay *) source->display;
|
|
|
|
*timeout = -1;
|
|
|
|
if (source->display->event_pause_count > 0)
|
|
return _gdk_event_queue_find_first (source->display) != NULL;
|
|
|
|
/* We have to add/remove the GPollFD if we want to update our
|
|
* poll event mask dynamically. Instead, let's just flush all
|
|
* write on idle instead, which is what this amounts to.
|
|
*/
|
|
|
|
if (_gdk_event_queue_find_first (source->display) != NULL)
|
|
return TRUE;
|
|
|
|
/* wl_display_prepare_read() needs to be balanced with either
|
|
* wl_display_read_events() or wl_display_cancel_read()
|
|
* (in gdk_event_source_check() */
|
|
if (source->reading)
|
|
return FALSE;
|
|
|
|
/* if prepare_read() returns non-zero, there are events to be dispatched */
|
|
if (wl_display_prepare_read (display->wl_display) != 0)
|
|
return TRUE;
|
|
source->reading = TRUE;
|
|
|
|
if (wl_display_flush (display->wl_display) < 0)
|
|
g_error ("Error flushing display: %s", g_strerror (errno));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
gdk_event_source_check (GSource *base)
|
|
{
|
|
GdkWaylandEventSource *source = (GdkWaylandEventSource *) base;
|
|
GdkWaylandDisplay *display_wayland = (GdkWaylandDisplay *) source->display;
|
|
|
|
if (source->display->event_pause_count > 0)
|
|
{
|
|
if (source->reading)
|
|
wl_display_cancel_read (display_wayland->wl_display);
|
|
source->reading = FALSE;
|
|
|
|
return _gdk_event_queue_find_first (source->display) != NULL;
|
|
}
|
|
|
|
/* read the events from the wayland fd into their respective queues if we have data */
|
|
if (source->reading)
|
|
{
|
|
if (source->pfd.revents & G_IO_IN)
|
|
{
|
|
if (wl_display_read_events (display_wayland->wl_display) < 0)
|
|
g_error ("Error reading events from display: %s", g_strerror (errno));
|
|
}
|
|
else
|
|
wl_display_cancel_read (display_wayland->wl_display);
|
|
source->reading = FALSE;
|
|
}
|
|
|
|
return _gdk_event_queue_find_first (source->display) != NULL ||
|
|
source->pfd.revents;
|
|
}
|
|
|
|
static gboolean
|
|
gdk_event_source_dispatch (GSource *base,
|
|
GSourceFunc callback,
|
|
gpointer data)
|
|
{
|
|
GdkWaylandEventSource *source = (GdkWaylandEventSource *) base;
|
|
GdkDisplay *display = source->display;
|
|
GdkEvent *event;
|
|
|
|
gdk_threads_enter ();
|
|
|
|
event = gdk_display_get_event (display);
|
|
|
|
if (event)
|
|
{
|
|
_gdk_event_emit (event);
|
|
|
|
gdk_event_free (event);
|
|
}
|
|
|
|
gdk_threads_leave ();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gdk_event_source_finalize (GSource *base)
|
|
{
|
|
GdkWaylandEventSource *source = (GdkWaylandEventSource *) base;
|
|
GdkWaylandDisplay *display = (GdkWaylandDisplay *) source->display;
|
|
|
|
if (source->reading)
|
|
wl_display_cancel_read (display->wl_display);
|
|
source->reading = FALSE;
|
|
}
|
|
|
|
static GSourceFuncs wl_glib_source_funcs = {
|
|
gdk_event_source_prepare,
|
|
gdk_event_source_check,
|
|
gdk_event_source_dispatch,
|
|
gdk_event_source_finalize
|
|
};
|
|
|
|
void
|
|
_gdk_wayland_display_deliver_event (GdkDisplay *display,
|
|
GdkEvent *event)
|
|
{
|
|
GList *node;
|
|
|
|
node = _gdk_event_queue_append (display, event);
|
|
_gdk_windowing_got_event (display, node, event,
|
|
_gdk_display_get_next_serial (display));
|
|
}
|
|
|
|
GSource *
|
|
_gdk_wayland_display_event_source_new (GdkDisplay *display)
|
|
{
|
|
GSource *source;
|
|
GdkWaylandEventSource *wl_source;
|
|
GdkWaylandDisplay *display_wayland;
|
|
char *name;
|
|
|
|
source = g_source_new (&wl_glib_source_funcs,
|
|
sizeof (GdkWaylandEventSource));
|
|
name = g_strdup_printf ("GDK Wayland Event source (%s)",
|
|
gdk_display_get_name (display));
|
|
g_source_set_name (source, name);
|
|
g_free (name);
|
|
wl_source = (GdkWaylandEventSource *) source;
|
|
|
|
display_wayland = GDK_WAYLAND_DISPLAY (display);
|
|
wl_source->display = display;
|
|
wl_source->pfd.fd = wl_display_get_fd (display_wayland->wl_display);
|
|
wl_source->pfd.events = G_IO_IN | G_IO_ERR | G_IO_HUP;
|
|
g_source_add_poll (source, &wl_source->pfd);
|
|
|
|
g_source_set_priority (source, GDK_PRIORITY_EVENTS);
|
|
g_source_set_can_recurse (source, TRUE);
|
|
g_source_attach (source, NULL);
|
|
|
|
return source;
|
|
}
|
|
|
|
void
|
|
_gdk_wayland_display_queue_events (GdkDisplay *display)
|
|
{
|
|
GdkWaylandDisplay *display_wayland;
|
|
GdkWaylandEventSource *source;
|
|
|
|
display_wayland = GDK_WAYLAND_DISPLAY (display);
|
|
source = (GdkWaylandEventSource *) display_wayland->event_source;
|
|
|
|
if (wl_display_dispatch_pending (display_wayland->wl_display) < 0)
|
|
{
|
|
g_warning ("Error %d (%s) dispatching to Wayland display.",
|
|
errno, g_strerror (errno));
|
|
exit (1);
|
|
}
|
|
|
|
if (source->pfd.revents & (G_IO_ERR | G_IO_HUP))
|
|
{
|
|
g_warning ("Lost connection to Wayland compositor.");
|
|
exit (1);
|
|
}
|
|
source->pfd.revents = 0;
|
|
}
|