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