]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkeventsource.c
gdk: Add _gdk_x11_event_translator_get_window()
[~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   /* Get cookie data here so it's available
181    * to every event translator and event filter.
182    */
183   if (xevent->type == GenericEvent)
184     XGetEventData (dpy, &xevent->xcookie);
185
186   filter_window = gdk_event_source_get_filter_window (event_source, xevent,
187                                                       &event_translator);
188   if (filter_window)
189     event->any.window = g_object_ref (filter_window);
190
191   /* Run default filters */
192   if (_gdk_default_filters)
193     {
194       /* Apply global filters */
195       result = gdk_event_apply_filters (xevent, event, NULL);
196     }
197
198   if (result == GDK_FILTER_CONTINUE &&
199       filter_window && filter_window->filters)
200     {
201       /* Apply per-window filters */
202       result = gdk_event_apply_filters (xevent, event, filter_window);
203     }
204
205   if (result != GDK_FILTER_CONTINUE)
206     {
207       if (xevent->type == GenericEvent)
208         XFreeEventData (dpy, &xevent->xcookie);
209
210       if (result == GDK_FILTER_REMOVE)
211         {
212           gdk_event_free (event);
213           return NULL;
214         }
215       else /* GDK_FILTER_TRANSLATE */
216         return event;
217     }
218
219   gdk_event_free (event);
220   event = NULL;
221
222   if (event_translator)
223     {
224       /* Event translator was gotten before in get_filter_window() */
225       event = _gdk_x11_event_translator_translate (event_translator,
226                                                    event_source->display,
227                                                    xevent);
228     }
229   else
230     {
231       GList *list = event_source->translators;
232
233       while (list && !event)
234         {
235           GdkEventTranslator *translator = list->data;
236
237           list = list->next;
238           event = _gdk_x11_event_translator_translate (translator,
239                                                        event_source->display,
240                                                        xevent);
241         }
242     }
243
244   if (event &&
245       (event->type == GDK_ENTER_NOTIFY ||
246        event->type == GDK_LEAVE_NOTIFY) &&
247       event->crossing.window != NULL)
248     {
249       /* Handle focusing (in the case where no window manager is running */
250       handle_focus_change (&event->crossing);
251     }
252
253   if (xevent->type == GenericEvent)
254     XFreeEventData (dpy, &xevent->xcookie);
255
256   return event;
257 }
258
259 static gboolean
260 gdk_check_xpending (GdkDisplay *display)
261 {
262   return XPending (GDK_DISPLAY_XDISPLAY (display));
263 }
264
265 static gboolean
266 gdk_event_source_prepare (GSource *source,
267                           gint    *timeout)
268 {
269   GdkDisplay *display = ((GdkEventSource*) source)->display;
270   gboolean retval;
271
272   GDK_THREADS_ENTER ();
273
274   *timeout = -1;
275   retval = (_gdk_event_queue_find_first (display) != NULL ||
276             gdk_check_xpending (display));
277
278   GDK_THREADS_LEAVE ();
279
280   return retval;
281 }
282
283 static gboolean
284 gdk_event_source_check (GSource *source)
285 {
286   GdkEventSource *event_source = (GdkEventSource*) source;
287   gboolean retval;
288
289   GDK_THREADS_ENTER ();
290
291   if (event_source->event_poll_fd.revents & G_IO_IN)
292     retval = (_gdk_event_queue_find_first (event_source->display) != NULL ||
293               gdk_check_xpending (event_source->display));
294   else
295     retval = FALSE;
296
297   GDK_THREADS_LEAVE ();
298
299   return retval;
300 }
301
302 void
303 _gdk_x11_display_queue_events (GdkDisplay *display)
304 {
305   GdkEvent *event;
306   XEvent xevent;
307   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
308   GdkEventSource *event_source;
309   GdkX11Display *display_x11;
310
311   display_x11 = GDK_X11_DISPLAY (display);
312   event_source = (GdkEventSource *) display_x11->event_source;
313
314   while (!_gdk_event_queue_find_first (display) && XPending (xdisplay))
315     {
316       XNextEvent (xdisplay, &xevent);
317
318       switch (xevent.type)
319         {
320         case KeyPress:
321         case KeyRelease:
322           break;
323         default:
324           if (XFilterEvent (&xevent, None))
325             continue;
326         }
327
328       event = gdk_event_source_translate_event (event_source, &xevent);
329
330       if (event)
331         {
332           GList *node;
333
334           node = _gdk_event_queue_append (display, event);
335           _gdk_windowing_got_event (display, node, event, xevent.xany.serial);
336         }
337     }
338 }
339
340 static gboolean
341 gdk_event_source_dispatch (GSource     *source,
342                            GSourceFunc  callback,
343                            gpointer     user_data)
344 {
345   GdkDisplay *display = ((GdkEventSource*) source)->display;
346   GdkEvent *event;
347
348   GDK_THREADS_ENTER ();
349
350   event = gdk_display_get_event (display);
351
352   if (event)
353     {
354       _gdk_event_emit (event);
355
356       gdk_event_free (event);
357     }
358
359   GDK_THREADS_LEAVE ();
360
361   return TRUE;
362 }
363
364 static void
365 gdk_event_source_finalize (GSource *source)
366 {
367   GdkEventSource *event_source = (GdkEventSource *)source;
368
369   g_list_free (event_source->translators);
370   event_source->translators = NULL;
371
372   event_sources = g_list_remove (event_sources, source);
373 }
374
375 GSource *
376 gdk_x11_event_source_new (GdkDisplay *display)
377 {
378   GSource *source;
379   GdkEventSource *event_source;
380   GdkX11Display *display_x11;
381   int connection_number;
382   char *name;
383
384   source = g_source_new (&event_funcs, sizeof (GdkEventSource));
385   name = g_strdup_printf ("GDK X11 Event source (%s)",
386                           gdk_display_get_name (display));
387   g_source_set_name (source, name);
388   g_free (name);
389   event_source = (GdkEventSource *) source;
390   event_source->display = display;
391
392   display_x11 = GDK_X11_DISPLAY (display);
393   connection_number = ConnectionNumber (display_x11->xdisplay);
394
395   event_source->event_poll_fd.fd = connection_number;
396   event_source->event_poll_fd.events = G_IO_IN;
397   g_source_add_poll (source, &event_source->event_poll_fd);
398
399   g_source_set_priority (source, GDK_PRIORITY_EVENTS);
400   g_source_set_can_recurse (source, TRUE);
401   g_source_attach (source, NULL);
402
403   event_sources = g_list_prepend (event_sources, source);
404
405   return source;
406 }
407
408 void
409 gdk_x11_event_source_add_translator (GdkEventSource     *source,
410                                      GdkEventTranslator *translator)
411 {
412   g_return_if_fail (GDK_IS_EVENT_TRANSLATOR (translator));
413
414   source->translators = g_list_append (source->translators, translator);
415 }
416
417 void
418 gdk_x11_event_source_select_events (GdkEventSource *source,
419                                     Window          window,
420                                     GdkEventMask    event_mask,
421                                     unsigned int    extra_x_mask)
422 {
423   unsigned int xmask = extra_x_mask;
424   GList *list;
425   gint i;
426
427   list = source->translators;
428
429   while (list)
430     {
431       GdkEventTranslator *translator = list->data;
432       GdkEventMask translator_mask, mask;
433
434       translator_mask = _gdk_x11_event_translator_get_handled_events (translator);
435       mask = event_mask & translator_mask;
436
437       if (mask != 0)
438         {
439           _gdk_x11_event_translator_select_window_events (translator, window, mask);
440           event_mask &= ~mask;
441         }
442
443       list = list->next;
444     }
445
446   for (i = 0; i < _gdk_x11_event_mask_table_size; i++)
447     {
448       if (event_mask & (1 << (i + 1)))
449         xmask |= _gdk_x11_event_mask_table[i];
450     }
451
452   XSelectInput (GDK_DISPLAY_XDISPLAY (source->display), window, xmask);
453 }