1 /* Clutter.
2 * An OpenGL based 'interactive canvas' library.
3 *
4 * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd
5 * Copyright (C) 2009, 2010 Intel Corp.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 *
20 *
21 *
22 * Authored by:
23 * Matthew Allum <mallum@openedhand.com>
24 * Emmanuele Bassi <ebassi@linux.intel.com>
25 */
26
27 #include "config.h"
28
29 #include "clutter-backend-x11.h"
30 #include "clutter-x11.h"
31
32 #include "clutter-backend-private.h"
33 #include "clutter-debug.h"
34 #include "clutter-event-private.h"
35 #include "clutter-main.h"
36 #include "clutter-private.h"
37 #include "clutter-stage-private.h"
38
39 #include <string.h>
40
41 #include <glib.h>
42
43 #if 0
44 /* XEMBED protocol support for toolkit embedding */
45 #define XEMBED_MAPPED (1 << 0)
46 #define MAX_SUPPORTED_XEMBED_VERSION 1
47
48 #define XEMBED_EMBEDDED_NOTIFY 0
49 #define XEMBED_WINDOW_ACTIVATE 1
50 #define XEMBED_WINDOW_DEACTIVATE 2
51 #define XEMBED_REQUEST_FOCUS 3
52 #define XEMBED_FOCUS_IN 4
53 #define XEMBED_FOCUS_OUT 5
54 #define XEMBED_FOCUS_NEXT 6
55 #define XEMBED_FOCUS_PREV 7
56 /* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */
57 #define XEMBED_MODALITY_ON 10
58 #define XEMBED_MODALITY_OFF 11
59 #define XEMBED_REGISTER_ACCELERATOR 12
60 #define XEMBED_UNREGISTER_ACCELERATOR 13
61 #define XEMBED_ACTIVATE_ACCELERATOR 14
62
63 static Window ParentEmbedderWin = None;
64 #endif
65
66 typedef struct _ClutterEventSource ClutterEventSource;
67
68 struct _ClutterEventSource
69 {
70 GSource source;
71
72 ClutterBackendX11 *backend;
73
74 GPollFD event_poll_fd;
75 };
76
77 ClutterEventX11 *
_clutter_event_x11_new(void)78 _clutter_event_x11_new (void)
79 {
80 return g_slice_new0 (ClutterEventX11);
81 }
82
83 ClutterEventX11 *
_clutter_event_x11_copy(ClutterEventX11 * event_x11)84 _clutter_event_x11_copy (ClutterEventX11 *event_x11)
85 {
86 if (event_x11 != NULL)
87 return g_slice_dup (ClutterEventX11, event_x11);
88
89 return NULL;
90 }
91
92 void
_clutter_event_x11_free(ClutterEventX11 * event_x11)93 _clutter_event_x11_free (ClutterEventX11 *event_x11)
94 {
95 if (event_x11 != NULL)
96 g_slice_free (ClutterEventX11, event_x11);
97 }
98
99 static gboolean clutter_event_prepare (GSource *source,
100 gint *timeout);
101 static gboolean clutter_event_check (GSource *source);
102 static gboolean clutter_event_dispatch (GSource *source,
103 GSourceFunc callback,
104 gpointer user_data);
105
106 static GSourceFuncs event_funcs = {
107 clutter_event_prepare,
108 clutter_event_check,
109 clutter_event_dispatch,
110 NULL
111 };
112
113 GSource *
_clutter_x11_event_source_new(ClutterBackendX11 * backend_x11)114 _clutter_x11_event_source_new (ClutterBackendX11 *backend_x11)
115 {
116 ClutterEventSource *event_source;
117 int connection_number;
118 GSource *source;
119 gchar *name;
120
121 connection_number = ConnectionNumber (backend_x11->xdpy);
122 CLUTTER_NOTE (EVENT, "Connection number: %d", connection_number);
123
124 source = g_source_new (&event_funcs, sizeof (ClutterEventSource));
125 event_source = (ClutterEventSource *) source;
126
127 name = g_strdup_printf ("Clutter X11 Event (connection: %d)",
128 connection_number);
129 g_source_set_name (source, name);
130 g_free (name);
131
132 event_source->backend = backend_x11;
133 event_source->event_poll_fd.fd = connection_number;
134 event_source->event_poll_fd.events = G_IO_IN;
135
136 g_source_add_poll (source, &event_source->event_poll_fd);
137 g_source_set_can_recurse (source, TRUE);
138
139 return source;
140 }
141
142 /**
143 * clutter_x11_handle_event:
144 * @xevent: pointer to XEvent structure
145 *
146 * This function processes a single X event; it can be used to hook
147 * into external X11 event processing (for example, a GDK filter
148 * function).
149 *
150 * If clutter_x11_disable_event_retrieval() has been called, you must
151 * let this function process events to update Clutter's internal state.
152 *
153 * Return value: #ClutterX11FilterReturn. %CLUTTER_X11_FILTER_REMOVE
154 * indicates that Clutter has internally handled the event and the
155 * caller should do no further processing. %CLUTTER_X11_FILTER_CONTINUE
156 * indicates that Clutter is either not interested in the event,
157 * or has used the event to update internal state without taking
158 * any exclusive action. %CLUTTER_X11_FILTER_TRANSLATE will not
159 * occur.
160 *
161 * Since: 0.8
162 */
163 ClutterX11FilterReturn
clutter_x11_handle_event(XEvent * xevent)164 clutter_x11_handle_event (XEvent *xevent)
165 {
166 ClutterX11FilterReturn result;
167 ClutterBackend *backend;
168 ClutterEvent *event;
169 gint spin = 1;
170 #ifdef HAVE_XGE
171 ClutterBackendX11 *backend_x11;
172 Display *xdisplay;
173 gboolean allocated_event;
174 #endif
175
176 /* The return values here are someone approximate; we return
177 * CLUTTER_X11_FILTER_REMOVE if a clutter event is
178 * generated for the event. This mostly, but not entirely,
179 * corresponds to whether other event processing should be
180 * excluded. As long as the stage window is not shared with another
181 * toolkit it should be safe, and never return
182 * %CLUTTER_X11_FILTER_REMOVE when more processing is needed.
183 */
184
185 result = CLUTTER_X11_FILTER_CONTINUE;
186
187 _clutter_threads_acquire_lock ();
188
189 backend = clutter_get_default_backend ();
190
191 event = clutter_event_new (CLUTTER_NOTHING);
192
193 #ifdef HAVE_XGE
194 backend_x11 = CLUTTER_BACKEND_X11 (backend);
195 xdisplay = backend_x11->xdpy;
196
197 allocated_event = XGetEventData (xdisplay, &xevent->xcookie);
198 #endif
199
200 if (_clutter_backend_translate_event (backend, xevent, event))
201 {
202 _clutter_event_push (event, FALSE);
203
204 result = CLUTTER_X11_FILTER_REMOVE;
205 }
206 else
207 {
208 clutter_event_free (event);
209 goto out;
210 }
211
212 /*
213 * Motion events can generate synthetic enter and leave events, so if we
214 * are processing a motion event, we need to spin the event loop at least
215 * two extra times to pump the enter/leave events through (otherwise they
216 * just get pushed down the queue and never processed).
217 */
218 if (event->type == CLUTTER_MOTION)
219 spin += 2;
220
221 while (spin > 0 && (event = clutter_event_get ()))
222 {
223 /* forward the event into clutter for emission etc. */
224 _clutter_stage_queue_event (event->any.stage, event, FALSE);
225 --spin;
226 }
227
228 out:
229 #ifdef HAVE_XGE
230 if (allocated_event)
231 XFreeEventData (xdisplay, &xevent->xcookie);
232 #endif
233
234 _clutter_threads_release_lock ();
235
236 return result;
237 }
238
239 static gboolean
clutter_event_prepare(GSource * source,gint * timeout)240 clutter_event_prepare (GSource *source,
241 gint *timeout)
242 {
243 ClutterBackendX11 *backend = ((ClutterEventSource *) source)->backend;
244 gboolean retval;
245
246 _clutter_threads_acquire_lock ();
247
248 *timeout = -1;
249 retval = (clutter_events_pending () || XPending (backend->xdpy));
250
251 _clutter_threads_release_lock ();
252
253 return retval;
254 }
255
256 static gboolean
clutter_event_check(GSource * source)257 clutter_event_check (GSource *source)
258 {
259 ClutterEventSource *event_source = (ClutterEventSource *) source;
260 ClutterBackendX11 *backend = event_source->backend;
261 gboolean retval;
262
263 _clutter_threads_acquire_lock ();
264
265 if (event_source->event_poll_fd.revents & G_IO_IN)
266 retval = (clutter_events_pending () || XPending (backend->xdpy));
267 else
268 retval = FALSE;
269
270 _clutter_threads_release_lock ();
271
272 return retval;
273 }
274
275 static void
events_queue(ClutterBackendX11 * backend_x11)276 events_queue (ClutterBackendX11 *backend_x11)
277 {
278 ClutterBackend *backend = CLUTTER_BACKEND (backend_x11);
279 Display *xdisplay = backend_x11->xdpy;
280 ClutterEvent *event;
281 XEvent xevent;
282
283 while (!clutter_events_pending () && XPending (xdisplay))
284 {
285 XNextEvent (xdisplay, &xevent);
286
287 event = clutter_event_new (CLUTTER_NOTHING);
288
289 #ifdef HAVE_XGE
290 XGetEventData (xdisplay, &xevent.xcookie);
291 #endif
292
293 if (_clutter_backend_translate_event (backend, &xevent, event))
294 _clutter_event_push (event, FALSE);
295 else
296 clutter_event_free (event);
297
298 #ifdef HAVE_XGE
299 XFreeEventData (xdisplay, &xevent.xcookie);
300 #endif
301 }
302 }
303
304 static gboolean
clutter_event_dispatch(GSource * source,GSourceFunc callback,gpointer user_data)305 clutter_event_dispatch (GSource *source,
306 GSourceFunc callback,
307 gpointer user_data)
308 {
309 ClutterBackendX11 *backend = ((ClutterEventSource *) source)->backend;
310 ClutterEvent *event;
311
312 _clutter_threads_acquire_lock ();
313
314 /* Grab the event(s), translate and figure out double click.
315 * The push onto queue (stack) if valid.
316 */
317 events_queue (backend);
318
319 /* Pop an event off the queue if any */
320 event = clutter_event_get ();
321 if (event != NULL)
322 {
323 /* forward the event into clutter for emission etc. */
324 _clutter_stage_queue_event (event->any.stage, event, FALSE);
325 }
326
327 _clutter_threads_release_lock ();
328
329 return TRUE;
330 }
331
332 /**
333 * clutter_x11_get_current_event_time: (skip)
334 *
335 * Retrieves the timestamp of the last X11 event processed by
336 * Clutter. This might be different from the timestamp returned
337 * by clutter_get_current_event_time(), as Clutter may synthesize
338 * or throttle events.
339 *
340 * Return value: a timestamp, in milliseconds
341 *
342 * Since: 1.0
343 */
344 Time
clutter_x11_get_current_event_time(void)345 clutter_x11_get_current_event_time (void)
346 {
347 ClutterBackend *backend = clutter_get_default_backend ();
348
349 return CLUTTER_BACKEND_X11 (backend)->last_event_time;
350 }
351
352 /**
353 * clutter_x11_event_get_key_group:
354 * @event: a #ClutterEvent of type %CLUTTER_KEY_PRESS or %CLUTTER_KEY_RELEASE
355 *
356 * Retrieves the group for the modifiers set in @event
357 *
358 * Return value: the group id
359 *
360 * Since: 1.4
361 */
362 gint
clutter_x11_event_get_key_group(const ClutterEvent * event)363 clutter_x11_event_get_key_group (const ClutterEvent *event)
364 {
365 ClutterEventX11 *event_x11;
366
367 g_return_val_if_fail (event != NULL, 0);
368 g_return_val_if_fail (event->type == CLUTTER_KEY_PRESS ||
369 event->type == CLUTTER_KEY_RELEASE, 0);
370
371 event_x11 = _clutter_event_get_platform_data (event);
372 if (event_x11 == NULL)
373 return 0;
374
375 return event_x11->key_group;
376 }
377
378 /**
379 * clutter_x11_event_sequence_get_touch_detail:
380 * @sequence: a #ClutterEventSequence
381 *
382 * Retrieves the touch detail froma #ClutterEventSequence.
383 *
384 * Return value: the touch detail
385 *
386 * Since: 1.12
387 */
388 guint
clutter_x11_event_sequence_get_touch_detail(const ClutterEventSequence * sequence)389 clutter_x11_event_sequence_get_touch_detail (const ClutterEventSequence *sequence)
390 {
391 g_return_val_if_fail (sequence != NULL, 0);
392
393 return GPOINTER_TO_UINT (sequence);
394 }
395