gtk2/gdk/x11/gdkeventsource.c
2020-07-25 00:47:36 +02:00

520 lines
15 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 "gdkeventsource.h"
#include "gdkinternals.h"
#include "gdksurface-x11.h"
#include "gdkprivate-x11.h"
#include "gdkdisplay-x11.h"
#include "xsettings-client.h"
static gboolean gdk_event_source_prepare (GSource *source,
int *timeout);
static gboolean gdk_event_source_check (GSource *source);
static gboolean gdk_event_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data);
static void gdk_event_source_finalize (GSource *source);
static GQuark quark_needs_enter = 0;
#define HAS_FOCUS(toplevel) \
((toplevel)->has_focus || (toplevel)->has_pointer_focus)
struct _GdkEventSource
{
GSource source;
GdkDisplay *display;
GPollFD event_poll_fd;
GList *translators;
};
static GSourceFuncs event_funcs = {
gdk_event_source_prepare,
gdk_event_source_check,
gdk_event_source_dispatch,
gdk_event_source_finalize
};
static GdkSurface *
gdk_event_source_get_filter_surface (GdkEventSource *event_source,
const XEvent *xevent,
GdkEventTranslator **event_translator)
{
GList *list = event_source->translators;
GdkSurface *surface;
*event_translator = NULL;
while (list)
{
GdkEventTranslator *translator = list->data;
list = list->next;
surface = _gdk_x11_event_translator_get_surface (translator,
event_source->display,
xevent);
if (surface)
{
*event_translator = translator;
return surface;
}
}
surface = gdk_x11_surface_lookup_for_display (event_source->display,
xevent->xany.window);
return surface;
}
static void
handle_focus_change (GdkEvent *event)
{
GdkToplevelX11 *toplevel;
GdkX11Screen *x11_screen;
gboolean focus_in, had_focus;
toplevel = _gdk_x11_surface_get_toplevel (gdk_event_get_surface (event));
x11_screen = GDK_X11_SCREEN (GDK_SURFACE_SCREEN (gdk_event_get_surface (event)));
focus_in = (gdk_event_get_event_type (event) == GDK_ENTER_NOTIFY);
if (x11_screen->wmspec_check_window)
return;
if (!toplevel || gdk_crossing_event_get_detail (event) == GDK_NOTIFY_INFERIOR)
return;
toplevel->has_pointer = focus_in;
if (!gdk_crossing_event_get_focus (event) || toplevel->has_focus_window)
return;
had_focus = HAS_FOCUS (toplevel);
toplevel->has_pointer_focus = focus_in;
if (HAS_FOCUS (toplevel) != had_focus)
{
GdkEvent *focus_event;
focus_event = gdk_focus_event_new (gdk_event_get_surface (event),
gdk_event_get_device (event),
gdk_event_get_source_device (event),
focus_in);
gdk_display_put_event (gdk_event_get_display (event), focus_event);
gdk_event_unref (focus_event);
}
}
static GdkEvent *
create_synth_crossing_event (GdkEventType evtype,
GdkCrossingMode mode,
GdkEvent *real_event)
{
GdkEvent *event;
double x, y;
g_assert (evtype == GDK_ENTER_NOTIFY || evtype == GDK_LEAVE_NOTIFY);
gdk_event_get_position (real_event, &x, &y);
event = gdk_crossing_event_new (evtype,
gdk_event_get_surface (real_event),
gdk_event_get_device (real_event),
gdk_event_get_source_device (real_event),
gdk_event_get_time (real_event),
gdk_event_get_modifier_state (real_event),
x, y,
mode,
GDK_NOTIFY_ANCESTOR);
return event;
}
static void
handle_touch_synthetic_crossing (GdkEvent *event)
{
GdkEventType evtype = gdk_event_get_event_type (event);
GdkEvent *crossing = NULL;
GdkSeat *seat = gdk_event_get_seat (event);
gboolean needs_enter, set_needs_enter = FALSE;
if (quark_needs_enter == 0)
quark_needs_enter = g_quark_from_static_string ("gdk-x11-needs-enter-after-touch-end");
needs_enter =
GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (seat), quark_needs_enter));
if (evtype == GDK_MOTION_NOTIFY && needs_enter)
{
set_needs_enter = FALSE;
crossing = create_synth_crossing_event (GDK_ENTER_NOTIFY,
GDK_CROSSING_DEVICE_SWITCH,
event);
}
else if (evtype == GDK_TOUCH_BEGIN && needs_enter &&
gdk_event_get_pointer_emulated (event))
{
set_needs_enter = FALSE;
crossing = create_synth_crossing_event (GDK_ENTER_NOTIFY,
GDK_CROSSING_TOUCH_BEGIN,
event);
}
else if (evtype == GDK_TOUCH_END &&
gdk_event_get_pointer_emulated (event))
{
set_needs_enter = TRUE;
crossing = create_synth_crossing_event (GDK_LEAVE_NOTIFY,
GDK_CROSSING_TOUCH_END,
event);
}
else if (evtype == GDK_ENTER_NOTIFY ||
evtype == GDK_LEAVE_NOTIFY)
{
/* We are receiving or shall receive a real crossing event,
* turn this off.
*/
set_needs_enter = FALSE;
}
else
return;
if (needs_enter != set_needs_enter)
{
if (!set_needs_enter)
g_object_steal_qdata (G_OBJECT (seat), quark_needs_enter);
else
g_object_set_qdata (G_OBJECT (seat), quark_needs_enter,
GUINT_TO_POINTER (TRUE));
}
if (crossing)
{
gdk_display_put_event (gdk_seat_get_display (seat), crossing);
gdk_event_unref (crossing);
}
}
static GdkEvent *
gdk_event_source_translate_event (GdkX11Display *x11_display,
const XEvent *xevent)
{
GdkEventSource *event_source = (GdkEventSource *) x11_display->event_source;
GdkEvent *event;
GdkFilterReturn result = GDK_FILTER_CONTINUE;
GdkDisplay *display = GDK_DISPLAY (x11_display);
GdkEventTranslator *event_translator;
GdkSurface *filter_surface;
Display *dpy;
GdkX11Screen *x11_screen;
gpointer cache;
x11_screen = GDK_X11_DISPLAY (display)->screen;
dpy = GDK_DISPLAY_XDISPLAY (display);
event = NULL;
filter_surface = gdk_event_source_get_filter_surface (event_source, xevent,
&event_translator);
/* apply XSettings filters */
if (xevent->xany.window == XRootWindow (dpy, 0))
result = gdk_xsettings_root_window_filter (xevent, x11_screen);
if (result == GDK_FILTER_CONTINUE &&
xevent->xany.window == x11_screen->xsettings_manager_window)
result = gdk_xsettings_manager_window_filter (xevent, x11_screen);
cache = gdk_surface_cache_get (display);
if (cache)
{
if (result == GDK_FILTER_CONTINUE)
result = gdk_surface_cache_shape_filter (xevent, cache);
if (result == GDK_FILTER_CONTINUE &&
xevent->xany.window == XRootWindow (dpy, 0))
result = gdk_surface_cache_filter (xevent, cache);
}
if (result == GDK_FILTER_CONTINUE)
result = _gdk_wm_protocols_filter (xevent, filter_surface, &event, NULL);
if (result == GDK_FILTER_CONTINUE &&
gdk_x11_drop_filter (filter_surface, xevent))
result = GDK_FILTER_REMOVE;
if (result != GDK_FILTER_CONTINUE)
{
if (result == GDK_FILTER_REMOVE)
return NULL;
else /* GDK_FILTER_TRANSLATE */
return event;
}
if (event_translator)
{
/* Event translator was gotten before in get_filter_window() */
event = _gdk_x11_event_translator_translate (event_translator,
display,
xevent);
}
else
{
GList *list = event_source->translators;
while (list && !event)
{
GdkEventTranslator *translator = list->data;
list = list->next;
event = _gdk_x11_event_translator_translate (translator,
display,
xevent);
}
}
if (event)
{
GdkEventType evtype = gdk_event_get_event_type (event);
if ((evtype == GDK_ENTER_NOTIFY ||
evtype == GDK_LEAVE_NOTIFY) &&
gdk_event_get_surface (event) != NULL)
{
/* Handle focusing (in the case where no window manager is running */
handle_focus_change (event);
}
if (evtype == GDK_TOUCH_BEGIN ||
evtype == GDK_TOUCH_END ||
evtype == GDK_MOTION_NOTIFY ||
evtype == GDK_ENTER_NOTIFY ||
evtype == GDK_LEAVE_NOTIFY)
{
handle_touch_synthetic_crossing (event);
}
}
return event;
}
gboolean
gdk_event_source_xevent (GdkX11Display *x11_display,
const XEvent *xevent)
{
GdkDisplay *display = GDK_DISPLAY (x11_display);
GdkEvent *event;
GList *node;
event = gdk_event_source_translate_event (x11_display, xevent);
if (event == NULL)
return FALSE;
node = _gdk_event_queue_append (display, event);
_gdk_windowing_got_event (display, node, event, xevent->xany.serial);
return TRUE;
}
static gboolean
gdk_check_xpending (GdkDisplay *display)
{
return XPending (GDK_DISPLAY_XDISPLAY (display));
}
static gboolean
gdk_event_source_prepare (GSource *source,
int *timeout)
{
GdkDisplay *display = ((GdkEventSource*) source)->display;
gboolean retval;
*timeout = -1;
if (display->event_pause_count > 0)
retval = _gdk_event_queue_find_first (display) != NULL;
else
retval = (_gdk_event_queue_find_first (display) != NULL ||
gdk_check_xpending (display));
return retval;
}
static gboolean
gdk_event_source_check (GSource *source)
{
GdkEventSource *event_source = (GdkEventSource*) source;
gboolean retval;
if (event_source->display->event_pause_count > 0)
retval = _gdk_event_queue_find_first (event_source->display) != NULL;
else if (event_source->event_poll_fd.revents & G_IO_IN)
retval = (_gdk_event_queue_find_first (event_source->display) != NULL ||
gdk_check_xpending (event_source->display));
else
retval = FALSE;
return retval;
}
void
_gdk_x11_display_queue_events (GdkDisplay *display)
{
Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
XEvent xevent;
gboolean unused;
while (!_gdk_event_queue_find_first (display) && XPending (xdisplay))
{
XNextEvent (xdisplay, &xevent);
switch (xevent.type)
{
case KeyPress:
case KeyRelease:
break;
default:
if (XFilterEvent (&xevent, None))
continue;
}
#ifdef HAVE_XGENERICEVENTS
/* Get cookie data here so it's available
* to every event translator and event filter.
*/
if (xevent.type == GenericEvent)
XGetEventData (xdisplay, &xevent.xcookie);
#endif
g_signal_emit_by_name (display, "xevent", &xevent, &unused);
#ifdef HAVE_XGENERICEVENTS
if (xevent.type == GenericEvent)
XFreeEventData (xdisplay, &xevent.xcookie);
#endif
}
}
static gboolean
gdk_event_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
GdkDisplay *display = ((GdkEventSource*) source)->display;
GdkEvent *event;
event = gdk_display_get_event (display);
if (event)
{
_gdk_event_emit (event);
gdk_event_unref (event);
}
return TRUE;
}
static void
gdk_event_source_finalize (GSource *source)
{
GdkEventSource *event_source = (GdkEventSource *)source;
g_list_free (event_source->translators);
event_source->translators = NULL;
}
GSource *
gdk_x11_event_source_new (GdkDisplay *display)
{
GSource *source;
GdkEventSource *event_source;
GdkX11Display *display_x11;
int connection_number;
char *name;
source = g_source_new (&event_funcs, sizeof (GdkEventSource));
name = g_strdup_printf ("GDK X11 Event source (%s)",
gdk_display_get_name (display));
g_source_set_name (source, name);
g_free (name);
event_source = (GdkEventSource *) source;
event_source->display = display;
display_x11 = GDK_X11_DISPLAY (display);
connection_number = ConnectionNumber (display_x11->xdisplay);
event_source->event_poll_fd.fd = connection_number;
event_source->event_poll_fd.events = G_IO_IN;
g_source_add_poll (source, &event_source->event_poll_fd);
g_source_set_priority (source, GDK_PRIORITY_EVENTS);
g_source_set_can_recurse (source, TRUE);
g_source_attach (source, NULL);
return source;
}
void
gdk_x11_event_source_add_translator (GdkEventSource *source,
GdkEventTranslator *translator)
{
g_return_if_fail (GDK_IS_EVENT_TRANSLATOR (translator));
source->translators = g_list_append (source->translators, translator);
}
void
gdk_x11_event_source_select_events (GdkEventSource *source,
Window window,
GdkEventMask event_mask,
unsigned int extra_x_mask)
{
unsigned int xmask = extra_x_mask;
GList *list;
int i;
list = source->translators;
while (list)
{
GdkEventTranslator *translator = list->data;
GdkEventMask translator_mask, mask;
translator_mask = _gdk_x11_event_translator_get_handled_events (translator);
mask = event_mask & translator_mask;
if (mask != 0)
{
_gdk_x11_event_translator_select_surface_events (translator, window, mask);
event_mask &= ~mask;
}
list = list->next;
}
for (i = 0; i < _gdk_x11_event_mask_table_size; i++)
{
if (event_mask & (1 << (i + 1)))
xmask |= _gdk_x11_event_mask_table[i];
}
XSelectInput (GDK_DISPLAY_XDISPLAY (source->display), window, xmask);
}