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 "gdk/gdkeventsprivate.h" |
23 | |
24 | #include "gdksurface-x11.h" |
25 | #include "gdkprivate-x11.h" |
26 | #include "gdkdisplay-x11.h" |
27 | #include "xsettings-client.h" |
28 | |
29 | |
30 | static gboolean gdk_event_source_prepare (GSource *source, |
31 | int *timeout); |
32 | static gboolean gdk_event_source_check (GSource *source); |
33 | static gboolean gdk_event_source_dispatch (GSource *source, |
34 | GSourceFunc callback, |
35 | gpointer user_data); |
36 | static void gdk_event_source_finalize (GSource *source); |
37 | |
38 | static GQuark quark_needs_enter = 0; |
39 | |
40 | #define HAS_FOCUS(toplevel) \ |
41 | ((toplevel)->has_focus || (toplevel)->has_pointer_focus) |
42 | |
43 | struct _GdkEventSource |
44 | { |
45 | GSource source; |
46 | |
47 | GdkDisplay *display; |
48 | GPollFD event_poll_fd; |
49 | GList *translators; |
50 | }; |
51 | |
52 | static GSourceFuncs event_funcs = { |
53 | gdk_event_source_prepare, |
54 | gdk_event_source_check, |
55 | gdk_event_source_dispatch, |
56 | gdk_event_source_finalize |
57 | }; |
58 | |
59 | static GdkSurface * |
60 | gdk_event_source_get_filter_surface (GdkEventSource *event_source, |
61 | const XEvent *xevent, |
62 | GdkEventTranslator **event_translator) |
63 | { |
64 | GList *list = event_source->translators; |
65 | GdkSurface *surface; |
66 | |
67 | *event_translator = NULL; |
68 | |
69 | while (list) |
70 | { |
71 | GdkEventTranslator *translator = list->data; |
72 | |
73 | list = list->next; |
74 | surface = _gdk_x11_event_translator_get_surface (translator, |
75 | display: event_source->display, |
76 | xevent); |
77 | if (surface) |
78 | { |
79 | *event_translator = translator; |
80 | return surface; |
81 | } |
82 | } |
83 | |
84 | surface = gdk_x11_surface_lookup_for_display (display: event_source->display, |
85 | window: xevent->xany.window); |
86 | |
87 | return surface; |
88 | } |
89 | |
90 | static void |
91 | handle_focus_change (GdkEvent *event) |
92 | { |
93 | GdkToplevelX11 *toplevel; |
94 | GdkX11Screen *x11_screen; |
95 | gboolean focus_in, had_focus; |
96 | |
97 | toplevel = _gdk_x11_surface_get_toplevel (window: gdk_event_get_surface (event)); |
98 | x11_screen = GDK_X11_SCREEN (GDK_SURFACE_SCREEN (gdk_event_get_surface (event))); |
99 | focus_in = (gdk_event_get_event_type (event) == GDK_ENTER_NOTIFY); |
100 | |
101 | if (x11_screen->wmspec_check_window) |
102 | return; |
103 | |
104 | if (!toplevel || gdk_crossing_event_get_detail (event) == GDK_NOTIFY_INFERIOR) |
105 | return; |
106 | |
107 | toplevel->has_pointer = focus_in; |
108 | |
109 | if (!gdk_crossing_event_get_focus (event) || toplevel->has_focus_window) |
110 | return; |
111 | |
112 | had_focus = HAS_FOCUS (toplevel); |
113 | toplevel->has_pointer_focus = focus_in; |
114 | |
115 | if (HAS_FOCUS (toplevel) != had_focus) |
116 | { |
117 | GdkEvent *focus_event; |
118 | |
119 | focus_event = gdk_focus_event_new (surface: gdk_event_get_surface (event), |
120 | device: gdk_event_get_device (event), |
121 | focus_in); |
122 | gdk_display_put_event (display: gdk_event_get_display (event), event: focus_event); |
123 | gdk_event_unref (event: focus_event); |
124 | } |
125 | } |
126 | |
127 | static GdkEvent * |
128 | create_synth_crossing_event (GdkEventType evtype, |
129 | GdkCrossingMode mode, |
130 | GdkEvent *real_event) |
131 | { |
132 | GdkEvent *event; |
133 | double x, y; |
134 | |
135 | g_assert (evtype == GDK_ENTER_NOTIFY || evtype == GDK_LEAVE_NOTIFY); |
136 | |
137 | gdk_event_get_position (event: real_event, x: &x, y: &y); |
138 | event = gdk_crossing_event_new (type: evtype, |
139 | surface: gdk_event_get_surface (event: real_event), |
140 | device: gdk_event_get_device (event: real_event), |
141 | time: gdk_event_get_time (event: real_event), |
142 | state: gdk_event_get_modifier_state (event: real_event), |
143 | x, y, |
144 | mode, |
145 | notify: GDK_NOTIFY_ANCESTOR); |
146 | |
147 | return event; |
148 | } |
149 | |
150 | static void |
151 | handle_touch_synthetic_crossing (GdkEvent *event) |
152 | { |
153 | GdkEventType evtype = gdk_event_get_event_type (event); |
154 | GdkEvent *crossing = NULL; |
155 | GdkSeat *seat = gdk_event_get_seat (event); |
156 | gboolean needs_enter, set_needs_enter = FALSE; |
157 | |
158 | if (quark_needs_enter == 0) |
159 | quark_needs_enter = g_quark_from_static_string (string: "gdk-x11-needs-enter-after-touch-end" ); |
160 | |
161 | needs_enter = |
162 | GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (seat), quark_needs_enter)); |
163 | |
164 | if (evtype == GDK_MOTION_NOTIFY && needs_enter) |
165 | { |
166 | set_needs_enter = FALSE; |
167 | crossing = create_synth_crossing_event (evtype: GDK_ENTER_NOTIFY, |
168 | mode: GDK_CROSSING_DEVICE_SWITCH, |
169 | real_event: event); |
170 | } |
171 | else if (evtype == GDK_TOUCH_BEGIN && needs_enter && |
172 | gdk_event_get_pointer_emulated (event)) |
173 | { |
174 | set_needs_enter = FALSE; |
175 | crossing = create_synth_crossing_event (evtype: GDK_ENTER_NOTIFY, |
176 | mode: GDK_CROSSING_TOUCH_BEGIN, |
177 | real_event: event); |
178 | } |
179 | else if (evtype == GDK_TOUCH_END && |
180 | gdk_event_get_pointer_emulated (event)) |
181 | { |
182 | set_needs_enter = TRUE; |
183 | crossing = create_synth_crossing_event (evtype: GDK_LEAVE_NOTIFY, |
184 | mode: GDK_CROSSING_TOUCH_END, |
185 | real_event: event); |
186 | } |
187 | else if (evtype == GDK_ENTER_NOTIFY || |
188 | evtype == GDK_LEAVE_NOTIFY) |
189 | { |
190 | /* We are receiving or shall receive a real crossing event, |
191 | * turn this off. |
192 | */ |
193 | set_needs_enter = FALSE; |
194 | } |
195 | else |
196 | return; |
197 | |
198 | if (needs_enter != set_needs_enter) |
199 | { |
200 | if (!set_needs_enter) |
201 | g_object_steal_qdata (G_OBJECT (seat), quark: quark_needs_enter); |
202 | else |
203 | g_object_set_qdata (G_OBJECT (seat), quark: quark_needs_enter, |
204 | GUINT_TO_POINTER (TRUE)); |
205 | } |
206 | |
207 | if (crossing) |
208 | { |
209 | gdk_display_put_event (display: gdk_seat_get_display (seat), event: crossing); |
210 | gdk_event_unref (event: crossing); |
211 | } |
212 | } |
213 | |
214 | static GdkEvent * |
215 | gdk_event_source_translate_event (GdkX11Display *x11_display, |
216 | const XEvent *xevent) |
217 | { |
218 | GdkEventSource *event_source = (GdkEventSource *) x11_display->event_source; |
219 | GdkEvent *event; |
220 | GdkFilterReturn result = GDK_FILTER_CONTINUE; |
221 | GdkDisplay *display = GDK_DISPLAY (x11_display); |
222 | GdkEventTranslator *event_translator; |
223 | GdkSurface *filter_surface; |
224 | Display *dpy; |
225 | GdkX11Screen *x11_screen; |
226 | gpointer cache; |
227 | |
228 | x11_screen = GDK_X11_DISPLAY (display)->screen; |
229 | dpy = GDK_DISPLAY_XDISPLAY (display); |
230 | |
231 | event = NULL; |
232 | filter_surface = gdk_event_source_get_filter_surface (event_source, xevent, |
233 | event_translator: &event_translator); |
234 | |
235 | /* apply XSettings filters */ |
236 | if (xevent->xany.window == XRootWindow (dpy, 0)) |
237 | result = gdk_xsettings_root_window_filter (xevent, data: x11_screen); |
238 | |
239 | if (result == GDK_FILTER_CONTINUE && |
240 | xevent->xany.window == x11_screen->xsettings_manager_window) |
241 | result = gdk_xsettings_manager_window_filter (xevent, data: x11_screen); |
242 | |
243 | cache = gdk_surface_cache_get (display); |
244 | if (cache) |
245 | { |
246 | if (result == GDK_FILTER_CONTINUE) |
247 | result = gdk_surface_cache_shape_filter (xevent, data: cache); |
248 | |
249 | if (result == GDK_FILTER_CONTINUE && |
250 | xevent->xany.window == XRootWindow (dpy, 0)) |
251 | result = gdk_surface_cache_filter (xevent, data: cache); |
252 | } |
253 | |
254 | if (result == GDK_FILTER_CONTINUE) |
255 | result = _gdk_wm_protocols_filter (xevent, win: filter_surface, event: &event, NULL); |
256 | |
257 | if (result == GDK_FILTER_CONTINUE && |
258 | gdk_x11_drop_filter (surface: filter_surface, xevent)) |
259 | result = GDK_FILTER_REMOVE; |
260 | |
261 | if (result != GDK_FILTER_CONTINUE) |
262 | { |
263 | if (result == GDK_FILTER_REMOVE) |
264 | return NULL; |
265 | else /* GDK_FILTER_TRANSLATE */ |
266 | return event; |
267 | } |
268 | |
269 | if (event_translator) |
270 | { |
271 | /* Event translator was gotten before in get_filter_window() */ |
272 | event = _gdk_x11_event_translator_translate (translator: event_translator, |
273 | display, |
274 | xevent); |
275 | } |
276 | else |
277 | { |
278 | GList *list = event_source->translators; |
279 | |
280 | while (list && !event) |
281 | { |
282 | GdkEventTranslator *translator = list->data; |
283 | |
284 | list = list->next; |
285 | event = _gdk_x11_event_translator_translate (translator, |
286 | display, |
287 | xevent); |
288 | } |
289 | } |
290 | |
291 | if (event) |
292 | { |
293 | GdkEventType evtype = gdk_event_get_event_type (event); |
294 | |
295 | if ((evtype == GDK_ENTER_NOTIFY || |
296 | evtype == GDK_LEAVE_NOTIFY) && |
297 | gdk_event_get_surface (event) != NULL) |
298 | { |
299 | /* Handle focusing (in the case where no window manager is running */ |
300 | handle_focus_change (event); |
301 | } |
302 | |
303 | if (evtype == GDK_TOUCH_BEGIN || |
304 | evtype == GDK_TOUCH_END || |
305 | evtype == GDK_MOTION_NOTIFY || |
306 | evtype == GDK_ENTER_NOTIFY || |
307 | evtype == GDK_LEAVE_NOTIFY) |
308 | { |
309 | handle_touch_synthetic_crossing (event); |
310 | } |
311 | } |
312 | |
313 | return event; |
314 | } |
315 | |
316 | gboolean |
317 | gdk_event_source_xevent (GdkX11Display *x11_display, |
318 | const XEvent *xevent) |
319 | { |
320 | GdkDisplay *display = GDK_DISPLAY (x11_display); |
321 | GdkEvent *event; |
322 | GList *node; |
323 | |
324 | event = gdk_event_source_translate_event (x11_display, xevent); |
325 | if (event == NULL) |
326 | return FALSE; |
327 | |
328 | node = _gdk_event_queue_append (display, event); |
329 | _gdk_windowing_got_event (display, event_link: node, event, serial: xevent->xany.serial); |
330 | |
331 | return TRUE; |
332 | } |
333 | |
334 | static gboolean |
335 | gdk_check_xpending (GdkDisplay *display) |
336 | { |
337 | return XPending (GDK_DISPLAY_XDISPLAY (display)); |
338 | } |
339 | |
340 | static gboolean |
341 | gdk_event_source_prepare (GSource *source, |
342 | int *timeout) |
343 | { |
344 | GdkDisplay *display = ((GdkEventSource*) source)->display; |
345 | gboolean retval; |
346 | |
347 | *timeout = -1; |
348 | |
349 | if (display->event_pause_count > 0) |
350 | retval = _gdk_event_queue_find_first (display) != NULL; |
351 | else |
352 | retval = (_gdk_event_queue_find_first (display) != NULL || |
353 | gdk_check_xpending (display)); |
354 | |
355 | return retval; |
356 | } |
357 | |
358 | static gboolean |
359 | gdk_event_source_check (GSource *source) |
360 | { |
361 | GdkEventSource *event_source = (GdkEventSource*) source; |
362 | gboolean retval; |
363 | |
364 | if (event_source->display->event_pause_count > 0) |
365 | retval = _gdk_event_queue_find_first (display: event_source->display) != NULL; |
366 | else if (event_source->event_poll_fd.revents & G_IO_IN) |
367 | retval = (_gdk_event_queue_find_first (display: event_source->display) != NULL || |
368 | gdk_check_xpending (display: event_source->display)); |
369 | else |
370 | retval = FALSE; |
371 | |
372 | return retval; |
373 | } |
374 | |
375 | void |
376 | _gdk_x11_display_queue_events (GdkDisplay *display) |
377 | { |
378 | Display *xdisplay = GDK_DISPLAY_XDISPLAY (display); |
379 | XEvent xevent; |
380 | gboolean unused; |
381 | |
382 | while (!_gdk_event_queue_find_first (display) && XPending (xdisplay)) |
383 | { |
384 | XNextEvent (xdisplay, &xevent); |
385 | |
386 | switch (xevent.type) |
387 | { |
388 | case KeyPress: |
389 | case KeyRelease: |
390 | break; |
391 | default: |
392 | if (XFilterEvent (&xevent, None)) |
393 | continue; |
394 | } |
395 | |
396 | #ifdef HAVE_XGENERICEVENTS |
397 | /* Get cookie data here so it's available |
398 | * to every event translator and event filter. |
399 | */ |
400 | if (xevent.type == GenericEvent) |
401 | XGetEventData (xdisplay, &xevent.xcookie); |
402 | #endif |
403 | |
404 | g_signal_emit_by_name (instance: display, detailed_signal: "xevent" , &xevent, &unused); |
405 | |
406 | #ifdef HAVE_XGENERICEVENTS |
407 | if (xevent.type == GenericEvent) |
408 | XFreeEventData (xdisplay, &xevent.xcookie); |
409 | #endif |
410 | } |
411 | } |
412 | |
413 | static gboolean |
414 | gdk_event_source_dispatch (GSource *source, |
415 | GSourceFunc callback, |
416 | gpointer user_data) |
417 | { |
418 | GdkDisplay *display = ((GdkEventSource*) source)->display; |
419 | GdkEvent *event; |
420 | |
421 | event = gdk_display_get_event (display); |
422 | |
423 | if (event) |
424 | { |
425 | _gdk_event_emit (event); |
426 | |
427 | gdk_event_unref (event); |
428 | } |
429 | |
430 | return TRUE; |
431 | } |
432 | |
433 | static void |
434 | gdk_event_source_finalize (GSource *source) |
435 | { |
436 | GdkEventSource *event_source = (GdkEventSource *)source; |
437 | |
438 | g_list_free (list: event_source->translators); |
439 | event_source->translators = NULL; |
440 | } |
441 | |
442 | GSource * |
443 | gdk_x11_event_source_new (GdkDisplay *display) |
444 | { |
445 | GSource *source; |
446 | GdkEventSource *event_source; |
447 | GdkX11Display *display_x11; |
448 | int connection_number; |
449 | char *name; |
450 | |
451 | source = g_source_new (source_funcs: &event_funcs, struct_size: sizeof (GdkEventSource)); |
452 | name = g_strdup_printf (format: "GDK X11 Event source (%s)" , |
453 | gdk_display_get_name (display)); |
454 | g_source_set_name (source, name); |
455 | g_free (mem: name); |
456 | event_source = (GdkEventSource *) source; |
457 | event_source->display = display; |
458 | |
459 | display_x11 = GDK_X11_DISPLAY (display); |
460 | connection_number = ConnectionNumber (display_x11->xdisplay); |
461 | |
462 | event_source->event_poll_fd.fd = connection_number; |
463 | event_source->event_poll_fd.events = G_IO_IN; |
464 | g_source_add_poll (source, fd: &event_source->event_poll_fd); |
465 | |
466 | g_source_set_priority (source, GDK_PRIORITY_EVENTS); |
467 | g_source_set_can_recurse (source, TRUE); |
468 | g_source_attach (source, NULL); |
469 | |
470 | return source; |
471 | } |
472 | |
473 | void |
474 | gdk_x11_event_source_add_translator (GdkEventSource *source, |
475 | GdkEventTranslator *translator) |
476 | { |
477 | g_return_if_fail (GDK_IS_EVENT_TRANSLATOR (translator)); |
478 | |
479 | source->translators = g_list_append (list: source->translators, data: translator); |
480 | } |
481 | |
482 | void |
483 | gdk_x11_event_source_select_events (GdkEventSource *source, |
484 | Window window, |
485 | GdkEventMask event_mask, |
486 | unsigned int ) |
487 | { |
488 | unsigned int xmask = extra_x_mask; |
489 | GList *list; |
490 | int i; |
491 | |
492 | list = source->translators; |
493 | |
494 | while (list) |
495 | { |
496 | GdkEventTranslator *translator = list->data; |
497 | GdkEventMask translator_mask, mask; |
498 | |
499 | translator_mask = _gdk_x11_event_translator_get_handled_events (translator); |
500 | mask = event_mask & translator_mask; |
501 | |
502 | if (mask != 0) |
503 | { |
504 | _gdk_x11_event_translator_select_surface_events (translator, window, event_mask: mask); |
505 | event_mask &= ~mask; |
506 | } |
507 | |
508 | list = list->next; |
509 | } |
510 | |
511 | for (i = 0; i < _gdk_x11_event_mask_table_size; i++) |
512 | { |
513 | if (event_mask & (1 << (i + 1))) |
514 | xmask |= _gdk_x11_event_mask_table[i]; |
515 | } |
516 | |
517 | XSelectInput (GDK_DISPLAY_XDISPLAY (source->display), window, xmask); |
518 | } |
519 | |