1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /*
4 * Copyright (C) 2016 Red Hat
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 *
21 * Written by:
22 * Jonas Ådahl <jadahl@gmail.com>
23 */
24
25 #include "config.h"
26
27 #include <glib-object.h>
28
29 #include "backends/meta-backend-private.h"
30 #include "backends/meta-renderer.h"
31 #include "backends/x11/meta-clutter-backend-x11.h"
32 #include "backends/x11/meta-keymap-x11.h"
33 #include "backends/x11/meta-seat-x11.h"
34 #include "backends/x11/meta-xkb-a11y-x11.h"
35 #include "backends/x11/nested/meta-stage-x11-nested.h"
36 #include "clutter/clutter-mutter.h"
37 #include "clutter/clutter.h"
38 #include "cogl/cogl-xlib.h"
39 #include "core/bell.h"
40 #include "meta/meta-backend.h"
41
42 typedef struct _MetaX11EventFilter MetaX11EventFilter;
43
44 struct _MetaX11EventFilter
45 {
46 MetaX11FilterFunc func;
47 gpointer data;
48 };
49
50 G_DEFINE_TYPE (MetaClutterBackendX11, meta_clutter_backend_x11,
51 CLUTTER_TYPE_BACKEND)
52
53
54 /* atoms; remember to add the code that assigns the atom value to
55 * the member of the MetaClutterBackendX11 structure if you add an
56 * atom name here. do not change the order!
57 */
58 static const gchar *atom_names[] = {
59 "_NET_WM_PID",
60 "_NET_WM_PING",
61 "_NET_WM_STATE",
62 "_NET_WM_USER_TIME",
63 "WM_PROTOCOLS",
64 "WM_DELETE_WINDOW",
65 "_XEMBED",
66 "_XEMBED_INFO",
67 "_NET_WM_NAME",
68 "UTF8_STRING",
69 };
70
71 #define N_ATOM_NAMES G_N_ELEMENTS (atom_names)
72
73 /* various flags corresponding to pre init setup calls */
74 static gboolean clutter_enable_xinput = TRUE;
75 static gboolean clutter_enable_stereo = FALSE;
76 static Display *_foreign_dpy = NULL;
77
78 /* options */
79 static gchar *clutter_display_name = NULL;
80 static gint clutter_screen = -1;
81 static gboolean clutter_synchronise = FALSE;
82
83 /* X error trap */
84 static int TrappedErrorCode = 0;
85 static int (* old_error_handler) (Display *, XErrorEvent *);
86
87 static MetaX11FilterReturn
cogl_xlib_filter(XEvent * xevent,ClutterEvent * event,gpointer data)88 cogl_xlib_filter (XEvent *xevent,
89 ClutterEvent *event,
90 gpointer data)
91 {
92 ClutterBackend *backend = data;
93 MetaX11FilterReturn retval;
94 CoglFilterReturn ret;
95
96 ret = cogl_xlib_renderer_handle_event (backend->cogl_renderer, xevent);
97 switch (ret)
98 {
99 case COGL_FILTER_REMOVE:
100 retval = META_X11_FILTER_REMOVE;
101 break;
102
103 case COGL_FILTER_CONTINUE:
104 default:
105 retval = META_X11_FILTER_CONTINUE;
106 break;
107 }
108
109 return retval;
110 }
111
112 static gboolean
meta_clutter_backend_x11_pre_parse(ClutterBackend * backend,GError ** error)113 meta_clutter_backend_x11_pre_parse (ClutterBackend *backend,
114 GError **error)
115 {
116 const gchar *env_string;
117
118 /* we don't fail here if DISPLAY is not set, as the user
119 * might pass the --display command line switch
120 */
121 env_string = g_getenv ("DISPLAY");
122 if (env_string)
123 {
124 clutter_display_name = g_strdup (env_string);
125 env_string = NULL;
126 }
127
128 env_string = g_getenv ("CLUTTER_DISABLE_XINPUT");
129 if (env_string)
130 {
131 clutter_enable_xinput = FALSE;
132 env_string = NULL;
133 }
134
135 return TRUE;
136 }
137
138 static gboolean
meta_clutter_backend_x11_post_parse(ClutterBackend * backend,GError ** error)139 meta_clutter_backend_x11_post_parse (ClutterBackend *backend,
140 GError **error)
141 {
142 MetaClutterBackendX11 *backend_x11 = META_CLUTTER_BACKEND_X11 (backend);
143 Atom atoms[N_ATOM_NAMES];
144
145 if (_foreign_dpy)
146 backend_x11->xdisplay = _foreign_dpy;
147
148 /* Only open connection if not already set by prior call to
149 * clutter_x11_set_display()
150 */
151 if (backend_x11->xdisplay == NULL)
152 {
153 if (clutter_display_name != NULL &&
154 *clutter_display_name != '\0')
155 {
156 g_debug ("XOpenDisplay on '%s'", clutter_display_name);
157
158 backend_x11->xdisplay = XOpenDisplay (clutter_display_name);
159 if (backend_x11->xdisplay == NULL)
160 {
161 g_set_error (error, CLUTTER_INIT_ERROR,
162 CLUTTER_INIT_ERROR_BACKEND,
163 "Unable to open display '%s'",
164 clutter_display_name);
165 return FALSE;
166 }
167 }
168 else
169 {
170 g_set_error_literal (error, CLUTTER_INIT_ERROR,
171 CLUTTER_INIT_ERROR_BACKEND,
172 "Unable to open display. You have to set the "
173 "DISPLAY environment variable, or use the "
174 "--display command line argument");
175 return FALSE;
176 }
177 }
178
179 g_assert (backend_x11->xdisplay != NULL);
180
181 g_debug ("Getting the X screen");
182
183 /* add event filter for Cogl events */
184 meta_clutter_x11_add_filter (cogl_xlib_filter, backend);
185
186 if (clutter_screen == -1)
187 backend_x11->xscreen = DefaultScreenOfDisplay (backend_x11->xdisplay);
188 else
189 backend_x11->xscreen = ScreenOfDisplay (backend_x11->xdisplay,
190 clutter_screen);
191
192 backend_x11->xscreen_num = XScreenNumberOfScreen (backend_x11->xscreen);
193 backend_x11->xscreen_width = WidthOfScreen (backend_x11->xscreen);
194 backend_x11->xscreen_height = HeightOfScreen (backend_x11->xscreen);
195
196 backend_x11->xwin_root = RootWindow (backend_x11->xdisplay,
197 backend_x11->xscreen_num);
198
199 backend_x11->display_name = g_strdup (clutter_display_name);
200
201 if (clutter_synchronise)
202 XSynchronize (backend_x11->xdisplay, True);
203
204 XInternAtoms (backend_x11->xdisplay,
205 (char **) atom_names, N_ATOM_NAMES,
206 False, atoms);
207
208 backend_x11->atom_NET_WM_PID = atoms[0];
209 backend_x11->atom_NET_WM_PING = atoms[1];
210 backend_x11->atom_NET_WM_STATE = atoms[2];
211 backend_x11->atom_NET_WM_USER_TIME = atoms[3];
212 backend_x11->atom_WM_PROTOCOLS = atoms[4];
213 backend_x11->atom_WM_DELETE_WINDOW = atoms[5];
214 backend_x11->atom_XEMBED = atoms[6];
215 backend_x11->atom_XEMBED_INFO = atoms[7];
216 backend_x11->atom_NET_WM_NAME = atoms[8];
217 backend_x11->atom_UTF8_STRING = atoms[9];
218
219 g_free (clutter_display_name);
220
221 g_debug ("X Display '%s'[%p] opened (screen:%d, root:%u, dpi:%f)",
222 backend_x11->display_name,
223 backend_x11->xdisplay,
224 backend_x11->xscreen_num,
225 (unsigned int) backend_x11->xwin_root,
226 clutter_backend_get_resolution (backend));
227
228 return TRUE;
229 }
230
231 static const GOptionEntry entries[] =
232 {
233 {
234 "display", 0,
235 G_OPTION_FLAG_IN_MAIN,
236 G_OPTION_ARG_STRING, &clutter_display_name,
237 N_("X display to use"), "DISPLAY"
238 },
239 {
240 "screen", 0,
241 G_OPTION_FLAG_IN_MAIN,
242 G_OPTION_ARG_INT, &clutter_screen,
243 N_("X screen to use"), "SCREEN"
244 },
245 { "synch", 0,
246 0,
247 G_OPTION_ARG_NONE, &clutter_synchronise,
248 N_("Make X calls synchronous"), NULL
249 },
250 {
251 "disable-xinput", 0,
252 G_OPTION_FLAG_REVERSE,
253 G_OPTION_ARG_NONE, &clutter_enable_xinput,
254 N_("Disable XInput support"), NULL
255 },
256 { NULL }
257 };
258
259 static void
meta_clutter_backend_x11_add_options(ClutterBackend * backend,GOptionGroup * group)260 meta_clutter_backend_x11_add_options (ClutterBackend *backend,
261 GOptionGroup *group)
262 {
263 g_option_group_add_entries (group, entries);
264 }
265
266 static void
meta_clutter_backend_x11_finalize(GObject * gobject)267 meta_clutter_backend_x11_finalize (GObject *gobject)
268 {
269 MetaClutterBackendX11 *backend_x11 = META_CLUTTER_BACKEND_X11 (gobject);
270
271 g_free (backend_x11->display_name);
272
273 meta_clutter_x11_remove_filter (cogl_xlib_filter, gobject);
274
275 XCloseDisplay (backend_x11->xdisplay);
276
277 G_OBJECT_CLASS (meta_clutter_backend_x11_parent_class)->finalize (gobject);
278 }
279
280 static ClutterFeatureFlags
meta_clutter_backend_x11_get_features(ClutterBackend * backend)281 meta_clutter_backend_x11_get_features (ClutterBackend *backend)
282 {
283 ClutterFeatureFlags flags = CLUTTER_FEATURE_STAGE_CURSOR;
284
285 flags |=
286 CLUTTER_BACKEND_CLASS (meta_clutter_backend_x11_parent_class)->get_features (backend);
287
288 return flags;
289 }
290
291 static void
update_last_event_time(MetaClutterBackendX11 * backend_x11,XEvent * xevent)292 update_last_event_time (MetaClutterBackendX11 *backend_x11,
293 XEvent *xevent)
294 {
295 Time current_time = CurrentTime;
296 Time last_time = backend_x11->last_event_time;
297
298 switch (xevent->type)
299 {
300 case KeyPress:
301 case KeyRelease:
302 current_time = xevent->xkey.time;
303 break;
304
305 case ButtonPress:
306 case ButtonRelease:
307 current_time = xevent->xbutton.time;
308 break;
309
310 case MotionNotify:
311 current_time = xevent->xmotion.time;
312 break;
313
314 case EnterNotify:
315 case LeaveNotify:
316 current_time = xevent->xcrossing.time;
317 break;
318
319 case PropertyNotify:
320 current_time = xevent->xproperty.time;
321 break;
322
323 default:
324 break;
325 }
326
327 /* only change the current event time if it's after the previous event
328 * time, or if it is at least 30 seconds earlier - in case the system
329 * clock was changed
330 */
331 if ((current_time != CurrentTime) &&
332 (current_time > last_time || (last_time - current_time > (30 * 1000))))
333 backend_x11->last_event_time = current_time;
334 }
335
336 static gboolean
check_onscreen_template(CoglRenderer * renderer,CoglOnscreenTemplate * onscreen_template,gboolean enable_stereo,GError ** error)337 check_onscreen_template (CoglRenderer *renderer,
338 CoglOnscreenTemplate *onscreen_template,
339 gboolean enable_stereo,
340 GError **error)
341 {
342 GError *internal_error = NULL;
343
344 cogl_onscreen_template_set_stereo_enabled (onscreen_template,
345 clutter_enable_stereo);
346
347 /* cogl_renderer_check_onscreen_template() is actually just a
348 * shorthand for creating a CoglDisplay, and calling
349 * cogl_display_setup() on it, then throwing the display away. If we
350 * could just return that display, then it would be more efficient
351 * not to use cogl_renderer_check_onscreen_template(). However, the
352 * backend API requires that we return an CoglDisplay that has not
353 * yet been setup, so one way or the other we'll have to discard the
354 * first display and make a new fresh one.
355 */
356 if (cogl_renderer_check_onscreen_template (renderer, onscreen_template, &internal_error))
357 {
358 clutter_enable_stereo = enable_stereo;
359
360 return TRUE;
361 }
362 else
363 {
364 g_set_error_literal (error, CLUTTER_INIT_ERROR,
365 CLUTTER_INIT_ERROR_BACKEND,
366 internal_error != NULL
367 ? internal_error->message
368 : "Creation of a CoglDisplay failed");
369
370 g_clear_error (&internal_error);
371
372 return FALSE;
373 }
374 }
375
376 static CoglDisplay *
meta_clutter_backend_x11_get_display(ClutterBackend * backend,CoglRenderer * renderer,CoglSwapChain * swap_chain,GError ** error)377 meta_clutter_backend_x11_get_display (ClutterBackend *backend,
378 CoglRenderer *renderer,
379 CoglSwapChain *swap_chain,
380 GError **error)
381 {
382 CoglOnscreenTemplate *onscreen_template;
383 CoglDisplay *display = NULL;
384 gboolean res = FALSE;
385
386 onscreen_template = cogl_onscreen_template_new (swap_chain);
387
388 /* It's possible that the current renderer doesn't support transparency
389 * or doesn't support stereo, so we try the different combinations.
390 */
391 if (clutter_enable_stereo)
392 res = check_onscreen_template (renderer, onscreen_template,
393 TRUE, error);
394
395 if (!res)
396 res = check_onscreen_template (renderer, onscreen_template,
397 FALSE, error);
398
399 if (res)
400 display = cogl_display_new (renderer, onscreen_template);
401
402 cogl_object_unref (onscreen_template);
403
404 return display;
405 }
406
407 static CoglRenderer *
meta_clutter_backend_x11_get_renderer(ClutterBackend * clutter_backend,GError ** error)408 meta_clutter_backend_x11_get_renderer (ClutterBackend *clutter_backend,
409 GError **error)
410 {
411 MetaBackend *backend = meta_get_backend ();
412 MetaRenderer *renderer = meta_backend_get_renderer (backend);
413
414 return meta_renderer_create_cogl_renderer (renderer);
415 }
416
417 static ClutterStageWindow *
meta_clutter_backend_x11_create_stage(ClutterBackend * backend,ClutterStage * wrapper,GError ** error)418 meta_clutter_backend_x11_create_stage (ClutterBackend *backend,
419 ClutterStage *wrapper,
420 GError **error)
421 {
422 ClutterStageWindow *stage;
423 GType stage_type;
424
425 if (meta_is_wayland_compositor ())
426 stage_type = META_TYPE_STAGE_X11_NESTED;
427 else
428 stage_type = META_TYPE_STAGE_X11;
429
430 stage = g_object_new (stage_type,
431 "backend", backend,
432 "wrapper", wrapper,
433 NULL);
434 return stage;
435 }
436
437 static gboolean
meta_clutter_backend_x11_process_event_filters(MetaClutterBackendX11 * backend_x11,gpointer native,ClutterEvent * event)438 meta_clutter_backend_x11_process_event_filters (MetaClutterBackendX11 *backend_x11,
439 gpointer native,
440 ClutterEvent *event)
441 {
442 XEvent *xevent = native;
443
444 /* X11 filter functions have a higher priority */
445 if (backend_x11->event_filters != NULL)
446 {
447 GSList *node = backend_x11->event_filters;
448
449 while (node != NULL)
450 {
451 MetaX11EventFilter *filter = node->data;
452
453 switch (filter->func (xevent, event, filter->data))
454 {
455 case META_X11_FILTER_CONTINUE:
456 break;
457
458 case META_X11_FILTER_TRANSLATE:
459 return TRUE;
460
461 case META_X11_FILTER_REMOVE:
462 return FALSE;
463
464 default:
465 break;
466 }
467
468 node = node->next;
469 }
470 }
471
472 return FALSE;
473 }
474
475 static gboolean
meta_clutter_backend_x11_translate_event(ClutterBackend * clutter_backend,gpointer native,ClutterEvent * event)476 meta_clutter_backend_x11_translate_event (ClutterBackend *clutter_backend,
477 gpointer native,
478 ClutterEvent *event)
479 {
480 MetaClutterBackendX11 *backend_x11 =
481 META_CLUTTER_BACKEND_X11 (clutter_backend);
482 MetaBackend *backend = meta_get_backend ();
483 MetaStageX11 *stage_x11;
484 ClutterSeat *seat;
485
486 if (meta_clutter_backend_x11_process_event_filters (backend_x11,
487 native,
488 event))
489 return TRUE;
490
491 /* we update the event time only for events that can
492 * actually reach Clutter's event queue
493 */
494 update_last_event_time (backend_x11, native);
495
496 stage_x11 =
497 META_STAGE_X11 (clutter_backend_get_stage_window (clutter_backend));
498 if (meta_stage_x11_translate_event (stage_x11, native, event))
499 return TRUE;
500
501 seat = meta_backend_get_default_seat (backend);
502 if (meta_seat_x11_translate_event (META_SEAT_X11 (seat), native, event))
503 return TRUE;
504
505 return FALSE;
506 }
507
508 static ClutterSeat *
meta_clutter_backend_x11_get_default_seat(ClutterBackend * clutter_backend)509 meta_clutter_backend_x11_get_default_seat (ClutterBackend *clutter_backend)
510 {
511 MetaBackend *backend = meta_get_backend ();
512
513 return meta_backend_get_default_seat (backend);
514 }
515
516 static gboolean
meta_clutter_backend_x11_is_display_server(ClutterBackend * backend)517 meta_clutter_backend_x11_is_display_server (ClutterBackend *backend)
518 {
519 return meta_is_wayland_compositor ();
520 }
521
522 static void
meta_clutter_backend_x11_init(MetaClutterBackendX11 * clutter_backend_x11)523 meta_clutter_backend_x11_init (MetaClutterBackendX11 *clutter_backend_x11)
524 {
525 clutter_backend_x11->last_event_time = CurrentTime;
526 }
527
528 static void
meta_clutter_backend_x11_class_init(MetaClutterBackendX11Class * klass)529 meta_clutter_backend_x11_class_init (MetaClutterBackendX11Class *klass)
530 {
531 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
532 ClutterBackendClass *clutter_backend_class = CLUTTER_BACKEND_CLASS (klass);
533
534 gobject_class->finalize = meta_clutter_backend_x11_finalize;
535
536 clutter_backend_class->pre_parse = meta_clutter_backend_x11_pre_parse;
537 clutter_backend_class->post_parse = meta_clutter_backend_x11_post_parse;
538 clutter_backend_class->add_options = meta_clutter_backend_x11_add_options;
539 clutter_backend_class->get_features = meta_clutter_backend_x11_get_features;
540
541 clutter_backend_class->get_display = meta_clutter_backend_x11_get_display;
542 clutter_backend_class->get_renderer = meta_clutter_backend_x11_get_renderer;
543 clutter_backend_class->create_stage = meta_clutter_backend_x11_create_stage;
544 clutter_backend_class->translate_event = meta_clutter_backend_x11_translate_event;
545 clutter_backend_class->get_default_seat = meta_clutter_backend_x11_get_default_seat;
546 clutter_backend_class->is_display_server = meta_clutter_backend_x11_is_display_server;
547 }
548
549 static int
error_handler(Display * xdisplay,XErrorEvent * error)550 error_handler (Display *xdisplay,
551 XErrorEvent *error)
552 {
553 TrappedErrorCode = error->error_code;
554 return 0;
555 }
556
557 void
meta_clutter_x11_trap_x_errors(void)558 meta_clutter_x11_trap_x_errors (void)
559 {
560 TrappedErrorCode = 0;
561 old_error_handler = XSetErrorHandler (error_handler);
562 }
563
564 gint
meta_clutter_x11_untrap_x_errors(void)565 meta_clutter_x11_untrap_x_errors (void)
566 {
567 XSetErrorHandler (old_error_handler);
568
569 return TrappedErrorCode;
570 }
571
572 Display *
meta_clutter_x11_get_default_display(void)573 meta_clutter_x11_get_default_display (void)
574 {
575 ClutterBackend *backend = clutter_get_default_backend ();
576
577 if (backend == NULL)
578 {
579 g_critical ("The Clutter backend has not been initialised");
580 return NULL;
581 }
582
583 if (!META_IS_CLUTTER_BACKEND_X11 (backend))
584 {
585 g_critical ("The Clutter backend is not a X11 backend");
586 return NULL;
587 }
588
589 return META_CLUTTER_BACKEND_X11 (backend)->xdisplay;
590 }
591
592 void
meta_clutter_x11_set_display(Display * xdisplay)593 meta_clutter_x11_set_display (Display *xdisplay)
594 {
595 if (_clutter_context_is_initialized ())
596 {
597 g_warning ("%s() can only be used before calling clutter_init()",
598 G_STRFUNC);
599 return;
600 }
601
602 _foreign_dpy= xdisplay;
603 }
604
605 int
meta_clutter_x11_get_default_screen(void)606 meta_clutter_x11_get_default_screen (void)
607 {
608 ClutterBackend *backend = clutter_get_default_backend ();
609
610 if (backend == NULL)
611 {
612 g_critical ("The Clutter backend has not been initialised");
613 return 0;
614 }
615
616 if (!META_IS_CLUTTER_BACKEND_X11 (backend))
617 {
618 g_critical ("The Clutter backend is not a X11 backend");
619 return 0;
620 }
621
622 return META_CLUTTER_BACKEND_X11 (backend)->xscreen_num;
623 }
624
625 Window
meta_clutter_x11_get_root_window(void)626 meta_clutter_x11_get_root_window (void)
627 {
628 ClutterBackend *backend = clutter_get_default_backend ();
629
630 if (backend == NULL)
631 {
632 g_critical ("The Clutter backend has not been initialised");
633 return None;
634 }
635
636 if (!META_IS_CLUTTER_BACKEND_X11 (backend))
637 {
638 g_critical ("The Clutter backend is not a X11 backend");
639 return None;
640 }
641
642 return META_CLUTTER_BACKEND_X11 (backend)->xwin_root;
643 }
644
645 void
meta_clutter_x11_add_filter(MetaX11FilterFunc func,gpointer data)646 meta_clutter_x11_add_filter (MetaX11FilterFunc func,
647 gpointer data)
648 {
649 MetaX11EventFilter *filter;
650 ClutterBackend *backend = clutter_get_default_backend ();
651 MetaClutterBackendX11 *backend_x11;
652
653 g_return_if_fail (func != NULL);
654
655 if (backend == NULL)
656 {
657 g_critical ("The Clutter backend has not been initialised");
658 return;
659 }
660
661 if (!META_IS_CLUTTER_BACKEND_X11 (backend))
662 {
663 g_critical ("The Clutter backend is not a X11 backend");
664 return;
665 }
666
667 backend_x11 = META_CLUTTER_BACKEND_X11 (backend);
668
669 filter = g_new0 (MetaX11EventFilter, 1);
670 filter->func = func;
671 filter->data = data;
672
673 backend_x11->event_filters =
674 g_slist_append (backend_x11->event_filters, filter);
675
676 return;
677 }
678
679 void
meta_clutter_x11_remove_filter(MetaX11FilterFunc func,gpointer data)680 meta_clutter_x11_remove_filter (MetaX11FilterFunc func,
681 gpointer data)
682 {
683 GSList *tmp_list, *this;
684 MetaX11EventFilter *filter;
685 ClutterBackend *backend = clutter_get_default_backend ();
686 MetaClutterBackendX11 *backend_x11;
687
688 g_return_if_fail (func != NULL);
689
690 if (backend == NULL)
691 {
692 g_critical ("The Clutter backend has not been initialised");
693 return;
694 }
695
696 if (!META_IS_CLUTTER_BACKEND_X11 (backend))
697 {
698 g_critical ("The Clutter backend is not a X11 backend");
699 return;
700 }
701
702 backend_x11 = META_CLUTTER_BACKEND_X11 (backend);
703
704 tmp_list = backend_x11->event_filters;
705
706 while (tmp_list)
707 {
708 filter = tmp_list->data;
709 this = tmp_list;
710 tmp_list = tmp_list->next;
711
712 if (filter->func == func && filter->data == data)
713 {
714 backend_x11->event_filters =
715 g_slist_remove_link (backend_x11->event_filters, this);
716
717 g_slist_free_1 (this);
718 g_free (filter);
719
720 return;
721 }
722 }
723 }
724
725 void
meta_clutter_x11_set_use_stereo_stage(gboolean use_stereo)726 meta_clutter_x11_set_use_stereo_stage (gboolean use_stereo)
727 {
728 if (_clutter_context_is_initialized ())
729 {
730 g_warning ("%s() can only be used before calling clutter_init()",
731 G_STRFUNC);
732 return;
733 }
734
735 g_debug ("STEREO stages are %s",
736 use_stereo ? "enabled" : "disabled");
737
738 clutter_enable_stereo = use_stereo;
739 }
740
741 gboolean
meta_clutter_x11_get_use_stereo_stage(void)742 meta_clutter_x11_get_use_stereo_stage (void)
743 {
744 return clutter_enable_stereo;
745 }
746