]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkeventsource.c
configure: Check for XGetEventData()
[~andy/gtk] / gdk / x11 / gdkeventsource.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include "gdkeventsource.h"
23
24 #include "gdkinternals.h"
25 #include "gdkwindow-x11.h"
26 #include "gdkprivate-x11.h"
27
28
29 static gboolean gdk_event_source_prepare  (GSource     *source,
30                                            gint        *timeout);
31 static gboolean gdk_event_source_check    (GSource     *source);
32 static gboolean gdk_event_source_dispatch (GSource     *source,
33                                            GSourceFunc  callback,
34                                            gpointer     user_data);
35 static void     gdk_event_source_finalize (GSource     *source);
36
37 #define HAS_FOCUS(toplevel)                           \
38   ((toplevel)->has_focus || (toplevel)->has_pointer_focus)
39
40 struct _GdkEventSource
41 {
42   GSource source;
43
44   GdkDisplay *display;
45   GPollFD event_poll_fd;
46   GList *translators;
47 };
48
49 static GSourceFuncs event_funcs = {
50   gdk_event_source_prepare,
51   gdk_event_source_check,
52   gdk_event_source_dispatch,
53   gdk_event_source_finalize
54 };
55
56 static GList *event_sources = NULL;
57
58 static gint
59 gdk_event_apply_filters (XEvent    *xevent,
60                          GdkEvent  *event,
61                          GdkWindow *window)
62 {
63   GList *tmp_list;
64   GdkFilterReturn result;
65
66   if (window == NULL)
67     tmp_list = _gdk_default_filters;
68   else
69     tmp_list = window->filters;
70
71   while (tmp_list)
72     {
73       GdkEventFilter *filter = (GdkEventFilter*) tmp_list->data;
74       GList *node;
75
76       if ((filter->flags & GDK_EVENT_FILTER_REMOVED) != 0)
77         {
78           tmp_list = tmp_list->next;
79           continue;
80         }
81
82       filter->ref_count++;
83       result = filter->function (xevent, event, filter->data);
84
85       /* Protect against unreffing the filter mutating the list */
86       node = tmp_list->next;
87
88       _gdk_event_filter_unref (window, filter);
89
90       tmp_list = node;
91
92       if (result != GDK_FILTER_CONTINUE)
93         return result;
94     }
95
96   return GDK_FILTER_CONTINUE;
97 }
98
99 static GdkWindow *
100 gdk_event_source_get_filter_window (GdkEventSource      *event_source,
101                                     XEvent              *xevent,
102                                     GdkEventTranslator **event_translator)
103 {
104   GList *list = event_source->translators;
105   GdkWindow *window;
106
107   *event_translator = NULL;
108
109   while (list)
110     {
111       GdkEventTranslator *translator = list->data;
112
113       list = list->next;
114       window = _gdk_x11_event_translator_get_window (translator,
115                                                      event_source->display,
116                                                      xevent);
117       if (window)
118         {
119           *event_translator = translator;
120           return window;
121         }
122     }
123
124   window = gdk_x11_window_lookup_for_display (event_source->display,
125                                               xevent->xany.window);
126
127   if (window && !GDK_IS_WINDOW (window))
128     window = NULL;
129
130   return window;
131 }
132
133 static void
134 handle_focus_change (GdkEventCrossing *event)
135 {
136   GdkToplevelX11 *toplevel;
137   gboolean focus_in, had_focus;
138
139   toplevel = _gdk_x11_window_get_toplevel (event->window);
140   focus_in = (event->type == GDK_ENTER_NOTIFY);
141
142   if (!toplevel || event->detail == GDK_NOTIFY_INFERIOR)
143     return;
144
145   toplevel->has_pointer = focus_in;
146
147   if (!event->focus || toplevel->has_focus_window)
148     return;
149
150   had_focus = HAS_FOCUS (toplevel);
151   toplevel->has_pointer_focus = focus_in;
152
153   if (HAS_FOCUS (toplevel) != had_focus)
154     {
155       GdkEvent *focus_event;
156
157       focus_event = gdk_event_new (GDK_FOCUS_CHANGE);
158       focus_event->focus_change.window = g_object_ref (event->window);
159       focus_event->focus_change.send_event = FALSE;
160       focus_event->focus_change.in = focus_in;
161       gdk_event_set_device (focus_event, gdk_event_get_device ((GdkEvent *) event));
162
163       gdk_event_put (focus_event);
164       gdk_event_free (focus_event);
165     }
166 }
167
168 static GdkEvent *
169 gdk_event_source_translate_event (GdkEventSource *event_source,
170                                   XEvent         *xevent)
171 {
172   GdkEvent *event = gdk_event_new (GDK_NOTHING);
173   GdkFilterReturn result = GDK_FILTER_CONTINUE;
174   GdkEventTranslator *event_translator;
175   GdkWindow *filter_window;
176   Display *dpy;
177
178   dpy = GDK_DISPLAY_XDISPLAY (event_source->display);
179
180 #ifdef HAVE_XGENERICEVENTS
181   /* Get cookie data here so it's available
182    * to every event translator and event filter.
183    */
184   if (xevent->type == GenericEvent)
185     XGetEventData (dpy, &xevent->xcookie);
186 #endif
187
188   filter_window = gdk_event_source_get_filter_window (event_source, xevent,
189                                                       &event_translator);
190   if (filter_window)
191     event->any.window = g_object_ref (filter_window);
192
193   /* Run default filters */
194   if (_gdk_default_filters)
195     {
196       /* Apply global filters */
197       result = gdk_event_apply_filters (xevent, event, NULL);
198     }
199
200   if (result == GDK_FILTER_CONTINUE &&
201       filter_window && filter_window->filters)
202     {
203       /* Apply per-window filters */
204       result = gdk_event_apply_filters (xevent, event, filter_window);
205     }
206
207   if (result != GDK_FILTER_CONTINUE)
208     {
209 #ifdef HAVE_XGENERICEVENTS
210       if (xevent->type == GenericEvent)
211         XFreeEventData (dpy, &xevent->xcookie);
212 #endif
213
214       if (result == GDK_FILTER_REMOVE)
215         {
216           gdk_event_free (event);
217           return NULL;
218         }
219       else /* GDK_FILTER_TRANSLATE */
220         return event;
221     }
222
223   gdk_event_free (event);
224   event = NULL;
225
226   if (event_translator)
227     {
228       /* Event translator was gotten before in get_filter_window() */
229       event = _gdk_x11_event_translator_translate (event_translator,
230                                                    event_source->display,
231                                                    xevent);
232     }
233   else
234     {
235       GList *list = event_source->translators;
236
237       while (list && !event)
238         {
239           GdkEventTranslator *translator = list->data;
240
241           list = list->next;
242           event = _gdk_x11_event_translator_translate (translator,
243                                                        event_source->display,
244                                                        xevent);
245         }
246     }
247
248   if (event &&
249       (event->type == GDK_ENTER_NOTIFY ||
250        event->type == GDK_LEAVE_NOTIFY) &&
251       event->crossing.window != NULL)
252     {
253       /* Handle focusing (in the case where no window manager is running */
254       handle_focus_change (&event->crossing);
255     }
256
257 #ifdef HAVE_XGENERICEVENTS
258   if (xevent->type == GenericEvent)
259     XFreeEventData (dpy, &xevent->xcookie);
260 #endif
261
262   return event;
263 }
264
265 static gboolean
266 gdk_check_xpending (GdkDisplay *display)
267 {
268   return XPending (GDK_DISPLAY_XDISPLAY (display));
269 }
270
271 static gboolean
272 gdk_event_source_prepare (GSource *source,
273                           gint    *timeout)
274 {
275   GdkDisplay *display = ((GdkEventSource*) source)->display;
276   gboolean retval;
277
278   GDK_THREADS_ENTER ();
279
280   *timeout = -1;
281   retval = (_gdk_event_queue_find_first (display) != NULL ||
282             gdk_check_xpending (display));
283
284   GDK_THREADS_LEAVE ();
285
286   return retval;
287 }
288
289 static gboolean
290 gdk_event_source_check (GSource *source)
291 {
292   GdkEventSource *event_source = (GdkEventSource*) source;
293   gboolean retval;
294
295   GDK_THREADS_ENTER ();
296
297   if (event_source->event_poll_fd.revents & G_IO_IN)
298     retval = (_gdk_event_queue_find_first (event_source->display) != NULL ||
299               gdk_check_xpending (event_source->display));
300   else
301     retval = FALSE;
302
303   GDK_THREADS_LEAVE ();
304
305   return retval;
306 }
307
308 void
309 _gdk_x11_display_queue_events (GdkDisplay *display)
310 {
311   GdkEvent *event;
312   XEvent xevent;
313   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
314   GdkEventSource *event_source;
315   GdkX11Display *display_x11;
316
317   display_x11 = GDK_X11_DISPLAY (display);
318   event_source = (GdkEventSource *) display_x11->event_source;
319
320   while (!_gdk_event_queue_find_first (display) && XPending (xdisplay))
321     {
322       XNextEvent (xdisplay, &xevent);
323
324       switch (xevent.type)
325         {
326         case KeyPress:
327         case KeyRelease:
328           break;
329         default:
330           if (XFilterEvent (&xevent, None))
331             continue;
332         }
333
334       event = gdk_event_source_translate_event (event_source, &xevent);
335
336       if (event)
337         {
338           GList *node;
339
340           node = _gdk_event_queue_append (display, event);
341           _gdk_windowing_got_event (display, node, event, xevent.xany.serial);
342         }
343     }
344 }
345
346 static gboolean
347 gdk_event_source_dispatch (GSource     *source,
348                            GSourceFunc  callback,
349                            gpointer     user_data)
350 {
351   GdkDisplay *display = ((GdkEventSource*) source)->display;
352   GdkEvent *event;
353
354   GDK_THREADS_ENTER ();
355
356   event = gdk_display_get_event (display);
357
358   if (event)
359     {
360       _gdk_event_emit (event);
361
362       gdk_event_free (event);
363     }
364
365   GDK_THREADS_LEAVE ();
366
367   return TRUE;
368 }
369
370 static void
371 gdk_event_source_finalize (GSource *source)
372 {
373   GdkEventSource *event_source = (GdkEventSource *)source;
374
375   g_list_free (event_source->translators);
376   event_source->translators = NULL;
377
378   event_sources = g_list_remove (event_sources, source);
379 }
380
381 GSource *
382 gdk_x11_event_source_new (GdkDisplay *display)
383 {
384   GSource *source;
385   GdkEventSource *event_source;
386   GdkX11Display *display_x11;
387   int connection_number;
388   char *name;
389
390   source = g_source_new (&event_funcs, sizeof (GdkEventSource));
391   name = g_strdup_printf ("GDK X11 Event source (%s)",
392                           gdk_display_get_name (display));
393   g_source_set_name (source, name);
394   g_free (name);
395   event_source = (GdkEventSource *) source;
396   event_source->display = display;
397
398   display_x11 = GDK_X11_DISPLAY (display);
399   connection_number = ConnectionNumber (display_x11->xdisplay);
400
401   event_source->event_poll_fd.fd = connection_number;
402   event_source->event_poll_fd.events = G_IO_IN;
403   g_source_add_poll (source, &event_source->event_poll_fd);
404
405   g_source_set_priority (source, GDK_PRIORITY_EVENTS);
406   g_source_set_can_recurse (source, TRUE);
407   g_source_attach (source, NULL);
408
409   event_sources = g_list_prepend (event_sources, source);
410
411   return source;
412 }
413
414 void
415 gdk_x11_event_source_add_translator (GdkEventSource     *source,
416                                      GdkEventTranslator *translator)
417 {
418   g_return_if_fail (GDK_IS_EVENT_TRANSLATOR (translator));
419
420   source->translators = g_list_append (source->translators, translator);
421 }
422
423 void
424 gdk_x11_event_source_select_events (GdkEventSource *source,
425                                     Window          window,
426                                     GdkEventMask    event_mask,
427                                     unsigned int    extra_x_mask)
428 {
429   unsigned int xmask = extra_x_mask;
430   GList *list;
431   gint i;
432
433   list = source->translators;
434
435   while (list)
436     {
437       GdkEventTranslator *translator = list->data;
438       GdkEventMask translator_mask, mask;
439
440       translator_mask = _gdk_x11_event_translator_get_handled_events (translator);
441       mask = event_mask & translator_mask;
442
443       if (mask != 0)
444         {
445           _gdk_x11_event_translator_select_window_events (translator, window, mask);
446           event_mask &= ~mask;
447         }
448
449       list = list->next;
450     }
451
452   for (i = 0; i < _gdk_x11_event_mask_table_size; i++)
453     {
454       if (event_mask & (1 << (i + 1)))
455         xmask |= _gdk_x11_event_mask_table[i];
456     }
457
458   XSelectInput (GDK_DISPLAY_XDISPLAY (source->display), window, xmask);
459 }