]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkeventsource.c
Merge branch 'master' into broadway
[~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 "gdkx.h"
26
27
28 static gboolean gdk_event_source_prepare  (GSource     *source,
29                                            gint        *timeout);
30 static gboolean gdk_event_source_check    (GSource     *source);
31 static gboolean gdk_event_source_dispatch (GSource     *source,
32                                            GSourceFunc  callback,
33                                            gpointer     user_data);
34 static void     gdk_event_source_finalize (GSource     *source);
35
36 #define HAS_FOCUS(toplevel)                           \
37   ((toplevel)->has_focus || (toplevel)->has_pointer_focus)
38
39 struct _GdkEventSource
40 {
41   GSource source;
42
43   GdkDisplay *display;
44   GPollFD event_poll_fd;
45   GList *translators;
46 };
47
48 static GSourceFuncs event_funcs = {
49   gdk_event_source_prepare,
50   gdk_event_source_check,
51   gdk_event_source_dispatch,
52   gdk_event_source_finalize
53 };
54
55 static GList *event_sources = NULL;
56
57 static gint
58 gdk_event_apply_filters (XEvent   *xevent,
59                          GdkEvent *event,
60                          GList   **filters)
61 {
62   GList *tmp_list;
63   GdkFilterReturn result;
64
65   tmp_list = *filters;
66
67   while (tmp_list)
68     {
69       GdkEventFilter *filter = (GdkEventFilter*) tmp_list->data;
70       GList *node;
71
72       if ((filter->flags & GDK_EVENT_FILTER_REMOVED) != 0)
73         {
74           tmp_list = tmp_list->next;
75           continue;
76         }
77
78       filter->ref_count++;
79       result = filter->function (xevent, event, filter->data);
80
81       /* get the next node after running the function since the
82          function may add or remove a next node */
83       node = tmp_list;
84       tmp_list = tmp_list->next;
85
86       filter->ref_count--;
87       if (filter->ref_count == 0)
88         {
89           *filters = g_list_remove_link (*filters, node);
90           g_list_free_1 (node);
91           g_free (filter);
92         }
93
94       if (result != GDK_FILTER_CONTINUE)
95         return result;
96     }
97
98   return GDK_FILTER_CONTINUE;
99 }
100
101 static GdkWindow *
102 gdk_event_source_get_filter_window (GdkEventSource *event_source,
103                                     XEvent         *xevent)
104 {
105   GdkWindow *window;
106
107   window = gdk_window_lookup_for_display (event_source->display,
108                                           xevent->xany.window);
109
110   if (window && !GDK_IS_WINDOW (window))
111     window = NULL;
112
113   return window;
114 }
115
116 static void
117 handle_focus_change (GdkEventCrossing *event)
118 {
119   GdkToplevelX11 *toplevel;
120   gboolean focus_in, had_focus;
121
122   toplevel = _gdk_x11_window_get_toplevel (event->window);
123   focus_in = (event->type == GDK_ENTER_NOTIFY);
124
125   if (!toplevel || event->detail == GDK_NOTIFY_INFERIOR)
126     return;
127
128   toplevel->has_pointer = focus_in;
129
130   if (!event->focus || toplevel->has_focus_window)
131     return;
132
133   had_focus = HAS_FOCUS (toplevel);
134   toplevel->has_pointer_focus = focus_in;
135
136   if (HAS_FOCUS (toplevel) != had_focus)
137     {
138       GdkEvent *focus_event;
139
140       focus_event = gdk_event_new (GDK_FOCUS_CHANGE);
141       focus_event->focus_change.window = g_object_ref (event->window);
142       focus_event->focus_change.send_event = FALSE;
143       focus_event->focus_change.in = focus_in;
144       gdk_event_set_device (focus_event, gdk_event_get_device ((GdkEvent *) event));
145
146       gdk_event_put (focus_event);
147       gdk_event_free (focus_event);
148     }
149 }
150
151 static GdkEvent *
152 gdk_event_source_translate_event (GdkEventSource *event_source,
153                                   XEvent         *xevent)
154 {
155   GdkEvent *event = gdk_event_new (GDK_NOTHING);
156   GList *list = event_source->translators;
157   GdkFilterReturn result;
158   GdkWindow *filter_window;
159
160   /* Run default filters */
161   if (_gdk_default_filters)
162     {
163       /* Apply global filters */
164
165       result = gdk_event_apply_filters (xevent, event,
166                                         &_gdk_default_filters);
167
168       if (result == GDK_FILTER_REMOVE)
169         {
170           gdk_event_free (event);
171           return NULL;
172         }
173       else if (result == GDK_FILTER_TRANSLATE)
174         return event;
175     }
176
177   filter_window = gdk_event_source_get_filter_window (event_source, xevent);
178
179   if (filter_window)
180     {
181       /* Apply per-window filters */
182       GdkFilterReturn result;
183
184       event->any.window = g_object_ref (filter_window);
185
186       if (filter_window->filters)
187         {
188           result = gdk_event_apply_filters (xevent, event,
189                                             &filter_window->filters);
190
191           if (result == GDK_FILTER_REMOVE)
192             {
193               gdk_event_free (event);
194               return NULL;
195             }
196           else if (result == GDK_FILTER_TRANSLATE)
197             return event;
198         }
199     }
200
201   gdk_event_free (event);
202   event = NULL;
203
204   while (list && !event)
205     {
206       GdkEventTranslator *translator = list->data;
207
208       list = list->next;
209       event = gdk_event_translator_translate (translator,
210                                               event_source->display,
211                                               xevent);
212     }
213
214   if (event &&
215       (event->type == GDK_ENTER_NOTIFY ||
216        event->type == GDK_LEAVE_NOTIFY) &&
217       event->crossing.window != NULL)
218     {
219       /* Handle focusing (in the case where no window manager is running */
220       handle_focus_change (&event->crossing);
221     }
222
223   return event;
224 }
225
226 static gboolean
227 gdk_check_xpending (GdkDisplay *display)
228 {
229   return XPending (GDK_DISPLAY_XDISPLAY (display));
230 }
231
232 static gboolean
233 gdk_event_source_prepare (GSource *source,
234                           gint    *timeout)
235 {
236   GdkDisplay *display = ((GdkEventSource*) source)->display;
237   gboolean retval;
238
239   GDK_THREADS_ENTER ();
240
241   *timeout = -1;
242   retval = (_gdk_event_queue_find_first (display) != NULL ||
243             gdk_check_xpending (display));
244
245   GDK_THREADS_LEAVE ();
246
247   return retval;
248 }
249
250 static gboolean
251 gdk_event_source_check (GSource *source)
252 {
253   GdkEventSource *event_source = (GdkEventSource*) source;
254   gboolean retval;
255
256   GDK_THREADS_ENTER ();
257
258   if (event_source->event_poll_fd.revents & G_IO_IN)
259     retval = (_gdk_event_queue_find_first (event_source->display) != NULL ||
260               gdk_check_xpending (event_source->display));
261   else
262     retval = FALSE;
263
264   GDK_THREADS_LEAVE ();
265
266   return retval;
267 }
268
269 void
270 _gdk_events_queue (GdkDisplay *display)
271 {
272   GdkEvent *event;
273   XEvent xevent;
274   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
275   GdkEventSource *event_source;
276   GdkDisplayX11 *display_x11;
277
278   display_x11 = GDK_DISPLAY_X11 (display);
279   event_source = (GdkEventSource *) display_x11->event_source;
280
281   while (!_gdk_event_queue_find_first (display) && XPending (xdisplay))
282     {
283       XNextEvent (xdisplay, &xevent);
284
285       switch (xevent.type)
286         {
287         case KeyPress:
288         case KeyRelease:
289           break;
290         default:
291           if (XFilterEvent (&xevent, None))
292             continue;
293         }
294
295       event = gdk_event_source_translate_event (event_source, &xevent);
296
297       if (event)
298         {
299           GList *node;
300
301           node = _gdk_event_queue_append (display, event);
302           _gdk_windowing_got_event (display, node, event, xevent.xany.serial);
303         }
304     }
305 }
306
307 static gboolean
308 gdk_event_source_dispatch (GSource     *source,
309                            GSourceFunc  callback,
310                            gpointer     user_data)
311 {
312   GdkDisplay *display = ((GdkEventSource*) source)->display;
313   GdkEvent *event;
314
315   GDK_THREADS_ENTER ();
316
317   event = gdk_display_get_event (display);
318
319   if (event)
320     {
321       _gdk_event_emit (event);
322
323       gdk_event_free (event);
324     }
325
326   GDK_THREADS_LEAVE ();
327
328   return TRUE;
329 }
330
331 static void
332 gdk_event_source_finalize (GSource *source)
333 {
334   GdkEventSource *event_source = (GdkEventSource *)source;
335
336   g_list_free (event_source->translators);
337   event_source->translators = NULL;
338
339   event_sources = g_list_remove (event_sources, source);
340 }
341
342 GSource *
343 gdk_event_source_new (GdkDisplay *display)
344 {
345   GSource *source;
346   GdkEventSource *event_source;
347   GdkDisplayX11 *display_x11;
348   int connection_number;
349   char *name;
350
351   source = g_source_new (&event_funcs, sizeof (GdkEventSource));
352   name = g_strdup_printf ("GDK X11 Event source (%s)",
353                           gdk_display_get_name (display));
354   g_source_set_name (source, name);
355   g_free (name);
356   event_source = (GdkEventSource *) source;
357   event_source->display = display;
358
359   display_x11 = GDK_DISPLAY_X11 (display);
360   connection_number = ConnectionNumber (display_x11->xdisplay);
361
362   event_source->event_poll_fd.fd = connection_number;
363   event_source->event_poll_fd.events = G_IO_IN;
364   g_source_add_poll (source, &event_source->event_poll_fd);
365
366   g_source_set_priority (source, GDK_PRIORITY_EVENTS);
367   g_source_set_can_recurse (source, TRUE);
368   g_source_attach (source, NULL);
369
370   event_sources = g_list_prepend (event_sources, source);
371
372   return source;
373 }
374
375 void
376 gdk_event_source_add_translator (GdkEventSource     *source,
377                                  GdkEventTranslator *translator)
378 {
379   g_return_if_fail (GDK_IS_EVENT_TRANSLATOR (translator));
380
381   source->translators = g_list_append (source->translators, translator);
382 }
383
384 void
385 gdk_event_source_select_events (GdkEventSource *source,
386                                 Window          window,
387                                 GdkEventMask    event_mask,
388                                 unsigned int    extra_x_mask)
389 {
390   unsigned int xmask = extra_x_mask;
391   GList *list;
392   gint i;
393
394   list = source->translators;
395
396   while (list)
397     {
398       GdkEventTranslator *translator = list->data;
399       GdkEventMask translator_mask, mask;
400
401       translator_mask = gdk_event_translator_get_handled_events (translator);
402       mask = event_mask & translator_mask;
403
404       if (mask != 0)
405         {
406           gdk_event_translator_select_window_events (translator, window, mask);
407           event_mask &= ~mask;
408         }
409
410       list = list->next;
411     }
412
413   for (i = 0; i < _gdk_nenvent_masks; i++)
414     {
415       if (event_mask & (1 << (i + 1)))
416         xmask |= _gdk_event_mask_table[i];
417     }
418
419   XSelectInput (GDK_DISPLAY_XDISPLAY (source->display), window, xmask);
420 }
421
422 /**
423  * gdk_events_pending:
424  *
425  * Checks if any events are ready to be processed for any display.
426  *
427  * Return value:  %TRUE if any events are pending.
428  **/
429 gboolean
430 gdk_events_pending (void)
431 {
432   GList *tmp_list;
433
434   for (tmp_list = event_sources; tmp_list; tmp_list = tmp_list->next)
435     {
436       GdkEventSource *tmp_source = tmp_list->data;
437       GdkDisplay *display = tmp_source->display;
438
439       if (_gdk_event_queue_find_first (display))
440         return TRUE;
441     }
442
443   for (tmp_list = event_sources; tmp_list; tmp_list = tmp_list->next)
444     {
445       GdkEventSource *tmp_source = tmp_list->data;
446       GdkDisplay *display = tmp_source->display;
447
448       if (gdk_check_xpending (display))
449         return TRUE;
450     }
451
452   return FALSE;
453 }