1
/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
4
* Copyright (C) Nicolas Bruguier 2007-2010 <gandalfn@club-internet.fr>
6
* cairo-compmgr is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU Lesser General Public
8
* License as published by the Free Software Foundation; either
9
* version 2.1 of the License, or (at your option) any later version.
11
* cairo-compmgr is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Lesser General Public License for more details.
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with cairo-compmgr. If not, write to:
18
* The Free Software Foundation, Inc.,
19
* 51 Franklin Street, Fifth Floor
20
* Boston, MA 02110-1301, USA.
24
#include <X11/Xresource.h>
25
#include <X11/extensions/Xcomposite.h>
26
#include <X11/extensions/Xdamage.h>
27
#include <X11/extensions/XShm.h>
28
#include <X11/extensions/Xfixes.h>
29
#include <X11/extensions/Xdbe.h>
30
#include <X11/extensions/XInput.h>
31
#include <X11/extensions/shape.h>
34
#include "ccm-debug.h"
35
#include "ccm-display.h"
36
#include "ccm-cursor.h"
37
#include "ccm-screen.h"
38
#include "ccm-watch.h"
39
#include "ccm-config.h"
40
#include "ccm-window.h"
42
G_DEFINE_TYPE (CCMDisplay, ccm_display, G_TYPE_OBJECT);
50
PROP_SHM_SHARED_PIXMAP
55
CCM_DISPLAY_OPTION_USE_XSHM,
56
CCM_DISPLAY_OPTION_USE_XDBE,
57
CCM_DISPLAY_UNMANAGED_SCREEN,
61
static gchar *CCMDisplayOptions[CCM_DISPLAY_OPTION_N] = {
75
static guint signals[N_SIGNALS] = { 0 };
91
struct _CCMDisplayPrivate
99
CCMExtension composite;
102
gboolean shm_shared_pixmap;
108
int type_button_press;
109
int type_button_release;
110
int type_motion_notify;
111
CCMPointerEvents last_events;
113
gchar *cursors_theme;
115
CCMCursor *cursor_current;
122
CCMConfig *options[CCM_DISPLAY_OPTION_N];
125
static gint CCMLastXError = 0;
126
static CCMDisplay* CCMDefaultDisplay = NULL;
128
#define CCM_DISPLAY_GET_PRIVATE(o) \
129
(G_TYPE_INSTANCE_GET_PRIVATE ((o), CCM_TYPE_DISPLAY, CCMDisplayPrivate))
132
ccm_display_set_property (GObject * object, guint prop_id, const GValue * value,
135
CCMDisplayPrivate *priv = CCM_DISPLAY_GET_PRIVATE (object);
141
priv->xdisplay = g_value_get_pointer (value);
150
ccm_display_get_property (GObject * object, guint prop_id, GValue * value,
153
CCMDisplayPrivate *priv = CCM_DISPLAY_GET_PRIVATE (object);
159
g_value_set_pointer (value, priv->xdisplay);
164
g_value_set_boolean (value, priv->use_shm);
169
g_value_set_boolean (value, priv->use_dbe);
172
case PROP_SHM_SHARED_PIXMAP:
174
GError *error = NULL;
176
ccm_config_get_boolean (priv->options[CCM_DISPLAY_OPTION_USE_XSHM],
181
g_warning ("Error on get xshm configuration value");
182
g_error_free (error);
185
g_value_set_boolean (value, xshm && priv->shm.available && priv->shm_shared_pixmap);
194
ccm_display_init (CCMDisplay * self)
196
self->priv = CCM_DISPLAY_GET_PRIVATE (self);
198
self->priv->xdisplay = NULL;
199
self->priv->nb_screens = 0;
201
self->priv->screens = NULL;
202
self->priv->shm_shared_pixmap = FALSE;
203
self->priv->use_shm = FALSE;
204
self->priv->use_dbe = FALSE;
205
self->priv->pointers = NULL;
206
self->priv->type_button_press = 0;
207
self->priv->type_button_release = 0;
208
self->priv->type_motion_notify = 0;
209
self->priv->last_events.press = 0;
210
self->priv->last_events.release = 0;
211
self->priv->last_events.motion = 0;
212
self->priv->cursors = g_hash_table_new_full (g_int_hash, g_int_equal,
213
g_free, g_object_unref);
214
self->priv->cursor_current = NULL;
218
ccm_display_finalize (GObject * object)
220
CCMDisplay *self = CCM_DISPLAY (object);
223
ccm_debug ("DISPLAY FINALIZE");
225
if (self == CCMDefaultDisplay)
226
CCMDefaultDisplay = NULL;
228
if (self->priv->cursors)
229
g_hash_table_destroy (self->priv->cursors);
231
if (self->priv->pointers)
235
for (item = self->priv->pointers; item; item = item->next)
236
XCloseDevice (self->priv->xdisplay, item->data);
237
g_slist_free (self->priv->pointers);
240
if (self->priv->nb_screens)
242
for (cpt = 0; cpt < self->priv->nb_screens; ++cpt)
244
if (self->priv->screens[cpt] && CCM_IS_SCREEN (self->priv->screens[cpt]))
246
g_object_unref (self->priv->screens[cpt]);
247
self->priv->screens[cpt] = NULL;
250
self->priv->nb_screens = 0;
252
g_slice_free1 (sizeof (CCMScreen *) * (self->priv->nb_screens + 1),
253
self->priv->screens);
256
for (cpt = 0; cpt < CCM_DISPLAY_OPTION_N; ++cpt)
257
g_object_unref (self->priv->options[cpt]);
260
fd_remove_watch (self->priv->fd);
262
G_OBJECT_CLASS (ccm_display_parent_class)->finalize (object);
266
ccm_display_class_init (CCMDisplayClass * klass)
268
GObjectClass *object_class = G_OBJECT_CLASS (klass);
270
g_type_class_add_private (klass, sizeof (CCMDisplayPrivate));
272
object_class->get_property = ccm_display_get_property;
273
object_class->set_property = ccm_display_set_property;
274
object_class->finalize = ccm_display_finalize;
276
g_object_class_install_property (object_class, PROP_XDISPLAY,
277
g_param_spec_pointer ("xdisplay",
281
G_PARAM_CONSTRUCT_ONLY));
283
g_object_class_install_property (object_class, PROP_USE_XSHM,
284
g_param_spec_boolean ("use_xshm",
289
g_object_class_install_property (object_class, PROP_SHM_SHARED_PIXMAP,
290
g_param_spec_boolean ("shm_shared_pixmap",
296
g_object_class_install_property (object_class, PROP_USE_XDBE,
297
g_param_spec_boolean ("use_xdbe",
299
"Use Double Buffer Extension",
304
g_signal_new ("event", G_OBJECT_CLASS_TYPE (object_class),
305
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
306
g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
309
signals[DAMAGE_EVENT] =
310
g_signal_new ("damage-event", G_OBJECT_CLASS_TYPE (object_class),
311
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
312
g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
315
signals[CURSOR_CHANGED] =
316
g_signal_new ("cursor-changed", G_OBJECT_CLASS_TYPE (object_class),
317
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
318
g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
323
ccm_display_load_config (CCMDisplay * self)
325
g_return_if_fail (self != NULL);
329
for (cpt = 0; cpt < CCM_DISPLAY_OPTION_N; ++cpt)
331
self->priv->options[cpt] = ccm_config_new (-1, NULL, CCMDisplayOptions[cpt]);
333
self->priv->use_shm = ccm_config_get_boolean (self->priv->options[CCM_DISPLAY_OPTION_USE_XSHM], NULL) &&
334
self->priv->shm.available;
335
self->priv->use_dbe = ccm_config_get_boolean (self->priv->options[CCM_DISPLAY_OPTION_USE_XDBE], NULL) &&
336
self->priv->dbe.available;
340
ccm_display_check_cursor (CCMDisplay * self, Atom cursor_name,
343
g_return_if_fail (self != NULL);
345
CCMCursor *current = NULL;
349
current = g_hash_table_lookup (self->priv->cursors, &cursor_name);
353
XFixesCursorImage *cursor;
355
cursor = XFixesGetCursorImage (CCM_DISPLAY_XDISPLAY (self));
356
ccm_debug ("CHECK CURSOR %li", cursor_name);
358
current = ccm_cursor_new (self, cursor);
361
g_hash_table_insert (self->priv->cursors,
362
g_memdup (&cursor_name, sizeof (gulong)),
368
XFixesCursorImage *cursor;
370
cursor = XFixesGetCursorImage (CCM_DISPLAY_XDISPLAY (self));
372
current = ccm_cursor_new (self, cursor);
377
if (self->priv->cursor_current != current)
379
gboolean animated = FALSE;
381
if (self->priv->cursor_current)
383
g_object_get (self->priv->cursor_current, "animated", &animated, NULL);
385
g_object_unref (self->priv->cursor_current);
387
self->priv->cursor_current = current;
389
g_signal_emit (self, signals[CURSOR_CHANGED], 0, self->priv->cursor_current);
395
ccm_display_get_pointers (CCMDisplay * self)
397
g_return_if_fail (self != NULL);
402
info = XListInputDevices (self->priv->xdisplay, &ndevices);
403
for (cpt = 0; cpt < ndevices; ++cpt)
405
XDeviceInfo *current = &info[cpt];
406
if (current->use == IsXExtensionPointer)
408
XDevice *device = XOpenDevice (self->priv->xdisplay, current->id);
409
ccm_debug ("Found device: %s (%d)", current->name, current->id);
410
self->priv->pointers = g_slist_prepend (self->priv->pointers, device);
417
ccm_display_init_shape (CCMDisplay * self)
419
g_return_val_if_fail (self != NULL, FALSE);
421
if (XShapeQueryExtension (self->priv->xdisplay,
422
&self->priv->shape.event_base,
423
&self->priv->shape.error_base))
425
self->priv->shape.available = TRUE;
426
ccm_debug ("SHAPE ERROR BASE: %i", self->priv->shape.error_base);
434
ccm_display_init_composite (CCMDisplay * self)
436
g_return_val_if_fail (self != NULL, FALSE);
438
if (XCompositeQueryExtension (self->priv->xdisplay,
439
&self->priv->composite.event_base,
440
&self->priv->composite.error_base))
442
self->priv->composite.available = TRUE;
443
ccm_debug ("COMPOSITE ERROR BASE: %i",
444
self->priv->composite.error_base);
452
ccm_display_init_damage (CCMDisplay * self)
454
g_return_val_if_fail (self != NULL, FALSE);
456
if (XDamageQueryExtension (self->priv->xdisplay,
457
&self->priv->damage.event_base,
458
&self->priv->damage.error_base))
460
self->priv->damage.available = TRUE;
461
ccm_debug ("DAMAGE ERROR BASE: %i", self->priv->damage.error_base);
469
ccm_display_init_shm (CCMDisplay * self)
471
g_return_val_if_fail (self != NULL, FALSE);
474
if (XShmQueryExtension (self->priv->xdisplay) &&
475
XShmQueryVersion (self->priv->xdisplay, &major, &minor,
476
&self->priv->shm_shared_pixmap))
478
self->priv->shm.available = TRUE;
486
ccm_display_init_dbe(CCMDisplay *self)
488
g_return_val_if_fail(self != NULL, FALSE);
492
if (XdbeQueryExtension (self->priv->xdisplay, &major, &minor))
494
self->priv->dbe.available = TRUE;
502
ccm_display_init_xfixes (CCMDisplay * self)
504
g_return_val_if_fail (self != NULL, FALSE);
506
if (XFixesQueryExtension (self->priv->xdisplay,
507
&self->priv->fixes.event_base,
508
&self->priv->fixes.error_base))
510
self->priv->fixes.available = TRUE;
511
ccm_debug ("FIXES ERROR BASE: %i", self->priv->fixes.error_base);
519
ccm_display_init_input (CCMDisplay * self)
521
g_return_val_if_fail (self != NULL, FALSE);
523
XExtensionVersion *version;
526
version = XQueryInputVersion (self->priv->xdisplay, XI_2_Major, XI_2_Minor);
528
version = XGetExtensionVersion (self->priv->xdisplay, INAME);
531
if (version && (version != (XExtensionVersion *) NoSuchExtension))
533
self->priv->input.available = TRUE;
542
ccm_display_error_handler (Display * dpy, XErrorEvent * evt)
546
XGetErrorText (dpy, evt->error_code, str, 128);
547
ccm_debug ("ERROR: Xerror: %s", str);
549
sprintf (str, "%d", evt->request_code);
550
XGetErrorDatabaseText (dpy, "XRequest", str, "", str, 128);
551
if (strcmp (str, ""))
552
ccm_debug ("ERROR: XRequest: (%s)", str);
554
CCMLastXError = evt->error_code;
556
ccm_debug_backtrace ();
562
ccm_display_process_events (CCMDisplay * self)
564
g_return_if_fail (self != NULL);
568
while (XEventsQueued (CCM_DISPLAY_XDISPLAY (self), QueuedAfterReading))
570
XNextEvent (CCM_DISPLAY_XDISPLAY (self), &xevent);
572
if (xevent.type == self->priv->damage.event_base + XDamageNotify)
574
XDamageNotifyEvent *event_damage = (XDamageNotifyEvent *) & xevent;
576
g_signal_emit (self, signals[DAMAGE_EVENT], 0,
577
event_damage->damage);
579
else if (xevent.type ==
580
self->priv->fixes.event_base + XFixesCursorNotify)
582
XFixesCursorNotifyEvent *event_cursor = (XFixesCursorNotifyEvent *) &xevent;
584
ccm_debug ("CURSOR NOTIFY %li", event_cursor->cursor_name);
585
ccm_display_check_cursor (self, event_cursor->cursor_name, TRUE);
589
gboolean proceed = FALSE;
591
// Check if event is not already proceed by device events
592
if (xevent.type == self->priv->type_button_press)
594
XDeviceButtonEvent *button_event =
595
(XDeviceButtonEvent *) g_memdup (&xevent, sizeof (XEvent));
597
proceed = self->priv->last_events.press == xevent.xany.serial;
598
self->priv->last_events.press = xevent.xany.serial;
600
xevent.xany.type = ButtonPress;
601
xevent.xany.serial = button_event->serial;
602
xevent.xany.send_event = button_event->send_event;
603
xevent.xany.display = button_event->display;
604
xevent.xany.window = button_event->window;
605
xevent.xbutton.root = button_event->root;
606
xevent.xbutton.subwindow = button_event->subwindow;
607
xevent.xbutton.time = button_event->time;
608
xevent.xbutton.x = button_event->x;
609
xevent.xbutton.y = button_event->y;
610
xevent.xbutton.x_root = button_event->x_root;
611
xevent.xbutton.y_root = button_event->y_root;
612
xevent.xbutton.state = button_event->state;
613
xevent.xbutton.button = button_event->button;
614
xevent.xbutton.same_screen = button_event->same_screen;
616
g_free (button_event);
618
else if (xevent.type == self->priv->type_button_release)
620
XDeviceButtonEvent *button_event =
621
(XDeviceButtonEvent *) g_memdup (&xevent, sizeof (XEvent));
623
proceed = self->priv->last_events.release == xevent.xany.serial;
624
self->priv->last_events.release = xevent.xany.serial;
626
xevent.xany.type = ButtonRelease;
627
xevent.xany.serial = button_event->serial;
628
xevent.xany.send_event = button_event->send_event;
629
xevent.xany.display = button_event->display;
630
xevent.xany.window = button_event->window;
631
xevent.xbutton.root = button_event->root;
632
xevent.xbutton.subwindow = button_event->subwindow;
633
xevent.xbutton.time = button_event->time;
634
xevent.xbutton.x = button_event->x;
635
xevent.xbutton.y = button_event->y;
636
xevent.xbutton.x_root = button_event->x_root;
637
xevent.xbutton.y_root = button_event->y_root;
638
xevent.xbutton.state = button_event->state;
639
xevent.xbutton.button = button_event->button;
640
xevent.xbutton.same_screen = button_event->same_screen;
642
g_free (button_event);
644
else if (xevent.type == self->priv->type_motion_notify)
646
XDeviceMotionEvent *motion_event =
647
(XDeviceMotionEvent *) g_memdup (&xevent, sizeof (XEvent));
649
proceed = self->priv->last_events.motion == xevent.xany.serial;
650
self->priv->last_events.motion = xevent.xany.serial;
652
xevent.xany.type = MotionNotify;
653
xevent.xany.serial = motion_event->serial;
654
xevent.xany.send_event = motion_event->send_event;
655
xevent.xany.display = motion_event->display;
656
xevent.xany.window = motion_event->window;
657
xevent.xmotion.root = motion_event->root;
658
xevent.xmotion.subwindow = motion_event->subwindow;
659
xevent.xmotion.time = motion_event->time;
660
xevent.xmotion.x = motion_event->x;
661
xevent.xmotion.y = motion_event->y;
662
xevent.xmotion.x_root = motion_event->x_root;
663
xevent.xmotion.y_root = motion_event->y_root;
664
xevent.xmotion.state = motion_event->state;
665
xevent.xmotion.is_hint = motion_event->is_hint;
666
xevent.xmotion.same_screen = motion_event->same_screen;
668
g_free (motion_event);
670
else if (xevent.type == ButtonPress)
672
proceed = self->priv->last_events.press == xevent.xany.serial;
673
self->priv->last_events.press = xevent.xany.serial;
675
else if (xevent.type == ButtonRelease)
677
proceed = self->priv->last_events.release == xevent.xany.serial;
678
self->priv->last_events.release = xevent.xany.serial;
680
else if (xevent.type == MotionNotify)
682
proceed = self->priv->last_events.motion == xevent.xany.serial;
683
self->priv->last_events.motion = xevent.xany.serial;
687
g_signal_emit (self, signals[EVENT], 0, &xevent);
693
ccm_display_new (gchar * display)
697
GSList *unmanaged = NULL;
700
xdisplay = XOpenDisplay (display);
703
g_warning ("Unable to open display %s", display);
707
self = g_object_new (CCM_TYPE_DISPLAY, "xdisplay", xdisplay, NULL);
709
ccm_display_init_dbe(self);
711
if (!ccm_display_init_shape (self))
713
g_object_unref (self);
714
g_warning ("Shape init failed for %s", display);
718
if (!ccm_display_init_composite (self))
720
g_object_unref (self);
721
g_warning ("Composite init failed for %s", display);
725
if (!ccm_display_init_damage (self))
727
g_object_unref (self);
728
g_warning ("Damage init failed for %s", display);
732
if (!ccm_display_init_shm (self))
734
g_object_unref (self);
735
g_warning ("SHM init failed for %s", display);
739
if (!ccm_display_init_xfixes (self))
741
g_object_unref (self);
742
g_warning ("FIXES init failed for %s", display);
746
if (!ccm_display_init_input (self))
748
g_object_unref (self);
749
g_warning ("TEST init failed for %s", display);
753
if (CCMDefaultDisplay == NULL) CCMDefaultDisplay = self;
755
ccm_display_get_pointers (self);
757
ccm_display_load_config (self);
759
XSetErrorHandler (ccm_display_error_handler);
761
self->priv->nb_screens = ScreenCount (self->priv->xdisplay);
762
self->priv->screens = g_slice_alloc0 (sizeof (CCMScreen *) * (self->priv->nb_screens + 1));
764
unmanaged = ccm_config_get_integer_list (self->priv->options[CCM_DISPLAY_UNMANAGED_SCREEN],
767
for (cpt = 0; cpt < self->priv->nb_screens; ++cpt)
769
gboolean found = FALSE;
775
for (item = unmanaged; item; item = item->next)
777
if (GPOINTER_TO_INT (item->data) == cpt)
785
self->priv->screens[cpt] = ccm_screen_new (self, cpt);
787
g_slist_free (unmanaged);
789
self->priv->fd = ConnectionNumber (CCM_DISPLAY_XDISPLAY (self));
790
fd_add_watch (self->priv->fd, self);
791
fd_set_read_callback (self->priv->fd,
792
(CCMWatchCallback) ccm_display_process_events);
793
fd_set_poll_callback (self->priv->fd,
794
(CCMWatchCallback) ccm_display_process_events);
800
ccm_display_get_xdisplay (CCMDisplay * self)
802
g_return_val_if_fail (self != NULL, NULL);
804
return self->priv->xdisplay;
808
ccm_display_get_screen (CCMDisplay * self, guint number)
810
g_return_val_if_fail (self != NULL, NULL);
811
g_return_val_if_fail (number < self->priv->nb_screens, NULL);
813
return self->priv->screens[number];
817
ccm_display_get_shape_notify_event_type (CCMDisplay * self)
819
g_return_val_if_fail (self != NULL, 0);
821
return self->priv->shape.event_base + ShapeNotify;
825
ccm_display_report_device_event (CCMDisplay * self, CCMScreen * screen,
828
g_return_val_if_fail (self != NULL, FALSE);
829
g_return_val_if_fail (screen != NULL, FALSE);
831
CCMWindow *root = ccm_screen_get_root_window (screen);
834
for (item = self->priv->pointers; item; item = item->next)
836
XDevice *pointer = (XDevice *) item->data;
841
XInputClassInfo *class;
842
XEventClass *event = g_new0 (XEventClass, 9 * pointer->num_classes);
844
for (class = pointer->classes, cpt = 0; cpt < pointer->num_classes;
847
switch (class->input_class)
850
DeviceButtonPress (pointer,
851
self->priv->type_button_press,
853
DeviceButtonRelease (pointer,
854
self->priv->type_button_release,
859
DeviceButton1Motion (pointer, 0, event[nb++]);
860
DeviceButton2Motion (pointer, 0, event[nb++]);
861
DeviceButton3Motion (pointer, 0, event[nb++]);
862
DeviceButton4Motion (pointer, 0, event[nb++]);
863
DeviceButton5Motion (pointer, 0, event[nb++]);
864
DeviceButtonMotion (pointer, 0, event[nb++]);
865
DeviceMotionNotify (pointer,
866
self->priv->type_motion_notify,
874
if (XSelectExtensionEvent (self->priv->xdisplay,
875
CCM_WINDOW_XWINDOW (root),
885
XEventClass *event = g_new0 (XEventClass, 1);;
886
NoExtensionEvent (pointer, 0, event[nb++]);
887
if (XSelectExtensionEvent (self->priv->xdisplay,
888
CCM_WINDOW_XWINDOW (root),
902
ccm_display_flush (CCMDisplay * self)
904
g_return_if_fail (self != NULL);
906
XFlush (self->priv->xdisplay);
910
ccm_display_sync (CCMDisplay * self)
912
g_return_if_fail (self != NULL);
914
XSync (self->priv->xdisplay, FALSE);
918
ccm_display_grab (CCMDisplay * self)
920
g_return_if_fail (self != NULL);
922
XGrabServer (self->priv->xdisplay);
926
ccm_display_ungrab (CCMDisplay * self)
928
g_return_if_fail (self != NULL);
930
XUngrabServer (self->priv->xdisplay);
934
ccm_display_trap_error (CCMDisplay * self)
940
ccm_display_pop_error (CCMDisplay * self)
942
g_return_val_if_fail (self != NULL, 0);
944
ccm_display_sync (self);
946
return CCMLastXError;
950
ccm_display_get_current_cursor (CCMDisplay * self, gboolean initiate)
952
g_return_val_if_fail (self != NULL, NULL);
954
if (initiate || !self->priv->cursor_current)
956
XFixesCursorImage *cursor;
958
cursor = XFixesGetCursorImage (CCM_DISPLAY_XDISPLAY (self));
959
ccm_display_check_cursor (self, cursor->atom, FALSE);
963
return (const CCMCursor *) self->priv->cursor_current;
967
ccm_display_get_default()
969
return CCMDefaultDisplay;