gtk3/gdk/x11/gdkdisplay-x11.c
Havoc Pennington b837ef5a6d Revamp and modernize X error traps
* add per-display gdk_x11_display_error_trap_push()
  (X11-specific because gdk_error_trap_push() probably
  should have been)
* make gdk_error_trap_push() handle only GDK displays
  not displays opened without a GDK wrapper
* make gdk_error_trap_pop() and gdk_x11_display_error_trap_pop()
  automatically sync only if needed, so manual gdk_flush() is not
  required
* add gdk_error_trap_pop_ignored() which just asynchronously
  ignores errors, so never needs to sync
* add G_GNUC_WARN_UNUSED_RESULT to plain pop(), because
  if you use plain pop() and don't need the return value,
  the async gdk_error_trap_pop_ignored() should be used
  instead. This results in lots of warnings to clean
  up in a later patch.

The main objective here was to avoid the need to sync just
to ignore an error. Now, syncing is automatic, and only
happens when we need to know the error code.

https://bugzilla.gnome.org/show_bug.cgi?id=629608
2010-09-18 18:19:27 -04:00

2925 lines
79 KiB
C

/* GDK - The GIMP Drawing Kit
* gdkdisplay-x11.c
*
* Copyright 2001 Sun Microsystems Inc.
* Copyright (C) 2004 Nokia Corporation
*
* Erwann Chenede <erwann.chenede@sun.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <glib/gprintf.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <glib.h>
#include "gdkx.h"
#include "gdkasync.h"
#include "gdkdisplay.h"
#include "gdkdisplay-x11.h"
#include "gdkeventsource.h"
#include "gdkeventtranslator.h"
#include "gdkscreen.h"
#include "gdkscreen-x11.h"
#include "gdkinternals.h"
#include "gdkdeviceprivate.h"
#include "gdkdevicemanager.h"
#include "xsettings-client.h"
#include <X11/Xatom.h>
#ifdef HAVE_XKB
#include <X11/XKBlib.h>
#endif
#ifdef HAVE_XFIXES
#include <X11/extensions/Xfixes.h>
#endif
#include <X11/extensions/shape.h>
#ifdef HAVE_XCOMPOSITE
#include <X11/extensions/Xcomposite.h>
#endif
#ifdef HAVE_XDAMAGE
#include <X11/extensions/Xdamage.h>
#endif
#ifdef HAVE_RANDR
#include <X11/extensions/Xrandr.h>
#endif
typedef struct _GdkErrorTrap GdkErrorTrap;
struct _GdkErrorTrap
{
/* Next sequence when trap was pushed, i.e. first sequence to
* ignore
*/
gulong start_sequence;
/* Next sequence when trap was popped, i.e. first sequence
* to not ignore. 0 if trap is still active.
*/
gulong end_sequence;
/* Most recent error code within the sequence */
int error_code;
};
static void gdk_display_x11_dispose (GObject *object);
static void gdk_display_x11_finalize (GObject *object);
static void gdk_display_x11_event_translator_init (GdkEventTranslatorIface *iface);
static gboolean gdk_display_x11_translate_event (GdkEventTranslator *translator,
GdkDisplay *display,
GdkEvent *event,
XEvent *xevent);
#ifdef HAVE_X11R6
static void gdk_internal_connection_watch (Display *display,
XPointer arg,
gint fd,
gboolean opening,
XPointer *watch_data);
#endif /* HAVE_X11R6 */
typedef struct _GdkEventTypeX11 GdkEventTypeX11;
struct _GdkEventTypeX11
{
gint base;
gint n_events;
};
/* Note that we never *directly* use WM_LOCALE_NAME, WM_PROTOCOLS,
* but including them here has the side-effect of getting them
* into the internal Xlib cache
*/
static const char *const precache_atoms[] = {
"UTF8_STRING",
"WM_CLIENT_LEADER",
"WM_DELETE_WINDOW",
"WM_ICON_NAME",
"WM_LOCALE_NAME",
"WM_NAME",
"WM_PROTOCOLS",
"WM_TAKE_FOCUS",
"WM_WINDOW_ROLE",
"_NET_ACTIVE_WINDOW",
"_NET_CURRENT_DESKTOP",
"_NET_FRAME_EXTENTS",
"_NET_STARTUP_ID",
"_NET_WM_CM_S0",
"_NET_WM_DESKTOP",
"_NET_WM_ICON",
"_NET_WM_ICON_NAME",
"_NET_WM_NAME",
"_NET_WM_PID",
"_NET_WM_PING",
"_NET_WM_STATE",
"_NET_WM_STATE_ABOVE",
"_NET_WM_STATE_BELOW",
"_NET_WM_STATE_FULLSCREEN",
"_NET_WM_STATE_MODAL",
"_NET_WM_STATE_MAXIMIZED_VERT",
"_NET_WM_STATE_MAXIMIZED_HORZ",
"_NET_WM_STATE_SKIP_TASKBAR",
"_NET_WM_STATE_SKIP_PAGER",
"_NET_WM_STATE_STICKY",
"_NET_WM_SYNC_REQUEST",
"_NET_WM_SYNC_REQUEST_COUNTER",
"_NET_WM_WINDOW_TYPE",
"_NET_WM_WINDOW_TYPE_NORMAL",
"_NET_WM_USER_TIME",
"_NET_VIRTUAL_ROOTS"
};
G_DEFINE_TYPE_WITH_CODE (GdkDisplayX11, _gdk_display_x11, GDK_TYPE_DISPLAY,
G_IMPLEMENT_INTERFACE (GDK_TYPE_EVENT_TRANSLATOR,
gdk_display_x11_event_translator_init))
static void
_gdk_display_x11_class_init (GdkDisplayX11Class * class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->dispose = gdk_display_x11_dispose;
object_class->finalize = gdk_display_x11_finalize;
}
static void
_gdk_display_x11_init (GdkDisplayX11 *display)
{
}
static void
gdk_display_x11_event_translator_init (GdkEventTranslatorIface *iface)
{
iface->translate_event = gdk_display_x11_translate_event;
}
static void
do_net_wm_state_changes (GdkWindow *window)
{
GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
GdkWindowState old_state;
if (GDK_WINDOW_DESTROYED (window) ||
gdk_window_get_window_type (window) != GDK_WINDOW_TOPLEVEL)
return;
old_state = gdk_window_get_state (window);
/* For found_sticky to remain TRUE, we have to also be on desktop
* 0xFFFFFFFF
*/
if (old_state & GDK_WINDOW_STATE_STICKY)
{
if (!(toplevel->have_sticky && toplevel->on_all_desktops))
gdk_synthesize_window_state (window,
GDK_WINDOW_STATE_STICKY,
0);
}
else
{
if (toplevel->have_sticky || toplevel->on_all_desktops)
gdk_synthesize_window_state (window,
0,
GDK_WINDOW_STATE_STICKY);
}
if (old_state & GDK_WINDOW_STATE_FULLSCREEN)
{
if (!toplevel->have_fullscreen)
gdk_synthesize_window_state (window,
GDK_WINDOW_STATE_FULLSCREEN,
0);
}
else
{
if (toplevel->have_fullscreen)
gdk_synthesize_window_state (window,
0,
GDK_WINDOW_STATE_FULLSCREEN);
}
/* Our "maximized" means both vertical and horizontal; if only one,
* we don't expose that via GDK
*/
if (old_state & GDK_WINDOW_STATE_MAXIMIZED)
{
if (!(toplevel->have_maxvert && toplevel->have_maxhorz))
gdk_synthesize_window_state (window,
GDK_WINDOW_STATE_MAXIMIZED,
0);
}
else
{
if (toplevel->have_maxvert && toplevel->have_maxhorz)
gdk_synthesize_window_state (window,
0,
GDK_WINDOW_STATE_MAXIMIZED);
}
}
static void
gdk_check_wm_desktop_changed (GdkWindow *window)
{
GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
Atom type;
gint format;
gulong nitems;
gulong bytes_after;
guchar *data;
gulong *desktop;
type = None;
gdk_error_trap_push ();
XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
GDK_WINDOW_XID (window),
gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP"),
0, G_MAXLONG, False, XA_CARDINAL, &type,
&format, &nitems,
&bytes_after, &data);
gdk_error_trap_pop ();
if (type != None)
{
desktop = (gulong *)data;
toplevel->on_all_desktops = (*desktop == 0xFFFFFFFF);
XFree (desktop);
}
else
toplevel->on_all_desktops = FALSE;
do_net_wm_state_changes (window);
}
static void
gdk_check_wm_state_changed (GdkWindow *window)
{
GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
Atom type;
gint format;
gulong nitems;
gulong bytes_after;
guchar *data;
Atom *atoms = NULL;
gulong i;
gboolean had_sticky = toplevel->have_sticky;
toplevel->have_sticky = FALSE;
toplevel->have_maxvert = FALSE;
toplevel->have_maxhorz = FALSE;
toplevel->have_fullscreen = FALSE;
type = None;
gdk_error_trap_push ();
XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window),
gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"),
0, G_MAXLONG, False, XA_ATOM, &type, &format, &nitems,
&bytes_after, &data);
gdk_error_trap_pop ();
if (type != None)
{
Atom sticky_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_STICKY");
Atom maxvert_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_MAXIMIZED_VERT");
Atom maxhorz_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_MAXIMIZED_HORZ");
Atom fullscreen_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_FULLSCREEN");
atoms = (Atom *)data;
i = 0;
while (i < nitems)
{
if (atoms[i] == sticky_atom)
toplevel->have_sticky = TRUE;
else if (atoms[i] == maxvert_atom)
toplevel->have_maxvert = TRUE;
else if (atoms[i] == maxhorz_atom)
toplevel->have_maxhorz = TRUE;
else if (atoms[i] == fullscreen_atom)
toplevel->have_fullscreen = TRUE;
++i;
}
XFree (atoms);
}
/* When have_sticky is turned on, we have to check the DESKTOP property
* as well.
*/
if (toplevel->have_sticky && !had_sticky)
gdk_check_wm_desktop_changed (window);
else
do_net_wm_state_changes (window);
}
static GdkWindow *
get_event_window (GdkEventTranslator *translator,
XEvent *xevent)
{
GdkDisplay *display;
Window xwindow;
display = (GdkDisplay *) translator;
switch (xevent->type)
{
case DestroyNotify:
xwindow = xevent->xdestroywindow.window;
break;
case UnmapNotify:
xwindow = xevent->xunmap.window;
break;
case MapNotify:
xwindow = xevent->xmap.window;
break;
case ConfigureNotify:
xwindow = xevent->xconfigure.window;
break;
default:
xwindow = xevent->xany.window;
}
return gdk_window_lookup_for_display (display, xwindow);
}
static gboolean
gdk_display_x11_translate_event (GdkEventTranslator *translator,
GdkDisplay *display,
GdkEvent *event,
XEvent *xevent)
{
GdkWindow *window;
GdkWindowObject *window_private;
GdkWindowImplX11 *window_impl = NULL;
GdkScreen *screen = NULL;
GdkScreenX11 *screen_x11 = NULL;
GdkToplevelX11 *toplevel = NULL;
GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
gboolean return_val;
Window xwindow = None;
/* Find the GdkWindow that this event relates to.
* Basically this means substructure events
* are reported same as structure events
*/
window = get_event_window (translator, xevent);
window_private = (GdkWindowObject *) window;
if (window)
{
/* We may receive events such as NoExpose/GraphicsExpose
* and ShmCompletion for pixmaps
*/
if (!GDK_IS_WINDOW (window))
return FALSE;
screen = GDK_WINDOW_SCREEN (window);
screen_x11 = GDK_SCREEN_X11 (screen);
toplevel = _gdk_x11_window_get_toplevel (window);
window_impl = GDK_WINDOW_IMPL_X11 (window_private->impl);
xwindow = GDK_WINDOW_XID (window);
g_object_ref (window);
}
event->any.window = window;
event->any.send_event = xevent->xany.send_event ? TRUE : FALSE;
if (window_private && GDK_WINDOW_DESTROYED (window))
{
if (xevent->type != DestroyNotify)
{
return_val = FALSE;
goto done;
}
}
if (xevent->type == DestroyNotify)
{
int i, n;
n = gdk_display_get_n_screens (display);
for (i = 0; i < n; i++)
{
screen = gdk_display_get_screen (display, i);
screen_x11 = GDK_SCREEN_X11 (screen);
if (screen_x11->wmspec_check_window == xwindow)
{
screen_x11->wmspec_check_window = None;
screen_x11->last_wmspec_check_time = 0;
g_free (screen_x11->window_manager_name);
screen_x11->window_manager_name = g_strdup ("unknown");
/* careful, reentrancy */
_gdk_x11_screen_window_manager_changed (screen);
return_val = FALSE;
goto done;
}
}
}
/* We do a "manual" conversion of the XEvent to a
* GdkEvent. The structures are mostly the same so
* the conversion is fairly straightforward. We also
* optionally print debugging info regarding events
* received.
*/
return_val = TRUE;
switch (xevent->type)
{
case KeymapNotify:
GDK_NOTE (EVENTS,
g_message ("keymap notify"));
/* Not currently handled */
return_val = FALSE;
break;
case Expose:
GDK_NOTE (EVENTS,
g_message ("expose:\t\twindow: %ld %d x,y: %d %d w,h: %d %d%s",
xevent->xexpose.window, xevent->xexpose.count,
xevent->xexpose.x, xevent->xexpose.y,
xevent->xexpose.width, xevent->xexpose.height,
event->any.send_event ? " (send)" : ""));
if (window_private == NULL)
{
return_val = FALSE;
break;
}
{
GdkRectangle expose_rect;
expose_rect.x = xevent->xexpose.x;
expose_rect.y = xevent->xexpose.y;
expose_rect.width = xevent->xexpose.width;
expose_rect.height = xevent->xexpose.height;
_gdk_window_process_expose (window, xevent->xexpose.serial, &expose_rect);
return_val = FALSE;
}
break;
case GraphicsExpose:
{
GdkRectangle expose_rect;
GDK_NOTE (EVENTS,
g_message ("graphics expose:\tdrawable: %ld",
xevent->xgraphicsexpose.drawable));
if (window_private == NULL)
{
return_val = FALSE;
break;
}
expose_rect.x = xevent->xgraphicsexpose.x;
expose_rect.y = xevent->xgraphicsexpose.y;
expose_rect.width = xevent->xgraphicsexpose.width;
expose_rect.height = xevent->xgraphicsexpose.height;
_gdk_window_process_expose (window, xevent->xgraphicsexpose.serial, &expose_rect);
return_val = FALSE;
}
break;
case NoExpose:
GDK_NOTE (EVENTS,
g_message ("no expose:\t\tdrawable: %ld",
xevent->xnoexpose.drawable));
event->no_expose.type = GDK_NO_EXPOSE;
event->no_expose.window = window;
break;
case VisibilityNotify:
#ifdef G_ENABLE_DEBUG
if (_gdk_debug_flags & GDK_DEBUG_EVENTS)
switch (xevent->xvisibility.state)
{
case VisibilityFullyObscured:
g_message ("visibility notify:\twindow: %ld none",
xevent->xvisibility.window);
break;
case VisibilityPartiallyObscured:
g_message ("visibility notify:\twindow: %ld partial",
xevent->xvisibility.window);
break;
case VisibilityUnobscured:
g_message ("visibility notify:\twindow: %ld full",
xevent->xvisibility.window);
break;
}
#endif /* G_ENABLE_DEBUG */
if (window_private == NULL)
{
return_val = FALSE;
break;
}
event->visibility.type = GDK_VISIBILITY_NOTIFY;
event->visibility.window = window;
switch (xevent->xvisibility.state)
{
case VisibilityFullyObscured:
event->visibility.state = GDK_VISIBILITY_FULLY_OBSCURED;
break;
case VisibilityPartiallyObscured:
event->visibility.state = GDK_VISIBILITY_PARTIAL;
break;
case VisibilityUnobscured:
event->visibility.state = GDK_VISIBILITY_UNOBSCURED;
break;
}
break;
case CreateNotify:
GDK_NOTE (EVENTS,
g_message ("create notify:\twindow: %ld x,y: %d %d w,h: %d %d b-w: %d parent: %ld ovr: %d",
xevent->xcreatewindow.window,
xevent->xcreatewindow.x,
xevent->xcreatewindow.y,
xevent->xcreatewindow.width,
xevent->xcreatewindow.height,
xevent->xcreatewindow.border_width,
xevent->xcreatewindow.parent,
xevent->xcreatewindow.override_redirect));
/* not really handled */
break;
case DestroyNotify:
GDK_NOTE (EVENTS,
g_message ("destroy notify:\twindow: %ld",
xevent->xdestroywindow.window));
/* Ignore DestroyNotify from SubstructureNotifyMask */
if (xevent->xdestroywindow.window == xevent->xdestroywindow.event)
{
event->any.type = GDK_DESTROY;
event->any.window = window;
return_val = window_private && !GDK_WINDOW_DESTROYED (window);
if (window && GDK_WINDOW_XID (window) != screen_x11->xroot_window)
gdk_window_destroy_notify (window);
}
else
return_val = FALSE;
break;
case UnmapNotify:
GDK_NOTE (EVENTS,
g_message ("unmap notify:\t\twindow: %ld",
xevent->xmap.window));
event->any.type = GDK_UNMAP;
event->any.window = window;
/* If we are shown (not withdrawn) and get an unmap, it means we
* were iconified in the X sense. If we are withdrawn, and get
* an unmap, it means we hid the window ourselves, so we
* will have already flipped the iconified bit off.
*/
if (window)
{
if (GDK_WINDOW_IS_MAPPED (window))
gdk_synthesize_window_state (window,
0,
GDK_WINDOW_STATE_ICONIFIED);
_gdk_xgrab_check_unmap (window, xevent->xany.serial);
}
break;
case MapNotify:
GDK_NOTE (EVENTS,
g_message ("map notify:\t\twindow: %ld",
xevent->xmap.window));
event->any.type = GDK_MAP;
event->any.window = window;
/* Unset iconified if it was set */
if (window && (((GdkWindowObject*)window)->state & GDK_WINDOW_STATE_ICONIFIED))
gdk_synthesize_window_state (window,
GDK_WINDOW_STATE_ICONIFIED,
0);
break;
case ReparentNotify:
GDK_NOTE (EVENTS,
g_message ("reparent notify:\twindow: %ld x,y: %d %d parent: %ld ovr: %d",
xevent->xreparent.window,
xevent->xreparent.x,
xevent->xreparent.y,
xevent->xreparent.parent,
xevent->xreparent.override_redirect));
/* Not currently handled */
return_val = FALSE;
break;
case ConfigureNotify:
GDK_NOTE (EVENTS,
g_message ("configure notify:\twindow: %ld x,y: %d %d w,h: %d %d b-w: %d above: %ld ovr: %d%s",
xevent->xconfigure.window,
xevent->xconfigure.x,
xevent->xconfigure.y,
xevent->xconfigure.width,
xevent->xconfigure.height,
xevent->xconfigure.border_width,
xevent->xconfigure.above,
xevent->xconfigure.override_redirect,
!window
? " (discarding)"
: GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD
? " (discarding child)"
: xevent->xconfigure.event != xevent->xconfigure.window
? " (discarding substructure)"
: ""));
if (window && GDK_WINDOW_TYPE (window) == GDK_WINDOW_ROOT)
{
window_private->width = xevent->xconfigure.width;
window_private->height = xevent->xconfigure.height;
_gdk_window_update_size (window);
_gdk_x11_drawable_update_size (window_private->impl);
_gdk_x11_screen_size_changed (screen, xevent);
}
#ifdef HAVE_XSYNC
if (toplevel && display_x11->use_sync && !XSyncValueIsZero (toplevel->pending_counter_value))
{
toplevel->current_counter_value = toplevel->pending_counter_value;
XSyncIntToValue (&toplevel->pending_counter_value, 0);
}
#endif
if (!window ||
xevent->xconfigure.event != xevent->xconfigure.window ||
GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD ||
GDK_WINDOW_TYPE (window) == GDK_WINDOW_ROOT)
return_val = FALSE;
else
{
event->configure.type = GDK_CONFIGURE;
event->configure.window = window;
event->configure.width = xevent->xconfigure.width;
event->configure.height = xevent->xconfigure.height;
if (!xevent->xconfigure.send_event &&
!xevent->xconfigure.override_redirect &&
!GDK_WINDOW_DESTROYED (window))
{
gint tx = 0;
gint ty = 0;
Window child_window = 0;
gdk_error_trap_push ();
if (XTranslateCoordinates (GDK_DRAWABLE_XDISPLAY (window),
GDK_DRAWABLE_XID (window),
screen_x11->xroot_window,
0, 0,
&tx, &ty,
&child_window))
{
event->configure.x = tx;
event->configure.y = ty;
}
gdk_error_trap_pop ();
}
else
{
event->configure.x = xevent->xconfigure.x;
event->configure.y = xevent->xconfigure.y;
}
window_private->x = event->configure.x;
window_private->y = event->configure.y;
window_private->width = xevent->xconfigure.width;
window_private->height = xevent->xconfigure.height;
_gdk_window_update_size (window);
_gdk_x11_drawable_update_size (window_private->impl);
if (window_private->resize_count >= 1)
{
window_private->resize_count -= 1;
if (window_private->resize_count == 0)
_gdk_moveresize_configure_done (display, window);
}
}
break;
case PropertyNotify:
GDK_NOTE (EVENTS,
g_message ("property notify:\twindow: %ld, atom(%ld): %s%s%s",
xevent->xproperty.window,
xevent->xproperty.atom,
"\"",
gdk_x11_get_xatom_name_for_display (display, xevent->xproperty.atom),
"\""));
if (window_private == NULL)
{
return_val = FALSE;
break;
}
/* We compare with the serial of the last time we mapped the
* window to avoid refetching properties that we set ourselves
*/
if (toplevel &&
xevent->xproperty.serial >= toplevel->map_serial)
{
if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"))
gdk_check_wm_state_changed (window);
if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP"))
gdk_check_wm_desktop_changed (window);
}
if (window_private->event_mask & GDK_PROPERTY_CHANGE_MASK)
{
event->property.type = GDK_PROPERTY_NOTIFY;
event->property.window = window;
event->property.atom = gdk_x11_xatom_to_atom_for_display (display, xevent->xproperty.atom);
event->property.time = xevent->xproperty.time;
event->property.state = xevent->xproperty.state;
}
else
return_val = FALSE;
break;
case SelectionClear:
GDK_NOTE (EVENTS,
g_message ("selection clear:\twindow: %ld",
xevent->xproperty.window));
if (_gdk_selection_filter_clear_event (&xevent->xselectionclear))
{
event->selection.type = GDK_SELECTION_CLEAR;
event->selection.window = window;
event->selection.selection = gdk_x11_xatom_to_atom_for_display (display, xevent->xselectionclear.selection);
event->selection.time = xevent->xselectionclear.time;
}
else
return_val = FALSE;
break;
case SelectionRequest:
GDK_NOTE (EVENTS,
g_message ("selection request:\twindow: %ld",
xevent->xproperty.window));
event->selection.type = GDK_SELECTION_REQUEST;
event->selection.window = window;
event->selection.selection = gdk_x11_xatom_to_atom_for_display (display, xevent->xselectionrequest.selection);
event->selection.target = gdk_x11_xatom_to_atom_for_display (display, xevent->xselectionrequest.target);
event->selection.property = gdk_x11_xatom_to_atom_for_display (display, xevent->xselectionrequest.property);
event->selection.requestor = xevent->xselectionrequest.requestor;
event->selection.time = xevent->xselectionrequest.time;
break;
case SelectionNotify:
GDK_NOTE (EVENTS,
g_message ("selection notify:\twindow: %ld",
xevent->xproperty.window));
event->selection.type = GDK_SELECTION_NOTIFY;
event->selection.window = window;
event->selection.selection = gdk_x11_xatom_to_atom_for_display (display, xevent->xselection.selection);
event->selection.target = gdk_x11_xatom_to_atom_for_display (display, xevent->xselection.target);
if (xevent->xselection.property == None)
event->selection.property = GDK_NONE;
else
event->selection.property = gdk_x11_xatom_to_atom_for_display (display, xevent->xselection.property);
event->selection.time = xevent->xselection.time;
break;
case ColormapNotify:
GDK_NOTE (EVENTS,
g_message ("colormap notify:\twindow: %ld",
xevent->xcolormap.window));
/* Not currently handled */
return_val = FALSE;
break;
case ClientMessage:
{
GList *tmp_list;
GdkFilterReturn result = GDK_FILTER_CONTINUE;
GdkAtom message_type = gdk_x11_xatom_to_atom_for_display (display, xevent->xclient.message_type);
GDK_NOTE (EVENTS,
g_message ("client message:\twindow: %ld",
xevent->xclient.window));
tmp_list = display_x11->client_filters;
while (tmp_list)
{
GdkClientFilter *filter = tmp_list->data;
tmp_list = tmp_list->next;
if (filter->type == message_type)
{
result = (*filter->function) (xevent, event, filter->data);
if (result != GDK_FILTER_CONTINUE)
break;
}
}
switch (result)
{
case GDK_FILTER_REMOVE:
return_val = FALSE;
break;
case GDK_FILTER_TRANSLATE:
return_val = TRUE;
break;
case GDK_FILTER_CONTINUE:
/* Send unknown ClientMessage's on to Gtk for it to use */
if (window_private == NULL)
{
return_val = FALSE;
}
else
{
event->client.type = GDK_CLIENT_EVENT;
event->client.window = window;
event->client.message_type = message_type;
event->client.data_format = xevent->xclient.format;
memcpy(&event->client.data, &xevent->xclient.data,
sizeof(event->client.data));
}
break;
}
}
break;
case MappingNotify:
GDK_NOTE (EVENTS,
g_message ("mapping notify"));
/* Let XLib know that there is a new keyboard mapping.
*/
XRefreshKeyboardMapping (&xevent->xmapping);
_gdk_keymap_keys_changed (display);
return_val = FALSE;
break;
default:
#ifdef HAVE_XFIXES
if (xevent->type - display_x11->xfixes_event_base == XFixesSelectionNotify)
{
XFixesSelectionNotifyEvent *selection_notify = (XFixesSelectionNotifyEvent *)xevent;
_gdk_x11_screen_process_owner_change (screen, xevent);
event->owner_change.type = GDK_OWNER_CHANGE;
event->owner_change.window = window;
event->owner_change.owner = selection_notify->owner;
event->owner_change.reason = selection_notify->subtype;
event->owner_change.selection =
gdk_x11_xatom_to_atom_for_display (display,
selection_notify->selection);
event->owner_change.time = selection_notify->timestamp;
event->owner_change.selection_time = selection_notify->selection_timestamp;
return_val = TRUE;
}
else
#endif
#ifdef HAVE_RANDR
if (xevent->type - display_x11->xrandr_event_base == RRScreenChangeNotify ||
xevent->type - display_x11->xrandr_event_base == RRNotify)
{
if (screen)
_gdk_x11_screen_size_changed (screen, xevent);
}
else
#endif
#if defined(HAVE_XCOMPOSITE) && defined (HAVE_XDAMAGE) && defined (HAVE_XFIXES)
if (display_x11->have_xdamage && window_private && window_private->composited &&
xevent->type == display_x11->xdamage_event_base + XDamageNotify &&
((XDamageNotifyEvent *) xevent)->damage == window_impl->damage)
{
XDamageNotifyEvent *damage_event = (XDamageNotifyEvent *) xevent;
XserverRegion repair;
GdkRectangle rect;
rect.x = window_private->x + damage_event->area.x;
rect.y = window_private->y + damage_event->area.y;
rect.width = damage_event->area.width;
rect.height = damage_event->area.height;
repair = XFixesCreateRegion (display_x11->xdisplay,
&damage_event->area, 1);
XDamageSubtract (display_x11->xdisplay,
window_impl->damage,
repair, None);
XFixesDestroyRegion (display_x11->xdisplay, repair);
if (window_private->parent != NULL)
_gdk_window_process_expose (GDK_WINDOW (window_private->parent),
damage_event->serial, &rect);
return_val = TRUE;
}
else
#endif
#ifdef HAVE_XKB
if (xevent->type == display_x11->xkb_event_type)
{
XkbEvent *xkb_event = (XkbEvent *) xevent;
switch (xkb_event->any.xkb_type)
{
case XkbNewKeyboardNotify:
case XkbMapNotify:
_gdk_keymap_keys_changed (display);
return_val = FALSE;
break;
case XkbStateNotify:
_gdk_keymap_state_changed (display, xevent);
break;
}
}
else
#endif
return_val = FALSE;
}
done:
if (return_val)
{
if (event->any.window)
g_object_ref (event->any.window);
}
else
{
/* Mark this event as having no resources to be freed */
event->any.window = NULL;
event->any.type = GDK_NOTHING;
}
if (window)
g_object_unref (window);
return return_val;
}
static GdkFilterReturn
gdk_wm_protocols_filter (GdkXEvent *xev,
GdkEvent *event,
gpointer data)
{
XEvent *xevent = (XEvent *)xev;
GdkWindow *win = event->any.window;
GdkDisplay *display;
Atom atom;
if (!win)
return GDK_FILTER_REMOVE;
display = GDK_WINDOW_DISPLAY (win);
atom = (Atom)xevent->xclient.data.l[0];
if (atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW"))
{
/* The delete window request specifies a window
* to delete. We don't actually destroy the
* window because "it is only a request". (The
* window might contain vital data that the
* program does not want destroyed). Instead
* the event is passed along to the program,
* which should then destroy the window.
*/
GDK_NOTE (EVENTS,
g_message ("delete window:\t\twindow: %ld",
xevent->xclient.window));
event->any.type = GDK_DELETE;
gdk_x11_window_set_user_time (win, xevent->xclient.data.l[1]);
return GDK_FILTER_TRANSLATE;
}
else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS"))
{
GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (event->any.window);
GdkWindowObject *private = (GdkWindowObject *)win;
/* There is no way of knowing reliably whether we are viewable;
* _gdk_x11_set_input_focus_safe() traps errors asynchronously.
*/
if (toplevel && private->accept_focus)
_gdk_x11_set_input_focus_safe (display, toplevel->focus_window,
RevertToParent,
xevent->xclient.data.l[1]);
return GDK_FILTER_REMOVE;
}
else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_PING") &&
!_gdk_x11_display_is_root_window (display,
xevent->xclient.window))
{
XClientMessageEvent xclient = xevent->xclient;
xclient.window = GDK_WINDOW_XROOTWIN (win);
XSendEvent (GDK_WINDOW_XDISPLAY (win),
xclient.window,
False,
SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xclient);
return GDK_FILTER_REMOVE;
}
else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_SYNC_REQUEST") &&
GDK_DISPLAY_X11 (display)->use_sync)
{
GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (event->any.window);
if (toplevel)
{
#ifdef HAVE_XSYNC
XSyncIntsToValue (&toplevel->pending_counter_value,
xevent->xclient.data.l[2],
xevent->xclient.data.l[3]);
#endif
}
return GDK_FILTER_REMOVE;
}
return GDK_FILTER_CONTINUE;
}
static void
_gdk_event_init (GdkDisplay *display)
{
GdkDisplayX11 *display_x11;
GdkDeviceManager *device_manager;
display_x11 = GDK_DISPLAY_X11 (display);
display_x11->event_source = gdk_event_source_new (display);
gdk_event_source_add_translator ((GdkEventSource *) display_x11->event_source,
GDK_EVENT_TRANSLATOR (display));
device_manager = gdk_display_get_device_manager (display);
gdk_event_source_add_translator ((GdkEventSource *) display_x11->event_source,
GDK_EVENT_TRANSLATOR (device_manager));
gdk_display_add_client_message_filter (display,
gdk_atom_intern_static_string ("WM_PROTOCOLS"),
gdk_wm_protocols_filter,
NULL);
}
static void
_gdk_input_init (GdkDisplay *display)
{
GdkDisplayX11 *display_x11;
GdkDeviceManager *device_manager;
GdkDevice *device;
GList *list, *l;
display_x11 = GDK_DISPLAY_X11 (display);
device_manager = gdk_display_get_device_manager (display);
/* For backwards compatibility, just add
* floating devices that are not keyboards.
*/
list = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_FLOATING);
for (l = list; l; l = l->next)
{
device = l->data;
if (device->source == GDK_SOURCE_KEYBOARD)
continue;
display_x11->input_devices = g_list_prepend (display_x11->input_devices, l->data);
}
g_list_free (list);
/* Now set "core" pointer to the first
* master device that is a pointer.
*/
list = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
for (l = list; l; l = l->next)
{
device = list->data;
if (device->source != GDK_SOURCE_MOUSE)
continue;
display->core_pointer = device;
break;
}
/* Add the core pointer to the devices list */
display_x11->input_devices = g_list_prepend (display_x11->input_devices, display->core_pointer);
g_list_free (list);
}
/**
* gdk_display_open:
* @display_name: the name of the display to open
* @returns: a #GdkDisplay, or %NULL if the display
* could not be opened.
*
* Opens a display.
*
* Since: 2.2
*/
GdkDisplay *
gdk_display_open (const gchar *display_name)
{
Display *xdisplay;
GdkDisplay *display;
GdkDisplayX11 *display_x11;
GdkWindowAttr attr;
gint argc;
gchar *argv[1];
const char *sm_client_id;
XClassHint *class_hint;
gulong pid;
gint i;
gint ignore;
gint maj, min;
xdisplay = XOpenDisplay (display_name);
if (!xdisplay)
return NULL;
display = g_object_new (GDK_TYPE_DISPLAY_X11, NULL);
display_x11 = GDK_DISPLAY_X11 (display);
display_x11->xdisplay = xdisplay;
#ifdef HAVE_X11R6
/* Set up handlers for Xlib internal connections */
XAddConnectionWatch (xdisplay, gdk_internal_connection_watch, NULL);
#endif /* HAVE_X11R6 */
_gdk_x11_precache_atoms (display, precache_atoms, G_N_ELEMENTS (precache_atoms));
/* RandR must be initialized before we initialize the screens */
display_x11->have_randr13 = FALSE;
#ifdef HAVE_RANDR
if (XRRQueryExtension (display_x11->xdisplay,
&display_x11->xrandr_event_base, &ignore))
{
int major, minor;
XRRQueryVersion (display_x11->xdisplay, &major, &minor);
if ((major == 1 && minor >= 3) || major > 1)
display_x11->have_randr13 = TRUE;
gdk_x11_register_standard_event_type (display, display_x11->xrandr_event_base, RRNumberEvents);
}
#endif
/* initialize the display's screens */
display_x11->screens = g_new (GdkScreen *, ScreenCount (display_x11->xdisplay));
for (i = 0; i < ScreenCount (display_x11->xdisplay); i++)
display_x11->screens[i] = _gdk_x11_screen_new (display, i);
/* We need to initialize events after we have the screen
* structures in places
*/
for (i = 0; i < ScreenCount (display_x11->xdisplay); i++)
_gdk_screen_x11_events_init (display_x11->screens[i]);
/*set the default screen */
display_x11->default_screen = display_x11->screens[DefaultScreen (display_x11->xdisplay)];
display->device_manager = _gdk_device_manager_new (display);
_gdk_event_init (display);
attr.window_type = GDK_WINDOW_TOPLEVEL;
attr.wclass = GDK_INPUT_OUTPUT;
attr.x = 10;
attr.y = 10;
attr.width = 10;
attr.height = 10;
attr.event_mask = 0;
display_x11->leader_gdk_window = gdk_window_new (GDK_SCREEN_X11 (display_x11->default_screen)->root_window,
&attr, GDK_WA_X | GDK_WA_Y);
(_gdk_x11_window_get_toplevel (display_x11->leader_gdk_window))->is_leader = TRUE;
display_x11->leader_window = GDK_WINDOW_XID (display_x11->leader_gdk_window);
display_x11->leader_window_title_set = FALSE;
#ifdef HAVE_XFIXES
if (XFixesQueryExtension (display_x11->xdisplay,
&display_x11->xfixes_event_base,
&ignore))
{
display_x11->have_xfixes = TRUE;
gdk_x11_register_standard_event_type (display,
display_x11->xfixes_event_base,
XFixesNumberEvents);
}
else
#endif
display_x11->have_xfixes = FALSE;
#ifdef HAVE_XCOMPOSITE
if (XCompositeQueryExtension (display_x11->xdisplay,
&ignore, &ignore))
{
int major, minor;
XCompositeQueryVersion (display_x11->xdisplay, &major, &minor);
/* Prior to Composite version 0.4, composited windows clipped their
* parents, so you had to use IncludeInferiors to draw to the parent
* This isn't useful for our purposes, so require 0.4
*/
display_x11->have_xcomposite = major > 0 || (major == 0 && minor >= 4);
}
else
#endif
display_x11->have_xcomposite = FALSE;
#ifdef HAVE_XDAMAGE
if (XDamageQueryExtension (display_x11->xdisplay,
&display_x11->xdamage_event_base,
&ignore))
{
display_x11->have_xdamage = TRUE;
gdk_x11_register_standard_event_type (display,
display_x11->xdamage_event_base,
XDamageNumberEvents);
}
else
#endif
display_x11->have_xdamage = FALSE;
display_x11->have_shapes = FALSE;
display_x11->have_input_shapes = FALSE;
if (XShapeQueryExtension (GDK_DISPLAY_XDISPLAY (display), &display_x11->shape_event_base, &ignore))
{
display_x11->have_shapes = TRUE;
#ifdef ShapeInput
if (XShapeQueryVersion (GDK_DISPLAY_XDISPLAY (display), &maj, &min))
display_x11->have_input_shapes = (maj == 1 && min >= 1);
#endif
}
display_x11->trusted_client = TRUE;
{
Window root, child;
int rootx, rooty, winx, winy;
unsigned int xmask;
gdk_error_trap_push ();
XQueryPointer (display_x11->xdisplay,
GDK_SCREEN_X11 (display_x11->default_screen)->xroot_window,
&root, &child, &rootx, &rooty, &winx, &winy, &xmask);
gdk_flush ();
if (G_UNLIKELY (gdk_error_trap_pop () == BadWindow))
{
g_warning ("Connection to display %s appears to be untrusted. Pointer and keyboard grabs and inter-client communication may not work as expected.", gdk_display_get_name (display));
display_x11->trusted_client = FALSE;
}
}
if (_gdk_synchronize)
XSynchronize (display_x11->xdisplay, True);
class_hint = XAllocClassHint();
class_hint->res_name = g_get_prgname ();
class_hint->res_class = (char *)gdk_get_program_class ();
/* XmbSetWMProperties sets the RESOURCE_NAME environment variable
* from argv[0], so we just synthesize an argument array here.
*/
argc = 1;
argv[0] = g_get_prgname ();
XmbSetWMProperties (display_x11->xdisplay,
display_x11->leader_window,
NULL, NULL, argv, argc, NULL, NULL,
class_hint);
XFree (class_hint);
sm_client_id = _gdk_get_sm_client_id ();
if (sm_client_id)
_gdk_windowing_display_set_sm_client_id (display, sm_client_id);
pid = getpid ();
XChangeProperty (display_x11->xdisplay,
display_x11->leader_window,
gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_PID"),
XA_CARDINAL, 32, PropModeReplace, (guchar *) & pid, 1);
/* We don't yet know a valid time. */
display_x11->user_time = 0;
#ifdef HAVE_XKB
{
gint xkb_major = XkbMajorVersion;
gint xkb_minor = XkbMinorVersion;
if (XkbLibraryVersion (&xkb_major, &xkb_minor))
{
xkb_major = XkbMajorVersion;
xkb_minor = XkbMinorVersion;
if (XkbQueryExtension (display_x11->xdisplay,
NULL, &display_x11->xkb_event_type, NULL,
&xkb_major, &xkb_minor))
{
Bool detectable_autorepeat_supported;
display_x11->use_xkb = TRUE;
XkbSelectEvents (display_x11->xdisplay,
XkbUseCoreKbd,
XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask,
XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask);
/* keep this in sync with _gdk_keymap_state_changed() */
XkbSelectEventDetails (display_x11->xdisplay,
XkbUseCoreKbd, XkbStateNotify,
XkbAllStateComponentsMask,
XkbGroupLockMask|XkbModifierLockMask);
XkbSetDetectableAutoRepeat (display_x11->xdisplay,
True,
&detectable_autorepeat_supported);
GDK_NOTE (MISC, g_message ("Detectable autorepeat %s.",
detectable_autorepeat_supported ?
"supported" : "not supported"));
display_x11->have_xkb_autorepeat = detectable_autorepeat_supported;
}
}
}
#endif
display_x11->use_sync = FALSE;
#ifdef HAVE_XSYNC
{
int major, minor;
int error_base, event_base;
if (XSyncQueryExtension (display_x11->xdisplay,
&event_base, &error_base) &&
XSyncInitialize (display_x11->xdisplay,
&major, &minor))
display_x11->use_sync = TRUE;
}
#endif
_gdk_input_init (display);
_gdk_dnd_init (display);
for (i = 0; i < ScreenCount (display_x11->xdisplay); i++)
_gdk_x11_screen_setup (display_x11->screens[i]);
g_signal_emit_by_name (display, "opened");
g_signal_emit_by_name (gdk_display_manager_get(),
"display_opened", display);
return display;
}
#ifdef HAVE_X11R6
/*
* XLib internal connection handling
*/
typedef struct _GdkInternalConnection GdkInternalConnection;
struct _GdkInternalConnection
{
gint fd;
GSource *source;
Display *display;
};
static gboolean
process_internal_connection (GIOChannel *gioc,
GIOCondition cond,
gpointer data)
{
GdkInternalConnection *connection = (GdkInternalConnection *)data;
GDK_THREADS_ENTER ();
XProcessInternalConnection ((Display*)connection->display, connection->fd);
GDK_THREADS_LEAVE ();
return TRUE;
}
gulong
_gdk_windowing_window_get_next_serial (GdkDisplay *display)
{
return NextRequest (GDK_DISPLAY_XDISPLAY (display));
}
static GdkInternalConnection *
gdk_add_connection_handler (Display *display,
guint fd)
{
GIOChannel *io_channel;
GdkInternalConnection *connection;
connection = g_new (GdkInternalConnection, 1);
connection->fd = fd;
connection->display = display;
io_channel = g_io_channel_unix_new (fd);
connection->source = g_io_create_watch (io_channel, G_IO_IN);
g_source_set_callback (connection->source,
(GSourceFunc)process_internal_connection, connection, NULL);
g_source_attach (connection->source, NULL);
g_io_channel_unref (io_channel);
return connection;
}
static void
gdk_remove_connection_handler (GdkInternalConnection *connection)
{
g_source_destroy (connection->source);
g_free (connection);
}
static void
gdk_internal_connection_watch (Display *display,
XPointer arg,
gint fd,
gboolean opening,
XPointer *watch_data)
{
if (opening)
*watch_data = (XPointer)gdk_add_connection_handler (display, fd);
else
gdk_remove_connection_handler ((GdkInternalConnection *)*watch_data);
}
#endif /* HAVE_X11R6 */
/**
* gdk_display_get_name:
* @display: a #GdkDisplay
*
* Gets the name of the display.
*
* Returns: a string representing the display name. This string is owned
* by GDK and should not be modified or freed.
*
* Since: 2.2
*/
G_CONST_RETURN gchar *
gdk_display_get_name (GdkDisplay *display)
{
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
return (gchar *) DisplayString (GDK_DISPLAY_X11 (display)->xdisplay);
}
/**
* gdk_display_get_n_screens:
* @display: a #GdkDisplay
*
* Gets the number of screen managed by the @display.
*
* Returns: number of screens.
*
* Since: 2.2
*/
gint
gdk_display_get_n_screens (GdkDisplay *display)
{
g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
return ScreenCount (GDK_DISPLAY_X11 (display)->xdisplay);
}
/**
* gdk_display_get_screen:
* @display: a #GdkDisplay
* @screen_num: the screen number
*
* Returns a screen object for one of the screens of the display.
*
* Returns: the #GdkScreen object
*
* Since: 2.2
*/
GdkScreen *
gdk_display_get_screen (GdkDisplay *display,
gint screen_num)
{
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
g_return_val_if_fail (ScreenCount (GDK_DISPLAY_X11 (display)->xdisplay) > screen_num, NULL);
return GDK_DISPLAY_X11 (display)->screens[screen_num];
}
/**
* gdk_display_get_default_screen:
* @display: a #GdkDisplay
*
* Get the default #GdkScreen for @display.
*
* Returns: the default #GdkScreen object for @display
*
* Since: 2.2
*/
GdkScreen *
gdk_display_get_default_screen (GdkDisplay *display)
{
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
return GDK_DISPLAY_X11 (display)->default_screen;
}
gboolean
_gdk_x11_display_is_root_window (GdkDisplay *display,
Window xroot_window)
{
GdkDisplayX11 *display_x11;
gint i;
g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
display_x11 = GDK_DISPLAY_X11 (display);
for (i = 0; i < ScreenCount (display_x11->xdisplay); i++)
{
if (GDK_SCREEN_XROOTWIN (display_x11->screens[i]) == xroot_window)
return TRUE;
}
return FALSE;
}
struct XPointerUngrabInfo {
GdkDisplay *display;
guint32 time;
};
static void
device_ungrab_callback (GdkDisplay *display,
gpointer data,
gulong serial)
{
GdkDevice *device = data;
_gdk_display_device_grab_update (display, device, serial);
}
#define XSERVER_TIME_IS_LATER(time1, time2) \
( (( time1 > time2 ) && ( time1 - time2 < ((guint32)-1)/2 )) || \
(( time1 < time2 ) && ( time2 - time1 > ((guint32)-1)/2 )) \
)
/**
* gdk_device_ungrab:
* @device: a #GdkDevice
* @time_: a timestap (e.g. %GDK_CURRENT_TIME).
*
* Release any grab on @device.
*
* Since: 3.0
*/
void
gdk_device_ungrab (GdkDevice *device,
guint32 time_)
{
GdkDisplay *display;
Display *xdisplay;
GdkDeviceGrabInfo *grab;
unsigned long serial;
g_return_if_fail (GDK_IS_DEVICE (device));
display = gdk_device_get_display (device);
xdisplay = GDK_DISPLAY_XDISPLAY (display);
serial = NextRequest (xdisplay);
GDK_DEVICE_GET_CLASS (device)->ungrab (device, time_);
XFlush (xdisplay);
grab = _gdk_display_get_last_device_grab (display, device);
if (grab &&
(time_ == GDK_CURRENT_TIME ||
grab->time == GDK_CURRENT_TIME ||
!XSERVER_TIME_IS_LATER (grab->time, time_)))
{
grab->serial_end = serial;
_gdk_x11_roundtrip_async (display,
device_ungrab_callback,
device);
}
}
/**
* gdk_display_beep:
* @display: a #GdkDisplay
*
* Emits a short beep on @display
*
* Since: 2.2
*/
void
gdk_display_beep (GdkDisplay *display)
{
g_return_if_fail (GDK_IS_DISPLAY (display));
#ifdef HAVE_XKB
XkbBell (GDK_DISPLAY_XDISPLAY (display), None, 0, None);
#else
XBell (GDK_DISPLAY_XDISPLAY (display), 0);
#endif
}
/**
* gdk_display_sync:
* @display: a #GdkDisplay
*
* Flushes any requests queued for the windowing system and waits until all
* requests have been handled. This is often used for making sure that the
* display is synchronized with the current state of the program. Calling
* gdk_display_sync() before gdk_error_trap_pop() makes sure that any errors
* generated from earlier requests are handled before the error trap is
* removed.
*
* This is most useful for X11. On windowing systems where requests are
* handled synchronously, this function will do nothing.
*
* Since: 2.2
*/
void
gdk_display_sync (GdkDisplay *display)
{
g_return_if_fail (GDK_IS_DISPLAY (display));
XSync (GDK_DISPLAY_XDISPLAY (display), False);
}
/**
* gdk_display_flush:
* @display: a #GdkDisplay
*
* Flushes any requests queued for the windowing system; this happens automatically
* when the main loop blocks waiting for new events, but if your application
* is drawing without returning control to the main loop, you may need
* to call this function explicitely. A common case where this function
* needs to be called is when an application is executing drawing commands
* from a thread other than the thread where the main loop is running.
*
* This is most useful for X11. On windowing systems where requests are
* handled synchronously, this function will do nothing.
*
* Since: 2.4
*/
void
gdk_display_flush (GdkDisplay *display)
{
g_return_if_fail (GDK_IS_DISPLAY (display));
if (!display->closed)
XFlush (GDK_DISPLAY_XDISPLAY (display));
}
/**
* gdk_display_get_default_group:
* @display: a #GdkDisplay
*
* Returns the default group leader window for all toplevel windows
* on @display. This window is implicitly created by GDK.
* See gdk_window_set_group().
*
* Return value: The default group leader window for @display
*
* Since: 2.4
**/
GdkWindow *
gdk_display_get_default_group (GdkDisplay *display)
{
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
return GDK_DISPLAY_X11 (display)->leader_gdk_window;
}
/**
* gdk_x11_display_grab:
* @display: a #GdkDisplay
*
* Call XGrabServer() on @display.
* To ungrab the display again, use gdk_x11_display_ungrab().
*
* gdk_x11_display_grab()/gdk_x11_display_ungrab() calls can be nested.
*
* Since: 2.2
**/
void
gdk_x11_display_grab (GdkDisplay *display)
{
GdkDisplayX11 *display_x11;
g_return_if_fail (GDK_IS_DISPLAY (display));
display_x11 = GDK_DISPLAY_X11 (display);
if (display_x11->grab_count == 0)
XGrabServer (display_x11->xdisplay);
display_x11->grab_count++;
}
/**
* gdk_x11_display_ungrab:
* @display: a #GdkDisplay
*
* Ungrab @display after it has been grabbed with
* gdk_x11_display_grab().
*
* Since: 2.2
**/
void
gdk_x11_display_ungrab (GdkDisplay *display)
{
GdkDisplayX11 *display_x11;
g_return_if_fail (GDK_IS_DISPLAY (display));
display_x11 = GDK_DISPLAY_X11 (display);;
g_return_if_fail (display_x11->grab_count > 0);
display_x11->grab_count--;
if (display_x11->grab_count == 0)
{
XUngrabServer (display_x11->xdisplay);
XFlush (display_x11->xdisplay);
}
}
static void
gdk_display_x11_dispose (GObject *object)
{
GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (object);
gint i;
g_list_foreach (display_x11->input_devices, (GFunc) g_object_run_dispose, NULL);
for (i = 0; i < ScreenCount (display_x11->xdisplay); i++)
_gdk_screen_close (display_x11->screens[i]);
if (display_x11->event_source)
{
g_source_destroy (display_x11->event_source);
g_source_unref (display_x11->event_source);
display_x11->event_source = NULL;
}
G_OBJECT_CLASS (_gdk_display_x11_parent_class)->dispose (object);
}
static void
gdk_display_x11_finalize (GObject *object)
{
GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (object);
gint i;
/* Keymap */
if (display_x11->keymap)
g_object_unref (display_x11->keymap);
/* Free motif Dnd */
if (display_x11->motif_target_lists)
{
for (i = 0; i < display_x11->motif_n_target_lists; i++)
g_list_free (display_x11->motif_target_lists[i]);
g_free (display_x11->motif_target_lists);
}
_gdk_x11_cursor_display_finalize (GDK_DISPLAY_OBJECT(display_x11));
/* Atom Hashtable */
g_hash_table_destroy (display_x11->atom_from_virtual);
g_hash_table_destroy (display_x11->atom_to_virtual);
/* Leader Window */
XDestroyWindow (display_x11->xdisplay, display_x11->leader_window);
/* list of filters for client messages */
g_list_foreach (display_x11->client_filters, (GFunc) g_free, NULL);
g_list_free (display_x11->client_filters);
/* List of event window extraction functions */
g_slist_foreach (display_x11->event_types, (GFunc)g_free, NULL);
g_slist_free (display_x11->event_types);
/* input GdkDevice list */
g_list_foreach (display_x11->input_devices, (GFunc) g_object_unref, NULL);
g_list_free (display_x11->input_devices);
/* input GdkWindow list */
g_list_foreach (display_x11->input_windows, (GFunc) g_free, NULL);
g_list_free (display_x11->input_windows);
/* Free all GdkScreens */
for (i = 0; i < ScreenCount (display_x11->xdisplay); i++)
g_object_unref (display_x11->screens[i]);
g_free (display_x11->screens);
g_free (display_x11->startup_notification_id);
/* X ID hashtable */
g_hash_table_destroy (display_x11->xid_ht);
XCloseDisplay (display_x11->xdisplay);
G_OBJECT_CLASS (_gdk_display_x11_parent_class)->finalize (object);
}
/**
* gdk_x11_lookup_xdisplay:
* @xdisplay: a pointer to an X Display
*
* Find the #GdkDisplay corresponding to @display, if any exists.
*
* Return value: the #GdkDisplay, if found, otherwise %NULL.
*
* Since: 2.2
**/
GdkDisplay *
gdk_x11_lookup_xdisplay (Display *xdisplay)
{
GSList *tmp_list;
for (tmp_list = _gdk_displays; tmp_list; tmp_list = tmp_list->next)
{
if (GDK_DISPLAY_XDISPLAY (tmp_list->data) == xdisplay)
return tmp_list->data;
}
return NULL;
}
/**
* _gdk_x11_display_screen_for_xrootwin:
* @display: a #GdkDisplay
* @xrootwin: window ID for one of of the screen's of the display.
*
* Given the root window ID of one of the screen's of a #GdkDisplay,
* finds the screen.
*
* Return value: the #GdkScreen corresponding to @xrootwin, or %NULL.
**/
GdkScreen *
_gdk_x11_display_screen_for_xrootwin (GdkDisplay *display,
Window xrootwin)
{
gint i;
for (i = 0; i < ScreenCount (GDK_DISPLAY_X11 (display)->xdisplay); i++)
{
GdkScreen *screen = gdk_display_get_screen (display, i);
if (GDK_SCREEN_XROOTWIN (screen) == xrootwin)
return screen;
}
return NULL;
}
/**
* gdk_x11_display_get_xdisplay:
* @display: a #GdkDisplay
* @returns: an X display.
*
* Returns the X display of a #GdkDisplay.
*
* Since: 2.2
*/
Display *
gdk_x11_display_get_xdisplay (GdkDisplay *display)
{
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
return GDK_DISPLAY_X11 (display)->xdisplay;
}
void
_gdk_windowing_set_default_display (GdkDisplay *display)
{
GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
const gchar *startup_id;
if (!display)
return;
g_free (display_x11->startup_notification_id);
display_x11->startup_notification_id = NULL;
startup_id = g_getenv ("DESKTOP_STARTUP_ID");
if (startup_id && *startup_id != '\0')
{
gchar *time_str;
if (!g_utf8_validate (startup_id, -1, NULL))
g_warning ("DESKTOP_STARTUP_ID contains invalid UTF-8");
else
display_x11->startup_notification_id = g_strdup (startup_id);
/* Find the launch time from the startup_id, if it's there. Newer spec
* states that the startup_id is of the form <unique>_TIME<timestamp>
*/
time_str = g_strrstr (startup_id, "_TIME");
if (time_str != NULL)
{
gulong retval;
gchar *end;
errno = 0;
/* Skip past the "_TIME" part */
time_str += 5;
retval = strtoul (time_str, &end, 0);
if (end != time_str && errno == 0)
display_x11->user_time = retval;
}
/* Clear the environment variable so it won't be inherited by
* child processes and confuse things.
*/
g_unsetenv ("DESKTOP_STARTUP_ID");
/* Set the startup id on the leader window so it
* applies to all windows we create on this display
*/
XChangeProperty (display_x11->xdisplay,
display_x11->leader_window,
gdk_x11_get_xatom_by_name_for_display (display, "_NET_STARTUP_ID"),
gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8,
PropModeReplace,
(guchar *)startup_id, strlen (startup_id));
}
}
static void
broadcast_xmessage (GdkDisplay *display,
const char *message_type,
const char *message_type_begin,
const char *message)
{
Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
GdkScreen *screen = gdk_display_get_default_screen (display);
GdkWindow *root_window = gdk_screen_get_root_window (screen);
Window xroot_window = GDK_WINDOW_XID (root_window);
Atom type_atom;
Atom type_atom_begin;
Window xwindow;
if (!G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client))
return;
{
XSetWindowAttributes attrs;
attrs.override_redirect = True;
attrs.event_mask = PropertyChangeMask | StructureNotifyMask;
xwindow =
XCreateWindow (xdisplay,
xroot_window,
-100, -100, 1, 1,
0,
CopyFromParent,
CopyFromParent,
(Visual *)CopyFromParent,
CWOverrideRedirect | CWEventMask,
&attrs);
}
type_atom = gdk_x11_get_xatom_by_name_for_display (display,
message_type);
type_atom_begin = gdk_x11_get_xatom_by_name_for_display (display,
message_type_begin);
{
XClientMessageEvent xclient;
const char *src;
const char *src_end;
char *dest;
char *dest_end;
memset(&xclient, 0, sizeof (xclient));
xclient.type = ClientMessage;
xclient.message_type = type_atom_begin;
xclient.display =xdisplay;
xclient.window = xwindow;
xclient.format = 8;
src = message;
src_end = message + strlen (message) + 1; /* +1 to include nul byte */
while (src != src_end)
{
dest = &xclient.data.b[0];
dest_end = dest + 20;
while (dest != dest_end &&
src != src_end)
{
*dest = *src;
++dest;
++src;
}
while (dest != dest_end)
{
*dest = 0;
++dest;
}
XSendEvent (xdisplay,
xroot_window,
False,
PropertyChangeMask,
(XEvent *)&xclient);
xclient.message_type = type_atom;
}
}
XDestroyWindow (xdisplay, xwindow);
XFlush (xdisplay);
}
/**
* gdk_x11_display_broadcast_startup_message:
* @display: a #GdkDisplay
* @message_type: startup notification message type ("new", "change",
* or "remove")
* @...: a list of key/value pairs (as strings), terminated by a
* %NULL key. (A %NULL value for a key will cause that key to be
* skipped in the output.)
*
* Sends a startup notification message of type @message_type to
* @display.
*
* This is a convenience function for use by code that implements the
* freedesktop startup notification specification. Applications should
* not normally need to call it directly. See the <ulink
* url="http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt">Startup
* Notification Protocol specification</ulink> for
* definitions of the message types and keys that can be used.
*
* Since: 2.12
**/
void
gdk_x11_display_broadcast_startup_message (GdkDisplay *display,
const char *message_type,
...)
{
GString *message;
va_list ap;
const char *key, *value, *p;
message = g_string_new (message_type);
g_string_append_c (message, ':');
va_start (ap, message_type);
while ((key = va_arg (ap, const char *)))
{
value = va_arg (ap, const char *);
if (!value)
continue;
g_string_append_printf (message, " %s=\"", key);
for (p = value; *p; p++)
{
switch (*p)
{
case ' ':
case '"':
case '\\':
g_string_append_c (message, '\\');
break;
}
g_string_append_c (message, *p);
}
g_string_append_c (message, '\"');
}
va_end (ap);
broadcast_xmessage (display,
"_NET_STARTUP_INFO",
"_NET_STARTUP_INFO_BEGIN",
message->str);
g_string_free (message, TRUE);
}
/**
* gdk_notify_startup_complete:
*
* Indicates to the GUI environment that the application has finished
* loading. If the applications opens windows, this function is
* normally called after opening the application's initial set of
* windows.
*
* GTK+ will call this function automatically after opening the first
* #GtkWindow unless gtk_window_set_auto_startup_notification() is called
* to disable that feature.
*
* Since: 2.2
**/
void
gdk_notify_startup_complete (void)
{
GdkDisplay *display;
GdkDisplayX11 *display_x11;
display = gdk_display_get_default ();
if (!display)
return;
display_x11 = GDK_DISPLAY_X11 (display);
if (display_x11->startup_notification_id == NULL)
return;
gdk_notify_startup_complete_with_id (display_x11->startup_notification_id);
}
/**
* gdk_notify_startup_complete_with_id:
* @startup_id: a startup-notification identifier, for which notification
* process should be completed
*
* Indicates to the GUI environment that the application has finished
* loading, using a given identifier.
*
* GTK+ will call this function automatically for #GtkWindow with custom
* startup-notification identifier unless
* gtk_window_set_auto_startup_notification() is called to disable
* that feature.
*
* Since: 2.12
**/
void
gdk_notify_startup_complete_with_id (const gchar* startup_id)
{
GdkDisplay *display;
display = gdk_display_get_default ();
if (!display)
return;
gdk_x11_display_broadcast_startup_message (display, "remove",
"ID", startup_id,
NULL);
}
/**
* gdk_display_supports_selection_notification:
* @display: a #GdkDisplay
*
* Returns whether #GdkEventOwnerChange events will be
* sent when the owner of a selection changes.
*
* Return value: whether #GdkEventOwnerChange events will
* be sent.
*
* Since: 2.6
**/
gboolean
gdk_display_supports_selection_notification (GdkDisplay *display)
{
GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
return display_x11->have_xfixes;
}
/**
* gdk_display_request_selection_notification:
* @display: a #GdkDisplay
* @selection: the #GdkAtom naming the selection for which
* ownership change notification is requested
*
* Request #GdkEventOwnerChange events for ownership changes
* of the selection named by the given atom.
*
* Return value: whether #GdkEventOwnerChange events will
* be sent.
*
* Since: 2.6
**/
gboolean
gdk_display_request_selection_notification (GdkDisplay *display,
GdkAtom selection)
{
#ifdef HAVE_XFIXES
GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
Atom atom;
if (display_x11->have_xfixes)
{
atom = gdk_x11_atom_to_xatom_for_display (display,
selection);
XFixesSelectSelectionInput (display_x11->xdisplay,
display_x11->leader_window,
atom,
XFixesSetSelectionOwnerNotifyMask |
XFixesSelectionWindowDestroyNotifyMask |
XFixesSelectionClientCloseNotifyMask);
return TRUE;
}
else
#endif
return FALSE;
}
/**
* gdk_display_supports_clipboard_persistence
* @display: a #GdkDisplay
*
* Returns whether the speicifed display supports clipboard
* persistance; i.e. if it's possible to store the clipboard data after an
* application has quit. On X11 this checks if a clipboard daemon is
* running.
*
* Returns: %TRUE if the display supports clipboard persistance.
*
* Since: 2.6
*/
gboolean
gdk_display_supports_clipboard_persistence (GdkDisplay *display)
{
Atom clipboard_manager;
/* It might make sense to cache this */
clipboard_manager = gdk_x11_get_xatom_by_name_for_display (display, "CLIPBOARD_MANAGER");
return XGetSelectionOwner (GDK_DISPLAY_X11 (display)->xdisplay, clipboard_manager) != None;
}
/**
* gdk_display_store_clipboard
* @display: a #GdkDisplay
* @clipboard_window: a #GdkWindow belonging to the clipboard owner
* @time_: a timestamp
* @targets: an array of targets that should be saved, or %NULL
* if all available targets should be saved.
* @n_targets: length of the @targets array
*
* Issues a request to the clipboard manager to store the
* clipboard data. On X11, this is a special program that works
* according to the freedesktop clipboard specification, available at
* <ulink url="http://www.freedesktop.org/Standards/clipboard-manager-spec">
* http://www.freedesktop.org/Standards/clipboard-manager-spec</ulink>.
*
* Since: 2.6
*/
void
gdk_display_store_clipboard (GdkDisplay *display,
GdkWindow *clipboard_window,
guint32 time_,
const GdkAtom *targets,
gint n_targets)
{
GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
Atom clipboard_manager, save_targets;
g_return_if_fail (GDK_WINDOW_IS_X11 (clipboard_window));
clipboard_manager = gdk_x11_get_xatom_by_name_for_display (display, "CLIPBOARD_MANAGER");
save_targets = gdk_x11_get_xatom_by_name_for_display (display, "SAVE_TARGETS");
gdk_error_trap_push ();
if (XGetSelectionOwner (display_x11->xdisplay, clipboard_manager) != None)
{
Atom property_name = None;
Atom *xatoms;
int i;
if (n_targets > 0)
{
property_name = gdk_x11_atom_to_xatom_for_display (display, _gdk_selection_property);
xatoms = g_new (Atom, n_targets);
for (i = 0; i < n_targets; i++)
xatoms[i] = gdk_x11_atom_to_xatom_for_display (display, targets[i]);
XChangeProperty (display_x11->xdisplay, GDK_WINDOW_XID (clipboard_window),
property_name, XA_ATOM,
32, PropModeReplace, (guchar *)xatoms, n_targets);
g_free (xatoms);
}
XConvertSelection (display_x11->xdisplay,
clipboard_manager, save_targets, property_name,
GDK_WINDOW_XID (clipboard_window), time_);
}
gdk_error_trap_pop ();
}
/**
* gdk_x11_display_get_user_time:
* @display: a #GdkDisplay
*
* Returns the timestamp of the last user interaction on
* @display. The timestamp is taken from events caused
* by user interaction such as key presses or pointer
* movements. See gdk_x11_window_set_user_time().
*
* Returns: the timestamp of the last user interaction
*
* Since: 2.8
*/
guint32
gdk_x11_display_get_user_time (GdkDisplay *display)
{
return GDK_DISPLAY_X11 (display)->user_time;
}
/**
* gdk_display_supports_shapes:
* @display: a #GdkDisplay
*
* Returns %TRUE if gdk_window_shape_combine_mask() can
* be used to create shaped windows on @display.
*
* Returns: %TRUE if shaped windows are supported
*
* Since: 2.10
*/
gboolean
gdk_display_supports_shapes (GdkDisplay *display)
{
return GDK_DISPLAY_X11 (display)->have_shapes;
}
/**
* gdk_display_supports_input_shapes:
* @display: a #GdkDisplay
*
* Returns %TRUE if gdk_window_input_shape_combine_mask() can
* be used to modify the input shape of windows on @display.
*
* Returns: %TRUE if windows with modified input shape are supported
*
* Since: 2.10
*/
gboolean
gdk_display_supports_input_shapes (GdkDisplay *display)
{
return GDK_DISPLAY_X11 (display)->have_input_shapes;
}
/**
* gdk_x11_display_get_startup_notification_id:
* @display: a #GdkDisplay
*
* Gets the startup notification ID for a display.
*
* Returns: the startup notification ID for @display
*
* Since: 2.12
*/
G_CONST_RETURN gchar *
gdk_x11_display_get_startup_notification_id (GdkDisplay *display)
{
return GDK_DISPLAY_X11 (display)->startup_notification_id;
}
/**
* gdk_display_supports_composite:
* @display: a #GdkDisplay
*
* Returns %TRUE if gdk_window_set_composited() can be used
* to redirect drawing on the window using compositing.
*
* Currently this only works on X11 with XComposite and
* XDamage extensions available.
*
* Returns: %TRUE if windows may be composited.
*
* Since: 2.12
*/
gboolean
gdk_display_supports_composite (GdkDisplay *display)
{
GdkDisplayX11 *x11_display = GDK_DISPLAY_X11 (display);
return x11_display->have_xcomposite &&
x11_display->have_xdamage &&
x11_display->have_xfixes;
}
/**
* gdk_display_list_devices:
* @display: a #GdkDisplay
*
* Returns the list of available input devices attached to @display.
* The list is statically allocated and should not be freed.
*
* Return value: (transfer none) (element-type GdkDevice):
* a list of #GdkDevice
*
* Since: 2.2
*
* Deprecated: 3.0: Use gdk_device_manager_list_devices() instead.
**/
GList *
gdk_display_list_devices (GdkDisplay *display)
{
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
return GDK_DISPLAY_X11 (display)->input_devices;
}
/**
* gdk_event_send_client_message_for_display:
* @display: the #GdkDisplay for the window where the message is to be sent.
* @event: the #GdkEvent to send, which should be a #GdkEventClient.
* @winid: the window to send the client message to.
*
* On X11, sends an X ClientMessage event to a given window. On
* Windows, sends a message registered with the name
* GDK_WIN32_CLIENT_MESSAGE.
*
* This could be used for communicating between different
* applications, though the amount of data is limited to 20 bytes on
* X11, and to just four bytes on Windows.
*
* Returns: non-zero on success.
*
* Since: 2.2
*/
gboolean
gdk_event_send_client_message_for_display (GdkDisplay *display,
GdkEvent *event,
GdkNativeWindow winid)
{
XEvent sev;
g_return_val_if_fail(event != NULL, FALSE);
/* Set up our event to send, with the exception of its target window */
sev.xclient.type = ClientMessage;
sev.xclient.display = GDK_DISPLAY_XDISPLAY (display);
sev.xclient.format = event->client.data_format;
sev.xclient.window = winid;
memcpy(&sev.xclient.data, &event->client.data, sizeof (sev.xclient.data));
sev.xclient.message_type = gdk_x11_atom_to_xatom_for_display (display, event->client.message_type);
return _gdk_send_xevent (display, winid, False, NoEventMask, &sev);
}
/**
* gdk_display_add_client_message_filter:
* @display: a #GdkDisplay for which this message filter applies
* @message_type: the type of ClientMessage events to receive.
* This will be checked against the @message_type field
* of the XClientMessage event struct.
* @func: the function to call to process the event.
* @data: user data to pass to @func.
*
* Adds a filter to be called when X ClientMessage events are received.
* See gdk_window_add_filter() if you are interested in filtering other
* types of events.
*
* Since: 2.2
**/
void
gdk_display_add_client_message_filter (GdkDisplay *display,
GdkAtom message_type,
GdkFilterFunc func,
gpointer data)
{
GdkClientFilter *filter;
g_return_if_fail (GDK_IS_DISPLAY (display));
filter = g_new (GdkClientFilter, 1);
filter->type = message_type;
filter->function = func;
filter->data = data;
GDK_DISPLAY_X11(display)->client_filters =
g_list_append (GDK_DISPLAY_X11 (display)->client_filters,
filter);
}
/**
* gdk_add_client_message_filter:
* @message_type: the type of ClientMessage events to receive. This will be
* checked against the <structfield>message_type</structfield> field of the
* XClientMessage event struct.
* @func: the function to call to process the event.
* @data: user data to pass to @func.
*
* Adds a filter to the default display to be called when X ClientMessage events
* are received. See gdk_display_add_client_message_filter().
**/
void
gdk_add_client_message_filter (GdkAtom message_type,
GdkFilterFunc func,
gpointer data)
{
gdk_display_add_client_message_filter (gdk_display_get_default (),
message_type, func, data);
}
/*
*--------------------------------------------------------------
* gdk_flush
*
* Flushes the Xlib output buffer and then waits
* until all requests have been received and processed
* by the X server. The only real use for this function
* is in dealing with XShm.
*
* Arguments:
*
* Results:
*
* Side effects:
*
*--------------------------------------------------------------
*/
void
gdk_flush (void)
{
GSList *tmp_list = _gdk_displays;
while (tmp_list)
{
XSync (GDK_DISPLAY_XDISPLAY (tmp_list->data), False);
tmp_list = tmp_list->next;
}
}
/**
* gdk_x11_register_standard_event_type:
* @display: a #GdkDisplay
* @event_base: first event type code to register
* @n_events: number of event type codes to register
*
* Registers interest in receiving extension events with type codes
* between @event_base and <literal>event_base + n_events - 1</literal>.
* The registered events must have the window field in the same place
* as core X events (this is not the case for e.g. XKB extension events).
*
* If an event type is registered, events of this type will go through
* global and window-specific filters (see gdk_window_add_filter()).
* Unregistered events will only go through global filters.
* GDK may register the events of some X extensions on its own.
*
* This function should only be needed in unusual circumstances, e.g.
* when filtering XInput extension events on the root window.
*
* Since: 2.4
**/
void
gdk_x11_register_standard_event_type (GdkDisplay *display,
gint event_base,
gint n_events)
{
GdkEventTypeX11 *event_type;
GdkDisplayX11 *display_x11;
display_x11 = GDK_DISPLAY_X11 (display);
event_type = g_new (GdkEventTypeX11, 1);
event_type->base = event_base;
event_type->n_events = n_events;
display_x11->event_types = g_slist_prepend (display_x11->event_types, event_type);
}
/* compare X sequence numbers handling wraparound */
#define SEQUENCE_COMPARE(a,op,b) (((long) (a) - (long) (b)) op 0)
/* delivers an error event from the error handler in gdkmain-x11.c */
void
_gdk_x11_display_error_event (GdkDisplay *display,
XErrorEvent *error)
{
GdkDisplayX11 *display_x11;
GSList *tmp_list;
gboolean ignore;
display_x11 = GDK_DISPLAY_X11 (display);
ignore = FALSE;
for (tmp_list = display_x11->error_traps;
tmp_list != NULL;
tmp_list = tmp_list->next)
{
GdkErrorTrap *trap;
trap = tmp_list->data;
if (SEQUENCE_COMPARE (trap->start_sequence, <=, error->serial) &&
(trap->end_sequence == 0 ||
SEQUENCE_COMPARE (trap->end_sequence, >, error->serial)))
{
ignore = TRUE;
trap->error_code = error->error_code;
}
}
if (!ignore)
{
gchar buf[64];
gchar *msg;
XGetErrorText (display_x11->xdisplay, error->error_code, buf, 63);
msg =
g_strdup_printf ("The program '%s' received an X Window System error.\n"
"This probably reflects a bug in the program.\n"
"The error was '%s'.\n"
" (Details: serial %ld error_code %d request_code %d minor_code %d)\n"
" (Note to programmers: normally, X errors are reported asynchronously;\n"
" that is, you will receive the error a while after causing it.\n"
" To debug your program, run it with the --sync command line\n"
" option to change this behavior. You can then get a meaningful\n"
" backtrace from your debugger if you break on the gdk_x_error() function.)",
g_get_prgname (),
buf,
error->serial,
error->error_code,
error->request_code,
error->minor_code);
#ifdef G_ENABLE_DEBUG
g_error ("%s", msg);
#else /* !G_ENABLE_DEBUG */
g_fprintf (stderr, "%s\n", msg);
exit (1);
#endif /* G_ENABLE_DEBUG */
}
}
static void
delete_outdated_error_traps (GdkDisplayX11 *display_x11)
{
GSList *tmp_list;
gulong processed_sequence;
processed_sequence = XLastKnownRequestProcessed (display_x11->xdisplay);
tmp_list = display_x11->error_traps;
while (tmp_list != NULL)
{
GdkErrorTrap *trap = tmp_list->data;
if (trap->end_sequence != 0 &&
SEQUENCE_COMPARE (trap->end_sequence, <, processed_sequence))
{
GSList *free_me = tmp_list;
tmp_list = tmp_list->next;
display_x11->error_traps =
g_slist_delete_link (display_x11->error_traps, free_me);
g_slice_free (GdkErrorTrap, trap);
}
else
{
tmp_list = tmp_list->next;
}
}
}
/**
* gdk_x11_display_error_trap_push:
*
* Begins a range of X requests for which X error events will be
* ignored. Unignored errors (when no trap is pushed) will abort the
* application.
*
* See also gdk_error_trap_push() to push a trap on all displays.
*
* Since: 3.0
*/
void
gdk_x11_display_error_trap_push (GdkDisplay *display)
{
GdkDisplayX11 *display_x11;
GdkErrorTrap *trap;
display_x11 = GDK_DISPLAY_X11 (display);
delete_outdated_error_traps (display_x11);
/* set up the Xlib callback to tell us about errors */
_gdk_x11_error_handler_push ();
trap = g_slice_new0 (GdkErrorTrap);
trap->start_sequence = XNextRequest (display_x11->xdisplay);
trap->error_code = Success;
display_x11->error_traps =
g_slist_prepend (display_x11->error_traps, trap);
}
static gint
gdk_x11_display_error_trap_pop_internal (GdkDisplay *display,
gboolean need_code)
{
GdkDisplayX11 *display_x11;
GdkErrorTrap *trap;
GSList *tmp_list;
int result;
display_x11 = GDK_DISPLAY_X11 (display);
g_return_val_if_fail (display_x11->error_traps != NULL, Success);
/* Find the first trap that hasn't been popped already */
trap = NULL; /* quiet gcc */
for (tmp_list = display_x11->error_traps;
tmp_list != NULL;
tmp_list = tmp_list->next)
{
trap = tmp_list->data;
if (trap->end_sequence == 0)
break;
}
g_return_val_if_fail (trap != NULL, Success);
g_assert (trap->end_sequence == 0);
/* May need to sync to fill in trap->error_code if we care about
* getting an error code.
*/
if (need_code)
{
gulong processed_sequence;
gulong next_sequence;
next_sequence = XNextRequest (display_x11->xdisplay);
processed_sequence = XLastKnownRequestProcessed (display_x11->xdisplay);
/* If our last request was already processed, there is no point
* in syncing. i.e. if last request was a round trip (or even if
* we got an event with the serial of a non-round-trip)
*/
if ((next_sequence - 1) != processed_sequence)
{
XSync (display_x11->xdisplay, False);
}
result = trap->error_code;
}
else
{
result = Success;
}
/* record end of trap, giving us a range of
* error sequences we'll ignore.
*/
trap->end_sequence = XNextRequest (display_x11->xdisplay);
/* remove the Xlib callback */
_gdk_x11_error_handler_pop ();
/* we may already be outdated */
delete_outdated_error_traps (display_x11);
return result;
}
/**
* gdk_x11_display_error_trap_pop:
* @display: the display
*
* Pops the error trap pushed by gdk_x11_display_error_trap_push().
* Will XSync() if necessary and will always block until
* the error is known to have occurred or not occurred,
* so the error code can be returned.
*
* If you don't need to use the return value,
* gdk_x11_display_error_trap_pop_ignored() would be more efficient.
*
* See gdk_error_trap_pop() for the all-displays-at-once
* equivalent.
*
* Since: 3.0
*
* Return value: X error code or 0 on success
*/
gint
gdk_x11_display_error_trap_pop (GdkDisplay *display)
{
g_return_val_if_fail (GDK_IS_DISPLAY_X11 (display), Success);
return gdk_x11_display_error_trap_pop_internal (display, TRUE);
}
/**
* gdk_x11_display_error_trap_pop_ignored:
* @display: the display
*
* Pops the error trap pushed by gdk_x11_display_error_trap_push().
* Does not block to see if an error occurred; merely records the
* range of requests to ignore errors for, and ignores those errors
* if they arrive asynchronously.
*
* See gdk_error_trap_pop_ignored() for the all-displays-at-once
* equivalent.
*
* Since: 3.0
*/
void
gdk_x11_display_error_trap_pop_ignored (GdkDisplay *display)
{
g_return_if_fail (GDK_IS_DISPLAY_X11 (display));
gdk_x11_display_error_trap_pop_internal (display, FALSE);
}