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