summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2010-11-12 13:18:58 +0100
committerAlexander Larsson <alexl@redhat.com>2010-11-25 22:09:19 +0100
commit5b480e1b0de0c8184cb2805ac85f7362c42fb2f6 (patch)
treedf5fd13a56b7ec224daa0281429bbc7e75b7128b
parenteb5a5004959227d22b2f98a157beda8600df00d3 (diff)
downloadgtk+-5b480e1b0de0c8184cb2805ac85f7362c42fb2f6.tar.gz
[broadway] Copy X backend to broadway
-rw-r--r--configure.ac271
-rw-r--r--gdk/Makefile.am8
-rw-r--r--gdk/broadway/Makefile.am71
-rw-r--r--gdk/broadway/MwmUtil.h136
-rw-r--r--gdk/broadway/gdkapplaunchcontext-broadway.c440
-rw-r--r--gdk/broadway/gdkasync.c845
-rw-r--r--gdk/broadway/gdkasync.h75
-rw-r--r--gdk/broadway/gdkcursor-broadway.c1040
-rw-r--r--gdk/broadway/gdkdevice-broadway.c502
-rw-r--r--gdk/broadway/gdkdevice-broadway.h52
-rw-r--r--gdk/broadway/gdkdevicemanager-broadway.c920
-rw-r--r--gdk/broadway/gdkdevicemanager-broadway.h54
-rw-r--r--gdk/broadway/gdkdisplay-broadway.c2977
-rw-r--r--gdk/broadway/gdkdisplay-broadway.h160
-rw-r--r--gdk/broadway/gdkdnd-broadway.c4035
-rw-r--r--gdk/broadway/gdkdrawable-broadway.c235
-rw-r--r--gdk/broadway/gdkdrawable-broadway.h75
-rw-r--r--gdk/broadway/gdkeventsource.c435
-rw-r--r--gdk/broadway/gdkeventsource.h46
-rw-r--r--gdk/broadway/gdkeventtranslator.c89
-rw-r--r--gdk/broadway/gdkeventtranslator.h65
-rw-r--r--gdk/broadway/gdkgeometry-broadway.c390
-rw-r--r--gdk/broadway/gdkglobals-broadway.c37
-rw-r--r--gdk/broadway/gdkim-broadway.c103
-rw-r--r--gdk/broadway/gdkinput.c231
-rw-r--r--gdk/broadway/gdkkeys-broadway.c1859
-rw-r--r--gdk/broadway/gdkmain-broadway.c698
-rw-r--r--gdk/broadway/gdkprivate-broadway.h162
-rw-r--r--gdk/broadway/gdkproperty-broadway.c761
-rw-r--r--gdk/broadway/gdkscreen-broadway.c2003
-rw-r--r--gdk/broadway/gdkscreen-broadway.h129
-rw-r--r--gdk/broadway/gdkselection-broadway.c891
-rw-r--r--gdk/broadway/gdksettings.c128
-rw-r--r--gdk/broadway/gdkspawn-broadway.c214
-rw-r--r--gdk/broadway/gdktestutils-broadway.c254
-rw-r--r--gdk/broadway/gdkvisual-broadway.c730
-rw-r--r--gdk/broadway/gdkwindow-broadway.c5583
-rw-r--r--gdk/broadway/gdkwindow-broadway.h162
-rw-r--r--gdk/broadway/gdkx.h169
-rw-r--r--gdk/broadway/gdkxftdefaults.c268
-rw-r--r--gdk/broadway/gdkxid.c133
-rw-r--r--gdk/broadway/xsettings-client.c611
-rw-r--r--gdk/broadway/xsettings-client.h79
-rw-r--r--gdk/broadway/xsettings-common.c267
-rw-r--r--gdk/broadway/xsettings-common.h132
-rw-r--r--gtk/Makefile.am6
-rw-r--r--tests/testsocket_common.c2
47 files changed, 28520 insertions, 13 deletions
diff --git a/configure.ac b/configure.ac
index 3ac0bec577..12ae11d95c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -258,13 +258,13 @@ else
gdktarget=x11
fi
-AC_ARG_WITH(gdktarget, [ --with-gdktarget=[[x11/win32/quartz]] select non-default GDK target],
+AC_ARG_WITH(gdktarget, [ --with-gdktarget=[[x11/win32/quartz/broadway]] select non-default GDK target],
gdktarget=$with_gdktarget)
AC_SUBST(gdktarget)
case $gdktarget in
- x11|win32|quartz) ;;
- *) AC_MSG_ERROR([Invalid target for GDK: use x11, quartz or win32.]);;
+ x11|win32|quartz|broadway) ;;
+ *) AC_MSG_ERROR([Invalid target for GDK: use x11, broadway, quartz or win32.]);;
esac
gdktargetlib=libgdk-$gdktarget-$GTK_API_VERSION.la
@@ -367,15 +367,18 @@ PKG_CHECK_MODULES(BASE_DEPENDENCIES,
## In addition to checking that cairo is present, we also need to
## check that the correct cairo backend is there. E.g. if the GDK
## target is win32 we need the cairo-win32 backend and so on.
-cairo_backend=$gdktarget
+cairo_backend=cairo-$gdktarget
# GDK calls the xlib backend "x11," cairo calls it "xlib." Other
# backend names are identical.
-if test "x$cairo_backend" = "xx11"; then
- cairo_backend=xlib
+if test "x$cairo_backend" = "xcairo-x11"; then
+ cairo_backend=cairo-xlib
+fi
+if test "x$cairo_backend" = "xcairo-broadway"; then
+ cairo_backend=cairo
fi
PKG_CHECK_MODULES(CAIRO_BACKEND,
- [cairo-$cairo_backend >= cairo_required_version])
+ [$cairo_backend >= cairo_required_version])
if test "$os_win32" != yes; then
# libtool option to control which symbols are exported
@@ -1185,6 +1188,250 @@ else
AM_CONDITIONAL(USE_QUARTZ, false)
fi
+if test "x$gdktarget" = "xbroadway" ; then
+ X_PACKAGES=fontconfig
+
+ #
+ # We use fontconfig very peripherally when decoding the default
+ # settings.
+ #
+ if $PKG_CONFIG --exists fontconfig; then : ; else
+ AC_MSG_ERROR([
+*** fontconfig (http://www.fontconfig.org) is required by the X11 backend.])
+ fi
+
+ #
+ # Check for basic X packages; we use pkg-config if available
+ #
+ if $PKG_CONFIG --exists x11 xext; then
+ have_base_x_pc=true
+ X_PACKAGES="$X_PACKAGES x11 xext"
+ x_libs="`$PKG_CONFIG --libs x11 xext`"
+ X_CFLAGS="`$PKG_CONFIG --cflags x11 xext`"
+
+ # Strip out any .la files that pkg-config might give us (this happens
+ # with -uninstalled.pc files)
+ x_libs_for_checks=
+ for I in $x_libs ; do
+ case $I in
+ *.la) ;;
+ *) x_libs_for_checks="$x_libs_for_checks $I" ;;
+ esac
+ done
+
+ GTK_PACKAGES_FOR_X="x11"
+ else
+ have_base_x_pc=false
+ AC_PATH_XTRA
+ if test x$no_x = xyes ; then
+ AC_MSG_ERROR([X development libraries not found])
+ fi
+
+ x_cflags="$X_CFLAGS"
+ x_libs_for_checks="$X_LIBS -lXext -lX11 $X_EXTRA_LIBS"
+
+ GTK_DEP_LIBS_FOR_X="$X_LIBS -lX11 $X_EXTRA_LIBS"
+ fi
+
+ # Extra libraries found during checks (-lXinerama, etc), not from pkg-config.
+ x_extra_libs=
+
+ gtk_save_cppflags="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+
+ gtk_save_LIBS=$LIBS
+ LIBS="$x_libs_for_checks $LIBS"
+
+ # Sanity check for the X11 and Xext libraries. While everything we need from
+ # Xext is optional, the chances a system has *none* of these things is so
+ # small that we just unconditionally require it.
+ AC_CHECK_FUNC(XOpenDisplay, :,
+ AC_MSG_ERROR([*** libX11 not found. Check 'config.log' for more details.]))
+ AC_CHECK_FUNC(XextFindDisplay, :,
+ AC_MSG_ERROR([*** libXext not found. Check 'config.log' for more details.]))
+
+ # Check for xReply
+
+ AC_MSG_CHECKING([if <X11/extensions/XIproto.h> is needed for xReply])
+ AC_TRY_COMPILE([#include <X11/Xlibint.h>],
+ [xReply *rep;],
+ [AC_MSG_RESULT([no])],
+ [AC_TRY_COMPILE([#include <X11/extensions/XIproto.h>
+#include <X11/Xlibint.h>],
+ [xReply *rep;],
+ [AC_MSG_RESULT([yes])
+ AC_DEFINE([NEED_XIPROTO_H_FOR_XREPLY], 1,
+ [Define if <X11/extensions/XIproto.h> needed for xReply])],
+ [AC_MSG_RESULT([unknown])
+ AC_MSG_ERROR([xReply type unavailable. X11 is too old])])])
+
+ # Check for XConvertCase, XInternAtoms (X11R6 specific)
+
+ AC_CHECK_FUNCS(XConvertCase XInternAtoms)
+
+ # Generic X11R6 check needed for XIM support; we could
+ # probably use this to replace the above, but we'll
+ # leave the separate checks for XConvertCase and XInternAtoms
+ # for clarity
+
+ have_x11r6=false
+ AC_CHECK_FUNC(XAddConnectionWatch,
+ have_x11r6=true)
+
+ if $have_x11r6; then
+ AC_DEFINE(HAVE_X11R6, 1, [Define if we have X11R6])
+ fi
+ AM_CONDITIONAL(HAVE_X11R6, $have_x11r6)
+
+ # Check for XKB support.
+
+ if test "x$enable_xkb" = "xyes"; then
+ AC_MSG_WARN(XKB support explicitly enabled)
+ AC_DEFINE(HAVE_XKB, 1, [Define to use XKB extension])
+ elif test "x$enable_xkb" = "xmaybe"; then
+ AC_CHECK_FUNC(XkbQueryExtension,
+ AC_DEFINE(HAVE_XKB, 1, [Define to use XKB extension]))
+ else
+ AC_MSG_WARN(XKB support explicitly disabled)
+ fi
+
+ # Check for shaped window extension
+
+ AC_CHECK_FUNC(XShapeCombineMask, :,
+ [AC_MSG_ERROR([Shape extension not found, check your development headers])])
+
+ # X SYNC check
+ gtk_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $x_cflags"
+
+ AC_CHECK_FUNC(XSyncQueryExtension,
+ [AC_CHECK_HEADER(X11/extensions/sync.h,
+ AC_DEFINE(HAVE_XSYNC, 1, [Have the SYNC extension library]),
+ :, [#include <X11/Xlib.h>])])
+
+ CFLAGS="$gtk_save_CFLAGS"
+
+ if test "x$enable_xinerama" = "xyes"; then
+ # Check for Xinerama extension (Solaris impl or Xfree impl)
+ gtk_save_cppflags="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $x_cflags"
+
+ # Check for XFree
+ AC_MSG_CHECKING(for Xinerama support on XFree86)
+
+ have_xfree_xinerama=false
+ if $PKG_CONFIG --exists xinerama ; then
+ have_xfree_xinerama=true
+ X_PACKAGES="$X_PACKAGES xinerama"
+ else
+ AC_CHECK_LIB(Xinerama, XineramaQueryExtension,
+ [AC_CHECK_HEADER(X11/extensions/Xinerama.h,
+ [GTK_ADD_LIB(x_extra_libs,Xinerama)
+ have_xfree_xinerama=true], :,
+ [#include <X11/Xlib.h>])])
+ fi
+
+ if $have_xfree_xinerama ; then
+ AC_DEFINE(HAVE_XFREE_XINERAMA, 1,
+ [Define to 1 if XFree Xinerama is available])
+ AC_DEFINE(HAVE_XINERAMA, 1,
+ [Define to 1 is Xinerama is available])
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+
+ case "$host" in
+ *-*-solaris*)
+ # Check for solaris
+ AC_MSG_CHECKING(for Xinerama support on Solaris)
+
+ have_solaris_xinerama=false
+ AC_CHECK_FUNC(XineramaGetInfo,
+ [AC_CHECK_HEADER(X11/extensions/xinerama.h,
+ [have_solaris_xinerama=true], :,
+ [#include <X11/Xlib.h>])])
+
+ if $have_solaris_xinerama ; then
+ AC_DEFINE(HAVE_SOLARIS_XINERAMA, 1,
+ [Define to 1 if solaris xinerama is available])
+ AC_DEFINE(HAVE_XINERAMA, 1,
+ [Define to 1 if xinerama is available])
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+ ;;
+ *)
+ ;;
+ esac
+ fi
+ fi
+
+ AC_DEFINE(XINPUT_NONE, 1, [Define to 1 if no XInput should be used])
+ AM_CONDITIONAL(XINPUT_XFREE, false)
+ AM_CONDITIONAL(XINPUT_2, false)
+
+ # Check for the RANDR extension
+ if $PKG_CONFIG --exists "xrandr >= 1.2.99" ; then
+ AC_DEFINE(HAVE_RANDR, 1, [Have the Xrandr extension library])
+
+ X_PACKAGES="$X_PACKAGES xrandr"
+ fi
+
+ # Checks for Xcursor library
+
+ if $PKG_CONFIG --exists xcursor ; then
+ AC_DEFINE(HAVE_XCURSOR, 1, [Have the Xcursor library])
+
+ X_PACKAGES="$X_PACKAGES xcursor"
+ fi
+
+ # Checks for XFixes extension
+
+ if $PKG_CONFIG --exists xfixes ; then
+ AC_DEFINE(HAVE_XFIXES, 1, [Have the XFIXES X extension])
+
+ X_PACKAGES="$X_PACKAGES xfixes"
+ GTK_PACKAGES_FOR_X="$GTK_PACKAGES_FOR_X xfixes"
+ fi
+
+ # Checks for Xcomposite extension
+
+ if $PKG_CONFIG --exists xcomposite ; then
+ AC_DEFINE(HAVE_XCOMPOSITE, 1, [Have the XCOMPOSITE X extension])
+
+ X_PACKAGES="$X_PACKAGES xcomposite"
+ GTK_PACKAGES_FOR_X="$GTK_PACKAGES_FOR_X xcomposite"
+ fi
+
+ # Checks for Xdamage extension
+
+ if $PKG_CONFIG --exists xdamage ; then
+ AC_DEFINE(HAVE_XDAMAGE, 1, [Have the XDAMAGE X extension])
+
+ X_PACKAGES="$X_PACKAGES xdamage"
+ GTK_PACKAGES_FOR_X="$GTK_PACKAGES_FOR_X xdamage"
+ fi
+
+ if $have_base_x_pc ; then
+ GDK_EXTRA_LIBS="$x_extra_libs"
+ else
+ GDK_EXTRA_LIBS="$X_LIBS $x_extra_libs -lXext -lX11 $GDK_EXTRA_LIBS"
+ fi
+
+ CPPFLAGS="$gtk_save_cppflags"
+ LIBS="$gtk_save_libs"
+
+ AM_CONDITIONAL(USE_X11, true)
+else
+ XPACKAGES=
+
+ AM_CONDITIONAL(XINPUT_XFREE, false)
+ AM_CONDITIONAL(XINPUT_2, false)
+ AM_CONDITIONAL(USE_X11, false)
+ AM_CONDITIONAL(HAVE_X11R6, false)
+fi
+
# Check for Pango flags
if test "x$gdktarget" = "xwin32"; then
@@ -1223,10 +1470,10 @@ CFLAGS="$saved_cflags"
LDFLAGS="$saved_ldflags"
# Pull in gio-unix for GDesktopAppInfo usage, see at least gdkapplaunchcontext-x11.c
-if test "x$gdktarget" = "xx11"; then
- GDK_PACKAGES="$PANGO_PACKAGES gio-unix-2.0 $X_PACKAGES gdk-pixbuf-2.0 cairo-$cairo_backend cairo-gobject"
+if test "x$gdktarget" = "xx11" || test "x$gdktarget" = "xbroadway" ; then
+ GDK_PACKAGES="$PANGO_PACKAGES gio-unix-2.0 $X_PACKAGES gdk-pixbuf-2.0 $cairo_backend cairo-gobject"
else
- GDK_PACKAGES="$PANGO_PACKAGES gio-2.0 gdk-pixbuf-2.0 cairo-$cairo_backend cairo-gobject"
+ GDK_PACKAGES="$PANGO_PACKAGES gio-2.0 gdk-pixbuf-2.0 $cairo_backend cairo-gobject"
fi
GDK_DEP_LIBS="$GDK_EXTRA_LIBS `$PKG_CONFIG --libs $GDK_PACKAGES`"
@@ -1584,6 +1831,9 @@ elif test "x$gdktarget" = "xwin32" ; then
elif test "x$gdktarget" = "xquartz" ; then
gdk_windowing='
#define GDK_WINDOWING_QUARTZ'
+elif test "x$gdktarget" = "xbroadway" ; then
+ gdk_windowing='
+#define GDK_WINDOWING_BROADWAY'
fi
if test x$gdk_wchar_h = xyes; then
@@ -1653,6 +1903,7 @@ build/Makefile
build/win32/Makefile
build/win32/vs9/Makefile
gdk/Makefile
+gdk/broadway/Makefile
gdk/x11/Makefile
gdk/win32/Makefile
gdk/win32/rc/Makefile
diff --git a/gdk/Makefile.am b/gdk/Makefile.am
index 9a48fe8042..f222a96315 100644
--- a/gdk/Makefile.am
+++ b/gdk/Makefile.am
@@ -9,7 +9,7 @@ INTROSPECTION_COMPILER_ARGS = \
--includedir=.
SUBDIRS = $(gdktarget) . tests
-DIST_SUBDIRS = win32 x11 quartz tests
+DIST_SUBDIRS = win32 x11 quartz broadway tests
CLEANFILES =
@@ -158,6 +158,10 @@ common_sources = \
gdkmarshalers.c \
gdkmarshalers.h
+libgdk_broadway_3_0_la_SOURCES = $(common_sources)
+libgdk_broadway_3_0_la_LIBADD = broadway/libgdk-broadway.la $(GDK_DEP_LIBS)
+libgdk_broadway_3_0_la_LDFLAGS = $(LDADD)
+
libgdk_x11_3_0_la_SOURCES = $(common_sources)
libgdk_x11_3_0_la_LIBADD = x11/libgdk-x11.la $(GDK_DEP_LIBS)
libgdk_x11_3_0_la_LDFLAGS = $(LDADD)
@@ -288,7 +292,7 @@ endif
lib_LTLIBRARIES = $(gdktargetlib)
-EXTRA_LTLIBRARIES = libgdk-x11-3.0.la libgdk-win32-3.0.la libgdk-quartz-3.0.la
+EXTRA_LTLIBRARIES = libgdk-broadway-3.0.la libgdk-x11-3.0.la libgdk-win32-3.0.la libgdk-quartz-3.0.la
MAINTAINERCLEANFILES = $(gdk_built_sources) stamp-gdkenumtypes.h
EXTRA_DIST += $(gdk_built_sources)
diff --git a/gdk/broadway/Makefile.am b/gdk/broadway/Makefile.am
new file mode 100644
index 0000000000..e61e92b64f
--- /dev/null
+++ b/gdk/broadway/Makefile.am
@@ -0,0 +1,71 @@
+## Process this file with automake to produce Makefile.in
+include $(top_srcdir)/Makefile.decl
+
+libgdkincludedir = $(includedir)/gtk-3.0/gdk
+
+INCLUDES = \
+ -DG_LOG_DOMAIN=\"Gdk\" \
+ -DGDK_COMPILATION \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/gdk \
+ -I$(top_builddir)/gdk \
+ $(GTK_DEBUG_FLAGS) \
+ $(GDK_DEP_CFLAGS)
+
+LDADDS = $(GDK_DEP_LIBS)
+
+noinst_LTLIBRARIES = libgdk-broadway.la
+
+libgdk_broadway_la_SOURCES = \
+ MwmUtil.h \
+ gdkapplaunchcontext-broadway.c \
+ gdkasync.c \
+ gdkasync.h \
+ gdkcursor-broadway.c \
+ gdkdevice-broadway.h \
+ gdkdevice-broadway.c \
+ gdkdevicemanager-broadway.h \
+ gdkdevicemanager-broadway.c \
+ gdkdisplay-broadway.c \
+ gdkdisplay-broadway.h \
+ gdkdnd-broadway.c \
+ gdkdrawable-broadway.c \
+ gdkdrawable-broadway.h \
+ gdkeventsource.c \
+ gdkeventsource.h \
+ gdkeventtranslator.c \
+ gdkeventtranslator.h \
+ gdkgeometry-broadway.c \
+ gdkglobals-broadway.c \
+ gdkim-broadway.c \
+ gdkinput.c \
+ gdkkeys-broadway.c \
+ gdkmain-broadway.c \
+ gdkproperty-broadway.c \
+ gdkscreen-broadway.c \
+ gdkscreen-broadway.h \
+ gdkselection-broadway.c \
+ gdkspawn-broadway.c \
+ gdktestutils-broadway.c \
+ gdkvisual-broadway.c \
+ gdkwindow-broadway.c \
+ gdkwindow-broadway.h \
+ gdkxftdefaults.c \
+ gdkxid.c \
+ gdkx.h \
+ gdkprivate-broadway.h \
+ xsettings-client.h \
+ xsettings-client.c \
+ xsettings-common.h \
+ xsettings-common.c
+
+libgdkinclude_HEADERS = \
+ gdkx.h
+
+
+# We need to include all these C files here since the conditionals
+# don't seem to be correctly expanded for the dist files.
+EXTRA_DIST += \
+ gdksettings.c
+
+-include $(top_srcdir)/git.mk
diff --git a/gdk/broadway/MwmUtil.h b/gdk/broadway/MwmUtil.h
new file mode 100644
index 0000000000..7d4c5bc645
--- /dev/null
+++ b/gdk/broadway/MwmUtil.h
@@ -0,0 +1,136 @@
+/**
+ *
+ * $Id$
+ *
+ * Copyright (C) 1995 Free Software Foundation, Inc.
+ *
+ * This file is part of the GNU LessTif Library.
+ *
+ * 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.
+ *
+ *
+ * * Feb 21 1999 - George Lebl (jirka@5z.com)
+ * Owen Taylor (otaylor@redhat.com)
+ *
+ * Modified so that the MotifWmHints structure defined here
+ * is suitable for client side use on 64-bit architectures.
+ * X expects fields with a format of 32 to be longs, even
+ * when sizeof(long) == 8.
+ **/
+
+#ifndef MWMUTIL_H_INCLUDED
+#define MWMUTIL_H_INCLUDED
+
+#include <X11/Xmd.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ unsigned long flags;
+ unsigned long functions;
+ unsigned long decorations;
+ long input_mode;
+ unsigned long status;
+} MotifWmHints, MwmHints;
+
+#define MWM_HINTS_FUNCTIONS (1L << 0)
+#define MWM_HINTS_DECORATIONS (1L << 1)
+#define MWM_HINTS_INPUT_MODE (1L << 2)
+#define MWM_HINTS_STATUS (1L << 3)
+
+#define MWM_FUNC_ALL (1L << 0)
+#define MWM_FUNC_RESIZE (1L << 1)
+#define MWM_FUNC_MOVE (1L << 2)
+#define MWM_FUNC_MINIMIZE (1L << 3)
+#define MWM_FUNC_MAXIMIZE (1L << 4)
+#define MWM_FUNC_CLOSE (1L << 5)
+
+#define MWM_DECOR_ALL (1L << 0)
+#define MWM_DECOR_BORDER (1L << 1)
+#define MWM_DECOR_RESIZEH (1L << 2)
+#define MWM_DECOR_TITLE (1L << 3)
+#define MWM_DECOR_MENU (1L << 4)
+#define MWM_DECOR_MINIMIZE (1L << 5)
+#define MWM_DECOR_MAXIMIZE (1L << 6)
+
+#define MWM_INPUT_MODELESS 0
+#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1
+#define MWM_INPUT_SYSTEM_MODAL 2
+#define MWM_INPUT_FULL_APPLICATION_MODAL 3
+#define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL
+
+#define MWM_TEAROFF_WINDOW (1L<<0)
+
+/*
+ * atoms
+ */
+#define _XA_MOTIF_BINDINGS "_MOTIF_BINDINGS"
+#define _XA_MOTIF_WM_HINTS "_MOTIF_WM_HINTS"
+#define _XA_MOTIF_WM_MESSAGES "_MOTIF_WM_MESSAGES"
+#define _XA_MOTIF_WM_OFFSET "_MOTIF_WM_OFFSET"
+#define _XA_MOTIF_WM_MENU "_MOTIF_WM_MENU"
+#define _XA_MOTIF_WM_INFO "_MOTIF_WM_INFO"
+#define _XA_MWM_HINTS _XA_MOTIF_WM_HINTS
+#define _XA_MWM_MESSAGES _XA_MOTIF_WM_MESSAGES
+#define _XA_MWM_MENU _XA_MOTIF_WM_MENU
+#define _XA_MWM_INFO _XA_MOTIF_WM_INFO
+
+
+/*
+ * _MWM_INFO property
+ */
+typedef struct {
+ long flags;
+ Window wm_window;
+} MotifWmInfo;
+
+typedef MotifWmInfo MwmInfo;
+
+#define MWM_INFO_STARTUP_STANDARD (1L<<0)
+#define MWM_INFO_STARTUP_CUSTOM (1L<<1)
+
+/*
+ * _MWM_HINTS property
+ */
+typedef struct {
+ unsigned long flags;
+ unsigned long functions;
+ unsigned long decorations;
+ long inputMode;
+ unsigned long status;
+} PropMotifWmHints;
+
+typedef PropMotifWmHints PropMwmHints;
+
+#define PROP_MOTIF_WM_HINTS_ELEMENTS 5
+#define PROP_MWM_HINTS_ELEMENTS PROP_MOTIF_WM_HINTS_ELEMENTS
+
+/*
+ * _MWM_INFO property, slight return
+ */
+typedef struct {
+ unsigned long flags;
+ unsigned long wmWindow;
+} PropMotifWmInfo;
+
+typedef PropMotifWmInfo PropMwmInfo;
+
+#define PROP_MOTIF_WM_INFO_ELEMENTS 2
+#define PROP_MWM_INFO_ELEMENTS PROP_MOTIF_WM_INFO_ELEMENTS
+
+G_END_DECLS
+
+#endif /* MWMUTIL_H_INCLUDED */
diff --git a/gdk/broadway/gdkapplaunchcontext-broadway.c b/gdk/broadway/gdkapplaunchcontext-broadway.c
new file mode 100644
index 0000000000..df3781edbb
--- /dev/null
+++ b/gdk/broadway/gdkapplaunchcontext-broadway.c
@@ -0,0 +1,440 @@
+/* gdkapplaunchcontext-x11.c - Gtk+ implementation for GAppLaunchContext
+
+ Copyright (C) 2007 Red Hat, Inc.
+
+ The Gnome 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.
+
+ The Gnome 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 the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Alexander Larsson <alexl@redhat.com>
+*/
+
+#include "config.h"
+
+#include "gdkapplaunchcontext.h"
+
+#include "gdkx.h"
+#include "gdkscreen.h"
+#include "gdkinternals.h"
+#include "gdkintl.h"
+
+#include <glib.h>
+#include <gio/gdesktopappinfo.h>
+
+#include <string.h>
+#include <unistd.h>
+
+static char *
+get_display_name (GFile *file)
+{
+ GFileInfo *info;
+ char *name, *tmp;
+
+ /* This does sync I/O, which isn't ideal.
+ * It should probably use the NautilusFile machinery
+ */
+
+ name = NULL;
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, 0, NULL, NULL);
+ if (info)
+ {
+ name = g_strdup (g_file_info_get_display_name (info));
+ g_object_unref (info);
+ }
+
+ if (name == NULL)
+ {
+ name = g_file_get_basename (file);
+ if (!g_utf8_validate (name, -1, NULL))
+ {
+ tmp = name;
+ name =
+ g_uri_escape_string (name, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH,
+ TRUE);
+ g_free (tmp);
+ }
+ }
+
+ return name;
+}
+
+static GIcon *
+get_icon (GFile *file)
+{
+ GFileInfo *info;
+ GIcon *icon;
+
+ icon = NULL;
+ info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_ICON, 0, NULL, NULL);
+ if (info)
+ {
+ icon = g_file_info_get_icon (info);
+ if (icon)
+ g_object_ref (icon);
+ g_object_unref (info);
+ }
+
+ return icon;
+}
+
+static char *
+gicon_to_string (GIcon *icon)
+{
+ GFile *file;
+ const char *const *names;
+
+ if (G_IS_FILE_ICON (icon))
+ {
+ file = g_file_icon_get_file (G_FILE_ICON (icon));
+ if (file)
+ return g_file_get_path (file);
+ }
+ else if (G_IS_THEMED_ICON (icon))
+ {
+ names = g_themed_icon_get_names (G_THEMED_ICON (icon));
+ if (names)
+ return g_strdup (names[0]);
+ }
+ else if (G_IS_EMBLEMED_ICON (icon))
+ {
+ GIcon *base;
+
+ base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
+
+ return gicon_to_string (base);
+ }
+
+ return NULL;
+}
+
+static void
+end_startup_notification (GdkDisplay *display,
+ const char *startup_id)
+{
+ gdk_x11_display_broadcast_startup_message (display, "remove",
+ "ID", startup_id,
+ NULL);
+}
+
+
+/* This should be fairly long, as it's confusing to users if a startup
+ * ends when it shouldn't (it appears that the startup failed, and
+ * they have to relaunch the app). Also the timeout only matters when
+ * there are bugs and apps don't end their own startup sequence.
+ *
+ * This timeout is a "last resort" timeout that ignores whether the
+ * startup sequence has shown activity or not. Metacity and the
+ * tasklist have smarter, and correspondingly able-to-be-shorter
+ * timeouts. The reason our timeout is dumb is that we don't monitor
+ * the sequence (don't use an SnMonitorContext)
+ */
+#define STARTUP_TIMEOUT_LENGTH_SECONDS 30
+#define STARTUP_TIMEOUT_LENGTH (STARTUP_TIMEOUT_LENGTH_SECONDS * 1000)
+
+typedef struct
+{
+ GdkDisplay *display;
+ char *startup_id;
+ GTimeVal time;
+} StartupNotificationData;
+
+static void
+free_startup_notification_data (gpointer data)
+{
+ StartupNotificationData *sn_data = data;
+
+ g_object_unref (sn_data->display);
+ g_free (sn_data->startup_id);
+ g_free (sn_data);
+}
+
+typedef struct
+{
+ GSList *contexts;
+ guint timeout_id;
+} StartupTimeoutData;
+
+static void
+free_startup_timeout (void *data)
+{
+ StartupTimeoutData *std;
+
+ std = data;
+
+ g_slist_foreach (std->contexts, (GFunc) free_startup_notification_data, NULL);
+ g_slist_free (std->contexts);
+
+ if (std->timeout_id != 0)
+ {
+ g_source_remove (std->timeout_id);
+ std->timeout_id = 0;
+ }
+
+ g_free (std);
+}
+
+static gboolean
+startup_timeout (void *data)
+{
+ StartupTimeoutData *std;
+ GSList *tmp;
+ GTimeVal now;
+ int min_timeout;
+
+ std = data;
+
+ min_timeout = STARTUP_TIMEOUT_LENGTH;
+
+ g_get_current_time (&now);
+
+ tmp = std->contexts;
+ while (tmp != NULL)
+ {
+ StartupNotificationData *sn_data;
+ GSList *next;
+ double elapsed;
+
+ sn_data = tmp->data;
+ next = tmp->next;
+
+ elapsed =
+ ((((double) now.tv_sec - sn_data->time.tv_sec) * G_USEC_PER_SEC +
+ (now.tv_usec - sn_data->time.tv_usec))) / 1000.0;
+
+ if (elapsed >= STARTUP_TIMEOUT_LENGTH)
+ {
+ std->contexts = g_slist_remove (std->contexts, sn_data);
+ end_startup_notification (sn_data->display, sn_data->startup_id);
+ free_startup_notification_data (sn_data);
+ }
+ else
+ {
+ min_timeout = MIN (min_timeout, (STARTUP_TIMEOUT_LENGTH - elapsed));
+ }
+
+ tmp = next;
+ }
+
+ if (std->contexts == NULL)
+ std->timeout_id = 0;
+ else
+ std->timeout_id = g_timeout_add_seconds ((min_timeout + 500)/1000, startup_timeout, std);
+
+ /* always remove this one, but we may have reinstalled another one. */
+ return FALSE;
+}
+
+
+static void
+add_startup_timeout (GdkScreen *screen,
+ const char *startup_id)
+{
+ StartupTimeoutData *data;
+ StartupNotificationData *sn_data;
+
+ data = g_object_get_data (G_OBJECT (screen), "appinfo-startup-data");
+
+ if (data == NULL)
+ {
+ data = g_new (StartupTimeoutData, 1);
+ data->contexts = NULL;
+ data->timeout_id = 0;
+
+ g_object_set_data_full (G_OBJECT (screen), "appinfo-startup-data",
+ data, free_startup_timeout);
+ }
+
+ sn_data = g_new (StartupNotificationData, 1);
+ sn_data->display = g_object_ref (gdk_screen_get_display (screen));
+ sn_data->startup_id = g_strdup (startup_id);
+ g_get_current_time (&sn_data->time);
+
+ data->contexts = g_slist_prepend (data->contexts, sn_data);
+
+ if (data->timeout_id == 0)
+ data->timeout_id = g_timeout_add_seconds (STARTUP_TIMEOUT_LENGTH_SECONDS,
+ startup_timeout, data);
+}
+
+
+char *
+_gdk_windowing_get_startup_notify_id (GAppLaunchContext *context,
+ GAppInfo *info,
+ GList *files)
+{
+ static int sequence = 0;
+ GdkAppLaunchContextPrivate *priv;
+ GdkDisplay *display;
+ GdkScreen *screen;
+ int files_count;
+ char *description;
+ char *icon_name;
+ const char *binary_name;
+ const char *application_id;
+ char *screen_str;
+ char *workspace_str;
+ GIcon *icon;
+ guint32 timestamp;
+ char *startup_id;
+
+ priv = GDK_APP_LAUNCH_CONTEXT (context)->priv;
+
+ if (priv->screen)
+ {
+ screen = priv->screen;
+ display = gdk_screen_get_display (priv->screen);
+ }
+ else if (priv->display)
+ {
+ display = priv->display;
+ screen = gdk_display_get_default_screen (display);
+ }
+ else
+ {
+ display = gdk_display_get_default ();
+ screen = gdk_display_get_default_screen (display);
+ }
+
+ files_count = g_list_length (files);
+ if (files_count == 0)
+ description = g_strdup_printf (_("Starting %s"), g_app_info_get_name (info));
+ else if (files_count == 1)
+ {
+ gchar *display_name = get_display_name (files->data);
+ description = g_strdup_printf (_("Opening %s"), display_name);
+ g_free (display_name);
+ }
+ else
+ description = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE,
+ "Opening %d Item",
+ "Opening %d Items",
+ files_count), files_count);
+
+ icon_name = NULL;
+ if (priv->icon_name)
+ icon_name = g_strdup (priv->icon_name);
+ else
+ {
+ icon = NULL;
+
+ if (priv->icon != NULL)
+ icon = g_object_ref (priv->icon);
+ else if (files_count == 1)
+ icon = get_icon (files->data);
+
+ if (icon == NULL)
+ {
+ icon = g_app_info_get_icon (info);
+ g_object_ref (icon);
+ }
+
+ if (icon)
+ icon_name = gicon_to_string (icon);
+
+ g_object_unref (icon);
+ }
+
+ binary_name = g_app_info_get_executable (info);
+
+ timestamp = priv->timestamp;
+ if (timestamp == GDK_CURRENT_TIME)
+ timestamp = gdk_x11_display_get_user_time (display);
+
+ screen_str = g_strdup_printf ("%d", gdk_screen_get_number (screen));
+ if (priv->workspace > -1)
+ workspace_str = g_strdup_printf ("%d", priv->workspace);
+ else
+ workspace_str = NULL;
+
+ if (G_IS_DESKTOP_APP_INFO (info))
+ application_id = g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (info));
+ else
+ application_id = NULL;
+
+ startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu",
+ g_get_prgname (),
+ (unsigned long)getpid (),
+ g_get_host_name (),
+ binary_name,
+ sequence++,
+ (unsigned long)timestamp);
+
+
+ gdk_x11_display_broadcast_startup_message (display, "new",
+ "ID", startup_id,
+ "NAME", g_app_info_get_name (info),
+ "SCREEN", screen_str,
+ "BIN", binary_name,
+ "ICON", icon_name,
+ "DESKTOP", workspace_str,
+ "DESCRIPTION", description,
+ "WMCLASS", NULL, /* FIXME */
+ "APPLICATION_ID", application_id,
+ NULL);
+
+ g_free (description);
+ g_free (screen_str);
+ g_free (workspace_str);
+ g_free (icon_name);
+
+ add_startup_timeout (screen, startup_id);
+
+ return startup_id;
+}
+
+
+void
+_gdk_windowing_launch_failed (GAppLaunchContext *context,
+ const char *startup_notify_id)
+{
+ GdkAppLaunchContextPrivate *priv;
+ GdkScreen *screen;
+ StartupTimeoutData *data;
+ StartupNotificationData *sn_data;
+ GSList *l;
+
+ priv = GDK_APP_LAUNCH_CONTEXT (context)->priv;
+
+ if (priv->screen)
+ screen = priv->screen;
+ else if (priv->display)
+ screen = gdk_display_get_default_screen (priv->display);
+ else
+ screen = gdk_display_get_default_screen (gdk_display_get_default ());
+
+ data = g_object_get_data (G_OBJECT (screen), "appinfo-startup-data");
+
+ if (data)
+ {
+ for (l = data->contexts; l != NULL; l = l->next)
+ {
+ sn_data = l->data;
+ if (strcmp (startup_notify_id, sn_data->startup_id) == 0)
+ {
+ data->contexts = g_slist_remove (data->contexts, sn_data);
+ end_startup_notification (sn_data->display, sn_data->startup_id);
+ free_startup_notification_data (sn_data);
+
+ break;
+ }
+ }
+
+ if (data->contexts == NULL)
+ {
+ g_source_remove (data->timeout_id);
+ data->timeout_id = 0;
+ }
+ }
+}
diff --git a/gdk/broadway/gdkasync.c b/gdk/broadway/gdkasync.c
new file mode 100644
index 0000000000..2f591b0906
--- /dev/null
+++ b/gdk/broadway/gdkasync.c
@@ -0,0 +1,845 @@
+/* GTK - The GIMP Toolkit
+ * gdkasync.c: Utility functions using the Xlib asynchronous interfaces
+ * Copyright (C) 2003, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+/* Portions of code in this file are based on code from Xlib
+ */
+/*
+Copyright 1986, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+*/
+#include "config.h"
+
+#include "gdkasync.h"
+#include "gdkx.h"
+
+#ifdef NEED_XIPROTO_H_FOR_XREPLY
+#include <X11/extensions/XIproto.h>
+#endif
+
+#include <X11/Xlibint.h>
+
+
+typedef struct _ChildInfoChildState ChildInfoChildState;
+typedef struct _ChildInfoState ChildInfoState;
+typedef struct _ListChildrenState ListChildrenState;
+typedef struct _SendEventState SendEventState;
+typedef struct _SetInputFocusState SetInputFocusState;
+typedef struct _RoundtripState RoundtripState;
+
+typedef enum {
+ CHILD_INFO_GET_PROPERTY,
+ CHILD_INFO_GET_WA,
+ CHILD_INFO_GET_GEOMETRY
+} ChildInfoReq;
+
+struct _ChildInfoChildState
+{
+ gulong seq[3];
+};
+
+struct _ChildInfoState
+{
+ gboolean get_wm_state;
+ Window *children;
+ guint nchildren;
+ GdkChildInfoX11 *child_info;
+ ChildInfoChildState *child_states;
+
+ guint current_child;
+ guint n_children_found;
+ gint current_request;
+ gboolean have_error;
+ gboolean child_has_error;
+};
+
+struct _ListChildrenState
+{
+ Display *dpy;
+ gulong get_property_req;
+ gboolean have_error;
+ gboolean has_wm_state;
+};
+
+struct _SendEventState
+{
+ Display *dpy;
+ Window window;
+ _XAsyncHandler async;
+ gulong send_event_req;
+ gulong get_input_focus_req;
+ gboolean have_error;
+ GdkSendXEventCallback callback;
+ gpointer data;
+};
+
+struct _SetInputFocusState
+{
+ Display *dpy;
+ _XAsyncHandler async;
+ gulong set_input_focus_req;
+ gulong get_input_focus_req;
+};
+
+struct _RoundtripState
+{
+ Display *dpy;
+ _XAsyncHandler async;
+ gulong get_input_focus_req;
+ GdkDisplay *display;
+ GdkRoundTripCallback callback;
+ gpointer data;
+};
+
+static gboolean
+callback_idle (gpointer data)
+{
+ SendEventState *state = (SendEventState *)data;
+
+ state->callback (state->window, !state->have_error, state->data);
+
+ g_free (state);
+
+ return FALSE;
+}
+
+static Bool
+send_event_handler (Display *dpy,
+ xReply *rep,
+ char *buf,
+ int len,
+ XPointer data)
+{
+ SendEventState *state = (SendEventState *)data;
+
+ if (dpy->last_request_read == state->send_event_req)
+ {
+ if (rep->generic.type == X_Error &&
+ rep->error.errorCode == BadWindow)
+ {
+ state->have_error = TRUE;
+ return True;
+ }
+ }
+ else if (dpy->last_request_read == state->get_input_focus_req)
+ {
+ xGetInputFocusReply replbuf;
+ xGetInputFocusReply *repl;
+
+ if (rep->generic.type != X_Error)
+ {
+ /* Actually does nothing, since there are no additional bytes
+ * to read, but maintain good form.
+ */
+ repl = (xGetInputFocusReply *)
+ _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
+ (sizeof(xGetInputFocusReply) - sizeof(xReply)) >> 2,
+ True);
+ }
+
+ if (state->callback)
+ gdk_threads_add_idle (callback_idle, state);
+
+ DeqAsyncHandler(state->dpy, &state->async);
+
+ return (rep->generic.type != X_Error);
+ }
+
+ return False;
+}
+
+static void
+client_message_to_wire (XClientMessageEvent *ev,
+ xEvent *event)
+{
+ int i;
+ event->u.clientMessage.window = ev->window;
+ event->u.u.type = ev->type;
+ event->u.u.detail = ev->format;
+ switch (ev->format)
+ {
+ case 8:
+ event->u.clientMessage.u.b.type = ev->message_type;
+ for (i = 0; i < 20; i++)
+ event->u.clientMessage.u.b.bytes[i] = ev->data.b[i];
+ break;
+ case 16:
+ event->u.clientMessage.u.s.type = ev->message_type;
+ event->u.clientMessage.u.s.shorts0 = ev->data.s[0];
+ event->u.clientMessage.u.s.shorts1 = ev->data.s[1];
+ event->u.clientMessage.u.s.shorts2 = ev->data.s[2];
+ event->u.clientMessage.u.s.shorts3 = ev->data.s[3];
+ event->u.clientMessage.u.s.shorts4 = ev->data.s[4];
+ event->u.clientMessage.u.s.shorts5 = ev->data.s[5];
+ event->u.clientMessage.u.s.shorts6 = ev->data.s[6];
+ event->u.clientMessage.u.s.shorts7 = ev->data.s[7];
+ event->u.clientMessage.u.s.shorts8 = ev->data.s[8];
+ event->u.clientMessage.u.s.shorts9 = ev->data.s[9];
+ break;
+ case 32:
+ event->u.clientMessage.u.l.type = ev->message_type;
+ event->u.clientMessage.u.l.longs0 = ev->data.l[0];
+ event->u.clientMessage.u.l.longs1 = ev->data.l[1];
+ event->u.clientMessage.u.l.longs2 = ev->data.l[2];
+ event->u.clientMessage.u.l.longs3 = ev->data.l[3];
+ event->u.clientMessage.u.l.longs4 = ev->data.l[4];
+ break;
+ default:
+ /* client passing bogus data, let server complain */
+ break;
+ }
+}
+
+void
+_gdk_x11_send_client_message_async (GdkDisplay *display,
+ Window window,
+ gboolean propagate,
+ glong event_mask,
+ XClientMessageEvent *event_send,
+ GdkSendXEventCallback callback,
+ gpointer data)
+{
+ Display *dpy;
+ SendEventState *state;
+
+ dpy = GDK_DISPLAY_XDISPLAY (display);
+
+ state = g_new (SendEventState, 1);
+
+ state->dpy = dpy;
+ state->window = window;
+ state->callback = callback;
+ state->data = data;
+ state->have_error = FALSE;
+
+ LockDisplay(dpy);
+
+ state->async.next = dpy->async_handlers;
+ state->async.handler = send_event_handler;
+ state->async.data = (XPointer) state;
+ dpy->async_handlers = &state->async;
+
+ {
+ register xSendEventReq *req;
+ xEvent ev;
+
+ client_message_to_wire (event_send, &ev);
+
+ GetReq(SendEvent, req);
+ req->destination = window;
+ req->propagate = propagate;
+ req->eventMask = event_mask;
+ /* gross, matches Xproto.h */
+#ifdef WORD64
+ memcpy ((char *) req->eventdata, (char *) &ev, SIZEOF(xEvent));
+#else
+ memcpy ((char *) &req->event, (char *) &ev, SIZEOF(xEvent));
+#endif
+
+ state->send_event_req = dpy->request;
+ }
+
+ /*
+ * XSync (dpy, 0)
+ */
+ {
+ xReq *req;
+
+ GetEmptyReq(GetInputFocus, req);
+ state->get_input_focus_req = dpy->request;
+ }
+
+ UnlockDisplay(dpy);
+ SyncHandle();
+}
+
+static Bool
+set_input_focus_handler (Display *dpy,
+ xReply *rep,
+ char *buf,
+ int len,
+ XPointer data)
+{
+ SetInputFocusState *state = (SetInputFocusState *)data;
+
+ if (dpy->last_request_read == state->set_input_focus_req)
+ {
+ if (rep->generic.type == X_Error &&
+ rep->error.errorCode == BadMatch)
+ {
+ /* Consume BadMatch errors, since we have no control
+ * over them.
+ */
+ return True;
+ }
+ }
+
+ if (dpy->last_request_read == state->get_input_focus_req)
+ {
+ xGetInputFocusReply replbuf;
+ xGetInputFocusReply *repl;
+
+ if (rep->generic.type != X_Error)
+ {
+ /* Actually does nothing, since there are no additional bytes
+ * to read, but maintain good form.
+ */
+ repl = (xGetInputFocusReply *)
+ _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
+ (sizeof(xGetInputFocusReply) - sizeof(xReply)) >> 2,
+ True);
+ }
+
+ DeqAsyncHandler(state->dpy, &state->async);
+
+ g_free (state);
+
+ return (rep->generic.type != X_Error);
+ }
+
+ return False;
+}
+
+void
+_gdk_x11_set_input_focus_safe (GdkDisplay *display,
+ Window window,
+ int revert_to,
+ Time time)
+{
+ Display *dpy;
+ SetInputFocusState *state;
+
+ dpy = GDK_DISPLAY_XDISPLAY (display);
+
+ state = g_new (SetInputFocusState, 1);
+
+ state->dpy = dpy;
+
+ LockDisplay(dpy);
+
+ state->async.next = dpy->async_handlers;
+ state->async.handler = set_input_focus_handler;
+ state->async.data = (XPointer) state;
+ dpy->async_handlers = &state->async;
+
+ {
+ xSetInputFocusReq *req;
+
+ GetReq(SetInputFocus, req);
+ req->focus = window;
+ req->revertTo = revert_to;
+ req->time = time;
+ state->set_input_focus_req = dpy->request;
+ }
+
+ /*
+ * XSync (dpy, 0)
+ */
+ {
+ xReq *req;
+
+ GetEmptyReq(GetInputFocus, req);
+ state->get_input_focus_req = dpy->request;
+ }
+
+ UnlockDisplay(dpy);
+ SyncHandle();
+}
+
+static Bool
+list_children_handler (Display *dpy,
+ xReply *rep,
+ char *buf,
+ int len,
+ XPointer data)
+{
+ ListChildrenState *state = (ListChildrenState *)data;
+
+ if (dpy->last_request_read != state->get_property_req)
+ return False;
+
+ if (rep->generic.type == X_Error)
+ {
+ state->have_error = TRUE;
+ return False;
+ }
+ else
+ {
+ xGetPropertyReply replbuf;
+ xGetPropertyReply *repl;
+
+ repl = (xGetPropertyReply *)
+ _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
+ (sizeof(xGetPropertyReply) - sizeof(xReply)) >> 2,
+ True);
+
+ state->has_wm_state = repl->propertyType != None;
+ /* Since we called GetProperty with longLength of 0, we don't
+ * have to worry about consuming the property data that would
+ * normally follow after the reply
+ */
+
+ return True;
+ }
+}
+
+static gboolean
+list_children_and_wm_state (Display *dpy,
+ Window w,
+ Atom wm_state_atom,
+ gboolean *has_wm_state,
+ Window **children,
+ unsigned int *nchildren)
+{
+ ListChildrenState state;
+ _XAsyncHandler async;
+ long nbytes;
+ xQueryTreeReply rep;
+ register xResourceReq *req;
+ xGetPropertyReq *prop_req;
+
+ LockDisplay(dpy);
+
+ *children = NULL;
+ *nchildren = 0;
+ *has_wm_state = FALSE;
+
+ state.have_error = FALSE;
+ state.has_wm_state = FALSE;
+
+ if (wm_state_atom)
+ {
+ async.next = dpy->async_handlers;
+ async.handler = list_children_handler;
+ async.data = (XPointer) &state;
+ dpy->async_handlers = &async;
+
+ GetReq (GetProperty, prop_req);
+ prop_req->window = w;
+ prop_req->property = wm_state_atom;
+ prop_req->type = AnyPropertyType;
+ prop_req->delete = False;
+ prop_req->longOffset = 0;
+ prop_req->longLength = 0;
+
+ state.get_property_req = dpy->request;
+ }
+
+ GetResReq(QueryTree, w, req);
+ if (!_XReply(dpy, (xReply *)&rep, 0, xFalse))
+ {
+ state.have_error = TRUE;
+ goto out;
+ }
+
+ if (rep.nChildren != 0)
+ {
+ nbytes = rep.nChildren << 2;
+ if (state.have_error)
+ {
+ _XEatData(dpy, (unsigned long) nbytes);
+ goto out;
+ }
+ *children = g_new (Window, rep.nChildren);
+ _XRead32 (dpy, (long *) *children, nbytes);
+ }
+
+ *nchildren = rep.nChildren;
+ *has_wm_state = state.has_wm_state;
+
+ out:
+ if (wm_state_atom)
+ DeqAsyncHandler(dpy, &async);
+ UnlockDisplay(dpy);
+ SyncHandle();
+
+ return !state.have_error;
+}
+
+static void
+handle_get_wa_reply (Display *dpy,
+ ChildInfoState *state,
+ xGetWindowAttributesReply *repl)
+{
+ GdkChildInfoX11 *child = &state->child_info[state->n_children_found];
+ child->is_mapped = repl->mapState != IsUnmapped;
+ child->window_class = repl->class;
+}
+
+static void
+handle_get_geometry_reply (Display *dpy,
+ ChildInfoState *state,
+ xGetGeometryReply *repl)
+{
+ GdkChildInfoX11 *child = &state->child_info[state->n_children_found];
+
+ child->x = cvtINT16toInt (repl->x);
+ child->y = cvtINT16toInt (repl->y);
+ child->width = repl->width;
+ child->height = repl->height;
+}
+
+static void
+handle_get_property_reply (Display *dpy,
+ ChildInfoState *state,
+ xGetPropertyReply *repl)
+{
+ GdkChildInfoX11 *child = &state->child_info[state->n_children_found];
+ child->has_wm_state = repl->propertyType != None;
+
+ /* Since we called GetProperty with longLength of 0, we don't
+ * have to worry about consuming the property data that would
+ * normally follow after the reply
+ */
+}
+
+static void
+next_child (ChildInfoState *state)
+{
+ if (state->current_request == CHILD_INFO_GET_GEOMETRY)
+ {
+ if (!state->have_error && !state->child_has_error)
+ {
+ state->child_info[state->n_children_found].window = state->children[state->current_child];
+ state->n_children_found++;
+ }
+ state->current_child++;
+ if (state->get_wm_state)
+ state->current_request = CHILD_INFO_GET_PROPERTY;
+ else
+ state->current_request = CHILD_INFO_GET_WA;
+ state->child_has_error = FALSE;
+ state->have_error = FALSE;
+ }
+ else
+ state->current_request++;
+}
+
+static Bool
+get_child_info_handler (Display *dpy,
+ xReply *rep,
+ char *buf,
+ int len,
+ XPointer data)
+{
+ Bool result = True;
+
+ ChildInfoState *state = (ChildInfoState *)data;
+
+ if (dpy->last_request_read != state->child_states[state->current_child].seq[state->current_request])
+ return False;
+
+ if (rep->generic.type == X_Error)
+ {
+ state->child_has_error = TRUE;
+ if (rep->error.errorCode != BadDrawable ||
+ rep->error.errorCode != BadWindow)
+ {
+ state->have_error = TRUE;
+ result = False;
+ }
+ }
+ else
+ {
+ switch (state->current_request)
+ {
+ case CHILD_INFO_GET_PROPERTY:
+ {
+ xGetPropertyReply replbuf;
+ xGetPropertyReply *repl;
+
+ repl = (xGetPropertyReply *)
+ _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
+ (sizeof(xGetPropertyReply) - sizeof(xReply)) >> 2,
+ True);
+
+ handle_get_property_reply (dpy, state, repl);
+ }
+ break;
+ case CHILD_INFO_GET_WA:
+ {
+ xGetWindowAttributesReply replbuf;
+ xGetWindowAttributesReply *repl;
+
+ repl = (xGetWindowAttributesReply *)
+ _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
+ (sizeof(xGetWindowAttributesReply) - sizeof(xReply)) >> 2,
+ True);
+
+ handle_get_wa_reply (dpy, state, repl);
+ }
+ break;
+ case CHILD_INFO_GET_GEOMETRY:
+ {
+ xGetGeometryReply replbuf;
+ xGetGeometryReply *repl;
+
+ repl = (xGetGeometryReply *)
+ _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
+ (sizeof(xGetGeometryReply) - sizeof(xReply)) >> 2,
+ True);
+
+ handle_get_geometry_reply (dpy, state, repl);
+ }
+ break;
+ }
+ }
+
+ next_child (state);
+
+ return result;
+}
+
+gboolean
+_gdk_x11_get_window_child_info (GdkDisplay *display,
+ Window window,
+ gboolean get_wm_state,
+ gboolean *win_has_wm_state,
+ GdkChildInfoX11 **children,
+ guint *nchildren)
+{
+ Display *dpy;
+ _XAsyncHandler async;
+ ChildInfoState state;
+ Atom wm_state_atom;
+ gboolean has_wm_state;
+ Bool result;
+ guint i;
+
+ *children = NULL;
+ *nchildren = 0;
+
+ dpy = GDK_DISPLAY_XDISPLAY (display);
+ if (get_wm_state)
+ wm_state_atom = gdk_x11_get_xatom_by_name_for_display (display, "WM_STATE");
+ else
+ wm_state_atom = None;
+
+ state.children = NULL;
+ state.nchildren = 0;
+
+ gdk_error_trap_push ();
+ result = list_children_and_wm_state (dpy, window,
+ win_has_wm_state ? wm_state_atom : None,
+ &has_wm_state,
+ &state.children, &state.nchildren);
+ gdk_error_trap_pop_ignored ();
+ if (!result)
+ {
+ g_free (state.children);
+ return FALSE;
+ }
+
+ if (has_wm_state)
+ {
+ if (win_has_wm_state)
+ *win_has_wm_state = TRUE;
+ g_free (state.children);
+ return TRUE;
+ }
+ else
+ {
+ if (win_has_wm_state)
+ *win_has_wm_state = FALSE;
+ }
+
+ state.get_wm_state = get_wm_state;
+ state.child_info = g_new (GdkChildInfoX11, state.nchildren);
+ state.child_states = g_new (ChildInfoChildState, state.nchildren);
+ state.current_child = 0;
+ state.n_children_found = 0;
+ if (get_wm_state)
+ state.current_request = CHILD_INFO_GET_PROPERTY;
+ else
+ state.current_request = CHILD_INFO_GET_WA;
+ state.have_error = FALSE;
+ state.child_has_error = FALSE;
+
+ LockDisplay(dpy);
+
+ async.next = dpy->async_handlers;
+ async.handler = get_child_info_handler;
+ async.data = (XPointer) &state;
+ dpy->async_handlers = &async;
+
+ for (i = 0; i < state.nchildren; i++)
+ {
+ xResourceReq *resource_req;
+ xGetPropertyReq *prop_req;
+ Window window = state.children[i];
+
+ if (get_wm_state)
+ {
+ GetReq (GetProperty, prop_req);
+ prop_req->window = window;
+ prop_req->property = wm_state_atom;
+ prop_req->type = AnyPropertyType;
+ prop_req->delete = False;
+ prop_req->longOffset = 0;
+ prop_req->longLength = 0;
+
+ state.child_states[i].seq[CHILD_INFO_GET_PROPERTY] = dpy->request;
+ }
+
+ GetResReq(GetWindowAttributes, window, resource_req);
+ state.child_states[i].seq[CHILD_INFO_GET_WA] = dpy->request;
+
+ GetResReq(GetGeometry, window, resource_req);
+ state.child_states[i].seq[CHILD_INFO_GET_GEOMETRY] = dpy->request;
+ }
+
+ if (i != 0)
+ {
+ /* Wait for the last reply
+ */
+ xGetGeometryReply rep;
+
+ /* On error, our async handler will get called
+ */
+ if (_XReply (dpy, (xReply *)&rep, 0, xTrue))
+ handle_get_geometry_reply (dpy, &state, &rep);
+
+ next_child (&state);
+ }
+
+ if (!state.have_error)
+ {
+ *children = state.child_info;
+ *nchildren = state.n_children_found;
+ }
+ else
+ {
+ g_free (state.child_info);
+ }
+
+ g_free (state.children);
+ g_free (state.child_states);
+
+ DeqAsyncHandler(dpy, &async);
+ UnlockDisplay(dpy);
+ SyncHandle();
+
+ return !state.have_error;
+}
+
+static gboolean
+roundtrip_callback_idle (gpointer data)
+{
+ RoundtripState *state = (RoundtripState *)data;
+
+ state->callback (state->display, state->data, state->get_input_focus_req);
+
+ g_free (state);
+
+ return FALSE;
+}
+
+static Bool
+roundtrip_handler (Display *dpy,
+ xReply *rep,
+ char *buf,
+ int len,
+ XPointer data)
+{
+ RoundtripState *state = (RoundtripState *)data;
+
+ if (dpy->last_request_read == state->get_input_focus_req)
+ {
+ xGetInputFocusReply replbuf;
+ xGetInputFocusReply *repl;
+
+ if (rep->generic.type != X_Error)
+ {
+ /* Actually does nothing, since there are no additional bytes
+ * to read, but maintain good form.
+ */
+ repl = (xGetInputFocusReply *)
+ _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
+ (sizeof(xGetInputFocusReply) - sizeof(xReply)) >> 2,
+ True);
+ }
+
+
+ if (state->callback)
+ gdk_threads_add_idle (roundtrip_callback_idle, state);
+
+ DeqAsyncHandler(state->dpy, &state->async);
+
+ return (rep->generic.type != X_Error);
+ }
+
+ return False;
+}
+
+void
+_gdk_x11_roundtrip_async (GdkDisplay *display,
+ GdkRoundTripCallback callback,
+ gpointer data)
+{
+ Display *dpy;
+ RoundtripState *state;
+
+ dpy = GDK_DISPLAY_XDISPLAY (display);
+
+ state = g_new (RoundtripState, 1);
+
+ state->display = display;
+ state->dpy = dpy;
+ state->callback = callback;
+ state->data = data;
+
+ LockDisplay(dpy);
+
+ state->async.next = dpy->async_handlers;
+ state->async.handler = roundtrip_handler;
+ state->async.data = (XPointer) state;
+ dpy->async_handlers = &state->async;
+
+ /*
+ * XSync (dpy, 0)
+ */
+ {
+ xReq *req;
+
+ GetEmptyReq(GetInputFocus, req);
+ state->get_input_focus_req = dpy->request;
+ }
+
+ UnlockDisplay(dpy);
+ SyncHandle();
+}
diff --git a/gdk/broadway/gdkasync.h b/gdk/broadway/gdkasync.h
new file mode 100644
index 0000000000..eebea396a6
--- /dev/null
+++ b/gdk/broadway/gdkasync.h
@@ -0,0 +1,75 @@
+/* GTK - The GIMP Toolkit
+ * gdkasync.h: Utility functions using the Xlib asynchronous interfaces
+ * Copyright (C) 2003, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+#ifndef __GDK_ASYNC_H__
+#define __GDK_ASYNC_H__
+
+#include <gdk/gdkdisplay.h>
+#include <X11/Xlib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GdkChildInfoX11 GdkChildInfoX11;
+
+typedef void (*GdkSendXEventCallback) (Window window,
+ gboolean success,
+ gpointer data);
+typedef void (*GdkRoundTripCallback) (GdkDisplay *display,
+ gpointer data,
+ gulong serial);
+
+struct _GdkChildInfoX11
+{
+ Window window;
+ gint x;
+ gint y;
+ gint width;
+ gint height;
+ guint is_mapped : 1;
+ guint has_wm_state : 1;
+ guint window_class : 2;
+};
+
+void _gdk_x11_send_client_message_async (GdkDisplay *display,
+ Window window,
+ gboolean propagate,
+ glong event_mask,
+ XClientMessageEvent *event_send,
+ GdkSendXEventCallback callback,
+ gpointer data);
+void _gdk_x11_set_input_focus_safe (GdkDisplay *display,
+ Window window,
+ int revert_to,
+ Time time);
+
+gboolean _gdk_x11_get_window_child_info (GdkDisplay *display,
+ Window window,
+ gboolean get_wm_state,
+ gboolean *win_has_wm_state,
+ GdkChildInfoX11 **children,
+ guint *nchildren);
+
+void _gdk_x11_roundtrip_async (GdkDisplay *display,
+ GdkRoundTripCallback callback,
+ gpointer data);
+
+G_END_DECLS
+
+#endif /* __GDK_ASYNC_H__ */
diff --git a/gdk/broadway/gdkcursor-broadway.c b/gdk/broadway/gdkcursor-broadway.c
new file mode 100644
index 0000000000..470f723554
--- /dev/null
+++ b/gdk/broadway/gdkcursor-broadway.c
@@ -0,0 +1,1040 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+/* needs to be first because any header might include gdk-pixbuf.h otherwise */
+#define GDK_PIXBUF_ENABLE_BACKEND
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "gdkcursor.h"
+
+#include "gdkprivate-broadway.h"
+#include "gdkdisplay-broadway.h"
+#include "gdkx.h"
+
+#include <X11/Xlib.h>
+#include <X11/cursorfont.h>
+#ifdef HAVE_XCURSOR
+#include <X11/Xcursor/Xcursor.h>
+#endif
+#ifdef HAVE_XFIXES
+#include <X11/extensions/Xfixes.h>
+#endif
+#include <string.h>
+#include <errno.h>
+
+
+static guint theme_serial = 0;
+
+/* cursor_cache holds a cache of non-pixmap cursors to avoid expensive
+ * libXcursor searches, cursors are added to it but only removed when
+ * their display is closed. We make the assumption that since there are
+ * a small number of display's and a small number of cursor's that this
+ * list will stay small enough not to be a problem.
+ */
+static GSList* cursor_cache = NULL;
+
+struct cursor_cache_key
+{
+ GdkDisplay* display;
+ GdkCursorType type;
+ const char* name;
+};
+
+/* Caller should check if there is already a match first.
+ * Cursor MUST be either a typed cursor or a pixmap with
+ * a non-NULL name.
+ */
+static void
+add_to_cache (GdkCursorPrivate* cursor)
+{
+ cursor_cache = g_slist_prepend (cursor_cache, cursor);
+
+ /* Take a ref so that if the caller frees it we still have it */
+ gdk_cursor_ref ((GdkCursor*) cursor);
+}
+
+/* Returns 0 on a match
+ */
+static gint
+cache_compare_func (gconstpointer listelem,
+ gconstpointer target)
+{
+ GdkCursorPrivate* cursor = (GdkCursorPrivate*)listelem;
+ struct cursor_cache_key* key = (struct cursor_cache_key*)target;
+
+ if ((cursor->cursor.type != key->type) ||
+ (cursor->display != key->display))
+ return 1; /* No match */
+
+ /* Elements marked as pixmap must be named cursors
+ * (since we don't store normal pixmap cursors
+ */
+ if (key->type == GDK_CURSOR_IS_PIXMAP)
+ return strcmp (key->name, cursor->name);
+
+ return 0; /* Match */
+}
+
+/* Returns the cursor if there is a match, NULL if not
+ * For named cursors type shall be GDK_CURSOR_IS_PIXMAP
+ * For unnamed, typed cursors, name shall be NULL
+ */
+static GdkCursorPrivate*
+find_in_cache (GdkDisplay *display,
+ GdkCursorType type,
+ const char *name)
+{
+ GSList* res;
+ struct cursor_cache_key key;
+
+ key.display = display;
+ key.type = type;
+ key.name = name;
+
+ res = g_slist_find_custom (cursor_cache, &key, cache_compare_func);
+
+ if (res)
+ return (GdkCursorPrivate *) res->data;
+
+ return NULL;
+}
+
+/* Called by gdk_display_x11_finalize to flush any cached cursors
+ * for a dead display.
+ */
+void
+_gdk_x11_cursor_display_finalize (GdkDisplay *display)
+{
+ GSList* item;
+ GSList** itemp; /* Pointer to the thing to fix when we delete an item */
+ item = cursor_cache;
+ itemp = &cursor_cache;
+ while (item)
+ {
+ GdkCursorPrivate* cursor = (GdkCursorPrivate*)(item->data);
+ if (cursor->display == display)
+ {
+ GSList* olditem;
+ gdk_cursor_unref ((GdkCursor*) cursor);
+ /* Remove this item from the list */
+ *(itemp) = item->next;
+ olditem = item;
+ item = g_slist_next (item);
+ g_slist_free_1 (olditem);
+ }
+ else
+ {
+ itemp = &(item->next);
+ item = g_slist_next (item);
+ }
+ }
+}
+
+static Cursor
+get_blank_cursor (GdkDisplay *display)
+{
+ GdkScreen *screen;
+ Pixmap pixmap;
+ XColor color;
+ Cursor cursor;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+
+ screen = gdk_display_get_default_screen (display);
+ surface = _gdk_x11_window_create_bitmap_surface (gdk_screen_get_root_window (screen), 1, 1);
+ /* Clear surface */
+ cr = cairo_create (surface);
+ cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ pixmap = cairo_xlib_surface_get_drawable (surface);
+
+ color.pixel = 0;
+ color.red = color.blue = color.green = 0;
+
+ if (display->closed)
+ cursor = None;
+ else
+ cursor = XCreatePixmapCursor (GDK_DISPLAY_XDISPLAY (display),
+ pixmap, pixmap,
+ &color, &color, 1, 1);
+ cairo_surface_destroy (surface);
+
+ return cursor;
+}
+
+/**
+ * gdk_cursor_new_for_display:
+ * @display: the #GdkDisplay for which the cursor will be created
+ * @cursor_type: cursor to create
+ *
+ * Creates a new cursor from the set of builtin cursors.
+ * Some useful ones are:
+ * <itemizedlist>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="right_ptr.png"></inlinegraphic> #GDK_RIGHT_PTR (right-facing arrow)
+ * </para></listitem>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="crosshair.png"></inlinegraphic> #GDK_CROSSHAIR (crosshair)
+ * </para></listitem>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="xterm.png"></inlinegraphic> #GDK_XTERM (I-beam)
+ * </para></listitem>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="watch.png"></inlinegraphic> #GDK_WATCH (busy)
+ * </para></listitem>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="fleur.png"></inlinegraphic> #GDK_FLEUR (for moving objects)
+ * </para></listitem>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="hand1.png"></inlinegraphic> #GDK_HAND1 (a right-pointing hand)
+ * </para></listitem>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="hand2.png"></inlinegraphic> #GDK_HAND2 (a left-pointing hand)
+ * </para></listitem>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="left_side.png"></inlinegraphic> #GDK_LEFT_SIDE (resize left side)
+ * </para></listitem>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="right_side.png"></inlinegraphic> #GDK_RIGHT_SIDE (resize right side)
+ * </para></listitem>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="top_left_corner.png"></inlinegraphic> #GDK_TOP_LEFT_CORNER (resize northwest corner)
+ * </para></listitem>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="top_right_corner.png"></inlinegraphic> #GDK_TOP_RIGHT_CORNER (resize northeast corner)
+ * </para></listitem>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="bottom_left_corner.png"></inlinegraphic> #GDK_BOTTOM_LEFT_CORNER (resize southwest corner)
+ * </para></listitem>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="bottom_right_corner.png"></inlinegraphic> #GDK_BOTTOM_RIGHT_CORNER (resize southeast corner)
+ * </para></listitem>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="top_side.png"></inlinegraphic> #GDK_TOP_SIDE (resize top side)
+ * </para></listitem>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="bottom_side.png"></inlinegraphic> #GDK_BOTTOM_SIDE (resize bottom side)
+ * </para></listitem>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="sb_h_double_arrow.png"></inlinegraphic> #GDK_SB_H_DOUBLE_ARROW (move vertical splitter)
+ * </para></listitem>
+ * <listitem><para>
+ * <inlinegraphic format="PNG" fileref="sb_v_double_arrow.png"></inlinegraphic> #GDK_SB_V_DOUBLE_ARROW (move horizontal splitter)
+ * </para></listitem>
+ * <listitem><para>
+ * #GDK_BLANK_CURSOR (Blank cursor). Since 2.16
+ * </para></listitem>
+ * </itemizedlist>
+ *
+ * Return value: a new #GdkCursor
+ *
+ * Since: 2.2
+ **/
+GdkCursor*
+gdk_cursor_new_for_display (GdkDisplay *display,
+ GdkCursorType cursor_type)
+{
+ GdkCursorPrivate *private;
+ GdkCursor *cursor;
+ Cursor xcursor;
+
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
+
+ if (display->closed)
+ {
+ xcursor = None;
+ }
+ else
+ {
+ private = find_in_cache (display, cursor_type, NULL);
+
+ if (private)
+ {
+ /* Cache had it, add a ref for this user */
+ gdk_cursor_ref ((GdkCursor*) private);
+
+ return (GdkCursor*) private;
+ }
+ else
+ {
+ if (cursor_type != GDK_BLANK_CURSOR)
+ xcursor = XCreateFontCursor (GDK_DISPLAY_XDISPLAY (display),
+ cursor_type);
+ else
+ xcursor = get_blank_cursor (display);
+ }
+ }
+
+ private = g_new (GdkCursorPrivate, 1);
+ private->display = display;
+ private->xcursor = xcursor;
+ private->name = NULL;
+ private->serial = theme_serial;
+
+ cursor = (GdkCursor *) private;
+ cursor->type = cursor_type;
+ cursor->ref_count = 1;
+
+ if (xcursor != None)
+ add_to_cache (private);
+
+ return cursor;
+}
+
+void
+_gdk_cursor_destroy (GdkCursor *cursor)
+{
+ GdkCursorPrivate *private;
+
+ g_return_if_fail (cursor != NULL);
+ g_return_if_fail (cursor->ref_count == 0);
+
+ private = (GdkCursorPrivate *) cursor;
+ if (!private->display->closed && private->xcursor)
+ XFreeCursor (GDK_DISPLAY_XDISPLAY (private->display), private->xcursor);
+
+ g_free (private->name);
+ g_free (private);
+}
+
+/**
+ * gdk_x11_cursor_get_xdisplay:
+ * @cursor: a #GdkCursor.
+ *
+ * Returns the display of a #GdkCursor.
+ *
+ * Return value: an Xlib <type>Display*</type>.
+ **/
+Display *
+gdk_x11_cursor_get_xdisplay (GdkCursor *cursor)
+{
+ g_return_val_if_fail (cursor != NULL, NULL);
+
+ return GDK_DISPLAY_XDISPLAY(((GdkCursorPrivate *)cursor)->display);
+}
+
+/**
+ * gdk_x11_cursor_get_xcursor:
+ * @cursor: a #GdkCursor.
+ *
+ * Returns the X cursor belonging to a #GdkCursor.
+ *
+ * Return value: an Xlib <type>Cursor</type>.
+ **/
+Cursor
+gdk_x11_cursor_get_xcursor (GdkCursor *cursor)
+{
+ g_return_val_if_fail (cursor != NULL, None);
+
+ return ((GdkCursorPrivate *)cursor)->xcursor;
+}
+
+/**
+ * gdk_cursor_get_display:
+ * @cursor: a #GdkCursor.
+ *
+ * Returns the display on which the #GdkCursor is defined.
+ *
+ * Returns: the #GdkDisplay associated to @cursor
+ *
+ * Since: 2.2
+ */
+
+GdkDisplay *
+gdk_cursor_get_display (GdkCursor *cursor)
+{
+ g_return_val_if_fail (cursor != NULL, NULL);
+
+ return ((GdkCursorPrivate *)cursor)->display;
+}
+
+#if defined(HAVE_XCURSOR) && defined(HAVE_XFIXES) && XFIXES_MAJOR >= 2
+
+/**
+ * gdk_cursor_get_image:
+ * @cursor: a #GdkCursor
+ *
+ * Returns a #GdkPixbuf with the image used to display the cursor.
+ *
+ * Note that depending on the capabilities of the windowing system and
+ * on the cursor, GDK may not be able to obtain the image data. In this
+ * case, %NULL is returned.
+ *
+ * Returns: a #GdkPixbuf representing @cursor, or %NULL
+ *
+ * Since: 2.8
+ */
+GdkPixbuf*
+gdk_cursor_get_image (GdkCursor *cursor)
+{
+ Display *xdisplay;
+ GdkCursorPrivate *private;
+ XcursorImages *images = NULL;
+ XcursorImage *image;
+ gint size;
+ gchar buf[32];
+ guchar *data, *p, tmp;
+ GdkPixbuf *pixbuf;
+ gchar *theme;
+
+ g_return_val_if_fail (cursor != NULL, NULL);
+
+ private = (GdkCursorPrivate *) cursor;
+
+ xdisplay = GDK_DISPLAY_XDISPLAY (private->display);
+
+ size = XcursorGetDefaultSize (xdisplay);
+ theme = XcursorGetTheme (xdisplay);
+
+ if (cursor->type == GDK_CURSOR_IS_PIXMAP)
+ {
+ if (private->name)
+ images = XcursorLibraryLoadImages (private->name, theme, size);
+ }
+ else
+ images = XcursorShapeLoadImages (cursor->type, theme, size);
+
+ if (!images)
+ return NULL;
+
+ image = images->images[0];
+
+ data = g_malloc (4 * image->width * image->height);
+ memcpy (data, image->pixels, 4 * image->width * image->height);
+
+ for (p = data; p < data + (4 * image->width * image->height); p += 4)
+ {
+ tmp = p[0];
+ p[0] = p[2];
+ p[2] = tmp;
+ }
+
+ pixbuf = gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB, TRUE,
+ 8, image->width, image->height,
+ 4 * image->width,
+ (GdkPixbufDestroyNotify)g_free, NULL);
+
+ if (private->name)
+ gdk_pixbuf_set_option (pixbuf, "name", private->name);
+ g_snprintf (buf, 32, "%d", image->xhot);
+ gdk_pixbuf_set_option (pixbuf, "x_hot", buf);
+ g_snprintf (buf, 32, "%d", image->yhot);
+ gdk_pixbuf_set_option (pixbuf, "y_hot", buf);
+
+ XcursorImagesDestroy (images);
+
+ return pixbuf;
+}
+
+void
+_gdk_x11_cursor_update_theme (GdkCursor *cursor)
+{
+ Display *xdisplay;
+ GdkCursorPrivate *private;
+ Cursor new_cursor = None;
+ GdkDisplayX11 *display_x11;
+
+ private = (GdkCursorPrivate *) cursor;
+ xdisplay = GDK_DISPLAY_XDISPLAY (private->display);
+ display_x11 = GDK_DISPLAY_X11 (private->display);
+
+ if (!display_x11->have_xfixes)
+ return;
+
+ if (private->serial == theme_serial)
+ return;
+
+ private->serial = theme_serial;
+
+ if (private->xcursor != None)
+ {
+ if (cursor->type == GDK_BLANK_CURSOR)
+ return;
+
+ if (cursor->type == GDK_CURSOR_IS_PIXMAP)
+ {
+ if (private->name)
+ new_cursor = XcursorLibraryLoadCursor (xdisplay, private->name);
+ }
+ else
+ new_cursor = XcursorShapeLoadCursor (xdisplay, cursor->type);
+
+ if (new_cursor != None)
+ {
+ XFixesChangeCursor (xdisplay, new_cursor, private->xcursor);
+ private->xcursor = new_cursor;
+ }
+ }
+}
+
+static void
+update_cursor (gpointer data,
+ gpointer user_data)
+{
+ GdkCursor *cursor;
+
+ cursor = (GdkCursor*)(data);
+
+ if (!cursor)
+ return;
+
+ _gdk_x11_cursor_update_theme (cursor);
+}
+
+/**
+ * gdk_x11_display_set_cursor_theme:
+ * @display: a #GdkDisplay
+ * @theme: the name of the cursor theme to use, or %NULL to unset
+ * a previously set value
+ * @size: the cursor size to use, or 0 to keep the previous size
+ *
+ * Sets the cursor theme from which the images for cursor
+ * should be taken.
+ *
+ * If the windowing system supports it, existing cursors created
+ * with gdk_cursor_new(), gdk_cursor_new_for_display() and
+ * gdk_cursor_new_for_name() are updated to reflect the theme
+ * change. Custom cursors constructed with
+ * gdk_cursor_new_from_pixbuf() will have to be handled
+ * by the application (GTK+ applications can learn about
+ * cursor theme changes by listening for change notification
+ * for the corresponding #GtkSetting).
+ *
+ * Since: 2.8
+ */
+void
+gdk_x11_display_set_cursor_theme (GdkDisplay *display,
+ const gchar *theme,
+ const gint size)
+{
+ GdkDisplayX11 *display_x11;
+ Display *xdisplay;
+ gchar *old_theme;
+ gint old_size;
+
+ g_return_if_fail (GDK_IS_DISPLAY (display));
+
+ display_x11 = GDK_DISPLAY_X11 (display);
+ xdisplay = GDK_DISPLAY_XDISPLAY (display);
+
+ old_theme = XcursorGetTheme (xdisplay);
+ old_size = XcursorGetDefaultSize (xdisplay);
+
+ if (old_size == size &&
+ (old_theme == theme ||
+ (old_theme && theme && strcmp (old_theme, theme) == 0)))
+ return;
+
+ theme_serial++;
+
+ XcursorSetTheme (xdisplay, theme);
+ if (size > 0)
+ XcursorSetDefaultSize (xdisplay, size);
+
+ g_slist_foreach (cursor_cache, update_cursor, NULL);
+}
+
+#else
+
+GdkPixbuf*
+gdk_cursor_get_image (GdkCursor *cursor)
+{
+ g_return_val_if_fail (cursor != NULL, NULL);
+
+ return NULL;
+}
+
+void
+gdk_x11_display_set_cursor_theme (GdkDisplay *display,
+ const gchar *theme,
+ const gint size)
+{
+ g_return_if_fail (GDK_IS_DISPLAY (display));
+}
+
+void
+_gdk_x11_cursor_update_theme (GdkCursor *cursor)
+{
+ g_return_if_fail (cursor != NULL);
+}
+
+#endif
+
+#ifdef HAVE_XCURSOR
+
+static XcursorImage*
+create_cursor_image (GdkPixbuf *pixbuf,
+ gint x,
+ gint y)
+{
+ guint width, height;
+ XcursorImage *xcimage;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+
+ xcimage = XcursorImageCreate (width, height);
+
+ xcimage->xhot = x;
+ xcimage->yhot = y;
+
+ surface = cairo_image_surface_create_for_data ((guchar *) xcimage->pixels,
+ CAIRO_FORMAT_ARGB32,
+ width,
+ height,
+ width * 4);
+
+ cr = cairo_create (surface);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ cairo_surface_destroy (surface);
+
+ return xcimage;
+}
+
+
+/**
+ * gdk_cursor_new_from_pixbuf:
+ * @display: the #GdkDisplay for which the cursor will be created
+ * @pixbuf: the #GdkPixbuf containing the cursor image
+ * @x: the horizontal offset of the 'hotspot' of the cursor.
+ * @y: the vertical offset of the 'hotspot' of the cursor.
+ *
+ * Creates a new cursor from a pixbuf.
+ *
+ * Not all GDK backends support RGBA cursors. If they are not
+ * supported, a monochrome approximation will be displayed.
+ * The functions gdk_display_supports_cursor_alpha() and
+ * gdk_display_supports_cursor_color() can be used to determine
+ * whether RGBA cursors are supported;
+ * gdk_display_get_default_cursor_size() and
+ * gdk_display_get_maximal_cursor_size() give information about
+ * cursor sizes.
+ *
+ * If @x or @y are <literal>-1</literal>, the pixbuf must have
+ * options named "x_hot" and "y_hot", resp., containing
+ * integer values between %0 and the width resp. height of
+ * the pixbuf. (Since: 3.0)
+ *
+ * On the X backend, support for RGBA cursors requires a
+ * sufficently new version of the X Render extension.
+ *
+ * Returns: a new #GdkCursor.
+ *
+ * Since: 2.4
+ */
+GdkCursor *
+gdk_cursor_new_from_pixbuf (GdkDisplay *display,
+ GdkPixbuf *pixbuf,
+ gint x,
+ gint y)
+{
+ XcursorImage *xcimage;
+ Cursor xcursor;
+ GdkCursorPrivate *private;
+ GdkCursor *cursor;
+ const char *option;
+ char *end;
+ gint64 value;
+
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
+ g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
+
+ if (x == -1 && (option = gdk_pixbuf_get_option (pixbuf, "x_hot")))
+ {
+ errno = 0;
+ end = NULL;
+ value = g_ascii_strtoll (option, &end, 10);
+ if (errno == 0 &&
+ end != option &&
+ value >= 0 && value < G_MAXINT)
+ x = (gint) value;
+ }
+ if (y == -1 && (option = gdk_pixbuf_get_option (pixbuf, "y_hot")))
+ {
+ errno = 0;
+ end = NULL;
+ value = g_ascii_strtoll (option, &end, 10);
+ if (errno == 0 &&
+ end != option &&
+ value >= 0 && value < G_MAXINT)
+ y = (gint) value;
+ }
+
+ g_return_val_if_fail (0 <= x && x < gdk_pixbuf_get_width (pixbuf), NULL);
+ g_return_val_if_fail (0 <= y && y < gdk_pixbuf_get_height (pixbuf), NULL);
+
+ if (display->closed)
+ xcursor = None;
+ else
+ {
+ xcimage = create_cursor_image (pixbuf, x, y);
+ xcursor = XcursorImageLoadCursor (GDK_DISPLAY_XDISPLAY (display), xcimage);
+ XcursorImageDestroy (xcimage);
+ }
+
+ private = g_new (GdkCursorPrivate, 1);
+ private->display = display;
+ private->xcursor = xcursor;
+ private->name = NULL;
+ private->serial = theme_serial;
+
+ cursor = (GdkCursor *) private;
+ cursor->type = GDK_CURSOR_IS_PIXMAP;
+ cursor->ref_count = 1;
+
+ return cursor;
+}
+
+/**
+ * gdk_cursor_new_from_name:
+ * @display: the #GdkDisplay for which the cursor will be created
+ * @name: the name of the cursor
+ *
+ * Creates a new cursor by looking up @name in the current cursor
+ * theme.
+ *
+ * Returns: a new #GdkCursor, or %NULL if there is no cursor with
+ * the given name
+ *
+ * Since: 2.8
+ */
+GdkCursor*
+gdk_cursor_new_from_name (GdkDisplay *display,
+ const gchar *name)
+{
+ Cursor xcursor;
+ Display *xdisplay;
+ GdkCursorPrivate *private;
+ GdkCursor *cursor;
+
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
+
+ if (display->closed)
+ xcursor = None;
+ else
+ {
+ private = find_in_cache (display, GDK_CURSOR_IS_PIXMAP, name);
+
+ if (private)
+ {
+ /* Cache had it, add a ref for this user */
+ gdk_cursor_ref ((GdkCursor*) private);
+
+ return (GdkCursor*) private;
+ }
+
+ xdisplay = GDK_DISPLAY_XDISPLAY (display);
+ xcursor = XcursorLibraryLoadCursor (xdisplay, name);
+ if (xcursor == None)
+ return NULL;
+ }
+
+ private = g_new (GdkCursorPrivate, 1);
+ private->display = display;
+ private->xcursor = xcursor;
+ private->name = g_strdup (name);
+ private->serial = theme_serial;
+
+ cursor = (GdkCursor *) private;
+ cursor->type = GDK_CURSOR_IS_PIXMAP;
+ cursor->ref_count = 1;
+ add_to_cache (private);
+
+ return cursor;
+}
+
+/**
+ * gdk_display_supports_cursor_alpha:
+ * @display: a #GdkDisplay
+ *
+ * Returns %TRUE if cursors can use an 8bit alpha channel
+ * on @display. Otherwise, cursors are restricted to bilevel
+ * alpha (i.e. a mask).
+ *
+ * Returns: whether cursors can have alpha channels.
+ *
+ * Since: 2.4
+ */
+gboolean
+gdk_display_supports_cursor_alpha (GdkDisplay *display)
+{
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
+
+ return XcursorSupportsARGB (GDK_DISPLAY_XDISPLAY (display));
+}
+
+/**
+ * gdk_display_supports_cursor_color:
+ * @display: a #GdkDisplay
+ *
+ * Returns %TRUE if multicolored cursors are supported
+ * on @display. Otherwise, cursors have only a forground
+ * and a background color.
+ *
+ * Returns: whether cursors can have multiple colors.
+ *
+ * Since: 2.4
+ */
+gboolean
+gdk_display_supports_cursor_color (GdkDisplay *display)
+{
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
+
+ return XcursorSupportsARGB (GDK_DISPLAY_XDISPLAY (display));
+}
+
+/**
+ * gdk_display_get_default_cursor_size:
+ * @display: a #GdkDisplay
+ *
+ * Returns the default size to use for cursors on @display.
+ *
+ * Returns: the default cursor size.
+ *
+ * Since: 2.4
+ */
+guint
+gdk_display_get_default_cursor_size (GdkDisplay *display)
+{
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
+
+ return XcursorGetDefaultSize (GDK_DISPLAY_XDISPLAY (display));
+}
+
+#else
+
+static GdkCursor*
+gdk_cursor_new_from_pixmap (GdkDisplay *display,
+ Pixmap source_pixmap,
+ Pixmap mask_pixmap,
+ const GdkColor *fg,
+ const GdkColor *bg,
+ gint x,
+ gint y)
+{
+ GdkCursorPrivate *private;
+ GdkCursor *cursor;
+ Cursor xcursor;
+ XColor xfg, xbg;
+
+ g_return_val_if_fail (fg != NULL, NULL);
+ g_return_val_if_fail (bg != NULL, NULL);
+
+ xfg.pixel = fg->pixel;
+ xfg.red = fg->red;
+ xfg.blue = fg->blue;
+ xfg.green = fg->green;
+ xbg.pixel = bg->pixel;
+ xbg.red = bg->red;
+ xbg.blue = bg->blue;
+ xbg.green = bg->green;
+
+ if (display->closed)
+ xcursor = None;
+ else
+ xcursor = XCreatePixmapCursor (GDK_DISPLAY_XDISPLAY (display),
+ source_pixmap, mask_pixmap, &xfg, &xbg, x, y);
+ private = g_new (GdkCursorPrivate, 1);
+ private->display = display;
+ private->xcursor = xcursor;
+ private->name = NULL;
+ private->serial = theme_serial;
+
+ cursor = (GdkCursor *) private;
+ cursor->type = GDK_CURSOR_IS_PIXMAP;
+ cursor->ref_count = 1;
+
+ return cursor;
+}
+
+GdkCursor *
+gdk_cursor_new_from_pixbuf (GdkDisplay *display,
+ GdkPixbuf *pixbuf,
+ gint x,
+ gint y)
+{
+ GdkCursor *cursor;
+ cairo_surface_t *pixmap, *mask;
+ guint width, height, n_channels, rowstride, data_stride, i, j;
+ guint8 *data, *mask_data, *pixels;
+ GdkColor fg = { 0, 0, 0, 0 };
+ GdkColor bg = { 0, 0xffff, 0xffff, 0xffff };
+ GdkScreen *screen;
+ cairo_surface_t *image;
+ cairo_t *cr;
+
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
+ g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+
+ g_return_val_if_fail (0 <= x && x < width, NULL);
+ g_return_val_if_fail (0 <= y && y < height, NULL);
+
+ n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+ data_stride = 4 * ((width + 31) / 32);
+ data = g_new0 (guint8, data_stride * height);
+ mask_data = g_new0 (guint8, data_stride * height);
+
+ for (j = 0; j < height; j++)
+ {
+ guint8 *src = pixels + j * rowstride;
+ guint8 *d = data + data_stride * j;
+ guint8 *md = mask_data + data_stride * j;
+
+ for (i = 0; i < width; i++)
+ {
+ if (src[1] < 0x80)
+ *d |= 1 << (i % 8);
+
+ if (n_channels == 3 || src[3] >= 0x80)
+ *md |= 1 << (i % 8);
+
+ src += n_channels;
+ if (i % 8 == 7)
+ {
+ d++;
+ md++;
+ }
+ }
+ }
+
+ screen = gdk_display_get_default_screen (display);
+
+ pixmap = _gdk_x11_window_create_bitmap_surface (gdk_screen_get_root_window (screen),
+ width, height);
+ cr = cairo_create (pixmap);
+ image = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_A1,
+ width, height, data_stride);
+ cairo_set_source_surface (cr, image, 0, 0);
+ cairo_surface_destroy (image);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ mask = _gdk_x11_window_create_bitmap_surface (gdk_screen_get_root_window (screen),
+ width, height);
+ cr = cairo_create (mask);
+ image = cairo_image_surface_create_for_data (mask_data, CAIRO_FORMAT_A1,
+ width, height, data_stride);
+ cairo_set_source_surface (cr, image, 0, 0);
+ cairo_surface_destroy (image);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ cursor = gdk_cursor_new_from_pixmap (display,
+ cairo_xlib_surface_get_drawable (pixmap),
+ cairo_xlib_surface_get_drawable (mask),
+ &fg, &bg,
+ x, y);
+
+ cairo_surface_destroy (pixmap);
+ cairo_surface_destroy (mask);
+
+ g_free (data);
+ g_free (mask_data);
+
+ return cursor;
+}
+
+GdkCursor*
+gdk_cursor_new_from_name (GdkDisplay *display,
+ const gchar *name)
+{
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
+
+ return NULL;
+}
+
+gboolean
+gdk_display_supports_cursor_alpha (GdkDisplay *display)
+{
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
+
+ return FALSE;
+}
+
+gboolean
+gdk_display_supports_cursor_color (GdkDisplay *display)
+{
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
+
+ return FALSE;
+}
+
+guint
+gdk_display_get_default_cursor_size (GdkDisplay *display)
+{
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
+
+ /* no idea, really */
+ return 20;
+}
+
+#endif
+
+
+/**
+ * gdk_display_get_maximal_cursor_size:
+ * @display: a #GdkDisplay
+ * @width: (out): the return location for the maximal cursor width
+ * @height: (out): the return location for the maximal cursor height
+ *
+ * Gets the maximal size to use for cursors on @display.
+ *
+ * Since: 2.4
+ */
+void
+gdk_display_get_maximal_cursor_size (GdkDisplay *display,
+ guint *width,
+ guint *height)
+{
+ GdkScreen *screen;
+ GdkWindow *window;
+
+ g_return_if_fail (GDK_IS_DISPLAY (display));
+
+ screen = gdk_display_get_default_screen (display);
+ window = gdk_screen_get_root_window (screen);
+ XQueryBestCursor (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XWINDOW (window),
+ 128, 128, width, height);
+}
diff --git a/gdk/broadway/gdkdevice-broadway.c b/gdk/broadway/gdkdevice-broadway.c
new file mode 100644
index 0000000000..3be222de08
--- /dev/null
+++ b/gdk/broadway/gdkdevice-broadway.c
@@ -0,0 +1,502 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 "gdkdevice-broadway.h"
+
+#include "gdkwindow.h"
+#include "gdkprivate-broadway.h"
+#include "gdkx.h"
+
+static gboolean gdk_device_core_get_history (GdkDevice *device,
+ GdkWindow *window,
+ guint32 start,
+ guint32 stop,
+ GdkTimeCoord ***events,
+ gint *n_events);
+static void gdk_device_core_get_state (GdkDevice *device,
+ GdkWindow *window,
+ gdouble *axes,
+ GdkModifierType *mask);
+static void gdk_device_core_set_window_cursor (GdkDevice *device,
+ GdkWindow *window,
+ GdkCursor *cursor);
+static void gdk_device_core_warp (GdkDevice *device,
+ GdkScreen *screen,
+ gint x,
+ gint y);
+static gboolean gdk_device_core_query_state (GdkDevice *device,
+ GdkWindow *window,
+ GdkWindow **root_window,
+ GdkWindow **child_window,
+ gint *root_x,
+ gint *root_y,
+ gint *win_x,
+ gint *win_y,
+ GdkModifierType *mask);
+static GdkGrabStatus gdk_device_core_grab (GdkDevice *device,
+ GdkWindow *window,
+ gboolean owner_events,
+ GdkEventMask event_mask,
+ GdkWindow *confine_to,
+ GdkCursor *cursor,
+ guint32 time_);
+static void gdk_device_core_ungrab (GdkDevice *device,
+ guint32 time_);
+static GdkWindow * gdk_device_core_window_at_position (GdkDevice *device,
+ gint *win_x,
+ gint *win_y,
+ GdkModifierType *mask,
+ gboolean get_toplevel);
+static void gdk_device_core_select_window_events (GdkDevice *device,
+ GdkWindow *window,
+ GdkEventMask event_mask);
+
+
+G_DEFINE_TYPE (GdkDeviceCore, gdk_device_core, GDK_TYPE_DEVICE)
+
+static void
+gdk_device_core_class_init (GdkDeviceCoreClass *klass)
+{
+ GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass);
+
+ device_class->get_history = gdk_device_core_get_history;
+ device_class->get_state = gdk_device_core_get_state;
+ device_class->set_window_cursor = gdk_device_core_set_window_cursor;
+ device_class->warp = gdk_device_core_warp;
+ device_class->query_state = gdk_device_core_query_state;
+ device_class->grab = gdk_device_core_grab;
+ device_class->ungrab = gdk_device_core_ungrab;
+ device_class->window_at_position = gdk_device_core_window_at_position;
+ device_class->select_window_events = gdk_device_core_select_window_events;
+}
+
+static void
+gdk_device_core_init (GdkDeviceCore *device_core)
+{
+ GdkDevice *device;
+
+ device = GDK_DEVICE (device_core);
+
+ _gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_X, 0, 0, 1);
+ _gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_Y, 0, 0, 1);
+}
+
+static gboolean
+impl_coord_in_window (GdkWindow *window,
+ int impl_x,
+ int impl_y)
+{
+ GdkWindowObject *priv = (GdkWindowObject *) window;
+
+ if (impl_x < priv->abs_x ||
+ impl_x > priv->abs_x + priv->width)
+ return FALSE;
+
+ if (impl_y < priv->abs_y ||
+ impl_y > priv->abs_y + priv->height)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+gdk_device_core_get_history (GdkDevice *device,
+ GdkWindow *window,
+ guint32 start,
+ guint32 stop,
+ GdkTimeCoord ***events,
+ gint *n_events)
+{
+ GdkWindowObject *priv;
+ XTimeCoord *xcoords;
+ GdkTimeCoord **coords;
+ GdkWindow *impl_window;
+ int tmp_n_events;
+ int i, j;
+
+ impl_window = _gdk_window_get_impl_window (window);
+ xcoords = XGetMotionEvents (GDK_DRAWABLE_XDISPLAY (window),
+ GDK_DRAWABLE_XID (impl_window),
+ start, stop, &tmp_n_events);
+ if (!xcoords)
+ return FALSE;
+
+ priv = (GdkWindowObject *) window;
+ coords = _gdk_device_allocate_history (device, tmp_n_events);
+
+ for (i = 0, j = 0; i < tmp_n_events; i++)
+ {
+ if (impl_coord_in_window (window, xcoords[i].x, xcoords[i].y))
+ {
+ coords[j]->time = xcoords[i].time;
+ coords[j]->axes[0] = xcoords[i].x - priv->abs_x;
+ coords[j]->axes[1] = xcoords[i].y - priv->abs_y;
+ j++;
+ }
+ }
+
+ XFree (xcoords);
+
+ /* free the events we allocated too much */
+ for (i = j; i < tmp_n_events; i++)
+ {
+ g_free (coords[i]);
+ coords[i] = NULL;
+ }
+
+ tmp_n_events = j;
+
+ if (tmp_n_events == 0)
+ {
+ gdk_device_free_history (coords, tmp_n_events);
+ return FALSE;
+ }
+
+ if (n_events)
+ *n_events = tmp_n_events;
+
+ if (events)
+ *events = coords;
+ else if (coords)
+ gdk_device_free_history (coords, tmp_n_events);
+
+ return TRUE;
+}
+
+static void
+gdk_device_core_get_state (GdkDevice *device,
+ GdkWindow *window,
+ gdouble *axes,
+ GdkModifierType *mask)
+{
+ gint x_int, y_int;
+
+ gdk_window_get_pointer (window, &x_int, &y_int, mask);
+
+ if (axes)
+ {
+ axes[0] = x_int;
+ axes[1] = y_int;
+ }
+}
+
+static void
+gdk_device_core_set_window_cursor (GdkDevice *device,
+ GdkWindow *window,
+ GdkCursor *cursor)
+{
+ GdkCursorPrivate *cursor_private;
+ Cursor xcursor;
+
+ cursor_private = (GdkCursorPrivate*) cursor;
+
+ if (!cursor)
+ xcursor = None;
+ else
+ xcursor = cursor_private->xcursor;
+
+ XDefineCursor (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ xcursor);
+}
+
+static void
+gdk_device_core_warp (GdkDevice *device,
+ GdkScreen *screen,
+ gint x,
+ gint y)
+{
+ Display *xdisplay;
+ Window dest;
+
+ xdisplay = GDK_DISPLAY_XDISPLAY (gdk_device_get_display (device));
+ dest = GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen));
+
+ XWarpPointer (xdisplay, None, dest, 0, 0, 0, 0, x, y);
+}
+
+static gboolean
+gdk_device_core_query_state (GdkDevice *device,
+ GdkWindow *window,
+ GdkWindow **root_window,
+ GdkWindow **child_window,
+ gint *root_x,
+ gint *root_y,
+ gint *win_x,
+ gint *win_y,
+ GdkModifierType *mask)
+{
+ GdkDisplay *display;
+ Window xroot_window, xchild_window;
+ int xroot_x, xroot_y, xwin_x, xwin_y;
+ unsigned int xmask;
+
+ display = gdk_window_get_display (window);
+
+ if (!XQueryPointer (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ &xroot_window,
+ &xchild_window,
+ &xroot_x,
+ &xroot_y,
+ &xwin_x,
+ &xwin_y,
+ &xmask))
+ {
+ return FALSE;
+ }
+
+ if (root_window)
+ *root_window = gdk_window_lookup_for_display (display, xroot_window);
+
+ if (child_window)
+ *child_window = gdk_window_lookup_for_display (display, xchild_window);
+
+ if (root_x)
+ *root_x = xroot_x;
+
+ if (root_y)
+ *root_y = xroot_y;
+
+ if (win_x)
+ *win_x = xwin_x;
+
+ if (win_y)
+ *win_y = xwin_y;
+
+ if (mask)
+ *mask = xmask;
+
+ return TRUE;
+}
+
+static GdkGrabStatus
+gdk_device_core_grab (GdkDevice *device,
+ GdkWindow *window,
+ gboolean owner_events,
+ GdkEventMask event_mask,
+ GdkWindow *confine_to,
+ GdkCursor *cursor,
+ guint32 time_)
+{
+ GdkDisplay *display;
+ Window xwindow, xconfine_to;
+ int status;
+
+ display = gdk_device_get_display (device);
+
+ xwindow = GDK_WINDOW_XID (window);
+
+ if (confine_to)
+ confine_to = _gdk_window_get_impl_window (confine_to);
+
+ if (!confine_to || GDK_WINDOW_DESTROYED (confine_to))
+ xconfine_to = None;
+ else
+ xconfine_to = GDK_WINDOW_XID (confine_to);
+
+ if (device->source == GDK_SOURCE_KEYBOARD)
+ {
+ /* Device is a keyboard */
+ status = XGrabKeyboard (GDK_DISPLAY_XDISPLAY (display),
+ xwindow,
+ owner_events,
+ GrabModeAsync, GrabModeAsync,
+ time_);
+ }
+ else
+ {
+ Cursor xcursor;
+ guint xevent_mask;
+ gint i;
+
+ /* Device is a pointer */
+ if (!cursor)
+ xcursor = None;
+ else
+ {
+ _gdk_x11_cursor_update_theme (cursor);
+ xcursor = ((GdkCursorPrivate *) cursor)->xcursor;
+ }
+
+ xevent_mask = 0;
+
+ for (i = 0; i < _gdk_nenvent_masks; i++)
+ {
+ if (event_mask & (1 << (i + 1)))
+ xevent_mask |= _gdk_event_mask_table[i];
+ }
+
+ /* We don't want to set a native motion hint mask, as we're emulating motion
+ * hints. If we set a native one we just wouldn't get any events.
+ */
+ xevent_mask &= ~PointerMotionHintMask;
+
+ status = XGrabPointer (GDK_DISPLAY_XDISPLAY (display),
+ xwindow,
+ owner_events,
+ xevent_mask,
+ GrabModeAsync, GrabModeAsync,
+ xconfine_to,
+ xcursor,
+ time_);
+ }
+
+ return _gdk_x11_convert_grab_status (status);
+}
+
+static void
+gdk_device_core_ungrab (GdkDevice *device,
+ guint32 time_)
+{
+ GdkDisplay *display;
+
+ display = gdk_device_get_display (device);
+
+ if (device->source == GDK_SOURCE_KEYBOARD)
+ XUngrabKeyboard (GDK_DISPLAY_XDISPLAY (display), time_);
+ else
+ XUngrabPointer (GDK_DISPLAY_XDISPLAY (display), time_);
+}
+
+static GdkWindow *
+gdk_device_core_window_at_position (GdkDevice *device,
+ gint *win_x,
+ gint *win_y,
+ GdkModifierType *mask,
+ gboolean get_toplevel)
+{
+ GdkDisplay *display;
+ GdkScreen *screen;
+ Display *xdisplay;
+ GdkWindow *window;
+ Window xwindow, root, child, last;
+ int xroot_x, xroot_y, xwin_x, xwin_y;
+ unsigned int xmask;
+
+ last = None;
+ display = gdk_device_get_display (device);
+ screen = gdk_display_get_default_screen (display);
+
+ /* This function really only works if the mouse pointer is held still
+ * during its operation. If it moves from one leaf window to another
+ * than we'll end up with inaccurate values for win_x, win_y
+ * and the result.
+ */
+ gdk_x11_display_grab (display);
+
+ xdisplay = GDK_SCREEN_XDISPLAY (screen);
+ xwindow = GDK_SCREEN_XROOTWIN (screen);
+
+ XQueryPointer (xdisplay, xwindow,
+ &root, &child,
+ &xroot_x, &xroot_y,
+ &xwin_x, &xwin_y,
+ &xmask);
+
+ if (root == xwindow)
+ xwindow = child;
+ else
+ xwindow = root;
+
+ while (xwindow)
+ {
+ last = xwindow;
+ XQueryPointer (xdisplay, xwindow,
+ &root, &xwindow,
+ &xroot_x, &xroot_y,
+ &xwin_x, &xwin_y,
+ &xmask);
+
+ if (get_toplevel && last != root &&
+ (window = gdk_window_lookup_for_display (display, last)) != NULL &&
+ GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
+ {
+ xwindow = last;
+ break;
+ }
+ }
+
+ gdk_x11_display_ungrab (display);
+
+ window = gdk_window_lookup_for_display (display, last);
+
+ if (win_x)
+ *win_x = (window) ? xwin_x : -1;
+
+ if (win_y)
+ *win_y = (window) ? xwin_y : -1;
+
+ if (mask)
+ *mask = xmask;
+
+ return window;
+}
+
+static void
+gdk_device_core_select_window_events (GdkDevice *device,
+ GdkWindow *window,
+ GdkEventMask event_mask)
+{
+ GdkEventMask filter_mask, window_mask;
+ guint xmask = 0;
+ gint i;
+
+ window_mask = gdk_window_get_events (window);
+ filter_mask = (GDK_POINTER_MOTION_MASK &
+ GDK_POINTER_MOTION_HINT_MASK &
+ GDK_BUTTON_MOTION_MASK &
+ GDK_BUTTON1_MOTION_MASK &
+ GDK_BUTTON2_MOTION_MASK &
+ GDK_BUTTON3_MOTION_MASK &
+ GDK_BUTTON_PRESS_MASK &
+ GDK_BUTTON_RELEASE_MASK &
+ GDK_KEY_PRESS_MASK &
+ GDK_KEY_RELEASE_MASK &
+ GDK_ENTER_NOTIFY_MASK &
+ GDK_LEAVE_NOTIFY_MASK &
+ GDK_FOCUS_CHANGE_MASK &
+ GDK_PROXIMITY_IN_MASK &
+ GDK_PROXIMITY_OUT_MASK &
+ GDK_SCROLL_MASK);
+
+ /* Filter out non-device events */
+ event_mask &= filter_mask;
+
+ /* Unset device events on window mask */
+ window_mask &= ~(filter_mask);
+
+ /* Combine masks */
+ event_mask |= window_mask;
+
+ for (i = 0; i < _gdk_nenvent_masks; i++)
+ {
+ if (event_mask & (1 << (i + 1)))
+ xmask |= _gdk_event_mask_table[i];
+ }
+
+ if (GDK_WINDOW_XID (window) != GDK_WINDOW_XROOTWIN (window))
+ xmask |= StructureNotifyMask | PropertyChangeMask;
+
+ XSelectInput (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XWINDOW (window),
+ xmask);
+}
diff --git a/gdk/broadway/gdkdevice-broadway.h b/gdk/broadway/gdkdevice-broadway.h
new file mode 100644
index 0000000000..9ff65c96ff
--- /dev/null
+++ b/gdk/broadway/gdkdevice-broadway.h
@@ -0,0 +1,52 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+#ifndef __GDK_DEVICE_BROADWAY_H__
+#define __GDK_DEVICE_BROADWAY_H__
+
+#include <gdk/gdkdeviceprivate.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_DEVICE_CORE (gdk_device_core_get_type ())
+#define GDK_DEVICE_CORE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_CORE, GdkDeviceCore))
+#define GDK_DEVICE_CORE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE_CORE, GdkDeviceCoreClass))
+#define GDK_IS_DEVICE_CORE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_CORE))
+#define GDK_IS_DEVICE_CORE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE_CORE))
+#define GDK_DEVICE_CORE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE_CORE, GdkDeviceCoreClass))
+
+typedef struct _GdkDeviceCore GdkDeviceCore;
+typedef struct _GdkDeviceCoreClass GdkDeviceCoreClass;
+
+struct _GdkDeviceCore
+{
+ GdkDevice parent_instance;
+};
+
+struct _GdkDeviceCoreClass
+{
+ GdkDeviceClass parent_class;
+};
+
+G_GNUC_INTERNAL
+GType gdk_device_core_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GDK_DEVICE_BROADWAY_H__ */
diff --git a/gdk/broadway/gdkdevicemanager-broadway.c b/gdk/broadway/gdkdevicemanager-broadway.c
new file mode 100644
index 0000000000..7b69bd05f4
--- /dev/null
+++ b/gdk/broadway/gdkdevicemanager-broadway.c
@@ -0,0 +1,920 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 "gdkdevicemanager-broadway.h"
+
+#include "gdktypes.h"
+#include "gdkdevicemanager.h"
+#include "gdkeventtranslator.h"
+#include "gdkdevice-broadway.h"
+#include "gdkkeysyms.h"
+#include "gdkprivate-broadway.h"
+#include "gdkx.h"
+
+#ifdef HAVE_XKB
+#include <X11/XKBlib.h>
+#endif
+
+
+#define HAS_FOCUS(toplevel) \
+ ((toplevel)->has_focus || (toplevel)->has_pointer_focus)
+
+static void gdk_device_manager_core_finalize (GObject *object);
+static void gdk_device_manager_core_constructed (GObject *object);
+
+static GList * gdk_device_manager_core_list_devices (GdkDeviceManager *device_manager,
+ GdkDeviceType type);
+static GdkDevice * gdk_device_manager_core_get_client_pointer (GdkDeviceManager *device_manager);
+
+static void gdk_device_manager_event_translator_init (GdkEventTranslatorIface *iface);
+
+static gboolean gdk_device_manager_core_translate_event (GdkEventTranslator *translator,
+ GdkDisplay *display,
+ GdkEvent *event,
+ XEvent *xevent);
+
+
+G_DEFINE_TYPE_WITH_CODE (GdkDeviceManagerCore, gdk_device_manager_core, GDK_TYPE_DEVICE_MANAGER,
+ G_IMPLEMENT_INTERFACE (GDK_TYPE_EVENT_TRANSLATOR,
+ gdk_device_manager_event_translator_init))
+
+static void
+gdk_device_manager_core_class_init (GdkDeviceManagerCoreClass *klass)
+{
+ GdkDeviceManagerClass *device_manager_class = GDK_DEVICE_MANAGER_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gdk_device_manager_core_finalize;
+ object_class->constructed = gdk_device_manager_core_constructed;
+ device_manager_class->list_devices = gdk_device_manager_core_list_devices;
+ device_manager_class->get_client_pointer = gdk_device_manager_core_get_client_pointer;
+}
+
+static void
+gdk_device_manager_event_translator_init (GdkEventTranslatorIface *iface)
+{
+ iface->translate_event = gdk_device_manager_core_translate_event;
+}
+
+static GdkDevice *
+create_core_pointer (GdkDeviceManager *device_manager,
+ GdkDisplay *display)
+{
+ return g_object_new (GDK_TYPE_DEVICE_CORE,
+ "name", "Core Pointer",
+ "type", GDK_DEVICE_TYPE_MASTER,
+ "input-source", GDK_SOURCE_MOUSE,
+ "input-mode", GDK_MODE_SCREEN,
+ "has-cursor", TRUE,
+ "display", display,
+ "device-manager", device_manager,
+ NULL);
+}
+
+static GdkDevice *
+create_core_keyboard (GdkDeviceManager *device_manager,
+ GdkDisplay *display)
+{
+ return g_object_new (GDK_TYPE_DEVICE_CORE,
+ "name", "Core Keyboard",
+ "type", GDK_DEVICE_TYPE_MASTER,
+ "input-source", GDK_SOURCE_KEYBOARD,
+ "input-mode", GDK_MODE_SCREEN,
+ "has-cursor", FALSE,
+ "display", display,
+ "device-manager", device_manager,
+ NULL);
+}
+
+static void
+gdk_device_manager_core_init (GdkDeviceManagerCore *device_manager)
+{
+}
+
+static void
+gdk_device_manager_core_finalize (GObject *object)
+{
+ GdkDeviceManagerCore *device_manager_core;
+
+ device_manager_core = GDK_DEVICE_MANAGER_CORE (object);
+
+ g_object_unref (device_manager_core->core_pointer);
+ g_object_unref (device_manager_core->core_keyboard);
+
+ G_OBJECT_CLASS (gdk_device_manager_core_parent_class)->finalize (object);
+}
+
+static void
+gdk_device_manager_core_constructed (GObject *object)
+{
+ GdkDeviceManagerCore *device_manager;
+ GdkDisplay *display;
+
+ device_manager = GDK_DEVICE_MANAGER_CORE (object);
+ display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (object));
+ device_manager->core_pointer = create_core_pointer (GDK_DEVICE_MANAGER (device_manager), display);
+ device_manager->core_keyboard = create_core_keyboard (GDK_DEVICE_MANAGER (device_manager), display);
+
+ _gdk_device_set_associated_device (device_manager->core_pointer, device_manager->core_keyboard);
+ _gdk_device_set_associated_device (device_manager->core_keyboard, device_manager->core_pointer);
+}
+
+static void
+translate_key_event (GdkDisplay *display,
+ GdkDeviceManagerCore *device_manager,
+ GdkEvent *event,
+ XEvent *xevent)
+{
+ GdkKeymap *keymap = gdk_keymap_get_for_display (display);
+ GdkModifierType consumed, state;
+ gunichar c = 0;
+ gchar buf[7];
+
+ event->key.type = xevent->xany.type == KeyPress ? GDK_KEY_PRESS : GDK_KEY_RELEASE;
+ event->key.time = xevent->xkey.time;
+ gdk_event_set_device (event, device_manager->core_keyboard);
+
+ event->key.state = (GdkModifierType) xevent->xkey.state;
+ event->key.group = _gdk_x11_get_group_for_state (display, xevent->xkey.state);
+ event->key.hardware_keycode = xevent->xkey.keycode;
+
+ event->key.keyval = GDK_KEY_VoidSymbol;
+
+ gdk_keymap_translate_keyboard_state (keymap,
+ event->key.hardware_keycode,
+ event->key.state,
+ event->key.group,
+ &event->key.keyval,
+ NULL, NULL, &consumed);
+
+ state = event->key.state & ~consumed;
+ _gdk_keymap_add_virtual_modifiers_compat (keymap, &state);
+ event->key.state |= state;
+
+ event->key.is_modifier = _gdk_keymap_key_is_modifier (keymap, event->key.hardware_keycode);
+
+ /* Fill in event->string crudely, since various programs
+ * depend on it.
+ */
+ event->key.string = NULL;
+
+ if (event->key.keyval != GDK_KEY_VoidSymbol)
+ c = gdk_keyval_to_unicode (event->key.keyval);
+
+ if (c)
+ {
+ gsize bytes_written;
+ gint len;
+
+ /* Apply the control key - Taken from Xlib
+ */
+ if (event->key.state & GDK_CONTROL_MASK)
+ {
+ if ((c >= '@' && c < '\177') || c == ' ') c &= 0x1F;
+ else if (c == '2')
+ {
+ event->key.string = g_memdup ("\0\0", 2);
+ event->key.length = 1;
+ buf[0] = '\0';
+ goto out;
+ }
+ else if (c >= '3' && c <= '7') c -= ('3' - '\033');
+ else if (c == '8') c = '\177';
+ else if (c == '/') c = '_' & 0x1F;
+ }
+
+ len = g_unichar_to_utf8 (c, buf);
+ buf[len] = '\0';
+
+ event->key.string = g_locale_from_utf8 (buf, len,
+ NULL, &bytes_written,
+ NULL);
+ if (event->key.string)
+ event->key.length = bytes_written;
+ }
+ else if (event->key.keyval == GDK_KEY_Escape)
+ {
+ event->key.length = 1;
+ event->key.string = g_strdup ("\033");
+ }
+ else if (event->key.keyval == GDK_KEY_Return ||
+ event->key.keyval == GDK_KEY_KP_Enter)
+ {
+ event->key.length = 1;
+ event->key.string = g_strdup ("\r");
+ }
+
+ if (!event->key.string)
+ {
+ event->key.length = 0;
+ event->key.string = g_strdup ("");
+ }
+
+ out:
+#ifdef G_ENABLE_DEBUG
+ if (_gdk_debug_flags & GDK_DEBUG_EVENTS)
+ {
+ g_message ("%s:\t\twindow: %ld key: %12s %d",
+ event->type == GDK_KEY_PRESS ? "key press " : "key release",
+ xevent->xkey.window,
+ event->key.keyval ? gdk_keyval_name (event->key.keyval) : "(none)",
+ event->key.keyval);
+
+ if (event->key.length > 0)
+ g_message ("\t\tlength: %4d string: \"%s\"",
+ event->key.length, buf);
+ }
+#endif /* G_ENABLE_DEBUG */
+ return;
+}
+
+#ifdef G_ENABLE_DEBUG
+static const char notify_modes[][19] = {
+ "NotifyNormal",
+ "NotifyGrab",
+ "NotifyUngrab",
+ "NotifyWhileGrabbed"
+};
+
+static const char notify_details[][23] = {
+ "NotifyAncestor",
+ "NotifyVirtual",
+ "NotifyInferior",
+ "NotifyNonlinear",
+ "NotifyNonlinearVirtual",
+ "NotifyPointer",
+ "NotifyPointerRoot",
+ "NotifyDetailNone"
+};
+#endif
+
+static void
+set_user_time (GdkWindow *window,
+ GdkEvent *event)
+{
+ g_return_if_fail (event != NULL);
+
+ window = gdk_window_get_toplevel (event->client.window);
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ /* If an event doesn't have a valid timestamp, we shouldn't use it
+ * to update the latest user interaction time.
+ */
+ if (gdk_event_get_time (event) != GDK_CURRENT_TIME)
+ gdk_x11_window_set_user_time (gdk_window_get_toplevel (window),
+ gdk_event_get_time (event));
+}
+
+static void
+generate_focus_event (GdkDeviceManagerCore *device_manager,
+ GdkWindow *window,
+ gboolean in)
+{
+ GdkEvent *event;
+
+ event = gdk_event_new (GDK_FOCUS_CHANGE);
+ event->focus_change.window = g_object_ref (window);
+ event->focus_change.send_event = FALSE;
+ event->focus_change.in = in;
+ gdk_event_set_device (event, device_manager->core_keyboard);
+
+ gdk_event_put (event);
+ gdk_event_free (event);
+}
+
+static gboolean
+set_screen_from_root (GdkDisplay *display,
+ GdkEvent *event,
+ Window xrootwin)
+{
+ GdkScreen *screen;
+
+ screen = _gdk_x11_display_screen_for_xrootwin (display, xrootwin);
+
+ if (screen)
+ {
+ gdk_event_set_screen (event, screen);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GdkCrossingMode
+translate_crossing_mode (int mode)
+{
+ switch (mode)
+ {
+ case NotifyNormal:
+ return GDK_CROSSING_NORMAL;
+ case NotifyGrab:
+ return GDK_CROSSING_GRAB;
+ case NotifyUngrab:
+ return GDK_CROSSING_UNGRAB;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static GdkNotifyType
+translate_notify_type (int detail)
+{
+ switch (detail)
+ {
+ case NotifyInferior:
+ return GDK_NOTIFY_INFERIOR;
+ case NotifyAncestor:
+ return GDK_NOTIFY_ANCESTOR;
+ case NotifyVirtual:
+ return GDK_NOTIFY_VIRTUAL;
+ case NotifyNonlinear:
+ return GDK_NOTIFY_NONLINEAR;
+ case NotifyNonlinearVirtual:
+ return GDK_NOTIFY_NONLINEAR_VIRTUAL;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static gboolean
+is_parent_of (GdkWindow *parent,
+ GdkWindow *child)
+{
+ GdkWindow *w;
+
+ w = child;
+ while (w != NULL)
+ {
+ if (w == parent)
+ return TRUE;
+
+ w = gdk_window_get_parent (w);
+ }
+
+ return FALSE;
+}
+
+static GdkWindow *
+get_event_window (GdkEventTranslator *translator,
+ XEvent *xevent)
+{
+ GdkDeviceManager *device_manager;
+ GdkDisplay *display;
+ GdkWindow *window;
+
+ device_manager = GDK_DEVICE_MANAGER (translator);
+ display = gdk_device_manager_get_display (device_manager);
+ window = gdk_window_lookup_for_display (display, xevent->xany.window);
+
+ /* Apply keyboard grabs to non-native windows */
+ if (xevent->type == KeyPress || xevent->type == KeyRelease)
+ {
+ GdkDeviceGrabInfo *info;
+ gulong serial;
+
+ serial = _gdk_windowing_window_get_next_serial (display);
+ info = _gdk_display_has_device_grab (display,
+ GDK_DEVICE_MANAGER_CORE (device_manager)->core_keyboard,
+ serial);
+ if (info &&
+ (!is_parent_of (info->window, window) ||
+ !info->owner_events))
+ {
+ /* Report key event against grab window */
+ window = info->window;
+ }
+ }
+
+ return window;
+}
+
+static gboolean
+gdk_device_manager_core_translate_event (GdkEventTranslator *translator,
+ GdkDisplay *display,
+ GdkEvent *event,
+ XEvent *xevent)
+{
+ GdkDeviceManagerCore *device_manager;
+ GdkWindow *window;
+ GdkWindowObject *window_private;
+ GdkWindowImplX11 *window_impl = NULL;
+ gboolean return_val;
+ GdkToplevelX11 *toplevel = NULL;
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+ device_manager = GDK_DEVICE_MANAGER_CORE (translator);
+ return_val = FALSE;
+
+ window = get_event_window (translator, xevent);
+ window_private = (GdkWindowObject *) window;
+
+ if (window)
+ {
+ if (GDK_WINDOW_DESTROYED (window) || !GDK_IS_WINDOW (window))
+ return FALSE;
+
+ toplevel = _gdk_x11_window_get_toplevel (window);
+ window_impl = GDK_WINDOW_IMPL_X11 (window_private->impl);
+ 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 (window &&
+ (xevent->type == MotionNotify ||
+ xevent->type == ButtonRelease))
+ {
+ if (_gdk_moveresize_handle_event (xevent))
+ {
+ 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 KeyPress:
+ if (window_private == NULL)
+ {
+ return_val = FALSE;
+ break;
+ }
+ translate_key_event (display, device_manager, event, xevent);
+ set_user_time (window, event);
+ break;
+
+ case KeyRelease:
+ if (window_private == NULL)
+ {
+ return_val = FALSE;
+ break;
+ }
+
+ /* Emulate detectable auto-repeat by checking to see
+ * if the next event is a key press with the same
+ * keycode and timestamp, and if so, ignoring the event.
+ */
+
+ if (!display_x11->have_xkb_autorepeat && XPending (xevent->xkey.display))
+ {
+ XEvent next_event;
+
+ XPeekEvent (xevent->xkey.display, &next_event);
+
+ if (next_event.type == KeyPress &&
+ next_event.xkey.keycode == xevent->xkey.keycode &&
+ next_event.xkey.time == xevent->xkey.time)
+ {
+ return_val = FALSE;
+ break;
+ }
+ }
+
+ translate_key_event (display, device_manager, event, xevent);
+ break;
+
+ case ButtonPress:
+ GDK_NOTE (EVENTS,
+ g_message ("button press:\t\twindow: %ld x,y: %d %d button: %d",
+ xevent->xbutton.window,
+ xevent->xbutton.x, xevent->xbutton.y,
+ xevent->xbutton.button));
+
+ if (window_private == NULL)
+ {
+ return_val = FALSE;
+ break;
+ }
+
+ /* If we get a ButtonPress event where the button is 4 or 5,
+ it's a Scroll event */
+ switch (xevent->xbutton.button)
+ {
+ case 4: /* up */
+ case 5: /* down */
+ case 6: /* left */
+ case 7: /* right */
+ event->scroll.type = GDK_SCROLL;
+
+ if (xevent->xbutton.button == 4)
+ event->scroll.direction = GDK_SCROLL_UP;
+ else if (xevent->xbutton.button == 5)
+ event->scroll.direction = GDK_SCROLL_DOWN;
+ else if (xevent->xbutton.button == 6)
+ event->scroll.direction = GDK_SCROLL_LEFT;
+ else
+ event->scroll.direction = GDK_SCROLL_RIGHT;
+
+ event->scroll.window = window;
+ event->scroll.time = xevent->xbutton.time;
+ event->scroll.x = (gdouble) xevent->xbutton.x;
+ event->scroll.y = (gdouble) xevent->xbutton.y;
+ event->scroll.x_root = (gdouble) xevent->xbutton.x_root;
+ event->scroll.y_root = (gdouble) xevent->xbutton.y_root;
+ event->scroll.state = (GdkModifierType) xevent->xbutton.state;
+ event->scroll.device = device_manager->core_pointer;
+
+ if (!set_screen_from_root (display, event, xevent->xbutton.root))
+ {
+ return_val = FALSE;
+ break;
+ }
+
+ break;
+
+ default:
+ event->button.type = GDK_BUTTON_PRESS;
+ event->button.window = window;
+ event->button.time = xevent->xbutton.time;
+ event->button.x = (gdouble) xevent->xbutton.x;
+ event->button.y = (gdouble) xevent->xbutton.y;
+ event->button.x_root = (gdouble) xevent->xbutton.x_root;
+ event->button.y_root = (gdouble) xevent->xbutton.y_root;
+ event->button.axes = NULL;
+ event->button.state = (GdkModifierType) xevent->xbutton.state;
+ event->button.button = xevent->xbutton.button;
+ event->button.device = device_manager->core_pointer;
+
+ if (!set_screen_from_root (display, event, xevent->xbutton.root))
+ return_val = FALSE;
+
+ break;
+ }
+
+ set_user_time (window, event);
+
+ break;
+
+ case ButtonRelease:
+ GDK_NOTE (EVENTS,
+ g_message ("button release:\twindow: %ld x,y: %d %d button: %d",
+ xevent->xbutton.window,
+ xevent->xbutton.x, xevent->xbutton.y,
+ xevent->xbutton.button));
+
+ if (window_private == NULL)
+ {
+ return_val = FALSE;
+ break;
+ }
+
+ /* We treat button presses as scroll wheel events, so ignore the release */
+ if (xevent->xbutton.button == 4 || xevent->xbutton.button == 5 ||
+ xevent->xbutton.button == 6 || xevent->xbutton.button == 7)
+ {
+ return_val = FALSE;
+ break;
+ }
+
+ event->button.type = GDK_BUTTON_RELEASE;
+ event->button.window = window;
+ event->button.time = xevent->xbutton.time;
+ event->button.x = (gdouble) xevent->xbutton.x;
+ event->button.y = (gdouble) xevent->xbutton.y;
+ event->button.x_root = (gdouble) xevent->xbutton.x_root;
+ event->button.y_root = (gdouble) xevent->xbutton.y_root;
+ event->button.axes = NULL;
+ event->button.state = (GdkModifierType) xevent->xbutton.state;
+ event->button.button = xevent->xbutton.button;
+ event->button.device = device_manager->core_pointer;
+
+ if (!set_screen_from_root (display, event, xevent->xbutton.root))
+ return_val = FALSE;
+
+ break;
+
+ case MotionNotify:
+ GDK_NOTE (EVENTS,
+ g_message ("motion notify:\t\twindow: %ld x,y: %d %d hint: %s",
+ xevent->xmotion.window,
+ xevent->xmotion.x, xevent->xmotion.y,
+ (xevent->xmotion.is_hint) ? "true" : "false"));
+
+ if (window_private == NULL)
+ {
+ return_val = FALSE;
+ break;
+ }
+
+ event->motion.type = GDK_MOTION_NOTIFY;
+ event->motion.window = window;
+ event->motion.time = xevent->xmotion.time;
+ event->motion.x = (gdouble) xevent->xmotion.x;
+ event->motion.y = (gdouble) xevent->xmotion.y;
+ event->motion.x_root = (gdouble) xevent->xmotion.x_root;
+ event->motion.y_root = (gdouble) xevent->xmotion.y_root;
+ event->motion.axes = NULL;
+ event->motion.state = (GdkModifierType) xevent->xmotion.state;
+ event->motion.is_hint = xevent->xmotion.is_hint;
+ event->motion.device = device_manager->core_pointer;
+
+ if (!set_screen_from_root (display, event, xevent->xbutton.root))
+ {
+ return_val = FALSE;
+ break;
+ }
+
+ break;
+
+ case EnterNotify:
+ GDK_NOTE (EVENTS,
+ g_message ("enter notify:\t\twindow: %ld detail: %d subwin: %ld",
+ xevent->xcrossing.window,
+ xevent->xcrossing.detail,
+ xevent->xcrossing.subwindow));
+
+ if (window_private == NULL)
+ {
+ return_val = FALSE;
+ break;
+ }
+
+ if (!set_screen_from_root (display, event, xevent->xbutton.root))
+ {
+ return_val = FALSE;
+ break;
+ }
+
+ event->crossing.type = GDK_ENTER_NOTIFY;
+ event->crossing.window = window;
+ gdk_event_set_device (event, device_manager->core_pointer);
+
+ /* If the subwindow field of the XEvent is non-NULL, then
+ * lookup the corresponding GdkWindow.
+ */
+ if (xevent->xcrossing.subwindow != None)
+ event->crossing.subwindow = gdk_window_lookup_for_display (display, xevent->xcrossing.subwindow);
+ else
+ event->crossing.subwindow = NULL;
+
+ event->crossing.time = xevent->xcrossing.time;
+ event->crossing.x = (gdouble) xevent->xcrossing.x;
+ event->crossing.y = (gdouble) xevent->xcrossing.y;
+ event->crossing.x_root = (gdouble) xevent->xcrossing.x_root;
+ event->crossing.y_root = (gdouble) xevent->xcrossing.y_root;
+
+ event->crossing.mode = translate_crossing_mode (xevent->xcrossing.mode);
+ event->crossing.detail = translate_notify_type (xevent->xcrossing.detail);
+
+ event->crossing.focus = xevent->xcrossing.focus;
+ event->crossing.state = xevent->xcrossing.state;
+
+ break;
+
+ case LeaveNotify:
+ GDK_NOTE (EVENTS,
+ g_message ("leave notify:\t\twindow: %ld detail: %d subwin: %ld",
+ xevent->xcrossing.window,
+ xevent->xcrossing.detail, xevent->xcrossing.subwindow));
+
+ if (window_private == NULL)
+ {
+ return_val = FALSE;
+ break;
+ }
+
+ if (!set_screen_from_root (display, event, xevent->xbutton.root))
+ {
+ return_val = FALSE;
+ break;
+ }
+
+ event->crossing.type = GDK_LEAVE_NOTIFY;
+ event->crossing.window = window;
+ gdk_event_set_device (event, device_manager->core_pointer);
+
+ /* If the subwindow field of the XEvent is non-NULL, then
+ * lookup the corresponding GdkWindow.
+ */
+ if (xevent->xcrossing.subwindow != None)
+ event->crossing.subwindow = gdk_window_lookup_for_display (display, xevent->xcrossing.subwindow);
+ else
+ event->crossing.subwindow = NULL;
+
+ event->crossing.time = xevent->xcrossing.time;
+ event->crossing.x = (gdouble) xevent->xcrossing.x;
+ event->crossing.y = (gdouble) xevent->xcrossing.y;
+ event->crossing.x_root = (gdouble) xevent->xcrossing.x_root;
+ event->crossing.y_root = (gdouble) xevent->xcrossing.y_root;
+
+ event->crossing.mode = translate_crossing_mode (xevent->xcrossing.mode);
+ event->crossing.detail = translate_notify_type (xevent->xcrossing.detail);
+
+ event->crossing.focus = xevent->xcrossing.focus;
+ event->crossing.state = xevent->xcrossing.state;
+
+ break;
+
+ /* We only care about focus events that indicate that _this_
+ * window (not a ancestor or child) got or lost the focus
+ */
+ case FocusIn:
+ GDK_NOTE (EVENTS,
+ g_message ("focus in:\t\twindow: %ld, detail: %s, mode: %s",
+ xevent->xfocus.window,
+ notify_details[xevent->xfocus.detail],
+ notify_modes[xevent->xfocus.mode]));
+
+ if (toplevel)
+ {
+ gboolean had_focus = HAS_FOCUS (toplevel);
+
+ switch (xevent->xfocus.detail)
+ {
+ case NotifyAncestor:
+ case NotifyVirtual:
+ /* When the focus moves from an ancestor of the window to
+ * the window or a descendent of the window, *and* the
+ * pointer is inside the window, then we were previously
+ * receiving keystroke events in the has_pointer_focus
+ * case and are now receiving them in the
+ * has_focus_window case.
+ */
+ if (toplevel->has_pointer &&
+ xevent->xfocus.mode != NotifyGrab &&
+ xevent->xfocus.mode != NotifyUngrab)
+ toplevel->has_pointer_focus = FALSE;
+
+ /* fall through */
+ case NotifyNonlinear:
+ case NotifyNonlinearVirtual:
+ if (xevent->xfocus.mode != NotifyGrab &&
+ xevent->xfocus.mode != NotifyUngrab)
+ toplevel->has_focus_window = TRUE;
+ /* We pretend that the focus moves to the grab
+ * window, so we pay attention to NotifyGrab
+ * NotifyUngrab, and ignore NotifyWhileGrabbed
+ */
+ if (xevent->xfocus.mode != NotifyWhileGrabbed)
+ toplevel->has_focus = TRUE;
+ break;
+ case NotifyPointer:
+ /* The X server sends NotifyPointer/NotifyGrab,
+ * but the pointer focus is ignored while a
+ * grab is in effect
+ */
+ if (xevent->xfocus.mode != NotifyGrab &&
+ xevent->xfocus.mode != NotifyUngrab)
+ toplevel->has_pointer_focus = TRUE;
+ break;
+ case NotifyInferior:
+ case NotifyPointerRoot:
+ case NotifyDetailNone:
+ break;
+ }
+
+ if (HAS_FOCUS (toplevel) != had_focus)
+ generate_focus_event (device_manager, window, TRUE);
+ }
+ break;
+ case FocusOut:
+ GDK_NOTE (EVENTS,
+ g_message ("focus out:\t\twindow: %ld, detail: %s, mode: %s",
+ xevent->xfocus.window,
+ notify_details[xevent->xfocus.detail],
+ notify_modes[xevent->xfocus.mode]));
+
+ if (toplevel)
+ {
+ gboolean had_focus = HAS_FOCUS (toplevel);
+
+ switch (xevent->xfocus.detail)
+ {
+ case NotifyAncestor:
+ case NotifyVirtual:
+ /* When the focus moves from the window or a descendent
+ * of the window to an ancestor of the window, *and* the
+ * pointer is inside the window, then we were previously
+ * receiving keystroke events in the has_focus_window
+ * case and are now receiving them in the
+ * has_pointer_focus case.
+ */
+ if (toplevel->has_pointer &&
+ xevent->xfocus.mode != NotifyGrab &&
+ xevent->xfocus.mode != NotifyUngrab)
+ toplevel->has_pointer_focus = TRUE;
+
+ /* fall through */
+ case NotifyNonlinear:
+ case NotifyNonlinearVirtual:
+ if (xevent->xfocus.mode != NotifyGrab &&
+ xevent->xfocus.mode != NotifyUngrab)
+ toplevel->has_focus_window = FALSE;
+ if (xevent->xfocus.mode != NotifyWhileGrabbed)
+ toplevel->has_focus = FALSE;
+ break;
+ case NotifyPointer:
+ if (xevent->xfocus.mode != NotifyGrab &&
+ xevent->xfocus.mode != NotifyUngrab)
+ toplevel->has_pointer_focus = FALSE;
+ break;
+ case NotifyInferior:
+ case NotifyPointerRoot:
+ case NotifyDetailNone:
+ break;
+ }
+
+ if (HAS_FOCUS (toplevel) != had_focus)
+ generate_focus_event (device_manager, window, FALSE);
+ }
+ break;
+
+ default:
+ return_val = FALSE;
+ }
+
+ done:
+ if (return_val)
+ {
+ if (event->any.window)
+ g_object_ref (event->any.window);
+
+ if (((event->any.type == GDK_ENTER_NOTIFY) ||
+ (event->any.type == GDK_LEAVE_NOTIFY)) &&
+ (event->crossing.subwindow != NULL))
+ g_object_ref (event->crossing.subwindow);
+ }
+ 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 GList *
+gdk_device_manager_core_list_devices (GdkDeviceManager *device_manager,
+ GdkDeviceType type)
+{
+ GdkDeviceManagerCore *device_manager_core;
+ GList *devices = NULL;
+
+ if (type == GDK_DEVICE_TYPE_MASTER)
+ {
+ device_manager_core = (GdkDeviceManagerCore *) device_manager;
+ devices = g_list_prepend (devices, device_manager_core->core_keyboard);
+ devices = g_list_prepend (devices, device_manager_core->core_pointer);
+ }
+
+ return devices;
+}
+
+static GdkDevice *
+gdk_device_manager_core_get_client_pointer (GdkDeviceManager *device_manager)
+{
+ GdkDeviceManagerCore *device_manager_core;
+
+ device_manager_core = (GdkDeviceManagerCore *) device_manager;
+ return device_manager_core->core_pointer;
+}
+
+GdkDeviceManager *
+_gdk_device_manager_new (GdkDisplay *display)
+{
+ return g_object_new (GDK_TYPE_DEVICE_MANAGER_CORE,
+ "display", display,
+ NULL);
+}
diff --git a/gdk/broadway/gdkdevicemanager-broadway.h b/gdk/broadway/gdkdevicemanager-broadway.h
new file mode 100644
index 0000000000..50ea91d34d
--- /dev/null
+++ b/gdk/broadway/gdkdevicemanager-broadway.h
@@ -0,0 +1,54 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+#ifndef __GDK_DEVICE_MANAGER_BROADWAY_H__
+#define __GDK_DEVICE_MANAGER_BROADWAY_H__
+
+#include <gdk/gdkdevicemanager.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_DEVICE_MANAGER_CORE (gdk_device_manager_core_get_type ())
+#define GDK_DEVICE_MANAGER_CORE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_MANAGER_CORE, GdkDeviceManagerCore))
+#define GDK_DEVICE_MANAGER_CORE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE_MANAGER_CORE, GdkDeviceManagerCoreClass))
+#define GDK_IS_DEVICE_MANAGER_CORE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_MANAGER_CORE))
+#define GDK_IS_DEVICE_MANAGER_CORE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE_MANAGER_CORE))
+#define GDK_DEVICE_MANAGER_CORE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE_MANAGER_CORE, GdkDeviceManagerCoreClass))
+
+typedef struct _GdkDeviceManagerCore GdkDeviceManagerCore;
+typedef struct _GdkDeviceManagerCoreClass GdkDeviceManagerCoreClass;
+
+struct _GdkDeviceManagerCore
+{
+ GdkDeviceManager parent_object;
+ GdkDevice *core_pointer;
+ GdkDevice *core_keyboard;
+};
+
+struct _GdkDeviceManagerCoreClass
+{
+ GdkDeviceManagerClass parent_class;
+};
+
+GType gdk_device_manager_core_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif /* __GDK_DEVICE_MANAGER_BROADWAY_H__ */
diff --git a/gdk/broadway/gdkdisplay-broadway.c b/gdk/broadway/gdkdisplay-broadway.c
new file mode 100644
index 0000000000..8a8e1ba176
--- /dev/null
+++ b/gdk/broadway/gdkdisplay-broadway.c
@@ -0,0 +1,2977 @@
+/* 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 "gdkdisplay-broadway.h"
+
+#include "gdkx.h"
+#include "gdkasync.h"
+#include "gdkdisplay.h"
+#include "gdkeventsource.h"
+#include "gdkeventtranslator.h"
+#include "gdkscreen.h"
+#include "gdkscreen-broadway.h"
+#include "gdkinternals.h"
+#include "gdkdeviceprivate.h"
+#include "gdkdevicemanager.h"
+#include "xsettings-client.h"
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.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_ignored ();
+
+ 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_ignored ();
+
+ 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_ignored ();
+ }
+ 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 (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
+ continue;
+
+ display_x11->input_devices = g_list_prepend (display_x11->input_devices,
+ g_object_ref (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 (gdk_device_get_source (device) != 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,
+ g_object_ref (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);
+ 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);
+
+ /* error traps */
+ while (display_x11->error_traps != NULL)
+ {
+ GdkErrorTrap *trap = display_x11->error_traps->data;
+
+ display_x11->error_traps =
+ g_slist_delete_link (display_x11->error_traps,
+ display_x11->error_traps);
+
+ if (trap->end_sequence == 0)
+ g_warning ("Display finalized with an unpopped error trap");
+
+ g_slice_free (GdkErrorTrap, trap);
+ }
+
+ 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')
+ {
+ if (!g_utf8_validate (startup_id, -1, NULL))
+ g_warning ("DESKTOP_STARTUP_ID contains invalid UTF-8");
+ else
+ gdk_x11_display_set_startup_notification_id (display, startup_id);
+
+ /* Clear the environment variable so it won't be inherited by
+ * child processes and confuse things.
+ */
+ g_unsetenv ("DESKTOP_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_ignored ();
+
+}
+
+/**
+ * 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_x11_display_set_startup_notification_id:
+ * @display: a #GdkDisplay
+ * @startup_id: the startup notification ID (must be valid utf8)
+ *
+ * Sets the startup notification ID for a display.
+ *
+ * This is usually taken from the value of the DESKTOP_STARTUP_ID
+ * environment variable, but in some cases (such as the application not
+ * being launched using exec()) it can come from other sources.
+ *
+ * If the ID contains the string "_TIME" then the portion following that
+ * string is taken to be the X11 timestamp of the event that triggered
+ * the application to be launched and the GDK current event time is set
+ * accordingly.
+ *
+ * The startup ID is also what is used to signal that the startup is
+ * complete (for example, when opening a window or when calling
+ * gdk_notify_startup_complete()).
+ *
+ * Since: 3.0
+ **/
+void
+gdk_x11_display_set_startup_notification_id (GdkDisplay *display,
+ const gchar *startup_id)
+{
+ GdkDisplayX11 *display_x11;
+ gchar *time_str;
+
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ g_free (display_x11->startup_notification_id);
+ 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;
+ }
+
+ /* 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));
+}
+
+/**
+ * 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;
+ break; /* only innermost trap gets the 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_warning ("%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:
+ * @display: a #GdkDisplay
+ *
+ * Begins a range of X requests on @display for which X error events
+ * will be ignored. Unignored errors (when no trap is pushed) will abort
+ * the application. Use gdk_x11_display_error_trap_pop() or
+ * gdk_x11_display_error_trap_pop_ignored()to lift a trap pushed
+ * with this function.
+ *
+ * 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);
+}
diff --git a/gdk/broadway/gdkdisplay-broadway.h b/gdk/broadway/gdkdisplay-broadway.h
new file mode 100644
index 0000000000..9ac0c3ae02
--- /dev/null
+++ b/gdk/broadway/gdkdisplay-broadway.h
@@ -0,0 +1,160 @@
+/*
+ * gdkdisplay-x11.h
+ *
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * 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.
+ */
+
+#ifndef __GDK_DISPLAY_X11__
+#define __GDK_DISPLAY_X11__
+
+#include <gdk/gdkdisplay.h>
+#include <gdk/gdkkeys.h>
+#include <gdk/gdkwindow.h>
+#include <gdk/gdkinternals.h>
+#include <gdk/gdkmain.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GdkDisplayX11 GdkDisplayX11;
+typedef struct _GdkDisplayX11Class GdkDisplayX11Class;
+
+#define GDK_TYPE_DISPLAY_X11 (_gdk_display_x11_get_type())
+#define GDK_DISPLAY_X11(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_DISPLAY_X11, GdkDisplayX11))
+#define GDK_DISPLAY_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_DISPLAY_X11, GdkDisplayX11Class))
+#define GDK_IS_DISPLAY_X11(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_DISPLAY_X11))
+#define GDK_IS_DISPLAY_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_DISPLAY_X11))
+#define GDK_DISPLAY_X11_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_DISPLAY_X11, GdkDisplayX11Class))
+
+struct _GdkDisplayX11
+{
+ GdkDisplay parent_instance;
+ Display *xdisplay;
+ GdkScreen *default_screen;
+ GdkScreen **screens;
+
+ GSource *event_source;
+
+ gint grab_count;
+
+ /* Keyboard related information */
+
+ gint xkb_event_type;
+ gboolean use_xkb;
+
+ /* Whether we were able to turn on detectable-autorepeat using
+ * XkbSetDetectableAutorepeat. If FALSE, we'll fall back
+ * to checking the next event with XPending(). */
+ gboolean have_xkb_autorepeat;
+
+ GdkKeymap *keymap;
+ guint keymap_serial;
+
+ gboolean have_xfixes;
+ gint xfixes_event_base;
+
+ gboolean have_xcomposite;
+ gboolean have_xdamage;
+ gint xdamage_event_base;
+
+ gboolean have_randr13;
+ gint xrandr_event_base;
+
+ /* If the SECURITY extension is in place, whether this client holds
+ * a trusted authorization and so is allowed to make various requests
+ * (grabs, properties etc.) Otherwise always TRUE. */
+ gboolean trusted_client;
+
+ /* drag and drop information */
+ GdkDragContext *current_dest_drag;
+
+ /* data needed for MOTIF DnD */
+
+ Window motif_drag_window;
+ GdkWindow *motif_drag_gdk_window;
+ GList **motif_target_lists;
+ gint motif_n_target_lists;
+
+ /* Mapping to/from virtual atoms */
+
+ GHashTable *atom_from_virtual;
+ GHashTable *atom_to_virtual;
+
+ /* Session Management leader window see ICCCM */
+ Window leader_window;
+ GdkWindow *leader_gdk_window;
+ gboolean leader_window_title_set;
+
+ /* list of filters for client messages */
+ GList *client_filters;
+
+ /* List of functions to go from extension event => X window */
+ GSList *event_types;
+
+ /* X ID hashtable */
+ GHashTable *xid_ht;
+
+ /* translation queue */
+ GQueue *translate_queue;
+
+ /* Input device */
+ /* input GdkDevice list */
+ GList *input_devices;
+
+ /* input GdkWindow list */
+ GList *input_windows;
+
+ /* Startup notification */
+ gchar *startup_notification_id;
+
+ /* Time of most recent user interaction. */
+ gulong user_time;
+
+ /* Sets of atoms for DND */
+ guint base_dnd_atoms_precached : 1;
+ guint xdnd_atoms_precached : 1;
+ guint motif_atoms_precached : 1;
+ guint use_sync : 1;
+
+ guint have_shapes : 1;
+ guint have_input_shapes : 1;
+ gint shape_event_base;
+
+ /* The offscreen window that has the pointer in it (if any) */
+ GdkWindow *active_offscreen_window;
+
+ GSList *error_traps;
+};
+
+struct _GdkDisplayX11Class
+{
+ GdkDisplayClass parent_class;
+};
+
+GType _gdk_display_x11_get_type (void);
+GdkScreen *_gdk_x11_display_screen_for_xrootwin (GdkDisplay *display,
+ Window xrootwin);
+void _gdk_x11_display_error_event (GdkDisplay *display,
+ XErrorEvent *error);
+
+G_END_DECLS
+
+#endif /* __GDK_DISPLAY_X11__ */
diff --git a/gdk/broadway/gdkdnd-broadway.c b/gdk/broadway/gdkdnd-broadway.c
new file mode 100644
index 0000000000..b10d7870e7
--- /dev/null
+++ b/gdk/broadway/gdkdnd-broadway.c
@@ -0,0 +1,4035 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gdkdnd.h"
+
+#include "gdkmain.h"
+#include "gdkx.h"
+#include "gdkasync.h"
+#include "gdkproperty.h"
+#include "gdkprivate-broadway.h"
+#include "gdkinternals.h"
+#include "gdkscreen-broadway.h"
+#include "gdkdisplay-broadway.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+#include <X11/extensions/Xcomposite.h>
+
+#include <string.h>
+
+typedef struct _GdkDragContextPrivateX11 GdkDragContextPrivateX11;
+
+typedef enum {
+ GDK_DRAG_STATUS_DRAG,
+ GDK_DRAG_STATUS_MOTION_WAIT,
+ GDK_DRAG_STATUS_ACTION_WAIT,
+ GDK_DRAG_STATUS_DROP
+} GtkDragStatus;
+
+typedef struct {
+ guint32 xid;
+ gint x, y, width, height;
+ gboolean mapped;
+ gboolean shape_selected;
+ gboolean shape_valid;
+ cairo_region_t *shape;
+} GdkCacheChild;
+
+typedef struct {
+ GList *children;
+ GHashTable *child_hash;
+ guint old_event_mask;
+ GdkScreen *screen;
+} GdkWindowCache;
+
+/* Structure that holds information about a drag in progress.
+ * this is used on both source and destination sides.
+ */
+struct _GdkDragContextPrivateX11 {
+ GdkDragContext context;
+
+ Atom motif_selection;
+ guint ref_count;
+
+ guint16 last_x; /* Coordinates from last event */
+ guint16 last_y;
+ GdkDragAction old_action; /* The last action we sent to the source */
+ GdkDragAction old_actions; /* The last actions we sent to the source */
+ GdkDragAction xdnd_actions; /* What is currently set in XdndActionList */
+
+ Window dest_xid; /* The last window we looked up */
+ Window drop_xid; /* The (non-proxied) window that is receiving drops */
+ guint xdnd_targets_set : 1; /* Whether we've already set XdndTypeList */
+ guint xdnd_actions_set : 1; /* Whether we've already set XdndActionList */
+ guint xdnd_have_actions : 1; /* Whether an XdndActionList was provided */
+ guint motif_targets_set : 1; /* Whether we've already set motif initiator info */
+ guint drag_status : 4; /* current status of drag */
+
+ guint drop_failed : 1; /* Whether the drop was unsuccessful */
+ guint version; /* Xdnd protocol version */
+
+ GSList *window_caches;
+ GdkDevice *device;
+};
+
+#define PRIVATE_DATA(context) ((GdkDragContextPrivateX11 *) GDK_DRAG_CONTEXT (context)->windowing_data)
+
+/* Forward declarations */
+
+static void gdk_window_cache_destroy (GdkWindowCache *cache);
+
+static void motif_read_target_table (GdkDisplay *display);
+
+static GdkFilterReturn motif_dnd_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data);
+
+static GdkFilterReturn xdnd_enter_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data);
+static GdkFilterReturn xdnd_leave_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data);
+static GdkFilterReturn xdnd_position_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data);
+static GdkFilterReturn xdnd_status_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data);
+static GdkFilterReturn xdnd_finished_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data);
+static GdkFilterReturn xdnd_drop_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data);
+
+static void xdnd_manage_source_filter (GdkDragContext *context,
+ GdkWindow *window,
+ gboolean add_filter);
+
+static void gdk_drag_context_finalize (GObject *object);
+
+static GList *contexts;
+
+static const struct {
+ const char *atom_name;
+ GdkFilterFunc func;
+} xdnd_filters[] = {
+ { "XdndEnter", xdnd_enter_filter },
+ { "XdndLeave", xdnd_leave_filter },
+ { "XdndPosition", xdnd_position_filter },
+ { "XdndStatus", xdnd_status_filter },
+ { "XdndFinished", xdnd_finished_filter },
+ { "XdndDrop", xdnd_drop_filter },
+};
+
+G_DEFINE_TYPE (GdkDragContext, gdk_drag_context, G_TYPE_OBJECT)
+
+static void
+gdk_drag_context_init (GdkDragContext *dragcontext)
+{
+ GdkDragContextPrivateX11 *private;
+
+ private = G_TYPE_INSTANCE_GET_PRIVATE (dragcontext,
+ GDK_TYPE_DRAG_CONTEXT,
+ GdkDragContextPrivateX11);
+
+ dragcontext->windowing_data = private;
+
+ contexts = g_list_prepend (contexts, dragcontext);
+}
+
+static void
+gdk_drag_context_class_init (GdkDragContextClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gdk_drag_context_finalize;
+
+ g_type_class_add_private (object_class, sizeof (GdkDragContextPrivateX11));
+}
+
+static void
+gdk_drag_context_finalize (GObject *object)
+{
+ GdkDragContext *context = GDK_DRAG_CONTEXT (object);
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+ GSList *tmp_list;
+
+ g_list_free (context->targets);
+
+ if (context->source_window)
+ {
+ if ((context->protocol == GDK_DRAG_PROTO_XDND) &&
+ !context->is_source)
+ xdnd_manage_source_filter (context, context->source_window, FALSE);
+
+ g_object_unref (context->source_window);
+ }
+
+ if (context->dest_window)
+ g_object_unref (context->dest_window);
+
+ for (tmp_list = private->window_caches; tmp_list; tmp_list = tmp_list->next)
+ gdk_window_cache_destroy (tmp_list->data);
+ g_slist_free (private->window_caches);
+
+ contexts = g_list_remove (contexts, context);
+
+ G_OBJECT_CLASS (gdk_drag_context_parent_class)->finalize (object);
+}
+
+/* Drag Contexts */
+
+/**
+ * gdk_drag_context_new:
+ *
+ * Creates a new #GdkDragContext.
+ *
+ * Return value: the newly created #GdkDragContext.
+ **/
+GdkDragContext *
+gdk_drag_context_new (void)
+{
+ return g_object_new (GDK_TYPE_DRAG_CONTEXT, NULL);
+}
+
+/**
+ * gdk_drag_context_set_device:
+ * @context: a #GdkDragContext
+ * @device: a #GdkDevice
+ *
+ * Associates a #GdkDevice to @context, so all Drag and Drop events
+ * for @context are emitted as if they came from this device.
+ **/
+void
+gdk_drag_context_set_device (GdkDragContext *context,
+ GdkDevice *device)
+{
+ GdkDragContextPrivateX11 *private;
+
+ g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+ g_return_if_fail (GDK_IS_DEVICE (device));
+
+ private = PRIVATE_DATA (context);
+
+ if (private->device)
+ {
+ g_object_unref (private->device);
+ private->device = NULL;
+ }
+
+ if (device)
+ private->device = g_object_ref (device);
+}
+
+/**
+ * gdk_drag_context_get_device:
+ * @context: a #GdkDragContext
+ *
+ * Returns the #GdkDevice associated to the drag context.
+ *
+ * Returns: The #GdkDevice associated to @context.
+ **/
+GdkDevice *
+gdk_drag_context_get_device (GdkDragContext *context)
+{
+ GdkDragContextPrivateX11 *private;
+
+ g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
+
+ private = PRIVATE_DATA (context);
+
+ return private->device;
+}
+
+static GdkDragContext *
+gdk_drag_context_find (GdkDisplay *display,
+ gboolean is_source,
+ Window source_xid,
+ Window dest_xid)
+{
+ GList *tmp_list = contexts;
+ GdkDragContext *context;
+ GdkDragContextPrivateX11 *private;
+ Window context_dest_xid;
+
+ while (tmp_list)
+ {
+ context = (GdkDragContext *)tmp_list->data;
+ private = PRIVATE_DATA (context);
+
+ if ((context->source_window && gdk_window_get_display (context->source_window) != display) ||
+ (context->dest_window && gdk_window_get_display (context->dest_window) != display))
+ continue;
+
+ context_dest_xid = context->dest_window ?
+ (private->drop_xid ?
+ private->drop_xid :
+ GDK_DRAWABLE_XID (context->dest_window)) :
+ None;
+
+ if ((!context->is_source == !is_source) &&
+ ((source_xid == None) || (context->source_window &&
+ (GDK_DRAWABLE_XID (context->source_window) == source_xid))) &&
+ ((dest_xid == None) || (context_dest_xid == dest_xid)))
+ return context;
+
+ tmp_list = tmp_list->next;
+ }
+
+ return NULL;
+}
+
+static void
+precache_target_list (GdkDragContext *context)
+{
+ if (context->targets)
+ {
+ GPtrArray *targets = g_ptr_array_new ();
+ GList *tmp_list;
+ int i;
+
+ for (tmp_list = context->targets; tmp_list; tmp_list = tmp_list->next)
+ g_ptr_array_add (targets, gdk_atom_name (GDK_POINTER_TO_ATOM (tmp_list->data)));
+
+ _gdk_x11_precache_atoms (GDK_WINDOW_DISPLAY (context->source_window),
+ (const gchar **)targets->pdata,
+ targets->len);
+
+ for (i =0; i < targets->len; i++)
+ g_free (targets->pdata[i]);
+
+ g_ptr_array_free (targets, TRUE);
+ }
+}
+
+/* Utility functions */
+
+static void
+free_cache_child (GdkCacheChild *child,
+ GdkDisplay *display)
+{
+ if (child->shape)
+ cairo_region_destroy (child->shape);
+
+ if (child->shape_selected && display)
+ {
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+ XShapeSelectInput (display_x11->xdisplay, child->xid, 0);
+ }
+
+ g_free (child);
+}
+
+static void
+gdk_window_cache_add (GdkWindowCache *cache,
+ guint32 xid,
+ gint x, gint y, gint width, gint height,
+ gboolean mapped)
+{
+ GdkCacheChild *child = g_new (GdkCacheChild, 1);
+
+ child->xid = xid;
+ child->x = x;
+ child->y = y;
+ child->width = width;
+ child->height = height;
+ child->mapped = mapped;
+ child->shape_selected = FALSE;
+ child->shape_valid = FALSE;
+ child->shape = NULL;
+
+ cache->children = g_list_prepend (cache->children, child);
+ g_hash_table_insert (cache->child_hash, GUINT_TO_POINTER (xid),
+ cache->children);
+}
+
+static GdkFilterReturn
+gdk_window_cache_shape_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *xevent = (XEvent *)xev;
+ GdkWindowCache *cache = data;
+
+ GdkDisplayX11 *display = GDK_DISPLAY_X11 (gdk_screen_get_display (cache->screen));
+
+ if (display->have_shapes &&
+ xevent->type == display->shape_event_base + ShapeNotify)
+ {
+ XShapeEvent *xse = (XShapeEvent*)xevent;
+ GList *node;
+
+ node = g_hash_table_lookup (cache->child_hash,
+ GUINT_TO_POINTER (xse->window));
+ if (node)
+ {
+ GdkCacheChild *child = node->data;
+ child->shape_valid = FALSE;
+ if (child->shape)
+ {
+ cairo_region_destroy (child->shape);
+ child->shape = NULL;
+ }
+ }
+
+ return GDK_FILTER_REMOVE;
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static GdkFilterReturn
+gdk_window_cache_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *xevent = (XEvent *)xev;
+ GdkWindowCache *cache = data;
+
+ switch (xevent->type)
+ {
+ case CirculateNotify:
+ break;
+ case ConfigureNotify:
+ {
+ XConfigureEvent *xce = &xevent->xconfigure;
+ GList *node;
+
+ node = g_hash_table_lookup (cache->child_hash,
+ GUINT_TO_POINTER (xce->window));
+ if (node)
+ {
+ GdkCacheChild *child = node->data;
+ child->x = xce->x;
+ child->y = xce->y;
+ child->width = xce->width;
+ child->height = xce->height;
+ if (xce->above == None && (node->next))
+ {
+ GList *last = g_list_last (cache->children);
+ cache->children = g_list_remove_link (cache->children, node);
+ last->next = node;
+ node->next = NULL;
+ node->prev = last;
+ }
+ else
+ {
+ GList *above_node = g_hash_table_lookup (cache->child_hash,
+ GUINT_TO_POINTER (xce->above));
+ if (above_node && node->next != above_node)
+ {
+ /* Put the window above (before in the list) above_node
+ */
+ cache->children = g_list_remove_link (cache->children, node);
+ node->prev = above_node->prev;
+ if (node->prev)
+ node->prev->next = node;
+ else
+ cache->children = node;
+ node->next = above_node;
+ above_node->prev = node;
+ }
+ }
+ }
+ break;
+ }
+ case CreateNotify:
+ {
+ XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
+
+ if (!g_hash_table_lookup (cache->child_hash,
+ GUINT_TO_POINTER (xcwe->window)))
+ gdk_window_cache_add (cache, xcwe->window,
+ xcwe->x, xcwe->y, xcwe->width, xcwe->height,
+ FALSE);
+ break;
+ }
+ case DestroyNotify:
+ {
+ XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
+ GList *node;
+
+ node = g_hash_table_lookup (cache->child_hash,
+ GUINT_TO_POINTER (xdwe->window));
+ if (node)
+ {
+ GdkCacheChild *child = node->data;
+
+ g_hash_table_remove (cache->child_hash,
+ GUINT_TO_POINTER (xdwe->window));
+ cache->children = g_list_remove_link (cache->children, node);
+ /* window is destroyed, no need to disable ShapeNotify */
+ free_cache_child (child, NULL);
+ g_list_free_1 (node);
+ }
+ break;
+ }
+ case MapNotify:
+ {
+ XMapEvent *xme = &xevent->xmap;
+ GList *node;
+
+ node = g_hash_table_lookup (cache->child_hash,
+ GUINT_TO_POINTER (xme->window));
+ if (node)
+ {
+ GdkCacheChild *child = node->data;
+ child->mapped = TRUE;
+ }
+ break;
+ }
+ case ReparentNotify:
+ break;
+ case UnmapNotify:
+ {
+ XMapEvent *xume = &xevent->xmap;
+ GList *node;
+
+ node = g_hash_table_lookup (cache->child_hash,
+ GUINT_TO_POINTER (xume->window));
+ if (node)
+ {
+ GdkCacheChild *child = node->data;
+ child->mapped = FALSE;
+ }
+ break;
+ }
+ default:
+ return GDK_FILTER_CONTINUE;
+ }
+ return GDK_FILTER_REMOVE;
+}
+
+static GdkWindowCache *
+gdk_window_cache_new (GdkScreen *screen)
+{
+ XWindowAttributes xwa;
+ Display *xdisplay = GDK_SCREEN_XDISPLAY (screen);
+ GdkWindow *root_window = gdk_screen_get_root_window (screen);
+ GdkChildInfoX11 *children;
+ guint nchildren, i;
+ Window cow;
+
+ GdkWindowCache *result = g_new (GdkWindowCache, 1);
+
+ result->children = NULL;
+ result->child_hash = g_hash_table_new (g_direct_hash, NULL);
+ result->screen = screen;
+
+ XGetWindowAttributes (xdisplay, GDK_WINDOW_XWINDOW (root_window), &xwa);
+ result->old_event_mask = xwa.your_event_mask;
+
+ if (G_UNLIKELY (!GDK_DISPLAY_X11 (GDK_SCREEN_X11 (screen)->display)->trusted_client))
+ {
+ GList *toplevel_windows, *list;
+ GdkWindow *window;
+ gint x, y, width, height;
+
+ toplevel_windows = gdk_screen_get_toplevel_windows (screen);
+ for (list = toplevel_windows; list; list = list->next) {
+ window = GDK_WINDOW (list->data);
+ gdk_window_get_geometry (window, &x, &y, &width, &height, NULL);
+ gdk_window_cache_add (result, GDK_WINDOW_XID (window),
+ x, y, width, height,
+ gdk_window_is_visible (window));
+ }
+ g_list_free (toplevel_windows);
+ return result;
+ }
+
+ XSelectInput (xdisplay, GDK_WINDOW_XWINDOW (root_window),
+ result->old_event_mask | SubstructureNotifyMask);
+ gdk_window_add_filter (root_window, gdk_window_cache_filter, result);
+ gdk_window_add_filter (NULL, gdk_window_cache_shape_filter, result);
+
+ if (!_gdk_x11_get_window_child_info (gdk_screen_get_display (screen),
+ GDK_WINDOW_XWINDOW (root_window),
+ FALSE, NULL,
+ &children, &nchildren))
+ return result;
+
+ for (i = 0; i < nchildren ; i++)
+ {
+ gdk_window_cache_add (result, children[i].window,
+ children[i].x, children[i].y, children[i].width, children[i].height,
+ children[i].is_mapped);
+ }
+
+ g_free (children);
+
+#ifdef HAVE_XCOMPOSITE
+ /*
+ * Add the composite overlay window to the cache, as this can be a reasonable
+ * Xdnd proxy as well.
+ * This is only done when the screen is composited in order to avoid mapping
+ * the COW. We assume that the CM is using the COW (which is true for pretty
+ * much any CM currently in use).
+ */
+ if (gdk_screen_is_composited (screen))
+ {
+ cow = XCompositeGetOverlayWindow (xdisplay, GDK_WINDOW_XWINDOW (root_window));
+ gdk_window_cache_add (result, cow, 0, 0, gdk_screen_get_width (screen), gdk_screen_get_height (screen), TRUE);
+ XCompositeReleaseOverlayWindow (xdisplay, GDK_WINDOW_XWINDOW (root_window));
+ }
+#endif
+
+ return result;
+}
+
+static void
+gdk_window_cache_destroy (GdkWindowCache *cache)
+{
+ GdkWindow *root_window = gdk_screen_get_root_window (cache->screen);
+
+ XSelectInput (GDK_WINDOW_XDISPLAY (root_window),
+ GDK_WINDOW_XWINDOW (root_window),
+ cache->old_event_mask);
+ gdk_window_remove_filter (root_window, gdk_window_cache_filter, cache);
+ gdk_window_remove_filter (NULL, gdk_window_cache_shape_filter, cache);
+
+ gdk_error_trap_push ();
+
+ g_list_foreach (cache->children, (GFunc)free_cache_child,
+ gdk_screen_get_display (cache->screen));
+
+ gdk_error_trap_pop_ignored ();
+
+ g_list_free (cache->children);
+ g_hash_table_destroy (cache->child_hash);
+
+ g_free (cache);
+}
+
+static gboolean
+is_pointer_within_shape (GdkDisplay *display,
+ GdkCacheChild *child,
+ gint x_pos,
+ gint y_pos)
+{
+ if (!child->shape_selected)
+ {
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+ XShapeSelectInput (display_x11->xdisplay, child->xid, ShapeNotifyMask);
+ child->shape_selected = TRUE;
+ }
+ if (!child->shape_valid)
+ {
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+ cairo_region_t *input_shape;
+
+ child->shape = _xwindow_get_shape (display_x11->xdisplay,
+ child->xid, ShapeBounding);
+#ifdef ShapeInput
+ input_shape = _xwindow_get_shape (display_x11->xdisplay,
+ child->xid, ShapeInput);
+ if (child->shape && input_shape)
+ {
+ cairo_region_intersect (child->shape, input_shape);
+ cairo_region_destroy (input_shape);
+ }
+ else if (input_shape)
+ {
+ child->shape = input_shape;
+ }
+#endif
+
+ child->shape_valid = TRUE;
+ }
+
+ return child->shape == NULL ||
+ cairo_region_contains_point (child->shape, x_pos, y_pos);
+}
+
+static Window
+get_client_window_at_coords_recurse (GdkDisplay *display,
+ Window win,
+ gboolean is_toplevel,
+ gint x,
+ gint y)
+{
+ GdkChildInfoX11 *children;
+ unsigned int nchildren;
+ int i;
+ gboolean found_child = FALSE;
+ GdkChildInfoX11 child = { 0, };
+ gboolean has_wm_state = FALSE;
+
+ if (!_gdk_x11_get_window_child_info (display, win, TRUE,
+ is_toplevel? &has_wm_state : NULL,
+ &children, &nchildren))
+ return None;
+
+ if (has_wm_state)
+ {
+ g_free (children);
+
+ return win;
+ }
+
+ for (i = nchildren - 1; (i >= 0) && !found_child; i--)
+ {
+ GdkChildInfoX11 *cur_child = &children[i];
+
+ if ((cur_child->is_mapped) && (cur_child->window_class == InputOutput) &&
+ (x >= cur_child->x) && (x < cur_child->x + cur_child->width) &&
+ (y >= cur_child->y) && (y < cur_child->y + cur_child->height))
+ {
+ x -= cur_child->x;
+ y -= cur_child->y;
+ child = *cur_child;
+ found_child = TRUE;
+ }
+ }
+
+ g_free (children);
+
+ if (found_child)
+ {
+ if (child.has_wm_state)
+ return child.window;
+ else
+ return get_client_window_at_coords_recurse (display, child.window, FALSE, x, y);
+ }
+ else
+ return None;
+}
+
+static Window
+get_client_window_at_coords (GdkWindowCache *cache,
+ Window ignore,
+ gint x_root,
+ gint y_root)
+{
+ GList *tmp_list;
+ Window retval = None;
+
+ gdk_error_trap_push ();
+
+ tmp_list = cache->children;
+
+ while (tmp_list && !retval)
+ {
+ GdkCacheChild *child = tmp_list->data;
+
+ if ((child->xid != ignore) && (child->mapped))
+ {
+ if ((x_root >= child->x) && (x_root < child->x + child->width) &&
+ (y_root >= child->y) && (y_root < child->y + child->height))
+ {
+ GdkDisplay *display = gdk_screen_get_display (cache->screen);
+
+ if (!is_pointer_within_shape (display, child,
+ x_root - child->x,
+ y_root - child->y))
+ {
+ tmp_list = tmp_list->next;
+ continue;
+ }
+
+ retval = get_client_window_at_coords_recurse (display,
+ child->xid, TRUE,
+ x_root - child->x,
+ y_root - child->y);
+ if (!retval)
+ retval = child->xid;
+ }
+ }
+ tmp_list = tmp_list->next;
+ }
+
+ gdk_error_trap_pop_ignored ();
+
+ if (retval)
+ return retval;
+ else
+ return GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (cache->screen));
+}
+
+/*************************************************************
+ ***************************** MOTIF *************************
+ *************************************************************/
+
+/* values used in the message type for Motif DND */
+enum {
+ XmTOP_LEVEL_ENTER,
+ XmTOP_LEVEL_LEAVE,
+ XmDRAG_MOTION,
+ XmDROP_SITE_ENTER,
+ XmDROP_SITE_LEAVE,
+ XmDROP_START,
+ XmDROP_FINISH,
+ XmDRAG_DROP_FINISH,
+ XmOPERATION_CHANGED
+};
+
+/* Values used to specify type of protocol to use */
+enum {
+ XmDRAG_NONE,
+ XmDRAG_DROP_ONLY,
+ XmDRAG_PREFER_PREREGISTER,
+ XmDRAG_PREREGISTER,
+ XmDRAG_PREFER_DYNAMIC,
+ XmDRAG_DYNAMIC,
+ XmDRAG_PREFER_RECEIVER
+};
+
+/* Operation codes */
+enum {
+ XmDROP_NOOP,
+ XmDROP_MOVE = 0x01,
+ XmDROP_COPY = 0x02,
+ XmDROP_LINK = 0x04
+};
+
+/* Drop site status */
+enum {
+ XmNO_DROP_SITE = 0x01,
+ XmDROP_SITE_INVALID = 0x02,
+ XmDROP_SITE_VALID = 0x03
+};
+
+/* completion status */
+enum {
+ XmDROP,
+ XmDROP_HELP,
+ XmDROP_CANCEL,
+ XmDROP_INTERRUPT
+};
+
+/* Byte swapping routines. The motif specification leaves it
+ * up to us to save a few bytes in the client messages
+ */
+static gchar local_byte_order = '\0';
+
+#ifdef G_ENABLE_DEBUG
+static void
+print_target_list (GList *targets)
+{
+ while (targets)
+ {
+ gchar *name = gdk_atom_name (GDK_POINTER_TO_ATOM (targets->data));
+ g_message ("\t%s", name);
+ g_free (name);
+ targets = targets->next;
+ }
+}
+#endif /* G_ENABLE_DEBUG */
+
+static void
+init_byte_order (void)
+{
+ guint32 myint = 0x01020304;
+ local_byte_order = (*(gchar *)&myint == 1) ? 'B' : 'l';
+}
+
+static guint16
+card16_to_host (guint16 x, gchar byte_order) {
+ if (byte_order == local_byte_order)
+ return x;
+ else
+ return (x << 8) | (x >> 8);
+}
+
+static guint32
+card32_to_host (guint32 x, gchar byte_order) {
+ if (byte_order == local_byte_order)
+ return x;
+ else
+ return (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);
+}
+
+/* Motif packs together fields of varying length into the
+ * client message. We can't rely on accessing these
+ * through data.s[], data.l[], etc, because on some architectures
+ * (i.e., Alpha) these won't be valid for format == 8.
+ */
+
+#define MOTIF_XCLIENT_BYTE(xevent,i) \
+ (xevent)->xclient.data.b[i]
+#define MOTIF_XCLIENT_SHORT(xevent,i) \
+ ((gint16 *)&((xevent)->xclient.data.b[0]))[i]
+#define MOTIF_XCLIENT_LONG(xevent,i) \
+ ((gint32 *)&((xevent)->xclient.data.b[0]))[i]
+
+#define MOTIF_UNPACK_BYTE(xevent,i) MOTIF_XCLIENT_BYTE(xevent,i)
+#define MOTIF_UNPACK_SHORT(xevent,i) \
+ card16_to_host (MOTIF_XCLIENT_SHORT(xevent,i), MOTIF_XCLIENT_BYTE(xevent, 1))
+#define MOTIF_UNPACK_LONG(xevent,i) \
+ card32_to_host (MOTIF_XCLIENT_LONG(xevent,i), MOTIF_XCLIENT_BYTE(xevent, 1))
+
+/***** Dest side ***********/
+
+/* Property placed on source windows */
+typedef struct _MotifDragInitiatorInfo {
+ guint8 byte_order;
+ guint8 protocol_version;
+ guint16 targets_index;
+ guint32 selection_atom;
+} MotifDragInitiatorInfo;
+
+/* Header for target table on the drag window */
+typedef struct _MotifTargetTableHeader {
+ guchar byte_order;
+ guchar protocol_version;
+ guint16 n_lists;
+ guint32 total_size;
+} MotifTargetTableHeader;
+
+/* Property placed on target windows */
+typedef struct _MotifDragReceiverInfo {
+ guint8 byte_order;
+ guint8 protocol_version;
+ guint8 protocol_style;
+ guint8 pad;
+ guint32 proxy_window;
+ guint16 num_drop_sites;
+ guint16 padding;
+ guint32 total_size;
+} MotifDragReceiverInfo;
+
+/* Target table handling */
+
+static GdkFilterReturn
+motif_drag_window_filter (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *xev = (XEvent *)xevent;
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (event->any.window);
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+ switch (xev->xany.type)
+ {
+ case DestroyNotify:
+ display_x11->motif_drag_window = None;
+ display_x11->motif_drag_gdk_window = NULL;
+ break;
+ case PropertyNotify:
+ if (display_x11->motif_target_lists &&
+ (xev->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_TARGETS")))
+ motif_read_target_table (display);
+ break;
+ }
+ return GDK_FILTER_REMOVE;
+}
+
+static Window
+motif_lookup_drag_window (GdkDisplay *display,
+ Display *lookup_xdisplay)
+{
+ Window retval = None;
+ gulong bytes_after, nitems;
+ Atom type;
+ gint format;
+ guchar *data;
+
+ XGetWindowProperty (lookup_xdisplay, RootWindow (lookup_xdisplay, 0),
+ gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_WINDOW"),
+ 0, 1, FALSE,
+ XA_WINDOW, &type, &format, &nitems, &bytes_after,
+ &data);
+
+ if ((format == 32) && (nitems == 1) && (bytes_after == 0))
+ {
+ retval = *(Window *)data;
+ GDK_NOTE (DND,
+ g_message ("Found drag window %#lx\n", GDK_DISPLAY_X11 (display)->motif_drag_window));
+ }
+
+ if (type != None)
+ XFree (data);
+
+ return retval;
+}
+
+/* Finds the window where global Motif drag information is stored.
+ * If it doesn't exist and 'create' is TRUE, create one.
+ */
+static Window
+motif_find_drag_window (GdkDisplay *display,
+ gboolean create)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+ if (!display_x11->motif_drag_window)
+ {
+ Atom motif_drag_window_atom = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_WINDOW");
+ display_x11->motif_drag_window = motif_lookup_drag_window (display, display_x11->xdisplay);
+
+ if (!display_x11->motif_drag_window && create)
+ {
+ /* Create a persistant window. (Copied from LessTif) */
+
+ Display *persistant_xdisplay;
+ XSetWindowAttributes attr;
+ persistant_xdisplay = XOpenDisplay (gdk_display_get_name (display));
+ XSetCloseDownMode (persistant_xdisplay, RetainPermanent);
+
+ XGrabServer (persistant_xdisplay);
+
+ display_x11->motif_drag_window = motif_lookup_drag_window (display, persistant_xdisplay);
+
+ if (!display_x11->motif_drag_window)
+ {
+ attr.override_redirect = True;
+ attr.event_mask = PropertyChangeMask;
+
+ display_x11->motif_drag_window =
+ XCreateWindow (persistant_xdisplay,
+ RootWindow (persistant_xdisplay, 0),
+ -100, -100, 10, 10, 0, 0,
+ InputOnly, (Visual *)CopyFromParent,
+ (CWOverrideRedirect | CWEventMask), &attr);
+
+ GDK_NOTE (DND,
+ g_message ("Created drag window %#lx\n", display_x11->motif_drag_window));
+
+ XChangeProperty (persistant_xdisplay,
+ RootWindow (persistant_xdisplay, 0),
+ motif_drag_window_atom, XA_WINDOW,
+ 32, PropModeReplace,
+ (guchar *)&motif_drag_window_atom, 1);
+
+ }
+ XUngrabServer (persistant_xdisplay);
+ XCloseDisplay (persistant_xdisplay);
+ }
+
+ /* There is a miniscule race condition here if the drag window
+ * gets destroyed exactly now.
+ */
+ if (display_x11->motif_drag_window)
+ {
+ display_x11->motif_drag_gdk_window =
+ gdk_window_foreign_new_for_display (display, display_x11->motif_drag_window);
+ gdk_window_add_filter (display_x11->motif_drag_gdk_window,
+ motif_drag_window_filter,
+ NULL);
+ }
+ }
+
+ return display_x11->motif_drag_window;
+}
+
+static void
+motif_read_target_table (GdkDisplay *display)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+ gulong bytes_after, nitems;
+ Atom type;
+ gint format;
+ gint i, j;
+
+ Atom motif_drag_targets_atom = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_TARGETS");
+
+ 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);
+ display_x11->motif_target_lists = NULL;
+ display_x11->motif_n_target_lists = 0;
+ }
+
+ if (motif_find_drag_window (display, FALSE))
+ {
+ guchar *data;
+ MotifTargetTableHeader *header = NULL;
+ guchar *target_bytes = NULL;
+ guchar *p;
+ gboolean success = FALSE;
+
+ gdk_error_trap_push ();
+ XGetWindowProperty (display_x11->xdisplay,
+ display_x11->motif_drag_window,
+ motif_drag_targets_atom,
+ 0, (sizeof(MotifTargetTableHeader)+3)/4, FALSE,
+ motif_drag_targets_atom,
+ &type, &format, &nitems, &bytes_after,
+ &data);
+
+ if (gdk_error_trap_pop () || (format != 8) || (nitems < sizeof (MotifTargetTableHeader)))
+ goto error;
+
+ header = (MotifTargetTableHeader *)data;
+
+ header->n_lists = card16_to_host (header->n_lists, header->byte_order);
+ header->total_size = card32_to_host (header->total_size, header->byte_order);
+
+ gdk_error_trap_push ();
+ XGetWindowProperty (display_x11->xdisplay,
+ display_x11->motif_drag_window,
+ motif_drag_targets_atom,
+ (sizeof(MotifTargetTableHeader)+3)/4,
+ (header->total_size + 3)/4 - (sizeof(MotifTargetTableHeader) + 3)/4,
+ FALSE,
+ motif_drag_targets_atom, &type, &format, &nitems,
+ &bytes_after, &target_bytes);
+
+ if (gdk_error_trap_pop () || (format != 8) || (bytes_after != 0) ||
+ (nitems != header->total_size - sizeof(MotifTargetTableHeader)))
+ goto error;
+
+ display_x11->motif_n_target_lists = header->n_lists;
+ display_x11->motif_target_lists = g_new0 (GList *, display_x11->motif_n_target_lists);
+
+ p = target_bytes;
+ for (i=0; i<header->n_lists; i++)
+ {
+ gint n_targets;
+ guint32 *targets;
+
+ if (p + sizeof(guint16) - target_bytes > nitems)
+ goto error;
+
+ n_targets = card16_to_host (*(gushort *)p, header->byte_order);
+
+ /* We need to make a copy of the targets, since it may
+ * be unaligned
+ */
+ targets = g_new (guint32, n_targets);
+ memcpy (targets, p + sizeof(guint16), sizeof(guint32) * n_targets);
+
+ p += sizeof(guint16) + n_targets * sizeof(guint32);
+ if (p - target_bytes > nitems)
+ goto error;
+
+ for (j=0; j<n_targets; j++)
+ display_x11->motif_target_lists[i] =
+ g_list_prepend (display_x11->motif_target_lists[i],
+ GUINT_TO_POINTER (card32_to_host (targets[j],
+ header->byte_order)));
+ g_free (targets);
+ display_x11->motif_target_lists[i] = g_list_reverse (display_x11->motif_target_lists[i]);
+ }
+
+ success = TRUE;
+
+ error:
+ if (header)
+ XFree (header);
+
+ if (target_bytes)
+ XFree (target_bytes);
+
+ if (!success)
+ {
+ if (display_x11->motif_target_lists)
+ {
+ g_free (display_x11->motif_target_lists);
+ display_x11->motif_target_lists = NULL;
+ display_x11->motif_n_target_lists = 0;
+ }
+ g_warning ("Error reading Motif target table\n");
+ }
+ }
+}
+
+static gint
+targets_sort_func (gconstpointer a, gconstpointer b)
+{
+ return (GPOINTER_TO_UINT (a) < GPOINTER_TO_UINT (b)) ?
+ -1 : ((GPOINTER_TO_UINT (a) > GPOINTER_TO_UINT (b)) ? 1 : 0);
+}
+
+/* Check if given (sorted) list is in the targets table */
+static gint
+motif_target_table_check (GdkDisplay *display,
+ GList *sorted)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+ GList *tmp_list1, *tmp_list2;
+ gint i;
+
+ for (i=0; i<display_x11->motif_n_target_lists; i++)
+ {
+ tmp_list1 = display_x11->motif_target_lists[i];
+ tmp_list2 = sorted;
+
+ while (tmp_list1 && tmp_list2)
+ {
+ if (tmp_list1->data != tmp_list2->data)
+ break;
+
+ tmp_list1 = tmp_list1->next;
+ tmp_list2 = tmp_list2->next;
+ }
+ if (!tmp_list1 && !tmp_list2) /* Found it */
+ return i;
+ }
+
+ return -1;
+}
+
+static gint
+motif_add_to_target_table (GdkDisplay *display,
+ GList *targets) /* targets is list of GdkAtom */
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+ GList *sorted = NULL;
+ gint index = -1;
+ gint i;
+ GList *tmp_list;
+
+ /* make a sorted copy of the list */
+
+ while (targets)
+ {
+ Atom xatom = gdk_x11_atom_to_xatom_for_display (display, GDK_POINTER_TO_ATOM (targets->data));
+ sorted = g_list_insert_sorted (sorted, GUINT_TO_POINTER (xatom), targets_sort_func);
+ targets = targets->next;
+ }
+
+ /* First check if it is there already */
+
+ if (display_x11->motif_target_lists)
+ index = motif_target_table_check (display, sorted);
+
+ /* We need to grab the server while doing this, to ensure
+ * atomiticity. Ugh
+ */
+
+ if (index < 0)
+ {
+ /* We need to make sure that it exists _before_ we grab the
+ * server, since we can't open a new connection after we
+ * grab the server.
+ */
+ motif_find_drag_window (display, TRUE);
+
+ gdk_x11_display_grab (display);
+ motif_read_target_table (display);
+
+ /* Check again, in case it was added in the meantime */
+
+ if (display_x11->motif_target_lists)
+ index = motif_target_table_check (display, sorted);
+
+ if (index < 0)
+ {
+ guint32 total_size = 0;
+ guchar *data;
+ guchar *p;
+ guint16 *p16;
+ MotifTargetTableHeader *header;
+
+ if (!display_x11->motif_target_lists)
+ {
+ display_x11->motif_target_lists = g_new (GList *, 1);
+ display_x11->motif_n_target_lists = 1;
+ }
+ else
+ {
+ display_x11->motif_n_target_lists++;
+ display_x11->motif_target_lists = g_realloc (display_x11->motif_target_lists,
+ sizeof(GList *) * display_x11->motif_n_target_lists);
+ }
+ display_x11->motif_target_lists[display_x11->motif_n_target_lists - 1] = sorted;
+ sorted = NULL;
+ index = display_x11->motif_n_target_lists - 1;
+
+ total_size = sizeof (MotifTargetTableHeader);
+ for (i = 0; i < display_x11->motif_n_target_lists ; i++)
+ total_size += sizeof(guint16) + sizeof(guint32) * g_list_length (display_x11->motif_target_lists[i]);
+
+ data = g_malloc (total_size);
+
+ header = (MotifTargetTableHeader *)data;
+ p = data + sizeof(MotifTargetTableHeader);
+
+ header->byte_order = local_byte_order;
+ header->protocol_version = 0;
+ header->n_lists = display_x11->motif_n_target_lists;
+ header->total_size = total_size;
+
+ for (i = 0; i < display_x11->motif_n_target_lists ; i++)
+ {
+ guint16 n_targets = g_list_length (display_x11->motif_target_lists[i]);
+ guint32 *targets = g_new (guint32, n_targets);
+ guint32 *p32 = targets;
+
+ tmp_list = display_x11->motif_target_lists[i];
+ while (tmp_list)
+ {
+ *p32 = GPOINTER_TO_UINT (tmp_list->data);
+
+ tmp_list = tmp_list->next;
+ p32++;
+ }
+
+ p16 = (guint16 *)p;
+ p += sizeof(guint16);
+
+ memcpy (p, targets, n_targets * sizeof(guint32));
+
+ *p16 = n_targets;
+ p += sizeof(guint32) * n_targets;
+ g_free (targets);
+ }
+
+ XChangeProperty (display_x11->xdisplay,
+ display_x11->motif_drag_window,
+ gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_TARGETS"),
+ gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_TARGETS"),
+ 8, PropModeReplace,
+ data, total_size);
+ }
+ gdk_x11_display_ungrab (display);
+ }
+
+ g_list_free (sorted);
+ return index;
+}
+
+/* Translate flags */
+
+static void
+motif_dnd_translate_flags (GdkDragContext *context, guint16 flags)
+{
+ guint recommended_op = flags & 0x000f;
+ guint possible_ops = (flags & 0x0f0) >> 4;
+
+ switch (recommended_op)
+ {
+ case XmDROP_MOVE:
+ context->suggested_action = GDK_ACTION_MOVE;
+ break;
+ case XmDROP_COPY:
+ context->suggested_action = GDK_ACTION_COPY;
+ break;
+ case XmDROP_LINK:
+ context->suggested_action = GDK_ACTION_LINK;
+ break;
+ default:
+ context->suggested_action = GDK_ACTION_COPY;
+ break;
+ }
+
+ context->actions = 0;
+ if (possible_ops & XmDROP_MOVE)
+ context->actions |= GDK_ACTION_MOVE;
+ if (possible_ops & XmDROP_COPY)
+ context->actions |= GDK_ACTION_COPY;
+ if (possible_ops & XmDROP_LINK)
+ context->actions |= GDK_ACTION_LINK;
+}
+
+static guint16
+motif_dnd_get_flags (GdkDragContext *context)
+{
+ guint16 flags = 0;
+
+ switch (context->suggested_action)
+ {
+ case GDK_ACTION_MOVE:
+ flags = XmDROP_MOVE;
+ break;
+ case GDK_ACTION_COPY:
+ flags = XmDROP_COPY;
+ break;
+ case GDK_ACTION_LINK:
+ flags = XmDROP_LINK;
+ break;
+ default:
+ flags = XmDROP_NOOP;
+ break;
+ }
+
+ if (context->actions & GDK_ACTION_MOVE)
+ flags |= XmDROP_MOVE << 8;
+ if (context->actions & GDK_ACTION_COPY)
+ flags |= XmDROP_COPY << 8;
+ if (context->actions & GDK_ACTION_LINK)
+ flags |= XmDROP_LINK << 8;
+
+ return flags;
+}
+
+/* Source Side */
+
+static void
+motif_set_targets (GdkDragContext *context)
+{
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+ MotifDragInitiatorInfo info;
+ gint i;
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
+
+ info.byte_order = local_byte_order;
+ info.protocol_version = 0;
+
+ info.targets_index = motif_add_to_target_table (display, context->targets);
+
+ for (i=0; ; i++)
+ {
+ gchar buf[20];
+ g_snprintf(buf, 20, "_GDK_SELECTION_%d", i);
+
+ private->motif_selection = gdk_x11_get_xatom_by_name_for_display (display, buf);
+ if (!XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display), private->motif_selection))
+ break;
+ }
+
+ info.selection_atom = private->motif_selection;
+
+ XChangeProperty (GDK_DRAWABLE_XDISPLAY (context->source_window),
+ GDK_DRAWABLE_XID (context->source_window),
+ private->motif_selection,
+ gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_INITIATOR_INFO"),
+ 8, PropModeReplace,
+ (guchar *)&info, sizeof (info));
+
+ private->motif_targets_set = 1;
+}
+
+static guint32
+motif_check_dest (GdkDisplay *display,
+ Window win)
+{
+ gboolean retval = FALSE;
+ guchar *data;
+ MotifDragReceiverInfo *info;
+ Atom type = None;
+ int format;
+ unsigned long nitems, after;
+ Atom motif_drag_receiver_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_RECEIVER_INFO");
+
+ gdk_error_trap_push ();
+ XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), win,
+ motif_drag_receiver_info_atom,
+ 0, (sizeof(*info)+3)/4, False, AnyPropertyType,
+ &type, &format, &nitems, &after,
+ &data);
+
+ if (gdk_error_trap_pop() == 0)
+ {
+ if (type != None)
+ {
+ info = (MotifDragReceiverInfo *)data;
+
+ if ((format == 8) && (nitems == sizeof(*info)))
+ {
+ if ((info->protocol_version == 0) &&
+ ((info->protocol_style == XmDRAG_PREFER_PREREGISTER) ||
+ (info->protocol_style == XmDRAG_PREFER_DYNAMIC) ||
+ (info->protocol_style == XmDRAG_DYNAMIC)))
+ retval = TRUE;
+ }
+ else
+ {
+ GDK_NOTE (DND,
+ g_warning ("Invalid Motif drag receiver property on window %ld\n", win));
+ }
+
+ XFree (info);
+ }
+ }
+
+ return retval ? win : None;
+}
+
+static void
+motif_send_enter (GdkDragContext *context,
+ guint32 time)
+{
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
+ XEvent xev;
+
+ if (!G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client))
+ return; /* Motif Dnd requires getting properties on the root window */
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_AND_DROP_MESSAGE");
+ xev.xclient.format = 8;
+ xev.xclient.window = GDK_DRAWABLE_XID (context->dest_window);
+
+ MOTIF_XCLIENT_BYTE (&xev, 0) = XmTOP_LEVEL_ENTER;
+ MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
+ MOTIF_XCLIENT_SHORT (&xev, 1) = 0;
+ MOTIF_XCLIENT_LONG (&xev, 1) = time;
+ MOTIF_XCLIENT_LONG (&xev, 2) = GDK_DRAWABLE_XID (context->source_window);
+
+ if (!private->motif_targets_set)
+ motif_set_targets (context);
+
+ MOTIF_XCLIENT_LONG (&xev, 3) = private->motif_selection;
+ MOTIF_XCLIENT_LONG (&xev, 4) = 0;
+
+ if (!_gdk_send_xevent (display,
+ GDK_DRAWABLE_XID (context->dest_window),
+ FALSE, 0, &xev))
+ GDK_NOTE (DND,
+ g_message ("Send event to %lx failed",
+ GDK_DRAWABLE_XID (context->dest_window)));
+}
+
+static void
+motif_send_leave (GdkDragContext *context,
+ guint32 time)
+{
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_AND_DROP_MESSAGE");
+ xev.xclient.format = 8;
+ xev.xclient.window = GDK_DRAWABLE_XID (context->dest_window);
+
+ MOTIF_XCLIENT_BYTE (&xev, 0) = XmTOP_LEVEL_LEAVE;
+ MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
+ MOTIF_XCLIENT_SHORT (&xev, 1) = 0;
+ MOTIF_XCLIENT_LONG (&xev, 1) = time;
+ MOTIF_XCLIENT_LONG (&xev, 2) = 0;
+ MOTIF_XCLIENT_LONG (&xev, 3) = 0;
+ MOTIF_XCLIENT_LONG (&xev, 4) = 0;
+
+ if (!_gdk_send_xevent (display,
+ GDK_DRAWABLE_XID (context->dest_window),
+ FALSE, 0, &xev))
+ GDK_NOTE (DND,
+ g_message ("Send event to %lx failed",
+ GDK_DRAWABLE_XID (context->dest_window)));
+}
+
+static gboolean
+motif_send_motion (GdkDragContext *context,
+ gint x_root,
+ gint y_root,
+ GdkDragAction action,
+ guint32 time)
+{
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
+ gboolean retval;
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_AND_DROP_MESSAGE");
+ xev.xclient.format = 8;
+ xev.xclient.window = GDK_DRAWABLE_XID (context->dest_window);
+
+ MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
+ MOTIF_XCLIENT_SHORT (&xev, 1) = motif_dnd_get_flags (context);
+ MOTIF_XCLIENT_LONG (&xev, 1) = time;
+ MOTIF_XCLIENT_LONG (&xev, 3) = 0;
+ MOTIF_XCLIENT_LONG (&xev, 4) = 0;
+
+ if ((context->suggested_action != private->old_action) ||
+ (context->actions != private->old_actions))
+ {
+ MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED;
+
+ /* private->drag_status = GDK_DRAG_STATUS_ACTION_WAIT; */
+ retval = TRUE;
+ }
+ else
+ {
+ MOTIF_XCLIENT_BYTE (&xev, 0) = XmDRAG_MOTION;
+
+ MOTIF_XCLIENT_SHORT (&xev, 4) = x_root;
+ MOTIF_XCLIENT_SHORT (&xev, 5) = y_root;
+
+ private->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
+ retval = FALSE;
+ }
+
+ if (!_gdk_send_xevent (display,
+ GDK_DRAWABLE_XID (context->dest_window),
+ FALSE, 0, &xev))
+ GDK_NOTE (DND,
+ g_message ("Send event to %lx failed",
+ GDK_DRAWABLE_XID (context->dest_window)));
+
+ return retval;
+}
+
+static void
+motif_send_drop (GdkDragContext *context, guint32 time)
+{
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_AND_DROP_MESSAGE");
+ xev.xclient.format = 8;
+ xev.xclient.window = GDK_DRAWABLE_XID (context->dest_window);
+
+ MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_START;
+ MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
+ MOTIF_XCLIENT_SHORT (&xev, 1) = motif_dnd_get_flags (context);
+ MOTIF_XCLIENT_LONG (&xev, 1) = time;
+
+ MOTIF_XCLIENT_SHORT (&xev, 4) = private->last_x;
+ MOTIF_XCLIENT_SHORT (&xev, 5) = private->last_y;
+
+ MOTIF_XCLIENT_LONG (&xev, 3) = private->motif_selection;
+ MOTIF_XCLIENT_LONG (&xev, 4) = GDK_DRAWABLE_XID (context->source_window);
+
+ if (!_gdk_send_xevent (display,
+ GDK_DRAWABLE_XID (context->dest_window),
+ FALSE, 0, &xev))
+ GDK_NOTE (DND,
+ g_message ("Send event to %lx failed",
+ GDK_DRAWABLE_XID (context->dest_window)));
+}
+
+/* Target Side */
+
+static gboolean
+motif_read_initiator_info (GdkDisplay *display,
+ Window source_window,
+ Atom atom,
+ GList **targets,
+ Atom *selection)
+{
+ GList *tmp_list;
+ Atom type;
+ gint format;
+ gulong nitems;
+ gulong bytes_after;
+ guchar *data;
+ MotifDragInitiatorInfo *initiator_info;
+
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+ gdk_error_trap_push ();
+ XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), source_window, atom,
+ 0, sizeof(*initiator_info), FALSE,
+ gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_INITIATOR_INFO"),
+ &type, &format, &nitems, &bytes_after,
+ &data);
+
+ if (gdk_error_trap_pop () || (format != 8) || (nitems != sizeof (MotifDragInitiatorInfo)) || (bytes_after != 0))
+ {
+ g_warning ("Error reading initiator info\n");
+ return FALSE;
+ }
+
+ initiator_info = (MotifDragInitiatorInfo *)data;
+
+ motif_read_target_table (display);
+
+ initiator_info->targets_index =
+ card16_to_host (initiator_info->targets_index, initiator_info->byte_order);
+ initiator_info->selection_atom =
+ card32_to_host (initiator_info->selection_atom, initiator_info->byte_order);
+
+ if (initiator_info->targets_index >= display_x11->motif_n_target_lists)
+ {
+ g_warning ("Invalid target index in TOP_LEVEL_ENTER MESSAGE");
+ XFree (initiator_info);
+ return FALSE;
+ }
+
+ tmp_list = g_list_last (display_x11->motif_target_lists[initiator_info->targets_index]);
+
+ *targets = NULL;
+ while (tmp_list)
+ {
+ GdkAtom atom = gdk_x11_xatom_to_atom_for_display (display, GPOINTER_TO_UINT (tmp_list->data));
+ *targets = g_list_prepend (*targets, GDK_ATOM_TO_POINTER (atom));
+ tmp_list = tmp_list->prev;
+ }
+
+#ifdef G_ENABLE_DEBUG
+ if (_gdk_debug_flags & GDK_DEBUG_DND)
+ print_target_list (*targets);
+#endif /* G_ENABLE_DEBUG */
+
+ *selection = initiator_info->selection_atom;
+
+ XFree (initiator_info);
+
+ return TRUE;
+}
+
+static GdkDragContext *
+motif_drag_context_new (GdkWindow *dest_window,
+ guint32 timestamp,
+ guint32 source_window,
+ guint32 atom)
+{
+ GdkDragContext *new_context;
+ GdkDragContextPrivateX11 *private;
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (dest_window);
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+ /* FIXME, current_dest_drag really shouldn't be NULL'd
+ * if we error below.
+ */
+ if (display_x11->current_dest_drag != NULL)
+ {
+ if (timestamp >= display_x11->current_dest_drag->start_time)
+ {
+ g_object_unref (display_x11->current_dest_drag);
+ display_x11->current_dest_drag = NULL;
+ }
+ else
+ return NULL;
+ }
+
+ new_context = gdk_drag_context_new ();
+ private = PRIVATE_DATA (new_context);
+
+ new_context->protocol = GDK_DRAG_PROTO_MOTIF;
+ new_context->is_source = FALSE;
+
+ new_context->source_window = gdk_window_lookup_for_display (display, source_window);
+ if (new_context->source_window)
+ g_object_ref (new_context->source_window);
+ else
+ {
+ new_context->source_window = gdk_window_foreign_new_for_display (display, source_window);
+ if (!new_context->source_window)
+ {
+ g_object_unref (new_context);
+ return NULL;
+ }
+ }
+
+ new_context->dest_window = dest_window;
+ g_object_ref (dest_window);
+ new_context->start_time = timestamp;
+
+ if (!motif_read_initiator_info (GDK_WINDOW_DISPLAY (dest_window),
+ source_window,
+ atom,
+ &new_context->targets,
+ &private->motif_selection))
+ {
+ g_object_unref (new_context);
+ return NULL;
+ }
+
+ return new_context;
+}
+
+/*
+ * The MOTIF drag protocol has no real provisions for distinguishing
+ * multiple simultaneous drops. If the sources grab the pointer
+ * when doing drags, that shouldn't happen, in any case. If it
+ * does, we can't do much except hope for the best.
+ */
+
+static GdkFilterReturn
+motif_top_level_enter (GdkEvent *event,
+ guint16 flags,
+ guint32 timestamp,
+ guint32 source_window,
+ guint32 atom)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (event->any.window));
+ GdkDragContext *new_context;
+
+ GDK_NOTE(DND, g_message ("Motif DND top level enter: flags: %#4x time: %d source_widow: %#4x atom: %d",
+ flags, timestamp, source_window, atom));
+
+ new_context = motif_drag_context_new (event->any.window, timestamp, source_window, atom);
+ if (!new_context)
+ return GDK_FILTER_REMOVE;
+
+ event->dnd.type = GDK_DRAG_ENTER;
+ event->dnd.context = new_context;
+ g_object_ref (new_context);
+
+ display_x11->current_dest_drag = new_context;
+
+ return GDK_FILTER_TRANSLATE;
+}
+
+static GdkFilterReturn
+motif_top_level_leave (GdkEvent *event,
+ guint16 flags,
+ guint32 timestamp)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (event->any.window));
+
+ GDK_NOTE(DND, g_message ("Motif DND top level leave: flags: %#4x time: %d",
+ flags, timestamp));
+
+ if ((display_x11->current_dest_drag != NULL) &&
+ (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
+ (timestamp >= display_x11->current_dest_drag->start_time))
+ {
+ event->dnd.type = GDK_DRAG_LEAVE;
+ /* Pass ownership of context to the event */
+ event->dnd.context = display_x11->current_dest_drag;
+
+ display_x11->current_dest_drag = NULL;
+
+ return GDK_FILTER_TRANSLATE;
+ }
+ else
+ return GDK_FILTER_REMOVE;
+}
+
+static GdkFilterReturn
+motif_motion (GdkEvent *event,
+ guint16 flags,
+ guint32 timestamp,
+ gint16 x_root,
+ gint16 y_root)
+{
+ GdkDragContextPrivateX11 *private;
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (event->any.window));
+
+ GDK_NOTE(DND, g_message ("Motif DND motion: flags: %#4x time: %d (%d, %d)",
+ flags, timestamp, x_root, y_root));
+
+ if ((display_x11->current_dest_drag != NULL) &&
+ (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
+ (timestamp >= display_x11->current_dest_drag->start_time))
+ {
+ private = PRIVATE_DATA (display_x11->current_dest_drag);
+
+ event->dnd.type = GDK_DRAG_MOTION;
+ event->dnd.context = display_x11->current_dest_drag;
+ g_object_ref (display_x11->current_dest_drag);
+
+ event->dnd.time = timestamp;
+
+ motif_dnd_translate_flags (display_x11->current_dest_drag, flags);
+
+ event->dnd.x_root = x_root;
+ event->dnd.y_root = y_root;
+
+ private->last_x = x_root;
+ private->last_y = y_root;
+
+ private->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
+
+ return GDK_FILTER_TRANSLATE;
+ }
+
+ return GDK_FILTER_REMOVE;
+}
+
+static GdkFilterReturn
+motif_operation_changed (GdkEvent *event,
+ guint16 flags,
+ guint32 timestamp)
+{
+ GdkDragContextPrivateX11 *private;
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (event->any.window));
+ GDK_NOTE(DND, g_message ("Motif DND operation changed: flags: %#4x time: %d",
+ flags, timestamp));
+
+ if ((display_x11->current_dest_drag != NULL) &&
+ (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
+ (timestamp >= display_x11->current_dest_drag->start_time))
+ {
+ event->dnd.type = GDK_DRAG_MOTION;
+ event->dnd.send_event = FALSE;
+ event->dnd.context = display_x11->current_dest_drag;
+ g_object_ref (display_x11->current_dest_drag);
+
+ event->dnd.time = timestamp;
+ private = PRIVATE_DATA (display_x11->current_dest_drag);
+
+ motif_dnd_translate_flags (display_x11->current_dest_drag, flags);
+
+ event->dnd.x_root = private->last_x;
+ event->dnd.y_root = private->last_y;
+
+ private->drag_status = GDK_DRAG_STATUS_ACTION_WAIT;
+
+ return GDK_FILTER_TRANSLATE;
+ }
+
+ return GDK_FILTER_REMOVE;
+}
+
+static GdkFilterReturn
+motif_drop_start (GdkEvent *event,
+ guint16 flags,
+ guint32 timestamp,
+ guint32 source_window,
+ guint32 atom,
+ gint16 x_root,
+ gint16 y_root)
+{
+ GdkDragContext *new_context;
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (event->any.window));
+
+ GDK_NOTE(DND, g_message ("Motif DND drop start: flags: %#4x time: %d (%d, %d) source_widow: %#4x atom: %d",
+ flags, timestamp, x_root, y_root, source_window, atom));
+
+ new_context = motif_drag_context_new (event->any.window, timestamp, source_window, atom);
+ if (!new_context)
+ return GDK_FILTER_REMOVE;
+
+ motif_dnd_translate_flags (new_context, flags);
+
+ event->dnd.type = GDK_DROP_START;
+ event->dnd.context = new_context;
+ event->dnd.time = timestamp;
+ event->dnd.x_root = x_root;
+ event->dnd.y_root = y_root;
+
+ gdk_x11_window_set_user_time (event->any.window, timestamp);
+
+ g_object_ref (new_context);
+ display_x11->current_dest_drag = new_context;
+
+ return GDK_FILTER_TRANSLATE;
+}
+
+static GdkFilterReturn
+motif_drag_status (GdkEvent *event,
+ guint16 flags,
+ guint32 timestamp)
+{
+ GdkDragContext *context;
+ GdkDisplay *display;
+
+ GDK_NOTE (DND,
+ g_message ("Motif status message: flags %x", flags));
+
+ display = gdk_window_get_display (event->any.window);
+ if (!display)
+ return GDK_FILTER_REMOVE;
+
+ context = gdk_drag_context_find (display, TRUE, GDK_DRAWABLE_XID (event->any.window), None);
+
+ if (context)
+ {
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+ if ((private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT) ||
+ (private->drag_status == GDK_DRAG_STATUS_ACTION_WAIT))
+ private->drag_status = GDK_DRAG_STATUS_DRAG;
+
+ event->dnd.type = GDK_DRAG_STATUS;
+ event->dnd.send_event = FALSE;
+ event->dnd.context = context;
+ g_object_ref (context);
+
+ event->dnd.time = timestamp;
+
+ if ((flags & 0x00f0) >> 4 == XmDROP_SITE_VALID)
+ {
+ switch (flags & 0x000f)
+ {
+ case XmDROP_NOOP:
+ context->action = 0;
+ break;
+ case XmDROP_MOVE:
+ context->action = GDK_ACTION_MOVE;
+ break;
+ case XmDROP_COPY:
+ context->action = GDK_ACTION_COPY;
+ break;
+ case XmDROP_LINK:
+ context->action = GDK_ACTION_LINK;
+ break;
+ }
+ }
+ else
+ context->action = 0;
+
+ return GDK_FILTER_TRANSLATE;
+ }
+ return GDK_FILTER_REMOVE;
+}
+
+static GdkFilterReturn
+motif_dnd_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *xevent = (XEvent *)xev;
+
+ guint8 reason;
+ guint16 flags;
+ guint32 timestamp;
+ guint32 source_window;
+ Atom atom;
+ gint16 x_root, y_root;
+ gboolean is_reply;
+
+ if (!event->any.window ||
+ gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
+ return GDK_FILTER_CONTINUE; /* Not for us */
+
+ /* First read some fields common to all Motif DND messages */
+
+ reason = MOTIF_UNPACK_BYTE (xevent, 0);
+ flags = MOTIF_UNPACK_SHORT (xevent, 1);
+ timestamp = MOTIF_UNPACK_LONG (xevent, 1);
+
+ is_reply = ((reason & 0x80) != 0);
+
+ switch (reason & 0x7f)
+ {
+ case XmTOP_LEVEL_ENTER:
+ source_window = MOTIF_UNPACK_LONG (xevent, 2);
+ atom = MOTIF_UNPACK_LONG (xevent, 3);
+ return motif_top_level_enter (event, flags, timestamp, source_window, atom);
+ case XmTOP_LEVEL_LEAVE:
+ return motif_top_level_leave (event, flags, timestamp);
+
+ case XmDRAG_MOTION:
+ x_root = MOTIF_UNPACK_SHORT (xevent, 4);
+ y_root = MOTIF_UNPACK_SHORT (xevent, 5);
+
+ if (!is_reply)
+ return motif_motion (event, flags, timestamp, x_root, y_root);
+ else
+ return motif_drag_status (event, flags, timestamp);
+
+ case XmDROP_SITE_ENTER:
+ return motif_drag_status (event, flags, timestamp);
+
+ case XmDROP_SITE_LEAVE:
+ return motif_drag_status (event,
+ XmNO_DROP_SITE << 8 | XmDROP_NOOP,
+ timestamp);
+ case XmDROP_START:
+ x_root = MOTIF_UNPACK_SHORT (xevent, 4);
+ y_root = MOTIF_UNPACK_SHORT (xevent, 5);
+ atom = MOTIF_UNPACK_LONG (xevent, 3);
+ source_window = MOTIF_UNPACK_LONG (xevent, 4);
+
+ if (!is_reply)
+ return motif_drop_start (event, flags, timestamp, source_window, atom, x_root, y_root);
+
+ break;
+ case XmOPERATION_CHANGED:
+ if (!is_reply)
+ return motif_operation_changed (event, flags, timestamp);
+ else
+ return motif_drag_status (event, flags, timestamp);
+
+ break;
+ /* To the best of my knowledge, these next two messages are
+ * not part of the protocol, though they are defined in
+ * the header files.
+ */
+ case XmDROP_FINISH:
+ case XmDRAG_DROP_FINISH:
+ break;
+ }
+
+ return GDK_FILTER_REMOVE;
+}
+
+/*************************************************************
+ ***************************** XDND **************************
+ *************************************************************/
+
+/* Utility functions */
+
+static struct {
+ const gchar *name;
+ GdkAtom atom;
+ GdkDragAction action;
+} xdnd_actions_table[] = {
+ { "XdndActionCopy", None, GDK_ACTION_COPY },
+ { "XdndActionMove", None, GDK_ACTION_MOVE },
+ { "XdndActionLink", None, GDK_ACTION_LINK },
+ { "XdndActionAsk", None, GDK_ACTION_ASK },
+ { "XdndActionPrivate", None, GDK_ACTION_COPY },
+ };
+
+static const gint xdnd_n_actions = sizeof(xdnd_actions_table) / sizeof(xdnd_actions_table[0]);
+static gboolean xdnd_actions_initialized = FALSE;
+
+static void
+xdnd_initialize_actions (void)
+{
+ gint i;
+
+ xdnd_actions_initialized = TRUE;
+ for (i=0; i < xdnd_n_actions; i++)
+ xdnd_actions_table[i].atom = gdk_atom_intern_static_string (xdnd_actions_table[i].name);
+}
+
+static GdkDragAction
+xdnd_action_from_atom (GdkDisplay *display,
+ Atom xatom)
+{
+ GdkAtom atom;
+ gint i;
+
+ if (xatom == None)
+ return 0;
+
+ atom = gdk_x11_xatom_to_atom_for_display (display, xatom);
+
+ if (!xdnd_actions_initialized)
+ xdnd_initialize_actions();
+
+ for (i=0; i<xdnd_n_actions; i++)
+ if (atom == xdnd_actions_table[i].atom)
+ return xdnd_actions_table[i].action;
+
+ return 0;
+}
+
+static Atom
+xdnd_action_to_atom (GdkDisplay *display,
+ GdkDragAction action)
+{
+ gint i;
+
+ if (!xdnd_actions_initialized)
+ xdnd_initialize_actions();
+
+ for (i=0; i<xdnd_n_actions; i++)
+ if (action == xdnd_actions_table[i].action)
+ return gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
+
+ return None;
+}
+
+/* Source side */
+
+static GdkFilterReturn
+xdnd_status_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data)
+{
+ GdkDisplay *display;
+ XEvent *xevent = (XEvent *)xev;
+ guint32 dest_window = xevent->xclient.data.l[0];
+ guint32 flags = xevent->xclient.data.l[1];
+ Atom action = xevent->xclient.data.l[4];
+ GdkDragContext *context;
+
+ if (!event->any.window ||
+ gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
+ return GDK_FILTER_CONTINUE; /* Not for us */
+
+ GDK_NOTE (DND,
+ g_message ("XdndStatus: dest_window: %#x action: %ld",
+ dest_window, action));
+
+ display = gdk_window_get_display (event->any.window);
+ context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
+
+ if (context)
+ {
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+ if (private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
+ private->drag_status = GDK_DRAG_STATUS_DRAG;
+
+ event->dnd.send_event = FALSE;
+ event->dnd.type = GDK_DRAG_STATUS;
+ event->dnd.context = context;
+ gdk_event_set_device (event, gdk_drag_context_get_device (context));
+ g_object_ref (context);
+
+ event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
+ if (!(action != 0) != !(flags & 1))
+ {
+ GDK_NOTE (DND,
+ g_warning ("Received status event with flags not corresponding to action!\n"));
+ action = 0;
+ }
+
+ context->action = xdnd_action_from_atom (display, action);
+
+ return GDK_FILTER_TRANSLATE;
+ }
+
+ return GDK_FILTER_REMOVE;
+}
+
+static GdkFilterReturn
+xdnd_finished_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data)
+{
+ GdkDisplay *display;
+ XEvent *xevent = (XEvent *)xev;
+ guint32 dest_window = xevent->xclient.data.l[0];
+ GdkDragContext *context;
+ GdkDragContextPrivateX11 *private;
+
+ if (!event->any.window ||
+ gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
+ return GDK_FILTER_CONTINUE; /* Not for us */
+
+ GDK_NOTE (DND,
+ g_message ("XdndFinished: dest_window: %#x", dest_window));
+
+ display = gdk_window_get_display (event->any.window);
+ context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
+
+ if (context)
+ {
+ private = PRIVATE_DATA (context);
+ if (private->version == 5)
+ private->drop_failed = xevent->xclient.data.l[1] == 0;
+
+ event->dnd.type = GDK_DROP_FINISHED;
+ event->dnd.context = context;
+ gdk_event_set_device (event, gdk_drag_context_get_device (context));
+ g_object_ref (context);
+
+ event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
+
+ return GDK_FILTER_TRANSLATE;
+ }
+
+ return GDK_FILTER_REMOVE;
+}
+
+static void
+xdnd_set_targets (GdkDragContext *context)
+{
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+ Atom *atomlist;
+ GList *tmp_list = context->targets;
+ gint i;
+ gint n_atoms = g_list_length (context->targets);
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
+
+ atomlist = g_new (Atom, n_atoms);
+ i = 0;
+ while (tmp_list)
+ {
+ atomlist[i] = gdk_x11_atom_to_xatom_for_display (display, GDK_POINTER_TO_ATOM (tmp_list->data));
+ tmp_list = tmp_list->next;
+ i++;
+ }
+
+ XChangeProperty (GDK_DRAWABLE_XDISPLAY (context->source_window),
+ GDK_DRAWABLE_XID (context->source_window),
+ gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
+ XA_ATOM, 32, PropModeReplace,
+ (guchar *)atomlist, n_atoms);
+
+ g_free (atomlist);
+
+ private->xdnd_targets_set = 1;
+}
+
+static void
+xdnd_set_actions (GdkDragContext *context)
+{
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+ Atom *atomlist;
+ gint i;
+ gint n_atoms;
+ guint actions;
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
+
+ if (!xdnd_actions_initialized)
+ xdnd_initialize_actions();
+
+ actions = context->actions;
+ n_atoms = 0;
+ for (i=0; i<xdnd_n_actions; i++)
+ {
+ if (actions & xdnd_actions_table[i].action)
+ {
+ actions &= ~xdnd_actions_table[i].action;
+ n_atoms++;
+ }
+ }
+
+ atomlist = g_new (Atom, n_atoms);
+
+ actions = context->actions;
+ n_atoms = 0;
+ for (i=0; i<xdnd_n_actions; i++)
+ {
+ if (actions & xdnd_actions_table[i].action)
+ {
+ actions &= ~xdnd_actions_table[i].action;
+ atomlist[n_atoms] = gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
+ n_atoms++;
+ }
+ }
+
+ XChangeProperty (GDK_DRAWABLE_XDISPLAY (context->source_window),
+ GDK_DRAWABLE_XID (context->source_window),
+ gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"),
+ XA_ATOM, 32, PropModeReplace,
+ (guchar *)atomlist, n_atoms);
+
+ g_free (atomlist);
+
+ private->xdnd_actions_set = TRUE;
+ private->xdnd_actions = context->actions;
+}
+
+static void
+send_client_message_async_cb (Window window,
+ gboolean success,
+ gpointer data)
+{
+ GdkDragContext *context = data;
+ GDK_NOTE (DND,
+ g_message ("Got async callback for #%lx, success = %d",
+ window, success));
+
+ /* On failure, we immediately continue with the protocol
+ * so we don't end up blocking for a timeout
+ */
+ if (!success &&
+ context->dest_window &&
+ window == GDK_WINDOW_XID (context->dest_window))
+ {
+ GdkEvent *temp_event;
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+
+ g_object_unref (context->dest_window);
+ context->dest_window = NULL;
+ context->action = 0;
+
+ private->drag_status = GDK_DRAG_STATUS_DRAG;
+
+ temp_event = gdk_event_new (GDK_DRAG_STATUS);
+ temp_event->dnd.window = g_object_ref (context->source_window);
+ temp_event->dnd.send_event = TRUE;
+ temp_event->dnd.context = g_object_ref (context);
+ temp_event->dnd.time = GDK_CURRENT_TIME;
+ gdk_event_set_device (temp_event, gdk_drag_context_get_device (context));
+
+ gdk_event_put (temp_event);
+
+ gdk_event_free (temp_event);
+ }
+
+ g_object_unref (context);
+}
+
+
+static GdkDisplay *
+gdk_drag_context_get_display (GdkDragContext *context)
+{
+ if (context->source_window)
+ return GDK_WINDOW_DISPLAY (context->source_window);
+ else if (context->dest_window)
+ return GDK_WINDOW_DISPLAY (context->dest_window);
+
+ g_assert_not_reached ();
+ return NULL;
+}
+
+static void
+send_client_message_async (GdkDragContext *context,
+ Window window,
+ gboolean propagate,
+ glong event_mask,
+ XClientMessageEvent *event_send)
+{
+ GdkDisplay *display = gdk_drag_context_get_display (context);
+
+ g_object_ref (context);
+
+ _gdk_x11_send_client_message_async (display, window,
+ propagate, event_mask, event_send,
+ send_client_message_async_cb, context);
+}
+
+static gboolean
+xdnd_send_xevent (GdkDragContext *context,
+ GdkWindow *window,
+ gboolean propagate,
+ XEvent *event_send)
+{
+ GdkDisplay *display = gdk_drag_context_get_display (context);
+ Window xwindow;
+ glong event_mask;
+
+ g_assert (event_send->xany.type == ClientMessage);
+
+ /* We short-circuit messages to ourselves */
+ if (gdk_window_get_window_type (window) != GDK_WINDOW_FOREIGN)
+ {
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (xdnd_filters); i++)
+ {
+ if (gdk_x11_get_xatom_by_name_for_display (display, xdnd_filters[i].atom_name) ==
+ event_send->xclient.message_type)
+ {
+ GdkEvent *temp_event;
+
+ temp_event = gdk_event_new (GDK_NOTHING);
+ temp_event->any.window = g_object_ref (window);
+
+ if ((*xdnd_filters[i].func) (event_send, temp_event, NULL) == GDK_FILTER_TRANSLATE)
+ {
+ gdk_event_put (temp_event);
+ gdk_event_free (temp_event);
+ }
+
+ return TRUE;
+ }
+ }
+ }
+
+ xwindow = GDK_WINDOW_XWINDOW (window);
+
+ if (_gdk_x11_display_is_root_window (display, xwindow))
+ event_mask = ButtonPressMask;
+ else
+ event_mask = 0;
+
+ send_client_message_async (context, xwindow, propagate, event_mask,
+ &event_send->xclient);
+
+ return TRUE;
+}
+
+static void
+xdnd_send_enter (GdkDragContext *context)
+{
+ XEvent xev;
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (context->dest_window);
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndEnter");
+ xev.xclient.format = 32;
+ xev.xclient.window = private->drop_xid ?
+ private->drop_xid :
+ GDK_DRAWABLE_XID (context->dest_window);
+ xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->source_window);
+ xev.xclient.data.l[1] = (private->version << 24); /* version */
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ GDK_NOTE(DND,
+ g_message ("Sending enter source window %#lx XDND protocol version %d\n",
+ GDK_DRAWABLE_XID (context->source_window), private->version));
+ if (g_list_length (context->targets) > 3)
+ {
+ if (!private->xdnd_targets_set)
+ xdnd_set_targets (context);
+ xev.xclient.data.l[1] |= 1;
+ }
+ else
+ {
+ GList *tmp_list = context->targets;
+ gint i = 2;
+
+ while (tmp_list)
+ {
+ xev.xclient.data.l[i] = gdk_x11_atom_to_xatom_for_display (display,
+ GDK_POINTER_TO_ATOM (tmp_list->data));
+ tmp_list = tmp_list->next;
+ i++;
+ }
+ }
+
+ if (!xdnd_send_xevent (context, context->dest_window,
+ FALSE, &xev))
+ {
+ GDK_NOTE (DND,
+ g_message ("Send event to %lx failed",
+ GDK_DRAWABLE_XID (context->dest_window)));
+ g_object_unref (context->dest_window);
+ context->dest_window = NULL;
+ }
+}
+
+static void
+xdnd_send_leave (GdkDragContext *context)
+{
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
+ XEvent xev;
+
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndLeave");
+ xev.xclient.format = 32;
+ xev.xclient.window = private->drop_xid ?
+ private->drop_xid :
+ GDK_DRAWABLE_XID (context->dest_window);
+ xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->source_window);
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ if (!xdnd_send_xevent (context, context->dest_window,
+ FALSE, &xev))
+ {
+ GDK_NOTE (DND,
+ g_message ("Send event to %lx failed",
+ GDK_DRAWABLE_XID (context->dest_window)));
+ g_object_unref (context->dest_window);
+ context->dest_window = NULL;
+ }
+}
+
+static void
+xdnd_send_drop (GdkDragContext *context, guint32 time)
+{
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndDrop");
+ xev.xclient.format = 32;
+ xev.xclient.window = private->drop_xid ?
+ private->drop_xid :
+ GDK_DRAWABLE_XID (context->dest_window);
+ xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->source_window);
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = time;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ if (!xdnd_send_xevent (context, context->dest_window,
+ FALSE, &xev))
+ {
+ GDK_NOTE (DND,
+ g_message ("Send event to %lx failed",
+ GDK_DRAWABLE_XID (context->dest_window)));
+ g_object_unref (context->dest_window);
+ context->dest_window = NULL;
+ }
+}
+
+static void
+xdnd_send_motion (GdkDragContext *context,
+ gint x_root,
+ gint y_root,
+ GdkDragAction action,
+ guint32 time)
+{
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndPosition");
+ xev.xclient.format = 32;
+ xev.xclient.window = private->drop_xid ?
+ private->drop_xid :
+ GDK_DRAWABLE_XID (context->dest_window);
+ xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->source_window);
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = (x_root << 16) | y_root;
+ xev.xclient.data.l[3] = time;
+ xev.xclient.data.l[4] = xdnd_action_to_atom (display, action);
+
+ if (!xdnd_send_xevent (context, context->dest_window,
+ FALSE, &xev))
+ {
+ GDK_NOTE (DND,
+ g_message ("Send event to %lx failed",
+ GDK_DRAWABLE_XID (context->dest_window)));
+ g_object_unref (context->dest_window);
+ context->dest_window = NULL;
+ }
+ private->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
+}
+
+static guint32
+xdnd_check_dest (GdkDisplay *display,
+ Window win,
+ guint *xdnd_version)
+{
+ gboolean retval = FALSE;
+ Atom type = None;
+ int format;
+ unsigned long nitems, after;
+ guchar *data;
+ Atom *version;
+ Window *proxy_data;
+ Window proxy;
+ Atom xdnd_proxy_atom = gdk_x11_get_xatom_by_name_for_display (display, "XdndProxy");
+ Atom xdnd_aware_atom = gdk_x11_get_xatom_by_name_for_display (display, "XdndAware");
+
+ proxy = None;
+
+ gdk_error_trap_push ();
+
+ if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), win,
+ xdnd_proxy_atom, 0,
+ 1, False, AnyPropertyType,
+ &type, &format, &nitems, &after,
+ &data) == Success)
+ {
+ if (type != None)
+ {
+ proxy_data = (Window *)data;
+
+ if ((format == 32) && (nitems == 1))
+ {
+ proxy = *proxy_data;
+ }
+ else
+ GDK_NOTE (DND,
+ g_warning ("Invalid XdndProxy "
+ "property on window %ld\n", win));
+
+ XFree (proxy_data);
+ }
+
+ if ((XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), proxy ? proxy : win,
+ xdnd_aware_atom, 0,
+ 1, False, AnyPropertyType,
+ &type, &format, &nitems, &after,
+ &data) == Success) &&
+ type != None)
+ {
+ version = (Atom *)data;
+
+ if ((format == 32) && (nitems == 1))
+ {
+ if (*version >= 3)
+ retval = TRUE;
+ if (xdnd_version)
+ *xdnd_version = *version;
+ }
+ else
+ GDK_NOTE (DND,
+ g_warning ("Invalid XdndAware "
+ "property on window %ld\n", win));
+
+ XFree (version);
+ }
+ }
+
+ gdk_error_trap_pop_ignored ();
+
+ return retval ? (proxy ? proxy : win) : None;
+}
+
+/* Target side */
+
+static void
+xdnd_read_actions (GdkDragContext *context)
+{
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
+ Atom type;
+ int format;
+ gulong nitems, after;
+ guchar *data;
+ Atom *atoms;
+
+ gint i;
+
+ PRIVATE_DATA (context)->xdnd_have_actions = FALSE;
+
+ if (gdk_window_get_window_type (context->source_window) == GDK_WINDOW_FOREIGN)
+ {
+ /* Get the XdndActionList, if set */
+
+ gdk_error_trap_push ();
+
+ if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_DRAWABLE_XID (context->source_window),
+ gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"),
+ 0, 65536,
+ False, XA_ATOM, &type, &format, &nitems,
+ &after, &data) == Success &&
+ type == XA_ATOM)
+ {
+ atoms = (Atom *)data;
+
+ context->actions = 0;
+
+ for (i=0; i<nitems; i++)
+ context->actions |= xdnd_action_from_atom (display, atoms[i]);
+
+ PRIVATE_DATA (context)->xdnd_have_actions = TRUE;
+
+#ifdef G_ENABLE_DEBUG
+ if (_gdk_debug_flags & GDK_DEBUG_DND)
+ {
+ GString *action_str = g_string_new (NULL);
+ if (context->actions & GDK_ACTION_MOVE)
+ g_string_append(action_str, "MOVE ");
+ if (context->actions & GDK_ACTION_COPY)
+ g_string_append(action_str, "COPY ");
+ if (context->actions & GDK_ACTION_LINK)
+ g_string_append(action_str, "LINK ");
+ if (context->actions & GDK_ACTION_ASK)
+ g_string_append(action_str, "ASK ");
+
+ g_message("Xdnd actions = %s", action_str->str);
+ g_string_free (action_str, TRUE);
+ }
+#endif /* G_ENABLE_DEBUG */
+
+ }
+
+ if (data)
+ XFree (data);
+
+ gdk_error_trap_pop_ignored ();
+ }
+ else
+ {
+ /* Local drag
+ */
+ GdkDragContext *source_context;
+
+ source_context = gdk_drag_context_find (display, TRUE,
+ GDK_DRAWABLE_XID (context->source_window),
+ GDK_DRAWABLE_XID (context->dest_window));
+
+ if (source_context)
+ {
+ context->actions = source_context->actions;
+ PRIVATE_DATA (context)->xdnd_have_actions = TRUE;
+ }
+ }
+}
+
+/* We have to make sure that the XdndActionList we keep internally
+ * is up to date with the XdndActionList on the source window
+ * because we get no notification, because Xdnd wasn't meant
+ * to continually send actions. So we select on PropertyChangeMask
+ * and add this filter.
+ */
+static GdkFilterReturn
+xdnd_source_window_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer cb_data)
+{
+ XEvent *xevent = (XEvent *)xev;
+ GdkDragContext *context = cb_data;
+ GdkDisplay *display = GDK_WINDOW_DISPLAY(event->any.window);
+
+ if ((xevent->xany.type == PropertyNotify) &&
+ (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList")))
+ {
+ xdnd_read_actions (context);
+
+ return GDK_FILTER_REMOVE;
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static void
+xdnd_manage_source_filter (GdkDragContext *context,
+ GdkWindow *window,
+ gboolean add_filter)
+{
+ if (!GDK_WINDOW_DESTROYED (window) &&
+ gdk_window_get_window_type (window) == GDK_WINDOW_FOREIGN)
+ {
+ gdk_error_trap_push ();
+
+ if (add_filter)
+ {
+ gdk_window_set_events (window,
+ gdk_window_get_events (window) |
+ GDK_PROPERTY_CHANGE_MASK);
+ gdk_window_add_filter (window, xdnd_source_window_filter, context);
+ }
+ else
+ {
+ gdk_window_remove_filter (window,
+ xdnd_source_window_filter,
+ context);
+ /* Should we remove the GDK_PROPERTY_NOTIFY mask?
+ * but we might want it for other reasons. (Like
+ * INCR selection transactions).
+ */
+ }
+
+ gdk_error_trap_pop_ignored ();
+ }
+}
+
+static void
+base_precache_atoms (GdkDisplay *display)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+ if (!display_x11->base_dnd_atoms_precached)
+ {
+ static const char *const precache_atoms[] = {
+ "ENLIGHTENMENT_DESKTOP",
+ "WM_STATE",
+ "XdndAware",
+ "XdndProxy",
+ "_MOTIF_DRAG_RECEIVER_INFO"
+ };
+
+ _gdk_x11_precache_atoms (display,
+ precache_atoms, G_N_ELEMENTS (precache_atoms));
+
+ display_x11->base_dnd_atoms_precached = TRUE;
+ }
+}
+
+static void
+xdnd_precache_atoms (GdkDisplay *display)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+ if (!display_x11->xdnd_atoms_precached)
+ {
+ static const char *const precache_atoms[] = {
+ "XdndActionAsk",
+ "XdndActionCopy",
+ "XdndActionLink",
+ "XdndActionList",
+ "XdndActionMove",
+ "XdndActionPrivate",
+ "XdndDrop",
+ "XdndEnter",
+ "XdndFinished",
+ "XdndLeave",
+ "XdndPosition",
+ "XdndSelection",
+ "XdndStatus",
+ "XdndTypeList"
+ };
+
+ _gdk_x11_precache_atoms (display,
+ precache_atoms, G_N_ELEMENTS (precache_atoms));
+
+ display_x11->xdnd_atoms_precached = TRUE;
+ }
+}
+
+static GdkFilterReturn
+xdnd_enter_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer cb_data)
+{
+ GdkDeviceManager *device_manager;
+ GdkDisplay *display;
+ GdkDisplayX11 *display_x11;
+ XEvent *xevent = (XEvent *)xev;
+ GdkDragContext *new_context;
+ gint i;
+
+ Atom type;
+ int format;
+ gulong nitems, after;
+ guchar *data;
+ Atom *atoms;
+
+ guint32 source_window;
+ gboolean get_types;
+ gint version;
+
+ if (!event->any.window ||
+ gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
+ return GDK_FILTER_CONTINUE; /* Not for us */
+
+ source_window = xevent->xclient.data.l[0];
+ get_types = ((xevent->xclient.data.l[1] & 1) != 0);
+ version = (xevent->xclient.data.l[1] & 0xff000000) >> 24;
+
+ display = GDK_WINDOW_DISPLAY (event->any.window);
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ xdnd_precache_atoms (display);
+
+ GDK_NOTE (DND,
+ g_message ("XdndEnter: source_window: %#x, version: %#x",
+ source_window, version));
+
+ if (version < 3)
+ {
+ /* Old source ignore */
+ GDK_NOTE (DND, g_message ("Ignored old XdndEnter message"));
+ return GDK_FILTER_REMOVE;
+ }
+
+ if (display_x11->current_dest_drag != NULL)
+ {
+ g_object_unref (display_x11->current_dest_drag);
+ display_x11->current_dest_drag = NULL;
+ }
+
+ new_context = gdk_drag_context_new ();
+ new_context->protocol = GDK_DRAG_PROTO_XDND;
+ PRIVATE_DATA(new_context)->version = version;
+
+ /* FIXME: Should extend DnD protocol to have device info */
+ device_manager = gdk_display_get_device_manager (display);
+ gdk_drag_context_set_device (new_context, gdk_device_manager_get_client_pointer (device_manager));
+
+ new_context->source_window = gdk_window_lookup_for_display (display, source_window);
+ if (new_context->source_window)
+ g_object_ref (new_context->source_window);
+ else
+ {
+ new_context->source_window = gdk_window_foreign_new_for_display (display, source_window);
+ if (!new_context->source_window)
+ {
+ g_object_unref (new_context);
+ return GDK_FILTER_REMOVE;
+ }
+ }
+ new_context->dest_window = event->any.window;
+ g_object_ref (new_context->dest_window);
+
+ new_context->targets = NULL;
+ if (get_types)
+ {
+ gdk_error_trap_push ();
+ XGetWindowProperty (GDK_DRAWABLE_XDISPLAY (event->any.window),
+ source_window,
+ gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
+ 0, 65536,
+ False, XA_ATOM, &type, &format, &nitems,
+ &after, &data);
+
+ if (gdk_error_trap_pop () || (format != 32) || (type != XA_ATOM))
+ {
+ g_object_unref (new_context);
+
+ if (data)
+ XFree (data);
+
+ return GDK_FILTER_REMOVE;
+ }
+
+ atoms = (Atom *)data;
+
+ for (i=0; i<nitems; i++)
+ new_context->targets =
+ g_list_append (new_context->targets,
+ GDK_ATOM_TO_POINTER (gdk_x11_xatom_to_atom_for_display (display,
+ atoms[i])));
+
+ XFree(atoms);
+ }
+ else
+ {
+ for (i=0; i<3; i++)
+ if (xevent->xclient.data.l[2+i])
+ new_context->targets =
+ g_list_append (new_context->targets,
+ GDK_ATOM_TO_POINTER (gdk_x11_xatom_to_atom_for_display (display,
+ xevent->xclient.data.l[2+i])));
+ }
+
+#ifdef G_ENABLE_DEBUG
+ if (_gdk_debug_flags & GDK_DEBUG_DND)
+ print_target_list (new_context->targets);
+#endif /* G_ENABLE_DEBUG */
+
+ xdnd_manage_source_filter (new_context, new_context->source_window, TRUE);
+ xdnd_read_actions (new_context);
+
+ event->dnd.type = GDK_DRAG_ENTER;
+ event->dnd.context = new_context;
+ gdk_event_set_device (event, gdk_drag_context_get_device (new_context));
+ g_object_ref (new_context);
+
+ display_x11->current_dest_drag = new_context;
+
+ return GDK_FILTER_TRANSLATE;
+}
+
+static GdkFilterReturn
+xdnd_leave_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *xevent = (XEvent *)xev;
+ guint32 source_window = xevent->xclient.data.l[0];
+ GdkDisplay *display;
+ GdkDisplayX11 *display_x11;
+
+ if (!event->any.window ||
+ gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
+ return GDK_FILTER_CONTINUE; /* Not for us */
+
+ GDK_NOTE (DND,
+ g_message ("XdndLeave: source_window: %#x",
+ source_window));
+
+ display = GDK_WINDOW_DISPLAY (event->any.window);
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ xdnd_precache_atoms (display);
+
+ if ((display_x11->current_dest_drag != NULL) &&
+ (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
+ (GDK_DRAWABLE_XID (display_x11->current_dest_drag->source_window) == source_window))
+ {
+ event->dnd.type = GDK_DRAG_LEAVE;
+ /* Pass ownership of context to the event */
+ event->dnd.context = display_x11->current_dest_drag;
+ gdk_event_set_device (event, gdk_drag_context_get_device (event->dnd.context));
+
+ display_x11->current_dest_drag = NULL;
+
+ return GDK_FILTER_TRANSLATE;
+ }
+ else
+ return GDK_FILTER_REMOVE;
+}
+
+static GdkFilterReturn
+xdnd_position_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *xevent = (XEvent *)xev;
+ guint32 source_window = xevent->xclient.data.l[0];
+ gint16 x_root = xevent->xclient.data.l[2] >> 16;
+ gint16 y_root = xevent->xclient.data.l[2] & 0xffff;
+ guint32 time = xevent->xclient.data.l[3];
+ Atom action = xevent->xclient.data.l[4];
+
+ GdkDisplay *display;
+ GdkDisplayX11 *display_x11;
+
+ if (!event->any.window ||
+ gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
+ return GDK_FILTER_CONTINUE; /* Not for us */
+
+ GDK_NOTE (DND,
+ g_message ("XdndPosition: source_window: %#x position: (%d, %d) time: %d action: %ld",
+ source_window, x_root, y_root, time, action));
+
+ display = GDK_WINDOW_DISPLAY (event->any.window);
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ xdnd_precache_atoms (display);
+
+ if ((display_x11->current_dest_drag != NULL) &&
+ (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
+ (GDK_DRAWABLE_XID (display_x11->current_dest_drag->source_window) == source_window))
+ {
+ event->dnd.type = GDK_DRAG_MOTION;
+ event->dnd.context = display_x11->current_dest_drag;
+ gdk_event_set_device (event, gdk_drag_context_get_device (event->dnd.context));
+ g_object_ref (display_x11->current_dest_drag);
+
+ event->dnd.time = time;
+
+ display_x11->current_dest_drag->suggested_action = xdnd_action_from_atom (display, action);
+
+ if (!(PRIVATE_DATA (display_x11->current_dest_drag))->xdnd_have_actions)
+ display_x11->current_dest_drag->actions = display_x11->current_dest_drag->suggested_action;
+
+ event->dnd.x_root = x_root;
+ event->dnd.y_root = y_root;
+
+ (PRIVATE_DATA (display_x11->current_dest_drag))->last_x = x_root;
+ (PRIVATE_DATA (display_x11->current_dest_drag))->last_y = y_root;
+
+ return GDK_FILTER_TRANSLATE;
+ }
+
+ return GDK_FILTER_REMOVE;
+}
+
+static GdkFilterReturn
+xdnd_drop_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *xevent = (XEvent *)xev;
+ guint32 source_window = xevent->xclient.data.l[0];
+ guint32 time = xevent->xclient.data.l[2];
+ GdkDisplay *display;
+ GdkDisplayX11 *display_x11;
+
+ if (!event->any.window ||
+ gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
+ return GDK_FILTER_CONTINUE; /* Not for us */
+
+ GDK_NOTE (DND,
+ g_message ("XdndDrop: source_window: %#x time: %d",
+ source_window, time));
+
+ display = GDK_WINDOW_DISPLAY (event->any.window);
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ xdnd_precache_atoms (display);
+
+ if ((display_x11->current_dest_drag != NULL) &&
+ (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
+ (GDK_DRAWABLE_XID (display_x11->current_dest_drag->source_window) == source_window))
+ {
+ GdkDragContextPrivateX11 *private;
+ private = PRIVATE_DATA (display_x11->current_dest_drag);
+
+ event->dnd.type = GDK_DROP_START;
+
+ event->dnd.context = display_x11->current_dest_drag;
+ gdk_event_set_device (event, gdk_drag_context_get_device (event->dnd.context));
+ g_object_ref (display_x11->current_dest_drag);
+
+ event->dnd.time = time;
+ event->dnd.x_root = private->last_x;
+ event->dnd.y_root = private->last_y;
+
+ gdk_x11_window_set_user_time (event->any.window, time);
+
+ return GDK_FILTER_TRANSLATE;
+ }
+
+ return GDK_FILTER_REMOVE;
+}
+
+/*************************************************************
+ ************************** Public API ***********************
+ *************************************************************/
+void
+_gdk_dnd_init (GdkDisplay *display)
+{
+ int i;
+ init_byte_order ();
+
+ gdk_display_add_client_message_filter (
+ display,
+ gdk_atom_intern_static_string ("_MOTIF_DRAG_AND_DROP_MESSAGE"),
+ motif_dnd_filter, NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (xdnd_filters); i++)
+ {
+ gdk_display_add_client_message_filter (
+ display,
+ gdk_atom_intern_static_string (xdnd_filters[i].atom_name),
+ xdnd_filters[i].func, NULL);
+ }
+}
+
+/* Source side */
+
+static void
+gdk_drag_do_leave (GdkDragContext *context, guint32 time)
+{
+ if (context->dest_window)
+ {
+ switch (context->protocol)
+ {
+ case GDK_DRAG_PROTO_MOTIF:
+ motif_send_leave (context, time);
+ break;
+ case GDK_DRAG_PROTO_XDND:
+ xdnd_send_leave (context);
+ break;
+ case GDK_DRAG_PROTO_ROOTWIN:
+ case GDK_DRAG_PROTO_NONE:
+ default:
+ break;
+ }
+
+ g_object_unref (context->dest_window);
+ context->dest_window = NULL;
+ }
+}
+
+/**
+ * gdk_drag_begin:
+ * @window: the source window for this drag.
+ * @targets: (transfer none) (element-type GdkAtom): the offered targets,
+ * as list of #GdkAtom<!-- -->s
+ *
+ * Starts a drag and creates a new drag context for it.
+ *
+ * This function is called by the drag source.
+ *
+ * Return value: a newly created #GdkDragContext.
+ **/
+GdkDragContext *
+gdk_drag_begin (GdkWindow *window,
+ GList *targets)
+{
+ GdkDragContext *new_context;
+ GdkDisplay *display;
+ GdkDevice *device;
+ GdkDeviceManager *device_manager;
+
+ g_return_val_if_fail (window != NULL, NULL);
+ g_return_val_if_fail (GDK_WINDOW_IS_X11 (window), NULL);
+
+ new_context = gdk_drag_context_new ();
+ new_context->is_source = TRUE;
+ new_context->source_window = window;
+ g_object_ref (window);
+
+ new_context->targets = g_list_copy (targets);
+ precache_target_list (new_context);
+
+ new_context->actions = 0;
+
+ display = gdk_window_get_display (window);
+ device_manager = gdk_display_get_device_manager (display);
+ device = gdk_device_manager_get_client_pointer (device_manager);
+ gdk_drag_context_set_device (new_context, device);
+
+ return new_context;
+}
+
+static GdkNativeWindow
+_gdk_drag_get_protocol_for_display (GdkDisplay *display,
+ GdkNativeWindow xid,
+ GdkDragProtocol *protocol,
+ guint *version)
+
+{
+ GdkWindow *window;
+ GdkNativeWindow retval;
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), None);
+
+ base_precache_atoms (display);
+
+ /* Check for a local drag
+ */
+ window = gdk_window_lookup_for_display (display, xid);
+ if (window &&
+ gdk_window_get_window_type (window) != GDK_WINDOW_FOREIGN)
+ {
+ if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
+ {
+ *protocol = GDK_DRAG_PROTO_XDND;
+ *version = 5;
+ xdnd_precache_atoms (display);
+ GDK_NOTE (DND, g_message ("Entering local Xdnd window %#x\n", xid));
+ return xid;
+ }
+ else if (_gdk_x11_display_is_root_window (display, (Window) xid))
+ {
+ *protocol = GDK_DRAG_PROTO_ROOTWIN;
+ GDK_NOTE (DND, g_message ("Entering root window\n"));
+ return xid;
+ }
+ }
+ else if ((retval = xdnd_check_dest (display, xid, version)))
+ {
+ *protocol = GDK_DRAG_PROTO_XDND;
+ xdnd_precache_atoms (display);
+ GDK_NOTE (DND, g_message ("Entering Xdnd window %#x\n", xid));
+ return retval;
+ }
+ else if ((retval = motif_check_dest (display, xid)))
+ {
+ *protocol = GDK_DRAG_PROTO_MOTIF;
+ GDK_NOTE (DND, g_message ("Entering motif window %#x\n", xid));
+ return retval;
+ }
+ else
+ {
+ /* Check if this is a root window */
+
+ gboolean rootwin = FALSE;
+ Atom type = None;
+ int format;
+ unsigned long nitems, after;
+ unsigned char *data;
+
+ if (_gdk_x11_display_is_root_window (display, (Window) xid))
+ rootwin = TRUE;
+
+ gdk_error_trap_push ();
+
+ if (!rootwin)
+ {
+ if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), xid,
+ gdk_x11_get_xatom_by_name_for_display (display, "ENLIGHTENMENT_DESKTOP"),
+ 0, 0, False, AnyPropertyType,
+ &type, &format, &nitems, &after, &data) == Success &&
+ type != None)
+ {
+ XFree (data);
+ rootwin = TRUE;
+ }
+ }
+
+ /* Until I find out what window manager the next one is for,
+ * I'm leaving it commented out. It's supported in the
+ * xscreensaver sources, though.
+ */
+#if 0
+ if (!rootwin)
+ {
+ if (XGetWindowProperty (gdk_display, win,
+ gdk_x11_get_xatom_by_name ("__SWM_VROOT"),
+ 0, 0, False, AnyPropertyType,
+ &type, &format, &nitems, &data) &&
+ type != None)
+ {
+ XFree (data);
+ rootwin = TRUE;
+ }
+ }
+#endif
+
+ gdk_error_trap_pop_ignored ();
+
+ if (rootwin)
+ {
+ GDK_NOTE (DND, g_message ("Entering root window\n"));
+ *protocol = GDK_DRAG_PROTO_ROOTWIN;
+ return xid;
+ }
+ }
+
+ *protocol = GDK_DRAG_PROTO_NONE;
+
+ return 0; /* a.k.a. None */
+}
+
+/**
+ * gdk_drag_get_protocol_for_display:
+ * @display: the #GdkDisplay where the destination window resides
+ * @xid: the windowing system id of the destination window.
+ * @protocol: location where the supported DND protocol is returned.
+ * @returns: the windowing system id of the window where the drop should happen. This
+ * may be @xid or the id of a proxy window, or zero if @xid doesn't
+ * support Drag and Drop.
+ *
+ * Finds out the DND protocol supported by a window.
+ *
+ * Since: 2.2
+ */
+GdkNativeWindow
+gdk_drag_get_protocol_for_display (GdkDisplay *display,
+ GdkNativeWindow xid,
+ GdkDragProtocol *protocol)
+{
+ return _gdk_drag_get_protocol_for_display (display, xid, protocol, NULL);
+}
+
+static GdkWindowCache *
+drag_context_find_window_cache (GdkDragContext *context,
+ GdkScreen *screen)
+{
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+ GSList *tmp_list;
+ GdkWindowCache *cache;
+
+ for (tmp_list = private->window_caches; tmp_list; tmp_list = tmp_list->next)
+ {
+ cache = tmp_list->data;
+ if (cache->screen == screen)
+ return cache;
+ }
+
+ cache = gdk_window_cache_new (screen);
+ private->window_caches = g_slist_prepend (private->window_caches, cache);
+
+ return cache;
+}
+
+/**
+ * gdk_drag_find_window_for_screen:
+ * @context: a #GdkDragContext
+ * @drag_window: a window which may be at the pointer position, but
+ * should be ignored, since it is put up by the drag source as an icon.
+ * @screen: the screen where the destination window is sought.
+ * @x_root: the x position of the pointer in root coordinates.
+ * @y_root: the y position of the pointer in root coordinates.
+ * @dest_window: (out): location to store the destination window in.
+ * @protocol: (out): location to store the DND protocol in.
+ *
+ * Finds the destination window and DND protocol to use at the
+ * given pointer position.
+ *
+ * This function is called by the drag source to obtain the
+ * @dest_window and @protocol parameters for gdk_drag_motion().
+ *
+ * Since: 2.2
+ **/
+void
+gdk_drag_find_window_for_screen (GdkDragContext *context,
+ GdkWindow *drag_window,
+ GdkScreen *screen,
+ gint x_root,
+ gint y_root,
+ GdkWindow **dest_window,
+ GdkDragProtocol *protocol)
+{
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+ GdkWindowCache *window_cache;
+ GdkDisplay *display;
+ Window dest;
+
+ g_return_if_fail (context != NULL);
+
+ display = GDK_WINDOW_DISPLAY (context->source_window);
+
+ window_cache = drag_context_find_window_cache (context, screen);
+
+ dest = get_client_window_at_coords (window_cache,
+ drag_window && GDK_WINDOW_IS_X11 (drag_window) ?
+ GDK_DRAWABLE_XID (drag_window) : None,
+ x_root, y_root);
+
+ if (private->dest_xid != dest)
+ {
+ Window recipient;
+ private->dest_xid = dest;
+
+ /* Check if new destination accepts drags, and which protocol */
+
+ /* There is some ugliness here. We actually need to pass
+ * _three_ pieces of information to drag_motion - dest_window,
+ * protocol, and the XID of the unproxied window. The first
+ * two are passed explicitely, the third implicitly through
+ * protocol->dest_xid.
+ */
+ if ((recipient = _gdk_drag_get_protocol_for_display (display, dest,
+ protocol, &private->version)))
+ {
+ *dest_window = gdk_window_lookup_for_display (display, recipient);
+ if (*dest_window)
+ g_object_ref (*dest_window);
+ else
+ *dest_window = gdk_window_foreign_new_for_display (display, recipient);
+ }
+ else
+ *dest_window = NULL;
+ }
+ else
+ {
+ *dest_window = context->dest_window;
+ if (*dest_window)
+ g_object_ref (*dest_window);
+ *protocol = context->protocol;
+ }
+}
+
+/**
+ * gdk_drag_motion:
+ * @context: a #GdkDragContext.
+ * @dest_window: the new destination window, obtained by
+ * gdk_drag_find_window().
+ * @protocol: the DND protocol in use, obtained by gdk_drag_find_window().
+ * @x_root: the x position of the pointer in root coordinates.
+ * @y_root: the y position of the pointer in root coordinates.
+ * @suggested_action: the suggested action.
+ * @possible_actions: the possible actions.
+ * @time_: the timestamp for this operation.
+ *
+ * Updates the drag context when the pointer moves or the
+ * set of actions changes.
+ *
+ * This function is called by the drag source.
+ *
+ * Return value: FIXME
+ **/
+gboolean
+gdk_drag_motion (GdkDragContext *context,
+ GdkWindow *dest_window,
+ GdkDragProtocol protocol,
+ gint x_root,
+ gint y_root,
+ GdkDragAction suggested_action,
+ GdkDragAction possible_actions,
+ guint32 time)
+{
+ GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
+
+ g_return_val_if_fail (context != NULL, FALSE);
+ g_return_val_if_fail (dest_window == NULL || GDK_WINDOW_IS_X11 (dest_window), FALSE);
+
+ private->old_actions = context->actions;
+ context->actions = possible_actions;
+
+ if (private->old_actions != possible_actions)
+ private->xdnd_actions_set = FALSE;
+
+ if (protocol == GDK_DRAG_PROTO_XDND && private->version == 0)
+ {
+ /* This ugly hack is necessary since GTK+ doesn't know about
+ * the XDND protocol version, and in particular doesn't know
+ * that gdk_drag_find_window_for_screen() has the side-effect
+ * of setting private->version, and therefore sometimes call
+ * gdk_drag_motion() without a prior call to
+ * gdk_drag_find_window_for_screen(). This happens, e.g.
+ * when GTK+ is proxying DND events to embedded windows.
+ */
+ if (dest_window)
+ {
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (dest_window);
+
+ xdnd_check_dest (display,
+ GDK_DRAWABLE_XID (dest_window),
+ &private->version);
+ }
+ }
+
+ /* When we have a Xdnd target, make sure our XdndActionList
+ * matches the current actions;
+ */
+ if (protocol == GDK_DRAG_PROTO_XDND && !private->xdnd_actions_set)
+ {
+ if (dest_window)
+ {
+ if (gdk_window_get_window_type (dest_window) == GDK_WINDOW_FOREIGN)
+ xdnd_set_actions (context);
+ else if (context->dest_window == dest_window)
+ {
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (dest_window);
+ GdkDragContext *dest_context;
+
+ dest_context = gdk_drag_context_find (display, FALSE,
+ GDK_DRAWABLE_XID (context->source_window),
+ GDK_DRAWABLE_XID (dest_window));
+
+ if (dest_context)
+ {
+ dest_context->actions = context->actions;
+ PRIVATE_DATA (dest_context)->xdnd_have_actions = TRUE;
+ }
+ }
+ }
+ }
+
+ if (context->dest_window != dest_window)
+ {
+ GdkEvent *temp_event;
+
+ /* Send a leave to the last destination */
+ gdk_drag_do_leave (context, time);
+ private->drag_status = GDK_DRAG_STATUS_DRAG;
+
+ /* Check if new destination accepts drags, and which protocol */
+
+ if (dest_window)
+ {
+ context->dest_window = dest_window;
+ private->drop_xid = private->dest_xid;
+ g_object_ref (context->dest_window);
+ context->protocol = protocol;
+
+ switch (protocol)
+ {
+ case GDK_DRAG_PROTO_MOTIF:
+ motif_send_enter (context, time);
+ break;
+
+ case GDK_DRAG_PROTO_XDND:
+ xdnd_send_enter (context);
+ break;
+
+ case GDK_DRAG_PROTO_ROOTWIN:
+ case GDK_DRAG_PROTO_NONE:
+ default:
+ break;
+ }
+ private->old_action = suggested_action;
+ context->suggested_action = suggested_action;
+ private->old_actions = possible_actions;
+ }
+ else
+ {
+ context->dest_window = NULL;
+ private->drop_xid = None;
+ context->action = 0;
+ }
+
+ /* Push a status event, to let the client know that
+ * the drag changed
+ */
+ temp_event = gdk_event_new (GDK_DRAG_STATUS);
+ temp_event->dnd.window = g_object_ref (context->source_window);
+ /* We use this to signal a synthetic status. Perhaps
+ * we should use an extra field...
+ */
+ temp_event->dnd.send_event = TRUE;
+
+ temp_event->dnd.context = g_object_ref (context);
+ temp_event->dnd.time = time;
+ gdk_event_set_device (temp_event, gdk_drag_context_get_device (context));
+
+ gdk_event_put (temp_event);
+ gdk_event_free (temp_event);
+ }
+ else
+ {
+ private->old_action = context->suggested_action;
+ context->suggested_action = suggested_action;
+ }
+
+ /* Send a drag-motion event */
+
+ private->last_x = x_root;
+ private->last_y = y_root;
+
+ if (context->dest_window)
+ {
+ if (private->drag_status == GDK_DRAG_STATUS_DRAG)
+ {
+ switch (context->protocol)
+ {
+ case GDK_DRAG_PROTO_MOTIF:
+ motif_send_motion (context, x_root, y_root, suggested_action, time);
+ break;
+
+ case GDK_DRAG_PROTO_XDND:
+ xdnd_send_motion (context, x_root, y_root, suggested_action, time);
+ break;
+
+ case GDK_DRAG_PROTO_ROOTWIN:
+ {
+ GdkEvent *temp_event;
+ /* GTK+ traditionally has used application/x-rootwin-drop,
+ * but the XDND spec specifies x-rootwindow-drop.
+ */
+ GdkAtom target1 = gdk_atom_intern_static_string ("application/x-rootwindow-drop");
+ GdkAtom target2 = gdk_atom_intern_static_string ("application/x-rootwin-drop");
+
+ if (g_list_find (context->targets,
+ GDK_ATOM_TO_POINTER (target1)) ||
+ g_list_find (context->targets,
+ GDK_ATOM_TO_POINTER (target2)))
+ context->action = context->suggested_action;
+ else
+ context->action = 0;
+
+ temp_event = gdk_event_new (GDK_DRAG_STATUS);
+ temp_event->dnd.window = g_object_ref (context->source_window);
+ temp_event->dnd.send_event = FALSE;
+ temp_event->dnd.context = g_object_ref (context);
+ temp_event->dnd.time = time;
+ gdk_event_set_device (temp_event, gdk_drag_context_get_device (context));
+
+ gdk_event_put (temp_event);
+ gdk_event_free (temp_event);
+ }
+ break;
+ case GDK_DRAG_PROTO_NONE:
+ g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gdk_drag_drop:
+ * @context: a #GdkDragContext.
+ * @time_: the timestamp for this operation.
+ *
+ * Drops on the current destination.
+ *
+ * This function is called by the drag source.
+ **/
+void
+gdk_drag_drop (GdkDragContext *context,
+ guint32 time)
+{
+ g_return_if_fail (context != NULL);
+
+ if (context->dest_window)
+ {
+ switch (context->protocol)
+ {
+ case GDK_DRAG_PROTO_MOTIF:
+ motif_send_leave (context, time);
+ motif_send_drop (context, time);
+ break;
+
+ case GDK_DRAG_PROTO_XDND:
+ xdnd_send_drop (context, time);
+ break;
+
+ case GDK_DRAG_PROTO_ROOTWIN:
+ g_warning ("Drops for GDK_DRAG_PROTO_ROOTWIN must be handled internally");
+ break;
+ case GDK_DRAG_PROTO_NONE:
+ g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_drop()");
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/**
+ * gdk_drag_abort:
+ * @context: a #GdkDragContext.
+ * @time_: the timestamp for this operation.
+ *
+ * Aborts a drag without dropping.
+ *
+ * This function is called by the drag source.
+ **/
+void
+gdk_drag_abort (GdkDragContext *context,
+ guint32 time)
+{
+ g_return_if_fail (context != NULL);
+
+ gdk_drag_do_leave (context, time);
+}
+
+/* Destination side */
+
+/**
+ * gdk_drag_status:
+ * @context: a #GdkDragContext.
+ * @action: the selected action which will be taken when a drop happens,
+ * or 0 to indicate that a drop will not be accepted.
+ * @time_: the timestamp for this operation.
+ *
+ * Selects one of the actions offered by the drag source.
+ *
+ * This function is called by the drag destination in response to
+ * gdk_drag_motion() called by the drag source.
+ **/
+void
+gdk_drag_status (GdkDragContext *context,
+ GdkDragAction action,
+ guint32 time)
+{
+ GdkDragContextPrivateX11 *private;
+ XEvent xev;
+ GdkDisplay *display;
+
+ g_return_if_fail (context != NULL);
+
+ private = PRIVATE_DATA (context);
+ display = GDK_WINDOW_DISPLAY (context->source_window);
+
+ context->action = action;
+
+ if (context->protocol == GDK_DRAG_PROTO_MOTIF)
+ {
+ gboolean need_coords = FALSE;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display,
+ "_MOTIF_DRAG_AND_DROP_MESSAGE");
+ xev.xclient.format = 8;
+ xev.xclient.window = GDK_DRAWABLE_XID (context->source_window);
+
+ if (private->drag_status == GDK_DRAG_STATUS_ACTION_WAIT)
+ {
+ MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED | 0x80;
+ }
+ else
+ {
+ if ((action != 0) != (private->old_action != 0))
+ {
+ if (action != 0)
+ {
+ MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_ENTER | 0x80;
+ need_coords = TRUE;
+ }
+ else
+ MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_LEAVE | 0x80;
+ }
+ else
+ {
+ MOTIF_XCLIENT_BYTE (&xev, 0) = XmDRAG_MOTION | 0x80;
+ need_coords = TRUE;
+ }
+ }
+
+ MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
+
+ switch (action)
+ {
+ case GDK_ACTION_MOVE:
+ MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_MOVE;
+ break;
+ case GDK_ACTION_COPY:
+ MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY;
+ break;
+ case GDK_ACTION_LINK:
+ MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_LINK;
+ break;
+ default:
+ MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP;
+ break;
+ }
+
+ if (action)
+ MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmDROP_SITE_VALID << 4);
+ else
+ MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmNO_DROP_SITE << 4);
+
+ MOTIF_XCLIENT_LONG (&xev, 1) = time;
+
+ if (need_coords)
+ {
+ MOTIF_XCLIENT_SHORT (&xev, 4) = private->last_x;
+ MOTIF_XCLIENT_SHORT (&xev, 5) = private->last_y;
+ }
+ else
+ MOTIF_XCLIENT_LONG (&xev, 2) = 0;
+
+ MOTIF_XCLIENT_LONG (&xev, 3) = 0;
+ MOTIF_XCLIENT_LONG (&xev, 4) = 0;
+
+ if (!_gdk_send_xevent (display,
+ GDK_DRAWABLE_XID (context->source_window),
+ FALSE, 0, &xev))
+ GDK_NOTE (DND,
+ g_message ("Send event to %lx failed",
+ GDK_DRAWABLE_XID (context->source_window)));
+ }
+ else if (context->protocol == GDK_DRAG_PROTO_XDND)
+ {
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndStatus");
+ xev.xclient.format = 32;
+ xev.xclient.window = GDK_DRAWABLE_XID (context->source_window);
+
+ xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->dest_window);
+ xev.xclient.data.l[1] = (action != 0) ? (2 | 1) : 0;
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = xdnd_action_to_atom (display, action);
+
+ if (!xdnd_send_xevent (context, context->source_window,
+ FALSE, &xev))
+ GDK_NOTE (DND,
+ g_message ("Send event to %lx failed",
+ GDK_DRAWABLE_XID (context->source_window)));
+ }
+
+ private->old_action = action;
+}
+
+/**
+ * gdk_drop_reply:
+ * @context: a #GdkDragContext.
+ * @ok: %TRUE if the drop is accepted.
+ * @time_: the timestamp for this operation.
+ *
+ * Accepts or rejects a drop.
+ *
+ * This function is called by the drag destination in response
+ * to a drop initiated by the drag source.
+ **/
+void
+gdk_drop_reply (GdkDragContext *context,
+ gboolean ok,
+ guint32 time)
+{
+ GdkDragContextPrivateX11 *private;
+
+ g_return_if_fail (context != NULL);
+
+ private = PRIVATE_DATA (context);
+
+ if (context->protocol == GDK_DRAG_PROTO_MOTIF)
+ {
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display,
+ "_MOTIF_DRAG_AND_DROP_MESSAGE");
+ xev.xclient.format = 8;
+
+ MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_START | 0x80;
+ MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
+ if (ok)
+ MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY |
+ (XmDROP_SITE_VALID << 4) |
+ (XmDROP_NOOP << 8) |
+ (XmDROP << 12);
+ else
+ MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP |
+ (XmNO_DROP_SITE << 4) |
+ (XmDROP_NOOP << 8) |
+ (XmDROP_CANCEL << 12);
+ MOTIF_XCLIENT_SHORT (&xev, 2) = private->last_x;
+ MOTIF_XCLIENT_SHORT (&xev, 3) = private->last_y;
+ MOTIF_XCLIENT_LONG (&xev, 2) = 0;
+ MOTIF_XCLIENT_LONG (&xev, 3) = 0;
+ MOTIF_XCLIENT_LONG (&xev, 4) = 0;
+
+ _gdk_send_xevent (display,
+ GDK_DRAWABLE_XID (context->source_window),
+ FALSE, 0, &xev);
+ }
+}
+
+/**
+ * gdk_drop_finish:
+ * @context: a #GtkDragContext.
+ * @success: %TRUE if the data was successfully received.
+ * @time_: the timestamp for this operation.
+ *
+ * Ends the drag operation after a drop.
+ *
+ * This function is called by the drag destination.
+ **/
+void
+gdk_drop_finish (GdkDragContext *context,
+ gboolean success,
+ guint32 time)
+{
+ g_return_if_fail (context != NULL);
+
+ if (context->protocol == GDK_DRAG_PROTO_XDND)
+ {
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndFinished");
+ xev.xclient.format = 32;
+ xev.xclient.window = GDK_DRAWABLE_XID (context->source_window);
+
+ xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->dest_window);
+ if (success)
+ {
+ xev.xclient.data.l[1] = 1;
+ xev.xclient.data.l[2] = xdnd_action_to_atom (display,
+ context->action);
+ }
+ else
+ {
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = None;
+ }
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ if (!xdnd_send_xevent (context, context->source_window,
+ FALSE, &xev))
+ GDK_NOTE (DND,
+ g_message ("Send event to %lx failed",
+ GDK_DRAWABLE_XID (context->source_window)));
+ }
+}
+
+
+/**
+ * gdk_window_register_dnd:
+ * @window: a #GdkWindow.
+ *
+ * Registers a window as a potential drop destination.
+ */
+void
+gdk_window_register_dnd (GdkWindow *window)
+{
+ static const gulong xdnd_version = 5;
+ MotifDragReceiverInfo info;
+ Atom motif_drag_receiver_info_atom;
+ GdkDisplay *display = gdk_window_get_display (window);
+
+ g_return_if_fail (window != NULL);
+
+ if (gdk_window_get_window_type (window) == GDK_WINDOW_OFFSCREEN)
+ return;
+
+ base_precache_atoms (display);
+
+ if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
+ return;
+ else
+ g_object_set_data (G_OBJECT (window), "gdk-dnd-registered", GINT_TO_POINTER (TRUE));
+
+ /* Set Motif drag receiver information property */
+
+ motif_drag_receiver_info_atom = gdk_x11_get_xatom_by_name_for_display (display,
+ "_MOTIF_DRAG_RECEIVER_INFO");
+ /* initialize to zero to avoid writing uninitialized data to socket */
+ memset(&info, 0, sizeof(info));
+ info.byte_order = local_byte_order;
+ info.protocol_version = 0;
+ info.protocol_style = XmDRAG_DYNAMIC;
+ info.proxy_window = None;
+ info.num_drop_sites = 0;
+ info.total_size = sizeof(info);
+
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_DRAWABLE_XID (window),
+ motif_drag_receiver_info_atom,
+ motif_drag_receiver_info_atom,
+ 8, PropModeReplace,
+ (guchar *)&info,
+ sizeof (info));
+
+ /* Set XdndAware */
+
+ /* The property needs to be of type XA_ATOM, not XA_INTEGER. Blech */
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_DRAWABLE_XID (window),
+ gdk_x11_get_xatom_by_name_for_display (display, "XdndAware"),
+ XA_ATOM, 32, PropModeReplace,
+ (guchar *)&xdnd_version, 1);
+}
+
+/**
+ * gdk_drag_get_selection:
+ * @context: a #GdkDragContext.
+ *
+ * Returns the selection atom for the current source window.
+ *
+ * Return value: the selection atom.
+ **/
+GdkAtom
+gdk_drag_get_selection (GdkDragContext *context)
+{
+ g_return_val_if_fail (context != NULL, GDK_NONE);
+
+ if (context->protocol == GDK_DRAG_PROTO_MOTIF)
+ return gdk_x11_xatom_to_atom_for_display (GDK_WINDOW_DISPLAY (context->source_window),
+ (PRIVATE_DATA (context))->motif_selection);
+ else if (context->protocol == GDK_DRAG_PROTO_XDND)
+ return gdk_atom_intern_static_string ("XdndSelection");
+ else
+ return GDK_NONE;
+}
+
+/**
+ * gdk_drag_drop_succeeded:
+ * @context: a #GdkDragContext
+ *
+ * Returns whether the dropped data has been successfully
+ * transferred. This function is intended to be used while
+ * handling a %GDK_DROP_FINISHED event, its return value is
+ * meaningless at other times.
+ *
+ * Return value: %TRUE if the drop was successful.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gdk_drag_drop_succeeded (GdkDragContext *context)
+{
+ GdkDragContextPrivateX11 *private;
+
+ g_return_val_if_fail (context != NULL, FALSE);
+
+ private = PRIVATE_DATA (context);
+
+ return !private->drop_failed;
+}
diff --git a/gdk/broadway/gdkdrawable-broadway.c b/gdk/broadway/gdkdrawable-broadway.c
new file mode 100644
index 0000000000..e481d133d4
--- /dev/null
+++ b/gdk/broadway/gdkdrawable-broadway.c
@@ -0,0 +1,235 @@
+/* GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gdkdrawable-broadway.h"
+
+#include "gdkx.h"
+#include "gdkprivate-broadway.h"
+#include "gdkscreen-broadway.h"
+#include "gdkdisplay-broadway.h"
+
+#include <cairo-xlib.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+
+static cairo_surface_t *gdk_x11_ref_cairo_surface (GdkDrawable *drawable);
+static cairo_surface_t *gdk_x11_create_cairo_surface (GdkDrawable *drawable,
+ int width,
+ int height);
+
+static const cairo_user_data_key_t gdk_x11_cairo_key;
+
+G_DEFINE_TYPE (GdkDrawableImplX11, _gdk_drawable_impl_x11, GDK_TYPE_DRAWABLE)
+
+static void
+_gdk_drawable_impl_x11_class_init (GdkDrawableImplX11Class *klass)
+{
+ GdkDrawableClass *drawable_class = GDK_DRAWABLE_CLASS (klass);
+
+ drawable_class->ref_cairo_surface = gdk_x11_ref_cairo_surface;
+ drawable_class->create_cairo_surface = gdk_x11_create_cairo_surface;
+}
+
+static void
+_gdk_drawable_impl_x11_init (GdkDrawableImplX11 *impl)
+{
+}
+
+/**
+ * _gdk_x11_drawable_finish:
+ * @drawable: a #GdkDrawableImplX11.
+ *
+ * Performs necessary cleanup prior to destroying a window.
+ **/
+void
+_gdk_x11_drawable_finish (GdkDrawable *drawable)
+{
+ GdkDrawableImplX11 *impl = GDK_DRAWABLE_IMPL_X11 (drawable);
+
+ if (impl->cairo_surface)
+ {
+ cairo_surface_finish (impl->cairo_surface);
+ cairo_surface_set_user_data (impl->cairo_surface, &gdk_x11_cairo_key,
+ NULL, NULL);
+ }
+}
+
+/**
+ * _gdk_x11_drawable_update_size:
+ * @drawable: a #GdkDrawableImplX11.
+ *
+ * Updates the state of the drawable (in particular the drawable's
+ * cairo surface) when its size has changed.
+ **/
+void
+_gdk_x11_drawable_update_size (GdkDrawable *drawable)
+{
+ GdkDrawableImplX11 *impl = GDK_DRAWABLE_IMPL_X11 (drawable);
+
+ if (impl->cairo_surface)
+ {
+ cairo_xlib_surface_set_size (impl->cairo_surface,
+ gdk_window_get_width (impl->wrapper),
+ gdk_window_get_height (impl->wrapper));
+ }
+}
+
+/*****************************************************
+ * X11 specific implementations of generic functions *
+ *****************************************************/
+
+static GdkDrawable *
+get_impl_drawable (GdkDrawable *drawable)
+{
+ if (GDK_IS_WINDOW (drawable))
+ return ((GdkWindowObject *)drawable)->impl;
+ else
+ {
+ g_warning (G_STRLOC " drawable is not a window");
+ return NULL;
+ }
+}
+
+/**
+ * gdk_x11_drawable_get_xdisplay:
+ * @drawable: a #GdkDrawable.
+ *
+ * Returns the display of a #GdkDrawable.
+ *
+ * Return value: an Xlib <type>Display*</type>.
+ **/
+Display *
+gdk_x11_drawable_get_xdisplay (GdkDrawable *drawable)
+{
+ if (GDK_IS_DRAWABLE_IMPL_X11 (drawable))
+ return GDK_SCREEN_XDISPLAY (GDK_DRAWABLE_IMPL_X11 (drawable)->screen);
+ else
+ return GDK_SCREEN_XDISPLAY (GDK_DRAWABLE_IMPL_X11 (get_impl_drawable (drawable))->screen);
+}
+
+/**
+ * gdk_x11_drawable_get_xid:
+ * @drawable: a #GdkDrawable.
+ *
+ * Returns the X resource (window) belonging to a #GdkDrawable.
+ *
+ * Return value: the ID of @drawable's X resource.
+ **/
+XID
+gdk_x11_drawable_get_xid (GdkDrawable *drawable)
+{
+ GdkDrawable *impl;
+
+ if (GDK_IS_WINDOW (drawable))
+ {
+ GdkWindow *window = (GdkWindow *)drawable;
+
+ /* Try to ensure the window has a native window */
+ if (!_gdk_window_has_impl (window))
+ {
+ gdk_window_ensure_native (window);
+
+ /* We sync here to ensure the window is created in the Xserver when
+ * this function returns. This is required because the returned XID
+ * for this window must be valid immediately, even with another
+ * connection to the Xserver */
+ gdk_display_sync (gdk_window_get_display (window));
+ }
+
+ if (!GDK_WINDOW_IS_X11 (window))
+ {
+ g_warning (G_STRLOC " drawable is not a native X11 window");
+ return None;
+ }
+
+ impl = ((GdkWindowObject *)drawable)->impl;
+ }
+ else
+ {
+ g_warning (G_STRLOC " drawable is not a window");
+ return None;
+ }
+
+ return ((GdkDrawableImplX11 *)impl)->xid;
+}
+
+GdkDrawable *
+gdk_x11_window_get_drawable_impl (GdkWindow *window)
+{
+ return ((GdkWindowObject *)window)->impl;
+}
+
+static void
+gdk_x11_cairo_surface_destroy (void *data)
+{
+ GdkDrawableImplX11 *impl = data;
+
+ impl->cairo_surface = NULL;
+}
+
+static cairo_surface_t *
+gdk_x11_create_cairo_surface (GdkDrawable *drawable,
+ int width,
+ int height)
+{
+ GdkDrawableImplX11 *impl = GDK_DRAWABLE_IMPL_X11 (drawable);
+ GdkVisual *visual;
+
+ visual = gdk_window_get_visual (impl->wrapper);
+ return cairo_xlib_surface_create (GDK_SCREEN_XDISPLAY (impl->screen),
+ impl->xid,
+ GDK_VISUAL_XVISUAL (visual),
+ width, height);
+}
+
+static cairo_surface_t *
+gdk_x11_ref_cairo_surface (GdkDrawable *drawable)
+{
+ GdkDrawableImplX11 *impl = GDK_DRAWABLE_IMPL_X11 (drawable);
+
+ if (GDK_IS_WINDOW_IMPL_X11 (drawable) &&
+ GDK_WINDOW_DESTROYED (impl->wrapper))
+ return NULL;
+
+ if (!impl->cairo_surface)
+ {
+ impl->cairo_surface = gdk_x11_create_cairo_surface (drawable,
+ gdk_window_get_width (impl->wrapper),
+ gdk_window_get_height (impl->wrapper));
+
+ if (impl->cairo_surface)
+ cairo_surface_set_user_data (impl->cairo_surface, &gdk_x11_cairo_key,
+ drawable, gdk_x11_cairo_surface_destroy);
+ }
+ else
+ cairo_surface_reference (impl->cairo_surface);
+
+ return impl->cairo_surface;
+}
diff --git a/gdk/broadway/gdkdrawable-broadway.h b/gdk/broadway/gdkdrawable-broadway.h
new file mode 100644
index 0000000000..7aca727833
--- /dev/null
+++ b/gdk/broadway/gdkdrawable-broadway.h
@@ -0,0 +1,75 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __GDK_DRAWABLE_BROADWAY_H__
+#define __GDK_DRAWABLE_BROADWAY_H__
+
+#include <gdk/gdkdrawable.h>
+#include <X11/Xlib.h>
+
+G_BEGIN_DECLS
+
+/* Drawable implementation for X11
+ */
+
+typedef struct _GdkDrawableImplX11 GdkDrawableImplX11;
+typedef struct _GdkDrawableImplX11Class GdkDrawableImplX11Class;
+
+#define GDK_TYPE_DRAWABLE_IMPL_X11 (_gdk_drawable_impl_x11_get_type ())
+#define GDK_DRAWABLE_IMPL_X11(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_DRAWABLE_IMPL_X11, GdkDrawableImplX11))
+#define GDK_DRAWABLE_IMPL_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_DRAWABLE_IMPL_X11, GdkDrawableImplX11Class))
+#define GDK_IS_DRAWABLE_IMPL_X11(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_DRAWABLE_IMPL_X11))
+#define GDK_IS_DRAWABLE_IMPL_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_DRAWABLE_IMPL_X11))
+#define GDK_DRAWABLE_IMPL_X11_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_DRAWABLE_IMPL_X11, GdkDrawableImplX11Class))
+
+struct _GdkDrawableImplX11
+{
+ GdkDrawable parent_instance;
+
+ GdkDrawable *wrapper;
+
+ Window xid;
+ GdkScreen *screen;
+
+ cairo_surface_t *cairo_surface;
+};
+
+struct _GdkDrawableImplX11Class
+{
+ GdkDrawableClass parent_class;
+
+};
+
+GType _gdk_drawable_impl_x11_get_type (void);
+
+/* Note that the following take GdkDrawableImplX11, not the wrapper drawable */
+void _gdk_x11_drawable_finish (GdkDrawable *drawable);
+void _gdk_x11_drawable_update_size (GdkDrawable *drawable);
+GdkDrawable *gdk_x11_window_get_drawable_impl (GdkWindow *window);
+
+G_END_DECLS
+
+#endif /* __GDK_DRAWABLE_BROADWAY_H__ */
diff --git a/gdk/broadway/gdkeventsource.c b/gdk/broadway/gdkeventsource.c
new file mode 100644
index 0000000000..f3b850e417
--- /dev/null
+++ b/gdk/broadway/gdkeventsource.c
@@ -0,0 +1,435 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 "gdkeventsource.h"
+
+#include "gdkinternals.h"
+#include "gdkx.h"
+
+
+static gboolean gdk_event_source_prepare (GSource *source,
+ gint *timeout);
+static gboolean gdk_event_source_check (GSource *source);
+static gboolean gdk_event_source_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data);
+static void gdk_event_source_finalize (GSource *source);
+
+#define HAS_FOCUS(toplevel) \
+ ((toplevel)->has_focus || (toplevel)->has_pointer_focus)
+
+struct _GdkEventSource
+{
+ GSource source;
+
+ GdkDisplay *display;
+ GPollFD event_poll_fd;
+ GList *translators;
+};
+
+static GSourceFuncs event_funcs = {
+ gdk_event_source_prepare,
+ gdk_event_source_check,
+ gdk_event_source_dispatch,
+ gdk_event_source_finalize
+};
+
+static GList *event_sources = NULL;
+
+static gint
+gdk_event_apply_filters (XEvent *xevent,
+ GdkEvent *event,
+ GList *filters)
+{
+ GList *tmp_list;
+ GdkFilterReturn result;
+
+ tmp_list = filters;
+
+ while (tmp_list)
+ {
+ GdkEventFilter *filter = (GdkEventFilter*) tmp_list->data;
+
+ tmp_list = tmp_list->next;
+ result = filter->function (xevent, event, filter->data);
+
+ if (result != GDK_FILTER_CONTINUE)
+ return result;
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static GdkWindow *
+gdk_event_source_get_filter_window (GdkEventSource *event_source,
+ XEvent *xevent)
+{
+ GdkWindow *window;
+
+ window = gdk_window_lookup_for_display (event_source->display,
+ xevent->xany.window);
+
+ if (window && !GDK_IS_WINDOW (window))
+ window = NULL;
+
+ return window;
+}
+
+static void
+handle_focus_change (GdkEventCrossing *event)
+{
+ GdkToplevelX11 *toplevel;
+ gboolean focus_in, had_focus;
+
+ toplevel = _gdk_x11_window_get_toplevel (event->window);
+ focus_in = (event->type == GDK_ENTER_NOTIFY);
+
+ if (!toplevel || event->detail == GDK_NOTIFY_INFERIOR)
+ return;
+
+ toplevel->has_pointer = focus_in;
+
+ if (!event->focus || toplevel->has_focus_window)
+ return;
+
+ had_focus = HAS_FOCUS (toplevel);
+ toplevel->has_pointer_focus = focus_in;
+
+ if (HAS_FOCUS (toplevel) != had_focus)
+ {
+ GdkEvent *focus_event;
+
+ focus_event = gdk_event_new (GDK_FOCUS_CHANGE);
+ focus_event->focus_change.window = g_object_ref (event->window);
+ focus_event->focus_change.send_event = FALSE;
+ focus_event->focus_change.in = focus_in;
+ gdk_event_set_device (focus_event, gdk_event_get_device ((GdkEvent *) event));
+
+ gdk_event_put (focus_event);
+ gdk_event_free (focus_event);
+ }
+}
+
+static GdkEvent *
+gdk_event_source_translate_event (GdkEventSource *event_source,
+ XEvent *xevent)
+{
+ GdkEvent *event = gdk_event_new (GDK_NOTHING);
+ GList *list = event_source->translators;
+ GdkFilterReturn result;
+ GdkWindow *filter_window;
+
+ /* Run default filters */
+ if (_gdk_default_filters)
+ {
+ /* Apply global filters */
+
+ result = gdk_event_apply_filters (xevent, event,
+ _gdk_default_filters);
+
+ if (result == GDK_FILTER_REMOVE)
+ {
+ gdk_event_free (event);
+ return NULL;
+ }
+ else if (result == GDK_FILTER_TRANSLATE)
+ return event;
+ }
+
+ filter_window = gdk_event_source_get_filter_window (event_source, xevent);
+
+ if (filter_window)
+ {
+ /* Apply per-window filters */
+ GdkWindowObject *filter_private = (GdkWindowObject *) filter_window;
+ GdkFilterReturn result;
+
+ event->any.window = g_object_ref (filter_window);
+
+ if (filter_private->filters)
+ {
+ result = gdk_event_apply_filters (xevent, event,
+ filter_private->filters);
+
+ if (result == GDK_FILTER_REMOVE)
+ {
+ gdk_event_free (event);
+ return NULL;
+ }
+ else if (result == GDK_FILTER_TRANSLATE)
+ return event;
+ }
+ }
+
+ gdk_event_free (event);
+ event = NULL;
+
+ while (list && !event)
+ {
+ GdkEventTranslator *translator = list->data;
+
+ list = list->next;
+ event = gdk_event_translator_translate (translator,
+ event_source->display,
+ xevent);
+ }
+
+ if (event &&
+ (event->type == GDK_ENTER_NOTIFY ||
+ event->type == GDK_LEAVE_NOTIFY) &&
+ event->crossing.window != NULL)
+ {
+ /* Handle focusing (in the case where no window manager is running */
+ handle_focus_change (&event->crossing);
+ }
+
+ return event;
+}
+
+static gboolean
+gdk_check_xpending (GdkDisplay *display)
+{
+ return XPending (GDK_DISPLAY_XDISPLAY (display));
+}
+
+static gboolean
+gdk_event_source_prepare (GSource *source,
+ gint *timeout)
+{
+ GdkDisplay *display = ((GdkEventSource*) source)->display;
+ gboolean retval;
+
+ GDK_THREADS_ENTER ();
+
+ *timeout = -1;
+ retval = (_gdk_event_queue_find_first (display) != NULL ||
+ gdk_check_xpending (display));
+
+ GDK_THREADS_LEAVE ();
+
+ return retval;
+}
+
+static gboolean
+gdk_event_source_check (GSource *source)
+{
+ GdkEventSource *event_source = (GdkEventSource*) source;
+ gboolean retval;
+
+ GDK_THREADS_ENTER ();
+
+ if (event_source->event_poll_fd.revents & G_IO_IN)
+ retval = (_gdk_event_queue_find_first (event_source->display) != NULL ||
+ gdk_check_xpending (event_source->display));
+ else
+ retval = FALSE;
+
+ GDK_THREADS_LEAVE ();
+
+ return retval;
+}
+
+void
+_gdk_events_queue (GdkDisplay *display)
+{
+ GdkEvent *event;
+ XEvent xevent;
+ Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
+ GdkEventSource *event_source;
+ GdkDisplayX11 *display_x11;
+
+ display_x11 = GDK_DISPLAY_X11 (display);
+ event_source = (GdkEventSource *) display_x11->event_source;
+
+ while (!_gdk_event_queue_find_first (display) && XPending (xdisplay))
+ {
+ XNextEvent (xdisplay, &xevent);
+
+ switch (xevent.type)
+ {
+ case KeyPress:
+ case KeyRelease:
+ break;
+ default:
+ if (XFilterEvent (&xevent, None))
+ continue;
+ }
+
+ event = gdk_event_source_translate_event (event_source, &xevent);
+
+ if (event)
+ {
+ GList *node;
+
+ node = _gdk_event_queue_append (display, event);
+ _gdk_windowing_got_event (display, node, event, xevent.xany.serial);
+ }
+ }
+}
+
+static gboolean
+gdk_event_source_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ GdkDisplay *display = ((GdkEventSource*) source)->display;
+ GdkEvent *event;
+
+ GDK_THREADS_ENTER ();
+
+ event = gdk_display_get_event (display);
+
+ if (event)
+ {
+ if (_gdk_event_func)
+ (*_gdk_event_func) (event, _gdk_event_data);
+
+ gdk_event_free (event);
+ }
+
+ GDK_THREADS_LEAVE ();
+
+ return TRUE;
+}
+
+static void
+gdk_event_source_finalize (GSource *source)
+{
+ GdkEventSource *event_source = (GdkEventSource *)source;
+
+ g_list_free (event_source->translators);
+ event_source->translators = NULL;
+
+ event_sources = g_list_remove (event_sources, source);
+}
+
+GSource *
+gdk_event_source_new (GdkDisplay *display)
+{
+ GSource *source;
+ GdkEventSource *event_source;
+ GdkDisplayX11 *display_x11;
+ int connection_number;
+ char *name;
+
+ source = g_source_new (&event_funcs, sizeof (GdkEventSource));
+ name = g_strdup_printf ("GDK X11 Event source (%s)",
+ gdk_display_get_name (display));
+ g_source_set_name (source, name);
+ g_free (name);
+ event_source = (GdkEventSource *) source;
+ event_source->display = display;
+
+ display_x11 = GDK_DISPLAY_X11 (display);
+ connection_number = ConnectionNumber (display_x11->xdisplay);
+
+ event_source->event_poll_fd.fd = connection_number;
+ event_source->event_poll_fd.events = G_IO_IN;
+ g_source_add_poll (source, &event_source->event_poll_fd);
+
+ g_source_set_priority (source, GDK_PRIORITY_EVENTS);
+ g_source_set_can_recurse (source, TRUE);
+ g_source_attach (source, NULL);
+
+ event_sources = g_list_prepend (event_sources, source);
+
+ return source;
+}
+
+void
+gdk_event_source_add_translator (GdkEventSource *source,
+ GdkEventTranslator *translator)
+{
+ g_return_if_fail (GDK_IS_EVENT_TRANSLATOR (translator));
+
+ source->translators = g_list_append (source->translators, translator);
+}
+
+void
+gdk_event_source_select_events (GdkEventSource *source,
+ Window window,
+ GdkEventMask event_mask,
+ unsigned int extra_x_mask)
+{
+ unsigned int xmask = extra_x_mask;
+ GList *list;
+ gint i;
+
+ list = source->translators;
+
+ while (list)
+ {
+ GdkEventTranslator *translator = list->data;
+ GdkEventMask translator_mask, mask;
+
+ translator_mask = gdk_event_translator_get_handled_events (translator);
+ mask = event_mask & translator_mask;
+
+ if (mask != 0)
+ {
+ gdk_event_translator_select_window_events (translator, window, mask);
+ event_mask &= ~mask;
+ }
+
+ list = list->next;
+ }
+
+ for (i = 0; i < _gdk_nenvent_masks; i++)
+ {
+ if (event_mask & (1 << (i + 1)))
+ xmask |= _gdk_event_mask_table[i];
+ }
+
+ XSelectInput (GDK_DISPLAY_XDISPLAY (source->display), window, xmask);
+}
+
+/**
+ * gdk_events_pending:
+ *
+ * Checks if any events are ready to be processed for any display.
+ *
+ * Return value: %TRUE if any events are pending.
+ **/
+gboolean
+gdk_events_pending (void)
+{
+ GList *tmp_list;
+
+ for (tmp_list = event_sources; tmp_list; tmp_list = tmp_list->next)
+ {
+ GdkEventSource *tmp_source = tmp_list->data;
+ GdkDisplay *display = tmp_source->display;
+
+ if (_gdk_event_queue_find_first (display))
+ return TRUE;
+ }
+
+ for (tmp_list = event_sources; tmp_list; tmp_list = tmp_list->next)
+ {
+ GdkEventSource *tmp_source = tmp_list->data;
+ GdkDisplay *display = tmp_source->display;
+
+ if (gdk_check_xpending (display))
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/gdk/broadway/gdkeventsource.h b/gdk/broadway/gdkeventsource.h
new file mode 100644
index 0000000000..424338c665
--- /dev/null
+++ b/gdk/broadway/gdkeventsource.h
@@ -0,0 +1,46 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+#ifndef __GDK_EVENT_SOURCE_H__
+#define __GDK_EVENT_SOURCE_H__
+
+#include "gdkeventtranslator.h"
+#include "gdkprivate-broadway.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GdkEventSource GdkEventSource;
+
+G_GNUC_INTERNAL
+GSource * gdk_event_source_new (GdkDisplay *display);
+
+G_GNUC_INTERNAL
+void gdk_event_source_add_translator (GdkEventSource *source,
+ GdkEventTranslator *translator);
+
+G_GNUC_INTERNAL
+void gdk_event_source_select_events (GdkEventSource *source,
+ Window window,
+ GdkEventMask event_mask,
+ unsigned int extra_x_mask);
+
+
+G_END_DECLS
+
+#endif /* __GDK_EVENT_SOURCE_H__ */
diff --git a/gdk/broadway/gdkeventtranslator.c b/gdk/broadway/gdkeventtranslator.c
new file mode 100644
index 0000000000..b4a285a962
--- /dev/null
+++ b/gdk/broadway/gdkeventtranslator.c
@@ -0,0 +1,89 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 "gdkeventtranslator.h"
+
+
+typedef GdkEventTranslatorIface GdkEventTranslatorInterface;
+G_DEFINE_INTERFACE (GdkEventTranslator, gdk_event_translator, G_TYPE_OBJECT);
+
+
+static void
+gdk_event_translator_default_init (GdkEventTranslatorInterface *iface)
+{
+}
+
+
+GdkEvent *
+gdk_event_translator_translate (GdkEventTranslator *translator,
+ GdkDisplay *display,
+ XEvent *xevent)
+{
+ GdkEventTranslatorIface *iface;
+ GdkEvent *event;
+
+ g_return_val_if_fail (GDK_IS_EVENT_TRANSLATOR (translator), NULL);
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
+
+ iface = GDK_EVENT_TRANSLATOR_GET_IFACE (translator);
+
+ if (!iface->translate_event)
+ return NULL;
+
+ event = gdk_event_new (GDK_NOTHING);
+
+ if ((iface->translate_event) (translator, display, event, xevent))
+ return event;
+
+ gdk_event_free (event);
+
+ return NULL;
+}
+
+GdkEventMask
+gdk_event_translator_get_handled_events (GdkEventTranslator *translator)
+{
+ GdkEventTranslatorIface *iface;
+
+ g_return_val_if_fail (GDK_IS_EVENT_TRANSLATOR (translator), 0);
+
+ iface = GDK_EVENT_TRANSLATOR_GET_IFACE (translator);
+
+ if (iface->get_handled_events)
+ return iface->get_handled_events (translator);
+
+ return 0;
+}
+
+void
+gdk_event_translator_select_window_events (GdkEventTranslator *translator,
+ Window window,
+ GdkEventMask event_mask)
+{
+ GdkEventTranslatorIface *iface;
+
+ g_return_if_fail (GDK_IS_EVENT_TRANSLATOR (translator));
+
+ iface = GDK_EVENT_TRANSLATOR_GET_IFACE (translator);
+
+ if (iface->select_window_events)
+ iface->select_window_events (translator, window, event_mask);
+}
diff --git a/gdk/broadway/gdkeventtranslator.h b/gdk/broadway/gdkeventtranslator.h
new file mode 100644
index 0000000000..55d2dc891e
--- /dev/null
+++ b/gdk/broadway/gdkeventtranslator.h
@@ -0,0 +1,65 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+#ifndef __GDK_EVENT_TRANSLATOR_H__
+#define __GDK_EVENT_TRANSLATOR_H__
+
+#include <gdk/gdktypes.h>
+#include <gdk/gdkdisplay.h>
+#include "gdkprivate-broadway.h"
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_EVENT_TRANSLATOR (gdk_event_translator_get_type ())
+#define GDK_EVENT_TRANSLATOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_EVENT_TRANSLATOR, GdkEventTranslator))
+#define GDK_IS_EVENT_TRANSLATOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_EVENT_TRANSLATOR))
+#define GDK_EVENT_TRANSLATOR_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GDK_TYPE_EVENT_TRANSLATOR, GdkEventTranslatorIface))
+
+typedef struct _GdkEventTranslatorIface GdkEventTranslatorIface;
+typedef struct _GdkEventTranslator GdkEventTranslator; /* Dummy typedef */
+
+struct _GdkEventTranslatorIface
+{
+ GTypeInterface iface;
+
+ /* VMethods */
+ gboolean (* translate_event) (GdkEventTranslator *translator,
+ GdkDisplay *display,
+ GdkEvent *event,
+ XEvent *xevent);
+
+ GdkEventMask (* get_handled_events) (GdkEventTranslator *translator);
+ void (* select_window_events) (GdkEventTranslator *translator,
+ Window window,
+ GdkEventMask event_mask);
+};
+
+GType gdk_event_translator_get_type (void) G_GNUC_CONST;
+
+GdkEvent * gdk_event_translator_translate (GdkEventTranslator *translator,
+ GdkDisplay *display,
+ XEvent *xevent);
+GdkEventMask gdk_event_translator_get_handled_events (GdkEventTranslator *translator);
+void gdk_event_translator_select_window_events (GdkEventTranslator *translator,
+ Window window,
+ GdkEventMask event_mask);
+
+G_END_DECLS
+
+#endif /* __GDK_EVENT_TRANSLATOR_H__ */
diff --git a/gdk/broadway/gdkgeometry-broadway.c b/gdk/broadway/gdkgeometry-broadway.c
new file mode 100644
index 0000000000..e0e62d6f9e
--- /dev/null
+++ b/gdk/broadway/gdkgeometry-broadway.c
@@ -0,0 +1,390 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 "gdkrectangle.h"
+#include "gdkprivate-broadway.h"
+#include "gdkx.h"
+#include "gdkinternals.h"
+#include "gdkscreen-broadway.h"
+#include "gdkdisplay-broadway.h"
+#include "gdkwindow-broadway.h"
+
+
+typedef struct _GdkWindowQueueItem GdkWindowQueueItem;
+typedef struct _GdkWindowParentPos GdkWindowParentPos;
+
+typedef enum {
+ GDK_WINDOW_QUEUE_TRANSLATE,
+ GDK_WINDOW_QUEUE_ANTIEXPOSE
+} GdkWindowQueueType;
+
+struct _GdkWindowQueueItem
+{
+ GdkWindow *window;
+ gulong serial;
+ GdkWindowQueueType type;
+ union {
+ struct {
+ cairo_region_t *area;
+ gint dx;
+ gint dy;
+ } translate;
+ struct {
+ cairo_region_t *area;
+ } antiexpose;
+ } u;
+};
+
+void
+_gdk_window_move_resize_child (GdkWindow *window,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ GdkWindowObject *obj;
+
+ g_return_if_fail (window != NULL);
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ obj = GDK_WINDOW_OBJECT (window);
+
+ if (width > 65535 ||
+ height > 65535)
+ {
+ g_warning ("Native children wider or taller than 65535 pixels are not supported");
+
+ if (width > 65535)
+ width = 65535;
+ if (height > 65535)
+ height = 65535;
+ }
+
+ obj->x = x;
+ obj->y = y;
+ obj->width = width;
+ obj->height = height;
+
+ /* We don't really care about origin overflow, because on overflow
+ the window won't be visible anyway and thus it will be shaped
+ to nothing */
+
+ _gdk_x11_window_tmp_unset_parent_bg (window);
+ _gdk_x11_window_tmp_unset_bg (window, TRUE);
+ XMoveResizeWindow (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ obj->x + obj->parent->abs_x,
+ obj->y + obj->parent->abs_y,
+ width, height);
+ _gdk_x11_window_tmp_reset_parent_bg (window);
+ _gdk_x11_window_tmp_reset_bg (window, TRUE);
+}
+
+static Bool
+expose_serial_predicate (Display *xdisplay,
+ XEvent *xev,
+ XPointer arg)
+{
+ gulong *serial = (gulong *)arg;
+
+ if (xev->xany.type == Expose || xev->xany.type == GraphicsExpose)
+ *serial = MIN (*serial, xev->xany.serial);
+
+ return False;
+}
+
+/* Find oldest possible serial for an outstanding expose event
+ */
+static gulong
+find_current_serial (Display *xdisplay)
+{
+ XEvent xev;
+ gulong serial = NextRequest (xdisplay);
+
+ XSync (xdisplay, False);
+
+ XCheckIfEvent (xdisplay, &xev, expose_serial_predicate, (XPointer)&serial);
+
+ return serial;
+}
+
+static void
+queue_delete_link (GQueue *queue,
+ GList *link)
+{
+ if (queue->tail == link)
+ queue->tail = link->prev;
+
+ queue->head = g_list_remove_link (queue->head, link);
+ g_list_free_1 (link);
+ queue->length--;
+}
+
+static void
+queue_item_free (GdkWindowQueueItem *item)
+{
+ if (item->window)
+ {
+ g_object_remove_weak_pointer (G_OBJECT (item->window),
+ (gpointer *)&(item->window));
+ }
+
+ if (item->type == GDK_WINDOW_QUEUE_ANTIEXPOSE)
+ cairo_region_destroy (item->u.antiexpose.area);
+ else
+ {
+ if (item->u.translate.area)
+ cairo_region_destroy (item->u.translate.area);
+ }
+
+ g_free (item);
+}
+
+static void
+gdk_window_queue (GdkWindow *window,
+ GdkWindowQueueItem *item)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
+
+ if (!display_x11->translate_queue)
+ display_x11->translate_queue = g_queue_new ();
+
+ /* Keep length of queue finite by, if it grows too long,
+ * figuring out the latest relevant serial and discarding
+ * irrelevant queue items.
+ */
+ if (display_x11->translate_queue->length >= 64)
+ {
+ gulong serial = find_current_serial (GDK_WINDOW_XDISPLAY (window));
+ GList *tmp_list = display_x11->translate_queue->head;
+
+ while (tmp_list)
+ {
+ GdkWindowQueueItem *item = tmp_list->data;
+ GList *next = tmp_list->next;
+
+ /* an overflow-safe (item->serial < serial) */
+ if (item->serial - serial > (gulong) G_MAXLONG)
+ {
+ queue_delete_link (display_x11->translate_queue, tmp_list);
+ queue_item_free (item);
+ }
+
+ tmp_list = next;
+ }
+ }
+
+ /* Catch the case where someone isn't processing events and there
+ * is an event stuck in the event queue with an old serial:
+ * If we can't reduce the queue length by the above method,
+ * discard anti-expose items. (We can't discard translate
+ * items
+ */
+ if (display_x11->translate_queue->length >= 64)
+ {
+ GList *tmp_list = display_x11->translate_queue->head;
+
+ while (tmp_list)
+ {
+ GdkWindowQueueItem *item = tmp_list->data;
+ GList *next = tmp_list->next;
+
+ if (item->type == GDK_WINDOW_QUEUE_ANTIEXPOSE)
+ {
+ queue_delete_link (display_x11->translate_queue, tmp_list);
+ queue_item_free (item);
+ }
+
+ tmp_list = next;
+ }
+ }
+
+ item->window = window;
+ item->serial = NextRequest (GDK_WINDOW_XDISPLAY (window));
+
+ g_object_add_weak_pointer (G_OBJECT (window),
+ (gpointer *)&(item->window));
+
+ g_queue_push_tail (display_x11->translate_queue, item);
+}
+
+static GC
+_get_scratch_gc (GdkWindowObject *window, cairo_region_t *clip_region)
+{
+ GdkScreenX11 *screen;
+ XRectangle *rectangles;
+ gint n_rects;
+ gint depth;
+
+ screen = GDK_SCREEN_X11 (gdk_window_get_screen (GDK_WINDOW (window)));
+ depth = gdk_visual_get_depth (gdk_window_get_visual (GDK_WINDOW (window))) - 1;
+
+ if (!screen->subwindow_gcs[depth])
+ {
+ XGCValues values;
+
+ values.graphics_exposures = True;
+ values.subwindow_mode = IncludeInferiors;
+
+ screen->subwindow_gcs[depth] = XCreateGC (screen->xdisplay,
+ GDK_WINDOW_XID (window),
+ GCSubwindowMode | GCGraphicsExposures,
+ &values);
+ }
+
+ _gdk_region_get_xrectangles (clip_region,
+ 0, 0,
+ &rectangles,
+ &n_rects);
+
+ XSetClipRectangles (screen->xdisplay,
+ screen->subwindow_gcs[depth],
+ 0, 0,
+ rectangles, n_rects,
+ YXBanded);
+
+ g_free (rectangles);
+ return screen->subwindow_gcs[depth];
+}
+
+
+
+void
+_gdk_x11_window_translate (GdkWindow *window,
+ cairo_region_t *area,
+ gint dx,
+ gint dy)
+{
+ GdkWindowQueueItem *item;
+ GC xgc;
+ GdkRectangle extents;
+ GdkWindowObject *private, *impl;
+ int px, py;
+
+ /* We need to get data from subwindows here, because we might have
+ * shaped a native window over the moving region (with bg none,
+ * so the pixels are still there). In fact we might need to get data
+ * from overlapping native window that are not children of this window,
+ * so we copy from the toplevel with INCLUDE_INFERIORS.
+ */
+ private = impl = (GdkWindowObject *) window;
+ px = py = 0;
+ while (private->parent != NULL &&
+ private->parent->window_type != GDK_WINDOW_ROOT)
+ {
+ dx -= private->parent->abs_x + private->x;
+ dy -= private->parent->abs_y + private->y;
+ private = (GdkWindowObject *) _gdk_window_get_impl_window ((GdkWindow *) private->parent);
+ }
+
+ cairo_region_get_extents (area, &extents);
+
+ xgc = _get_scratch_gc (impl, area);
+
+ cairo_region_translate (area, -dx, -dy); /* Move to source region */
+
+ item = g_new (GdkWindowQueueItem, 1);
+ item->type = GDK_WINDOW_QUEUE_TRANSLATE;
+ item->u.translate.area = cairo_region_copy (area);
+ item->u.translate.dx = dx;
+ item->u.translate.dy = dy;
+ gdk_window_queue (window, item);
+
+ XCopyArea (GDK_WINDOW_XDISPLAY (impl),
+ GDK_DRAWABLE_IMPL_X11 (private->impl)->xid,
+ GDK_DRAWABLE_IMPL_X11 (impl->impl)->xid,
+ xgc,
+ extents.x - dx, extents.y - dy,
+ extents.width, extents.height,
+ extents.x, extents.y);
+}
+
+gboolean
+_gdk_x11_window_queue_antiexpose (GdkWindow *window,
+ cairo_region_t *area)
+{
+ GdkWindowQueueItem *item = g_new (GdkWindowQueueItem, 1);
+ item->type = GDK_WINDOW_QUEUE_ANTIEXPOSE;
+ item->u.antiexpose.area = area;
+
+ gdk_window_queue (window, item);
+
+ return TRUE;
+}
+
+void
+_gdk_window_process_expose (GdkWindow *window,
+ gulong serial,
+ GdkRectangle *area)
+{
+ cairo_region_t *invalidate_region = cairo_region_create_rectangle (area);
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
+
+ if (display_x11->translate_queue)
+ {
+ GList *tmp_list = display_x11->translate_queue->head;
+
+ while (tmp_list)
+ {
+ GdkWindowQueueItem *item = tmp_list->data;
+ GList *next = tmp_list->next;
+
+ /* an overflow-safe (serial < item->serial) */
+ if (serial - item->serial > (gulong) G_MAXLONG)
+ {
+ if (item->window == window)
+ {
+ if (item->type == GDK_WINDOW_QUEUE_TRANSLATE)
+ {
+ if (item->u.translate.area)
+ {
+ cairo_region_t *intersection;
+
+ intersection = cairo_region_copy (invalidate_region);
+ cairo_region_intersect (intersection, item->u.translate.area);
+ cairo_region_subtract (invalidate_region, intersection);
+ cairo_region_translate (intersection, item->u.translate.dx, item->u.translate.dy);
+ cairo_region_union (invalidate_region, intersection);
+ cairo_region_destroy (intersection);
+ }
+ else
+ cairo_region_translate (invalidate_region, item->u.translate.dx, item->u.translate.dy);
+ }
+ else /* anti-expose */
+ {
+ cairo_region_subtract (invalidate_region, item->u.antiexpose.area);
+ }
+ }
+ }
+ else
+ {
+ queue_delete_link (display_x11->translate_queue, tmp_list);
+ queue_item_free (item);
+ }
+ tmp_list = next;
+ }
+ }
+
+ if (!cairo_region_is_empty (invalidate_region))
+ _gdk_window_invalidate_for_expose (window, invalidate_region);
+
+ cairo_region_destroy (invalidate_region);
+}
diff --git a/gdk/broadway/gdkglobals-broadway.c b/gdk/broadway/gdkglobals-broadway.c
new file mode 100644
index 0000000000..f55375bdd6
--- /dev/null
+++ b/gdk/broadway/gdkglobals-broadway.c
@@ -0,0 +1,37 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gdktypes.h"
+#include "gdkprivate-broadway.h"
+
+#include <stdio.h>
+
+
+gboolean _gdk_use_xshm = TRUE; /* used as a cmd line arg */
+GdkAtom _gdk_selection_property;
+gboolean _gdk_synchronize = FALSE;
diff --git a/gdk/broadway/gdkim-broadway.c b/gdk/broadway/gdkim-broadway.c
new file mode 100644
index 0000000000..b027f4b931
--- /dev/null
+++ b/gdk/broadway/gdkim-broadway.c
@@ -0,0 +1,103 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gdkx.h"
+#include "gdkmain.h"
+#include "gdkinternals.h"
+#include "gdkdisplay-broadway.h"
+
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/* If this variable is FALSE, it indicates that we should
+ * avoid trying to use multibyte conversion functions and
+ * assume everything is 1-byte per character
+ */
+static gboolean gdk_use_mb;
+
+void
+_gdk_x11_initialize_locale (void)
+{
+ wchar_t result;
+ gchar *current_locale;
+ static char *last_locale = NULL;
+
+ gdk_use_mb = FALSE;
+
+ current_locale = setlocale (LC_ALL, NULL);
+
+ if (last_locale && strcmp (last_locale, current_locale) == 0)
+ return;
+
+ g_free (last_locale);
+ last_locale = g_strdup (current_locale);
+
+ if (XSupportsLocale ())
+ XSetLocaleModifiers ("");
+
+ if ((strcmp (current_locale, "C")) && (strcmp (current_locale, "POSIX")))
+ {
+ gdk_use_mb = TRUE;
+
+#ifndef X_LOCALE
+ /* Detect ancient GNU libc, where mb == UTF8. Not useful unless it's
+ * really a UTF8 locale. The below still probably will
+ * screw up on Greek, Cyrillic, etc, encoded as UTF8.
+ */
+
+ if ((MB_CUR_MAX == 2) &&
+ (mbstowcs (&result, "\xdd\xa5", 1) > 0) &&
+ result == 0x765)
+ {
+ if ((strlen (current_locale) < 4) ||
+ g_ascii_strcasecmp (current_locale + strlen(current_locale) - 4,
+ "utf8"))
+ gdk_use_mb = FALSE;
+ }
+#endif /* X_LOCALE */
+ }
+
+ GDK_NOTE (XIM,
+ g_message ("%s multi-byte string functions.",
+ gdk_use_mb ? "Using" : "Not using"));
+
+ return;
+}
+
+gchar*
+gdk_set_locale (void)
+{
+ if (!setlocale (LC_ALL,""))
+ g_warning ("locale not supported by C library");
+
+ _gdk_x11_initialize_locale ();
+
+ return setlocale (LC_ALL, NULL);
+}
diff --git a/gdk/broadway/gdkinput.c b/gdk/broadway/gdkinput.c
new file mode 100644
index 0000000000..24b32eaf0a
--- /dev/null
+++ b/gdk/broadway/gdkinput.c
@@ -0,0 +1,231 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gdkscreen-broadway.h"
+#include "gdkdisplay-broadway.h"
+#include "gdkwindow.h"
+
+#include <stdlib.h>
+
+
+/* Addition used for extension_events mask */
+#define GDK_ALL_DEVICES_MASK (1<<30)
+
+struct _GdkInputWindow
+{
+ GList *windows; /* GdkWindow:s with extension_events set */
+
+ /* gdk window */
+ GdkWindow *impl_window; /* an impl window */
+};
+
+
+/**
+ * gdk_devices_list:
+ *
+ * Returns the list of available input devices for the default display.
+ * The list is statically allocated and should not be freed.
+ *
+ * Return value: (transfer none) (element-type GdkDevice): a list of #GdkDevice
+ *
+ * Deprecated: 3.0: Use gdk_device_manager_list_devices() instead.
+ **/
+GList *
+gdk_devices_list (void)
+{
+ return gdk_display_list_devices (gdk_display_get_default ());
+}
+
+static void
+_gdk_input_select_device_events (GdkWindow *impl_window,
+ GdkDevice *device)
+{
+ guint event_mask;
+ GdkWindowObject *w;
+ GdkInputWindow *iw;
+ GdkInputMode mode;
+ gboolean has_cursor;
+ GdkDeviceType type;
+ GList *l;
+
+ event_mask = 0;
+ iw = ((GdkWindowObject *)impl_window)->input_window;
+
+ g_object_get (device,
+ "type", &type,
+ "input-mode", &mode,
+ "has-cursor", &has_cursor,
+ NULL);
+
+ if (iw == NULL ||
+ mode == GDK_MODE_DISABLED ||
+ type == GDK_DEVICE_TYPE_MASTER)
+ return;
+
+ for (l = iw->windows; l != NULL; l = l->next)
+ {
+ w = l->data;
+
+ if (has_cursor || (w->extension_events & GDK_ALL_DEVICES_MASK))
+ {
+ event_mask = w->extension_events;
+
+ if (event_mask)
+ event_mask |= GDK_PROXIMITY_OUT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
+
+ gdk_window_set_device_events ((GdkWindow *) w, device, event_mask);
+ }
+ }
+}
+
+static void
+unset_extension_events (GdkWindow *window)
+{
+ GdkWindowObject *window_private;
+ GdkWindowObject *impl_window;
+ GdkDisplayX11 *display_x11;
+ GdkInputWindow *iw;
+
+ window_private = (GdkWindowObject*) window;
+ impl_window = (GdkWindowObject *)_gdk_window_get_impl_window (window);
+ iw = impl_window->input_window;
+
+ display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
+
+ if (window_private->extension_events != 0)
+ {
+ g_assert (iw != NULL);
+ g_assert (g_list_find (iw->windows, window) != NULL);
+
+ iw->windows = g_list_remove (iw->windows, window);
+ if (iw->windows == NULL)
+ {
+ impl_window->input_window = NULL;
+ display_x11->input_windows = g_list_remove (display_x11->input_windows, iw);
+ g_free (iw);
+ }
+ }
+
+ window_private->extension_events = 0;
+}
+
+/**
+ * gdk_input_set_extension_events:
+ * @window: a #GdkWindow.
+ * @mask: the event mask
+ * @mode: the type of extension events that are desired.
+ *
+ * Turns extension events on or off for a particular window,
+ * and specifies the event mask for extension events.
+ *
+ * Deprecated: 3.0: Use gdk_window_set_device_events() instead.
+ **/
+void
+gdk_input_set_extension_events (GdkWindow *window,
+ gint mask,
+ GdkExtensionMode mode)
+{
+ GdkWindowObject *window_private;
+ GdkWindowObject *impl_window;
+ GdkInputWindow *iw;
+ GdkDisplayX11 *display_x11;
+#ifndef XINPUT_NONE
+ GList *tmp_list;
+#endif
+
+ g_return_if_fail (window != NULL);
+ g_return_if_fail (GDK_WINDOW_IS_X11 (window));
+
+ window_private = (GdkWindowObject*) window;
+ display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
+ if (GDK_WINDOW_DESTROYED (window))
+ return;
+
+ impl_window = (GdkWindowObject *)_gdk_window_get_impl_window (window);
+
+ if (mode == GDK_EXTENSION_EVENTS_ALL && mask != 0)
+ mask |= GDK_ALL_DEVICES_MASK;
+
+ if (mode == GDK_EXTENSION_EVENTS_NONE)
+ mask = 0;
+
+ iw = impl_window->input_window;
+
+ if (mask != 0)
+ {
+ if (!iw)
+ {
+ iw = g_new0 (GdkInputWindow,1);
+
+ iw->impl_window = (GdkWindow *)impl_window;
+
+ iw->windows = NULL;
+
+ display_x11->input_windows = g_list_append (display_x11->input_windows, iw);
+ impl_window->input_window = iw;
+ }
+
+ if (window_private->extension_events == 0)
+ iw->windows = g_list_append (iw->windows, window);
+ window_private->extension_events = mask;
+ }
+ else
+ {
+ unset_extension_events (window);
+ }
+
+#ifndef XINPUT_NONE
+ for (tmp_list = display_x11->input_devices; tmp_list; tmp_list = tmp_list->next)
+ {
+ GdkDevice *dev = tmp_list->data;
+ _gdk_input_select_device_events (GDK_WINDOW (impl_window), dev);
+ }
+#endif /* !XINPUT_NONE */
+}
+
+void
+_gdk_input_window_destroy (GdkWindow *window)
+{
+ unset_extension_events (window);
+}
+
+void
+_gdk_input_check_extension_events (GdkDevice *device)
+{
+ GdkDisplayX11 *display_impl;
+ GdkInputWindow *input_window;
+ GList *tmp_list;
+
+ display_impl = GDK_DISPLAY_X11 (gdk_device_get_display (device));
+
+ for (tmp_list = display_impl->input_windows; tmp_list; tmp_list = tmp_list->next)
+ {
+ input_window = tmp_list->data;
+ _gdk_input_select_device_events (input_window->impl_window, device);
+ }
+}
diff --git a/gdk/broadway/gdkkeys-broadway.c b/gdk/broadway/gdkkeys-broadway.c
new file mode 100644
index 0000000000..aa9a456958
--- /dev/null
+++ b/gdk/broadway/gdkkeys-broadway.c
@@ -0,0 +1,1859 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gdkx.h"
+#include "gdkprivate-broadway.h"
+#include "gdkinternals.h"
+#include "gdkdisplay-broadway.h"
+#include "gdkkeysyms.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+
+#ifdef HAVE_XKB
+#include <X11/XKBlib.h>
+
+/* OSF-4.0 is apparently missing this macro
+ */
+# ifndef XkbKeySymEntry
+# define XkbKeySymEntry(d,k,sl,g) \
+ (XkbKeySym(d,k,((XkbKeyGroupsWidth(d,k)*(g))+(sl))))
+# endif
+#endif /* HAVE_XKB */
+
+typedef struct _GdkKeymapX11 GdkKeymapX11;
+typedef struct _GdkKeymapClass GdkKeymapX11Class;
+
+#define GDK_TYPE_KEYMAP_X11 (gdk_keymap_x11_get_type ())
+#define GDK_KEYMAP_X11(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_KEYMAP_X11, GdkKeymapX11))
+#define GDK_IS_KEYMAP_X11(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_KEYMAP_X11))
+
+typedef struct _DirectionCacheEntry DirectionCacheEntry;
+
+struct _DirectionCacheEntry
+{
+ guint serial;
+ Atom group_atom;
+ PangoDirection direction;
+};
+
+struct _GdkKeymapX11
+{
+ GdkKeymap parent_instance;
+
+ gint min_keycode;
+ gint max_keycode;
+ KeySym* keymap;
+ gint keysyms_per_keycode;
+ XModifierKeymap* mod_keymap;
+ guint lock_keysym;
+ GdkModifierType group_switch_mask;
+ GdkModifierType num_lock_mask;
+ GdkModifierType modmap[8];
+ PangoDirection current_direction;
+ guint sun_keypad : 1;
+ guint have_direction : 1;
+ guint caps_lock_state : 1;
+ guint num_lock_state : 1;
+ guint current_serial;
+
+#ifdef HAVE_XKB
+ XkbDescPtr xkb_desc;
+ /* We cache the directions */
+ Atom current_group_atom;
+ guint current_cache_serial;
+ /* A cache of size four should be more than enough, people usually
+ * have two groups around, and the xkb limit is four. It still
+ * works correct for more than four groups. It's just the
+ * cache.
+ */
+ DirectionCacheEntry group_direction_cache[4];
+#endif
+};
+
+#define KEYMAP_USE_XKB(keymap) GDK_DISPLAY_X11 ((keymap)->display)->use_xkb
+#define KEYMAP_XDISPLAY(keymap) GDK_DISPLAY_XDISPLAY ((keymap)->display)
+
+static GType gdk_keymap_x11_get_type (void);
+static void gdk_keymap_x11_class_init (GdkKeymapX11Class *klass);
+static void gdk_keymap_x11_init (GdkKeymapX11 *keymap);
+static void gdk_keymap_x11_finalize (GObject *object);
+
+static GdkKeymapClass *parent_class = NULL;
+
+static GType
+gdk_keymap_x11_get_type (void)
+{
+ static GType object_type = 0;
+
+ if (!object_type)
+ {
+ const GTypeInfo object_info =
+ {
+ sizeof (GdkKeymapClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gdk_keymap_x11_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GdkKeymapX11),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gdk_keymap_x11_init,
+ };
+
+ object_type = g_type_register_static (GDK_TYPE_KEYMAP,
+ g_intern_static_string ("GdkKeymapX11"),
+ &object_info, 0);
+ }
+
+ return object_type;
+}
+
+static void
+gdk_keymap_x11_class_init (GdkKeymapX11Class *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->finalize = gdk_keymap_x11_finalize;
+}
+
+static void
+gdk_keymap_x11_init (GdkKeymapX11 *keymap)
+{
+ keymap->min_keycode = 0;
+ keymap->max_keycode = 0;
+
+ keymap->keymap = NULL;
+ keymap->keysyms_per_keycode = 0;
+ keymap->mod_keymap = NULL;
+
+ keymap->num_lock_mask = 0;
+ keymap->sun_keypad = FALSE;
+ keymap->group_switch_mask = 0;
+ keymap->lock_keysym = GDK_KEY_Caps_Lock;
+ keymap->have_direction = FALSE;
+ keymap->current_serial = 0;
+
+#ifdef HAVE_XKB
+ keymap->xkb_desc = NULL;
+ keymap->current_group_atom = 0;
+ keymap->current_cache_serial = 0;
+#endif
+
+}
+
+static void
+gdk_keymap_x11_finalize (GObject *object)
+{
+ GdkKeymapX11 *keymap_x11 = GDK_KEYMAP_X11 (object);
+
+ if (keymap_x11->keymap)
+ XFree (keymap_x11->keymap);
+
+ if (keymap_x11->mod_keymap)
+ XFreeModifiermap (keymap_x11->mod_keymap);
+
+#ifdef HAVE_XKB
+ if (keymap_x11->xkb_desc)
+ XkbFreeKeyboard (keymap_x11->xkb_desc, XkbAllComponentsMask, True);
+#endif
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static inline void
+update_keyrange (GdkKeymapX11 *keymap_x11)
+{
+ if (keymap_x11->max_keycode == 0)
+ XDisplayKeycodes (KEYMAP_XDISPLAY (GDK_KEYMAP (keymap_x11)),
+ &keymap_x11->min_keycode, &keymap_x11->max_keycode);
+}
+
+#ifdef HAVE_XKB
+
+static void
+update_modmap (Display *display,
+ GdkKeymapX11 *keymap_x11)
+{
+ static struct {
+ const gchar *name;
+ Atom atom;
+ GdkModifierType mask;
+ } vmods[] = {
+ { "Meta", 0, GDK_META_MASK },
+ { "Super", 0, GDK_SUPER_MASK },
+ { "Hyper", 0, GDK_HYPER_MASK },
+ { NULL, 0, 0 }
+ };
+
+ gint i, j, k;
+
+ if (!vmods[0].atom)
+ for (i = 0; vmods[i].name; i++)
+ vmods[i].atom = XInternAtom (display, vmods[i].name, FALSE);
+
+ for (i = 0; i < 8; i++)
+ keymap_x11->modmap[i] = 1 << i;
+
+ for (i = 0; i < XkbNumVirtualMods; i++)
+ {
+ for (j = 0; vmods[j].atom; j++)
+ {
+ if (keymap_x11->xkb_desc->names->vmods[i] == vmods[j].atom)
+ {
+ for (k = 0; k < 8; k++)
+ {
+ if (keymap_x11->xkb_desc->server->vmods[i] & (1 << k))
+ keymap_x11->modmap[k] |= vmods[j].mask;
+ }
+ }
+ }
+ }
+}
+
+static XkbDescPtr
+get_xkb (GdkKeymapX11 *keymap_x11)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_KEYMAP (keymap_x11)->display);
+ Display *xdisplay = display_x11->xdisplay;
+
+ update_keyrange (keymap_x11);
+
+ if (keymap_x11->xkb_desc == NULL)
+ {
+ keymap_x11->xkb_desc = XkbGetMap (xdisplay, XkbKeySymsMask | XkbKeyTypesMask | XkbModifierMapMask | XkbVirtualModsMask, XkbUseCoreKbd);
+ if (keymap_x11->xkb_desc == NULL)
+ {
+ g_error ("Failed to get keymap");
+ return NULL;
+ }
+
+ XkbGetNames (xdisplay, XkbGroupNamesMask | XkbVirtualModNamesMask, keymap_x11->xkb_desc);
+
+ update_modmap (xdisplay, keymap_x11);
+ }
+ else if (keymap_x11->current_serial != display_x11->keymap_serial)
+ {
+ XkbGetUpdatedMap (xdisplay, XkbKeySymsMask | XkbKeyTypesMask | XkbModifierMapMask | XkbVirtualModsMask,
+ keymap_x11->xkb_desc);
+ XkbGetNames (xdisplay, XkbGroupNamesMask | XkbVirtualModNamesMask, keymap_x11->xkb_desc);
+
+ update_modmap (xdisplay, keymap_x11);
+ }
+
+ keymap_x11->current_serial = display_x11->keymap_serial;
+
+ if (keymap_x11->num_lock_mask == 0)
+ keymap_x11->num_lock_mask = XkbKeysymToModifiers (KEYMAP_XDISPLAY (GDK_KEYMAP (keymap_x11)), XK_Num_Lock);
+
+ return keymap_x11->xkb_desc;
+}
+#endif /* HAVE_XKB */
+
+/* Whether we were able to turn on detectable-autorepeat using
+ * XkbSetDetectableAutorepeat. If FALSE, we'll fall back
+ * to checking the next event with XPending().
+ */
+
+/**
+ * gdk_keymap_get_for_display:
+ * @display: the #GdkDisplay.
+ * @returns: the #GdkKeymap attached to @display.
+ *
+ * Returns the #GdkKeymap attached to @display.
+ *
+ * Since: 2.2
+ **/
+GdkKeymap*
+gdk_keymap_get_for_display (GdkDisplay *display)
+{
+ GdkDisplayX11 *display_x11;
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ if (!display_x11->keymap)
+ display_x11->keymap = g_object_new (gdk_keymap_x11_get_type (), NULL);
+
+ display_x11->keymap->display = display;
+
+ return display_x11->keymap;
+}
+
+/* Find the index of the group/level pair within the keysyms for a key.
+ * We round up the number of keysyms per keycode to the next even number,
+ * otherwise we lose a whole group of keys
+ */
+#define KEYSYM_INDEX(keymap_impl, group, level) \
+ (2 * ((group) % (gint)((keymap_impl->keysyms_per_keycode + 1) / 2)) + (level))
+#define KEYSYM_IS_KEYPAD(s) (((s) >= 0xff80 && (s) <= 0xffbd) || \
+ ((s) >= 0x11000000 && (s) <= 0x1100ffff))
+
+static gint
+get_symbol (const KeySym *syms,
+ GdkKeymapX11 *keymap_x11,
+ gint group,
+ gint level)
+{
+ gint index;
+
+ index = KEYSYM_INDEX(keymap_x11, group, level);
+ if (index >= keymap_x11->keysyms_per_keycode)
+ return NoSymbol;
+
+ return syms[index];
+}
+
+static void
+set_symbol (KeySym *syms,
+ GdkKeymapX11 *keymap_x11,
+ gint group,
+ gint level,
+ KeySym sym)
+{
+ gint index;
+
+ index = KEYSYM_INDEX(keymap_x11, group, level);
+ if (index >= keymap_x11->keysyms_per_keycode)
+ return;
+
+ syms[index] = sym;
+}
+
+static void
+update_keymaps (GdkKeymapX11 *keymap_x11)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_KEYMAP (keymap_x11)->display);
+ Display *xdisplay = display_x11->xdisplay;
+
+#ifdef HAVE_XKB
+ g_assert (!KEYMAP_USE_XKB (GDK_KEYMAP (keymap_x11)));
+#endif
+
+ if (keymap_x11->keymap == NULL ||
+ keymap_x11->current_serial != display_x11->keymap_serial)
+ {
+ gint i;
+ gint map_size;
+ gint keycode;
+
+ keymap_x11->current_serial = display_x11->keymap_serial;
+
+ update_keyrange (keymap_x11);
+
+ if (keymap_x11->keymap)
+ XFree (keymap_x11->keymap);
+
+ if (keymap_x11->mod_keymap)
+ XFreeModifiermap (keymap_x11->mod_keymap);
+
+ keymap_x11->keymap = XGetKeyboardMapping (xdisplay, keymap_x11->min_keycode,
+ keymap_x11->max_keycode - keymap_x11->min_keycode + 1,
+ &keymap_x11->keysyms_per_keycode);
+
+
+ /* GDK_KEY_ISO_Left_Tab, as usually configured through XKB, really messes
+ * up the whole idea of "consumed modifiers" because shift is consumed.
+ * However, <shift>Tab is not usually GDK_KEY_ISO_Left_Tab without XKB,
+ * we we fudge the map here.
+ */
+ keycode = keymap_x11->min_keycode;
+ while (keycode <= keymap_x11->max_keycode)
+ {
+ KeySym *syms = keymap_x11->keymap + (keycode - keymap_x11->min_keycode) * keymap_x11->keysyms_per_keycode;
+ /* Check both groups */
+ for (i = 0 ; i < 2 ; i++)
+ {
+ if (get_symbol (syms, keymap_x11, i, 0) == GDK_KEY_Tab)
+ set_symbol (syms, keymap_x11, i, 1, GDK_KEY_ISO_Left_Tab);
+ }
+
+ /*
+ * If there is one keysym and the key symbol has upper and lower
+ * case variants fudge the keymap
+ */
+ if (get_symbol (syms, keymap_x11, 0, 1) == 0)
+ {
+ guint lower;
+ guint upper;
+
+ gdk_keyval_convert_case (get_symbol (syms, keymap_x11, 0, 0), &lower, &upper);
+ if (lower != upper)
+ {
+ set_symbol (syms, keymap_x11, 0, 0, lower);
+ set_symbol (syms, keymap_x11, 0, 1, upper);
+ }
+ }
+
+
+ ++keycode;
+ }
+
+ keymap_x11->mod_keymap = XGetModifierMapping (xdisplay);
+
+ keymap_x11->lock_keysym = GDK_KEY_VoidSymbol;
+ keymap_x11->group_switch_mask = 0;
+ keymap_x11->num_lock_mask = 0;
+
+ for (i = 0; i < 8; i++)
+ keymap_x11->modmap[i] = 1 << i;
+
+ /* There are 8 sets of modifiers, with each set containing
+ * max_keypermod keycodes.
+ */
+ map_size = 8 * keymap_x11->mod_keymap->max_keypermod;
+ for (i = 0; i < map_size; i++)
+ {
+ /* Get the key code at this point in the map. */
+ gint keycode = keymap_x11->mod_keymap->modifiermap[i];
+ gint j;
+ KeySym *syms;
+ guint mask;
+
+ /* Ignore invalid keycodes. */
+ if (keycode < keymap_x11->min_keycode ||
+ keycode > keymap_x11->max_keycode)
+ continue;
+
+ syms = keymap_x11->keymap + (keycode - keymap_x11->min_keycode) * keymap_x11->keysyms_per_keycode;
+
+ mask = 0;
+ for (j = 0; j < keymap_x11->keysyms_per_keycode; j++)
+ {
+ if (syms[j] == GDK_KEY_Meta_L ||
+ syms[j] == GDK_KEY_Meta_R)
+ mask |= GDK_META_MASK;
+ else if (syms[j] == GDK_KEY_Hyper_L ||
+ syms[j] == GDK_KEY_Hyper_R)
+ mask |= GDK_HYPER_MASK;
+ else if (syms[j] == GDK_KEY_Super_L ||
+ syms[j] == GDK_KEY_Super_R)
+ mask |= GDK_SUPER_MASK;
+ }
+
+ keymap_x11->modmap[i/keymap_x11->mod_keymap->max_keypermod] |= mask;
+
+ /* The fourth modifier, GDK_MOD1_MASK is 1 << 3.
+ * Each group of max_keypermod entries refers to the same modifier.
+ */
+ mask = 1 << (i / keymap_x11->mod_keymap->max_keypermod);
+
+ switch (mask)
+ {
+ case GDK_LOCK_MASK:
+ /* Get the Lock keysym. If any keysym bound to the Lock modifier
+ * is Caps_Lock, we will interpret the modifier as Caps_Lock;
+ * otherwise, if any is bound to Shift_Lock, we will interpret
+ * the modifier as Shift_Lock. Otherwise, the lock modifier
+ * has no effect.
+ */
+ for (j = 0; j < keymap_x11->keysyms_per_keycode; j++)
+ {
+ if (syms[j] == GDK_KEY_Caps_Lock)
+ keymap_x11->lock_keysym = GDK_KEY_Caps_Lock;
+ else if (syms[j] == GDK_KEY_Shift_Lock &&
+ keymap_x11->lock_keysym == GDK_KEY_VoidSymbol)
+ keymap_x11->lock_keysym = GDK_KEY_Shift_Lock;
+ }
+ break;
+
+ case GDK_CONTROL_MASK:
+ case GDK_SHIFT_MASK:
+ case GDK_MOD1_MASK:
+ /* Some keyboard maps are known to map Mode_Switch as an
+ * extra Mod1 key. In circumstances like that, it won't be
+ * used to switch groups.
+ */
+ break;
+
+ default:
+ /* Find the Mode_Switch and Num_Lock modifiers. */
+ for (j = 0; j < keymap_x11->keysyms_per_keycode; j++)
+ {
+ if (syms[j] == GDK_KEY_Mode_switch)
+ {
+ /* This modifier swaps groups */
+ keymap_x11->group_switch_mask |= mask;
+ }
+ else if (syms[j] == GDK_KEY_Num_Lock)
+ {
+ /* This modifier is used for Num_Lock */
+ keymap_x11->num_lock_mask |= mask;
+ }
+ }
+ break;
+ }
+ }
+
+ /* Hack: The Sun X server puts the keysym to use when the Num Lock
+ * modifier is on in the third element of the keysym array, instead
+ * of the second.
+ */
+ if ((strcmp (ServerVendor (xdisplay), "Sun Microsystems, Inc.") == 0) &&
+ (keymap_x11->keysyms_per_keycode > 2))
+ keymap_x11->sun_keypad = TRUE;
+ else
+ keymap_x11->sun_keypad = FALSE;
+ }
+}
+
+static const KeySym*
+get_keymap (GdkKeymapX11 *keymap_x11)
+{
+ update_keymaps (keymap_x11);
+
+ return keymap_x11->keymap;
+}
+
+#define GET_EFFECTIVE_KEYMAP(keymap) get_effective_keymap ((keymap), G_STRFUNC)
+
+static GdkKeymap *
+get_effective_keymap (GdkKeymap *keymap,
+ const char *function)
+{
+ if (!keymap)
+ {
+ GDK_NOTE (MULTIHEAD,
+ g_message ("reverting to default display keymap in %s",
+ function));
+ return gdk_keymap_get_default ();
+ }
+
+ return keymap;
+}
+
+#if HAVE_XKB
+static PangoDirection
+get_direction (XkbDescRec *xkb,
+ gint group)
+{
+ gint code;
+
+ gint rtl_minus_ltr = 0; /* total number of RTL keysyms minus LTR ones */
+
+ for (code = xkb->min_key_code; code <= xkb->max_key_code; code++)
+ {
+ gint level = 0;
+ KeySym sym = XkbKeySymEntry (xkb, code, level, group);
+ PangoDirection dir = pango_unichar_direction (gdk_keyval_to_unicode (sym));
+
+ switch (dir)
+ {
+ case PANGO_DIRECTION_RTL:
+ rtl_minus_ltr++;
+ break;
+ case PANGO_DIRECTION_LTR:
+ rtl_minus_ltr--;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (rtl_minus_ltr > 0)
+ return PANGO_DIRECTION_RTL;
+ else
+ return PANGO_DIRECTION_LTR;
+}
+
+static PangoDirection
+get_direction_from_cache (GdkKeymapX11 *keymap_x11,
+ XkbDescPtr xkb,
+ gint group)
+{
+ Atom group_atom = xkb->names->groups[group];
+
+ gboolean cache_hit = FALSE;
+ DirectionCacheEntry *cache = keymap_x11->group_direction_cache;
+
+ PangoDirection direction = PANGO_DIRECTION_NEUTRAL;
+ gint i;
+
+ if (keymap_x11->have_direction)
+ {
+ /* lookup in cache */
+ for (i = 0; i < G_N_ELEMENTS (keymap_x11->group_direction_cache); i++)
+ {
+ if (cache[i].group_atom == group_atom)
+ {
+ cache_hit = TRUE;
+ cache[i].serial = keymap_x11->current_cache_serial++; /* freshen */
+ direction = cache[i].direction;
+ group_atom = cache[i].group_atom;
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* initialize cache */
+ for (i = 0; i < G_N_ELEMENTS (keymap_x11->group_direction_cache); i++)
+ {
+ cache[i].group_atom = 0;
+ cache[i].serial = keymap_x11->current_cache_serial;
+ }
+ keymap_x11->current_cache_serial++;
+ }
+
+ /* insert in cache */
+ if (!cache_hit)
+ {
+ gint oldest = 0;
+
+ direction = get_direction (xkb, group);
+
+ /* remove the oldest entry */
+ for (i = 0; i < G_N_ELEMENTS (keymap_x11->group_direction_cache); i++)
+ {
+ if (cache[i].serial < cache[oldest].serial)
+ oldest = i;
+ }
+
+ cache[oldest].group_atom = group_atom;
+ cache[oldest].direction = direction;
+ cache[oldest].serial = keymap_x11->current_cache_serial++;
+ }
+
+ return direction;
+}
+
+static int
+get_num_groups (GdkKeymap *keymap,
+ XkbDescPtr xkb)
+{
+ Display *display = KEYMAP_XDISPLAY (keymap);
+ XkbGetControls(display, XkbSlowKeysMask, xkb);
+ XkbGetUpdatedMap (display, XkbKeySymsMask | XkbKeyTypesMask |
+ XkbModifierMapMask | XkbVirtualModsMask, xkb);
+ return xkb->ctrls->num_groups;
+}
+
+static gboolean
+update_direction (GdkKeymapX11 *keymap_x11,
+ gint group)
+{
+ XkbDescPtr xkb = get_xkb (keymap_x11);
+ Atom group_atom;
+ gboolean had_direction;
+ PangoDirection old_direction;
+
+ had_direction = keymap_x11->have_direction;
+ old_direction = keymap_x11->current_direction;
+
+ group_atom = xkb->names->groups[group];
+
+ /* a group change? */
+ if (!keymap_x11->have_direction || keymap_x11->current_group_atom != group_atom)
+ {
+ keymap_x11->current_direction = get_direction_from_cache (keymap_x11, xkb, group);
+ keymap_x11->current_group_atom = group_atom;
+ keymap_x11->have_direction = TRUE;
+ }
+
+ return !had_direction || old_direction != keymap_x11->current_direction;
+}
+
+static gboolean
+update_lock_state (GdkKeymapX11 *keymap_x11,
+ gint locked_mods)
+{
+ gboolean caps_lock_state;
+ gboolean num_lock_state;
+
+ caps_lock_state = keymap_x11->caps_lock_state;
+ num_lock_state = keymap_x11->num_lock_state;
+
+ keymap_x11->caps_lock_state = (locked_mods & GDK_LOCK_MASK) != 0;
+ keymap_x11->num_lock_state = (locked_mods & keymap_x11->num_lock_mask) != 0;
+
+ return (caps_lock_state != keymap_x11->caps_lock_state)
+ || (num_lock_state != keymap_x11->num_lock_state);
+}
+
+/* keep this in sync with the XkbSelectEventDetails() call
+ * in gdk_display_open()
+ */
+void
+_gdk_keymap_state_changed (GdkDisplay *display,
+ XEvent *xevent)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+ XkbEvent *xkb_event = (XkbEvent *)xevent;
+
+ if (display_x11->keymap)
+ {
+ GdkKeymapX11 *keymap_x11 = GDK_KEYMAP_X11 (display_x11->keymap);
+
+ if (update_direction (keymap_x11, XkbStateGroup (&xkb_event->state)))
+ g_signal_emit_by_name (keymap_x11, "direction-changed");
+
+ if (update_lock_state (keymap_x11, xkb_event->state.locked_mods))
+ g_signal_emit_by_name (keymap_x11, "state-changed");
+ }
+}
+
+#endif /* HAVE_XKB */
+
+void
+_gdk_keymap_keys_changed (GdkDisplay *display)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+ ++display_x11->keymap_serial;
+
+ if (display_x11->keymap)
+ g_signal_emit_by_name (display_x11->keymap, "keys_changed", 0);
+}
+
+/**
+ * gdk_keymap_get_direction:
+ * @keymap: a #GdkKeymap or %NULL to use the default keymap
+ *
+ * Returns the direction of effective layout of the keymap.
+ *
+ * Returns: %PANGO_DIRECTION_LTR or %PANGO_DIRECTION_RTL
+ * if it can determine the direction. %PANGO_DIRECTION_NEUTRAL
+ * otherwise.
+ **/
+PangoDirection
+gdk_keymap_get_direction (GdkKeymap *keymap)
+{
+ keymap = GET_EFFECTIVE_KEYMAP (keymap);
+
+#if HAVE_XKB
+ if (KEYMAP_USE_XKB (keymap))
+ {
+ GdkKeymapX11 *keymap_x11 = GDK_KEYMAP_X11 (keymap);
+
+ if (!keymap_x11->have_direction)
+ {
+ GdkDisplay *display = GDK_KEYMAP (keymap_x11)->display;
+ XkbStateRec state_rec;
+
+ XkbGetState (GDK_DISPLAY_XDISPLAY (display), XkbUseCoreKbd,
+ &state_rec);
+ update_direction (keymap_x11, XkbStateGroup (&state_rec));
+ }
+
+ return keymap_x11->current_direction;
+ }
+ else
+#endif /* HAVE_XKB */
+ return PANGO_DIRECTION_NEUTRAL;
+}
+
+/**
+ * gdk_keymap_have_bidi_layouts:
+ * @keymap: a #GdkKeymap or %NULL to use the default keymap
+ *
+ * Determines if keyboard layouts for both right-to-left and left-to-right
+ * languages are in use.
+ *
+ * Returns: %TRUE if there are layouts in both directions, %FALSE otherwise
+ *
+ * Since: 2.12
+ **/
+gboolean
+gdk_keymap_have_bidi_layouts (GdkKeymap *keymap)
+{
+ keymap = GET_EFFECTIVE_KEYMAP (keymap);
+
+#if HAVE_XKB
+ if (KEYMAP_USE_XKB (keymap))
+ {
+ GdkKeymapX11 *keymap_x11 = GDK_KEYMAP_X11 (keymap);
+ XkbDescPtr xkb = get_xkb (keymap_x11);
+ int num_groups = get_num_groups (keymap, xkb);
+
+ int i;
+ gboolean have_ltr_keyboard = FALSE;
+ gboolean have_rtl_keyboard = FALSE;
+
+ for (i = 0; i < num_groups; i++)
+ {
+ if (get_direction_from_cache (keymap_x11, xkb, i) == PANGO_DIRECTION_RTL)
+ have_rtl_keyboard = TRUE;
+ else
+ have_ltr_keyboard = TRUE;
+ }
+
+ return have_ltr_keyboard && have_rtl_keyboard;
+ }
+ else
+#endif /* HAVE_XKB */
+ return FALSE;
+}
+
+/**
+ * gdk_keymap_get_caps_lock_state:
+ * @keymap: a #GdkKeymap
+ *
+ * Returns whether the Caps Lock modifer is locked.
+ *
+ * Returns: %TRUE if Caps Lock is on
+ *
+ * Since: 2.16
+ */
+gboolean
+gdk_keymap_get_caps_lock_state (GdkKeymap *keymap)
+{
+ GdkKeymapX11 *keymap_x11;
+
+ keymap = GET_EFFECTIVE_KEYMAP (keymap);
+
+ keymap_x11 = GDK_KEYMAP_X11 (keymap);
+
+ return keymap_x11->caps_lock_state;
+}
+
+/**
+ * gdk_keymap_get_num_lock_state:
+ * @keymap: a #GdkKeymap
+ *
+ * Returns whether the Num Lock modifer is locked.
+ *
+ * Returns: %TRUE if Num Lock is on
+ *
+ * Since: 3.0
+ */
+gboolean
+gdk_keymap_get_num_lock_state (GdkKeymap *keymap)
+{
+ GdkKeymapX11 *keymap_x11;
+
+ keymap = GET_EFFECTIVE_KEYMAP (keymap);
+
+ keymap_x11 = GDK_KEYMAP_X11 (keymap);
+
+ return keymap_x11->num_lock_state;
+}
+
+/**
+ * gdk_keymap_get_entries_for_keyval:
+ * @keymap: (allow-none): a #GdkKeymap, or %NULL to use the default keymap
+ * @keyval: a keyval, such as %GDK_a, %GDK_Up, %GDK_Return, etc.
+ * @keys: (out): return location for an array of #GdkKeymapKey
+ * @n_keys: (out): return location for number of elements in returned array
+ *
+ * Obtains a list of keycode/group/level combinations that will
+ * generate @keyval. Groups and levels are two kinds of keyboard mode;
+ * in general, the level determines whether the top or bottom symbol
+ * on a key is used, and the group determines whether the left or
+ * right symbol is used. On US keyboards, the shift key changes the
+ * keyboard level, and there are no groups. A group switch key might
+ * convert a keyboard between Hebrew to English modes, for example.
+ * #GdkEventKey contains a %group field that indicates the active
+ * keyboard group. The level is computed from the modifier mask.
+ * The returned array should be freed
+ * with g_free().
+ *
+ * Return value: %TRUE if keys were found and returned
+ **/
+gboolean
+gdk_keymap_get_entries_for_keyval (GdkKeymap *keymap,
+ guint keyval,
+ GdkKeymapKey **keys,
+ gint *n_keys)
+{
+ GArray *retval;
+ GdkKeymapX11 *keymap_x11;
+
+ g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
+ g_return_val_if_fail (keys != NULL, FALSE);
+ g_return_val_if_fail (n_keys != NULL, FALSE);
+ g_return_val_if_fail (keyval != 0, FALSE);
+
+ keymap = GET_EFFECTIVE_KEYMAP (keymap);
+ keymap_x11 = GDK_KEYMAP_X11 (keymap);
+
+ retval = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey));
+
+#ifdef HAVE_XKB
+ if (KEYMAP_USE_XKB (keymap))
+ {
+ /* See sec 15.3.4 in XKB docs */
+
+ XkbDescRec *xkb = get_xkb (keymap_x11);
+ gint keycode;
+
+ keycode = keymap_x11->min_keycode;
+
+ while (keycode <= keymap_x11->max_keycode)
+ {
+ gint max_shift_levels = XkbKeyGroupsWidth (xkb, keycode); /* "key width" */
+ gint group = 0;
+ gint level = 0;
+ gint total_syms = XkbKeyNumSyms (xkb, keycode);
+ gint i = 0;
+ KeySym *entry;
+
+ /* entry is an array with all syms for group 0, all
+ * syms for group 1, etc. and for each group the
+ * shift level syms are in order
+ */
+ entry = XkbKeySymsPtr (xkb, keycode);
+
+ while (i < total_syms)
+ {
+ /* check out our cool loop invariant */
+ g_assert (i == (group * max_shift_levels + level));
+
+ if (entry[i] == keyval)
+ {
+ /* Found a match */
+ GdkKeymapKey key;
+
+ key.keycode = keycode;
+ key.group = group;
+ key.level = level;
+
+ g_array_append_val (retval, key);
+
+ g_assert (XkbKeySymEntry (xkb, keycode, level, group) ==
+ keyval);
+ }
+
+ ++level;
+
+ if (level == max_shift_levels)
+ {
+ level = 0;
+ ++group;
+ }
+
+ ++i;
+ }
+
+ ++keycode;
+ }
+ }
+ else
+#endif
+ {
+ const KeySym *map = get_keymap (keymap_x11);
+ gint keycode;
+
+ keycode = keymap_x11->min_keycode;
+ while (keycode <= keymap_x11->max_keycode)
+ {
+ const KeySym *syms = map + (keycode - keymap_x11->min_keycode) * keymap_x11->keysyms_per_keycode;
+ gint i = 0;
+
+ while (i < keymap_x11->keysyms_per_keycode)
+ {
+ if (syms[i] == keyval)
+ {
+ /* found a match */
+ GdkKeymapKey key;
+
+ key.keycode = keycode;
+
+ /* The "classic" non-XKB keymap has 2 levels per group */
+ key.group = i / 2;
+ key.level = i % 2;
+
+ g_array_append_val (retval, key);
+ }
+
+ ++i;
+ }
+
+ ++keycode;
+ }
+ }
+
+ if (retval->len > 0)
+ {
+ *keys = (GdkKeymapKey*) retval->data;
+ *n_keys = retval->len;
+ }
+ else
+ {
+ *keys = NULL;
+ *n_keys = 0;
+ }
+
+ g_array_free (retval, retval->len > 0 ? FALSE : TRUE);
+
+ return *n_keys > 0;
+}
+
+/**
+ * gdk_keymap_get_entries_for_keycode:
+ * @keymap: (allow-none): a #GdkKeymap or %NULL to use the default keymap
+ * @hardware_keycode: a keycode
+ * @keys: (out): return location for array of #GdkKeymapKey, or %NULL
+ * @keyvals: (out): return location for array of keyvals, or %NULL
+ * @n_entries: length of @keys and @keyvals
+ *
+ * Returns the keyvals bound to @hardware_keycode.
+ * The Nth #GdkKeymapKey in @keys is bound to the Nth
+ * keyval in @keyvals. Free the returned arrays with g_free().
+ * When a keycode is pressed by the user, the keyval from
+ * this list of entries is selected by considering the effective
+ * keyboard group and level. See gdk_keymap_translate_keyboard_state().
+ *
+ * Returns: %TRUE if there were any entries
+ **/
+gboolean
+gdk_keymap_get_entries_for_keycode (GdkKeymap *keymap,
+ guint hardware_keycode,
+ GdkKeymapKey **keys,
+ guint **keyvals,
+ gint *n_entries)
+{
+ GdkKeymapX11 *keymap_x11;
+
+ GArray *key_array;
+ GArray *keyval_array;
+
+ g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
+ g_return_val_if_fail (n_entries != NULL, FALSE);
+
+ keymap = GET_EFFECTIVE_KEYMAP (keymap);
+ keymap_x11 = GDK_KEYMAP_X11 (keymap);
+
+ update_keyrange (keymap_x11);
+
+ if (hardware_keycode < keymap_x11->min_keycode ||
+ hardware_keycode > keymap_x11->max_keycode)
+ {
+ if (keys)
+ *keys = NULL;
+ if (keyvals)
+ *keyvals = NULL;
+
+ *n_entries = 0;
+ return FALSE;
+ }
+
+ if (keys)
+ key_array = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey));
+ else
+ key_array = NULL;
+
+ if (keyvals)
+ keyval_array = g_array_new (FALSE, FALSE, sizeof (guint));
+ else
+ keyval_array = NULL;
+
+#ifdef HAVE_XKB
+ if (KEYMAP_USE_XKB (keymap))
+ {
+ /* See sec 15.3.4 in XKB docs */
+
+ XkbDescRec *xkb = get_xkb (keymap_x11);
+ gint max_shift_levels;
+ gint group = 0;
+ gint level = 0;
+ gint total_syms;
+ gint i = 0;
+ KeySym *entry;
+
+ max_shift_levels = XkbKeyGroupsWidth (xkb, hardware_keycode); /* "key width" */
+ total_syms = XkbKeyNumSyms (xkb, hardware_keycode);
+
+ /* entry is an array with all syms for group 0, all
+ * syms for group 1, etc. and for each group the
+ * shift level syms are in order
+ */
+ entry = XkbKeySymsPtr (xkb, hardware_keycode);
+
+ while (i < total_syms)
+ {
+ /* check out our cool loop invariant */
+ g_assert (i == (group * max_shift_levels + level));
+
+ if (key_array)
+ {
+ GdkKeymapKey key;
+
+ key.keycode = hardware_keycode;
+ key.group = group;
+ key.level = level;
+
+ g_array_append_val (key_array, key);
+ }
+
+ if (keyval_array)
+ g_array_append_val (keyval_array, entry[i]);
+
+ ++level;
+
+ if (level == max_shift_levels)
+ {
+ level = 0;
+ ++group;
+ }
+
+ ++i;
+ }
+ }
+ else
+#endif
+ {
+ const KeySym *map = get_keymap (keymap_x11);
+ const KeySym *syms;
+ gint i = 0;
+
+ syms = map + (hardware_keycode - keymap_x11->min_keycode) * keymap_x11->keysyms_per_keycode;
+
+ while (i < keymap_x11->keysyms_per_keycode)
+ {
+ if (key_array)
+ {
+ GdkKeymapKey key;
+
+ key.keycode = hardware_keycode;
+
+ /* The "classic" non-XKB keymap has 2 levels per group */
+ key.group = i / 2;
+ key.level = i % 2;
+
+ g_array_append_val (key_array, key);
+ }
+
+ if (keyval_array)
+ g_array_append_val (keyval_array, syms[i]);
+
+ ++i;
+ }
+ }
+
+ *n_entries = 0;
+
+ if (keys)
+ {
+ *n_entries = key_array->len;
+ *keys = (GdkKeymapKey*) g_array_free (key_array, FALSE);
+ }
+
+ if (keyvals)
+ {
+ *n_entries = keyval_array->len;
+ *keyvals = (guint*) g_array_free (keyval_array, FALSE);
+ }
+
+ return *n_entries > 0;
+}
+
+
+/**
+ * gdk_keymap_lookup_key:
+ * @keymap: a #GdkKeymap or %NULL to use the default keymap
+ * @key: a #GdkKeymapKey with keycode, group, and level initialized
+ *
+ * Looks up the keyval mapped to a keycode/group/level triplet.
+ * If no keyval is bound to @key, returns 0. For normal user input,
+ * you want to use gdk_keymap_translate_keyboard_state() instead of
+ * this function, since the effective group/level may not be
+ * the same as the current keyboard state.
+ *
+ * Return value: a keyval, or 0 if none was mapped to the given @key
+ **/
+guint
+gdk_keymap_lookup_key (GdkKeymap *keymap,
+ const GdkKeymapKey *key)
+{
+ GdkKeymapX11 *keymap_x11;
+
+ g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), 0);
+ g_return_val_if_fail (key != NULL, 0);
+ g_return_val_if_fail (key->group < 4, 0);
+
+ keymap = GET_EFFECTIVE_KEYMAP (keymap);
+ keymap_x11 = GDK_KEYMAP_X11 (keymap);
+
+#ifdef HAVE_XKB
+ if (KEYMAP_USE_XKB (keymap))
+ {
+ XkbDescRec *xkb = get_xkb (keymap_x11);
+
+ return XkbKeySymEntry (xkb, key->keycode, key->level, key->group);
+ }
+ else
+#endif
+ {
+ const KeySym *map = get_keymap (keymap_x11);
+ const KeySym *syms = map + (key->keycode - keymap_x11->min_keycode) * keymap_x11->keysyms_per_keycode;
+ return get_symbol (syms, keymap_x11, key->group, key->level);
+ }
+}
+
+#ifdef HAVE_XKB
+/* This is copied straight from XFree86 Xlib, to:
+ * - add the group and level return.
+ * - change the interpretation of mods_rtrn as described
+ * in the docs for gdk_keymap_translate_keyboard_state()
+ * It's unchanged for ease of diff against the Xlib sources; don't
+ * reformat it.
+ */
+static Bool
+MyEnhancedXkbTranslateKeyCode(register XkbDescPtr xkb,
+ KeyCode key,
+ register unsigned int mods,
+ unsigned int * mods_rtrn,
+ KeySym * keysym_rtrn,
+ int * group_rtrn,
+ int * level_rtrn)
+{
+ XkbKeyTypeRec *type;
+ int col,nKeyGroups;
+ unsigned preserve,effectiveGroup;
+ KeySym *syms;
+
+ if (mods_rtrn!=NULL)
+ *mods_rtrn = 0;
+
+ nKeyGroups= XkbKeyNumGroups(xkb,key);
+ if ((!XkbKeycodeInRange(xkb,key))||(nKeyGroups==0)) {
+ if (keysym_rtrn!=NULL)
+ *keysym_rtrn = NoSymbol;
+ return False;
+ }
+
+ syms = XkbKeySymsPtr(xkb,key);
+
+ /* find the offset of the effective group */
+ col = 0;
+ effectiveGroup= XkbGroupForCoreState(mods);
+ if ( effectiveGroup>=nKeyGroups ) {
+ unsigned groupInfo= XkbKeyGroupInfo(xkb,key);
+ switch (XkbOutOfRangeGroupAction(groupInfo)) {
+ default:
+ effectiveGroup %= nKeyGroups;
+ break;
+ case XkbClampIntoRange:
+ effectiveGroup = nKeyGroups-1;
+ break;
+ case XkbRedirectIntoRange:
+ effectiveGroup = XkbOutOfRangeGroupNumber(groupInfo);
+ if (effectiveGroup>=nKeyGroups)
+ effectiveGroup= 0;
+ break;
+ }
+ }
+ col= effectiveGroup*XkbKeyGroupsWidth(xkb,key);
+ type = XkbKeyKeyType(xkb,key,effectiveGroup);
+
+ preserve= 0;
+ if (type->map) { /* find the column (shift level) within the group */
+ register int i;
+ register XkbKTMapEntryPtr entry;
+ /* ---- Begin section modified for GDK ---- */
+ int found = 0;
+
+ for (i=0,entry=type->map;i<type->map_count;i++,entry++) {
+ if (mods_rtrn) {
+ int bits = 0;
+ unsigned long tmp = entry->mods.mask;
+ while (tmp) {
+ if ((tmp & 1) == 1)
+ bits++;
+ tmp >>= 1;
+ }
+ /* We always add one-modifiers levels to mods_rtrn since
+ * they can't wipe out bits in the state unless the
+ * level would be triggered. But return other modifiers
+ *
+ */
+ if (bits == 1 || (mods&type->mods.mask)==entry->mods.mask)
+ *mods_rtrn |= entry->mods.mask;
+ }
+
+ if (!found&&entry->active&&((mods&type->mods.mask)==entry->mods.mask)) {
+ col+= entry->level;
+ if (type->preserve)
+ preserve= type->preserve[i].mask;
+
+ if (level_rtrn)
+ *level_rtrn = entry->level;
+
+ found = 1;
+ }
+ }
+ /* ---- End section modified for GDK ---- */
+ }
+
+ if (keysym_rtrn!=NULL)
+ *keysym_rtrn= syms[col];
+ if (mods_rtrn) {
+ /* ---- Begin section modified for GDK ---- */
+ *mods_rtrn &= ~preserve;
+ /* ---- End section modified for GDK ---- */
+
+ /* ---- Begin stuff GDK comments out of the original Xlib version ---- */
+ /* This is commented out because xkb_info is a private struct */
+
+#if 0
+ /* The Motif VTS doesn't get the help callback called if help
+ * is bound to Shift+<whatever>, and it appears as though it
+ * is XkbTranslateKeyCode that is causing the problem. The
+ * core X version of XTranslateKey always OR's in ShiftMask
+ * and LockMask for mods_rtrn, so this "fix" keeps this behavior
+ * and solves the VTS problem.
+ */
+ if ((xkb->dpy)&&(xkb->dpy->xkb_info)&&
+ (xkb->dpy->xkb_info->xlib_ctrls&XkbLC_AlwaysConsumeShiftAndLock)) { *mods_rtrn|= (ShiftMask|LockMask);
+ }
+#endif
+
+ /* ---- End stuff GDK comments out of the original Xlib version ---- */
+ }
+
+ /* ---- Begin stuff GDK adds to the original Xlib version ---- */
+
+ if (group_rtrn)
+ *group_rtrn = effectiveGroup;
+
+ /* ---- End stuff GDK adds to the original Xlib version ---- */
+
+ return (syms[col] != NoSymbol);
+}
+#endif /* HAVE_XKB */
+
+/* Translates from keycode/state to keysymbol using the traditional interpretation
+ * of the keyboard map. See section 12.7 of the Xlib reference manual
+ */
+static guint
+translate_keysym (GdkKeymapX11 *keymap_x11,
+ guint hardware_keycode,
+ gint group,
+ GdkModifierType state,
+ gint *effective_group,
+ gint *effective_level)
+{
+ const KeySym *map = get_keymap (keymap_x11);
+ const KeySym *syms = map + (hardware_keycode - keymap_x11->min_keycode) * keymap_x11->keysyms_per_keycode;
+
+#define SYM(k,g,l) get_symbol (syms, k,g,l)
+
+ GdkModifierType shift_modifiers;
+ gint shift_level;
+ guint tmp_keyval;
+ gint num_lock_index;
+
+ shift_modifiers = GDK_SHIFT_MASK;
+ if (keymap_x11->lock_keysym == GDK_KEY_Shift_Lock)
+ shift_modifiers |= GDK_LOCK_MASK;
+
+ /* Fall back to the first group if the passed in group is empty
+ */
+ if (!(SYM (keymap_x11, group, 0) || SYM (keymap_x11, group, 1)) &&
+ (SYM (keymap_x11, 0, 0) || SYM (keymap_x11, 0, 1)))
+ group = 0;
+
+ /* Hack: On Sun, the Num Lock modifier uses the third element in the
+ * keysym array, and Mode_Switch does not apply for a keypad key.
+ */
+ if (keymap_x11->sun_keypad)
+ {
+ num_lock_index = 2;
+
+ if (group != 0)
+ {
+ gint i;
+
+ for (i = 0; i < keymap_x11->keysyms_per_keycode; i++)
+ if (KEYSYM_IS_KEYPAD (SYM (keymap_x11, 0, i)))
+ group = 0;
+ }
+ }
+ else
+ num_lock_index = 1;
+
+ if ((state & keymap_x11->num_lock_mask) &&
+ KEYSYM_IS_KEYPAD (SYM (keymap_x11, group, num_lock_index)))
+ {
+ /* Shift, Shift_Lock cancel Num_Lock
+ */
+ shift_level = (state & shift_modifiers) ? 0 : num_lock_index;
+ if (!SYM (keymap_x11, group, shift_level) && SYM (keymap_x11, group, 0))
+ shift_level = 0;
+
+ tmp_keyval = SYM (keymap_x11, group, shift_level);
+ }
+ else
+ {
+ /* Fall back to the first level if no symbol for the level
+ * we were passed.
+ */
+ shift_level = (state & shift_modifiers) ? 1 : 0;
+ if (!SYM (keymap_x11, group, shift_level) && SYM (keymap_x11, group, 0))
+ shift_level = 0;
+
+ tmp_keyval = SYM (keymap_x11, group, shift_level);
+
+ if (keymap_x11->lock_keysym == GDK_KEY_Caps_Lock && (state & GDK_LOCK_MASK) != 0)
+ {
+ guint upper = gdk_keyval_to_upper (tmp_keyval);
+ if (upper != tmp_keyval)
+ tmp_keyval = upper;
+ }
+ }
+
+ if (effective_group)
+ *effective_group = group;
+
+ if (effective_level)
+ *effective_level = shift_level;
+
+ return tmp_keyval;
+
+#undef SYM
+}
+
+/**
+ * gdk_keymap_translate_keyboard_state:
+ * @keymap: (allow-none): a #GdkKeymap, or %NULL to use the default
+ * @hardware_keycode: a keycode
+ * @state: a modifier state
+ * @group: active keyboard group
+ * @keyval: (out) (allow-none): return location for keyval, or %NULL
+ * @effective_group: (out) (allow-none): return location for effective group, or %NULL
+ * @level: (out) (allow-none): return location for level, or %NULL
+ * @consumed_modifiers: (out) (allow-none): return location for modifiers that were used to
+ * determine the group or level, or %NULL
+ *
+ * Translates the contents of a #GdkEventKey into a keyval, effective
+ * group, and level. Modifiers that affected the translation and
+ * are thus unavailable for application use are returned in
+ * @consumed_modifiers. See <xref linkend="key-group-explanation"/> for an explanation of
+ * groups and levels. The @effective_group is the group that was
+ * actually used for the translation; some keys such as Enter are not
+ * affected by the active keyboard group. The @level is derived from
+ * @state. For convenience, #GdkEventKey already contains the translated
+ * keyval, so this function isn't as useful as you might think.
+ *
+ * <note><para>
+ * @consumed_modifiers gives modifiers that should be masked out
+ * from @state when comparing this key press to a hot key. For
+ * instance, on a US keyboard, the <literal>plus</literal>
+ * symbol is shifted, so when comparing a key press to a
+ * <literal>&lt;Control&gt;plus</literal> accelerator &lt;Shift&gt; should
+ * be masked out.
+ * </para>
+ * <informalexample><programlisting>
+ * &sol;* We want to ignore irrelevant modifiers like ScrollLock *&sol;
+ * &num;define ALL_ACCELS_MASK (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK)
+ * gdk_keymap_translate_keyboard_state (keymap, event->hardware_keycode,
+ * event->state, event->group,
+ * &amp;keyval, NULL, NULL, &amp;consumed);
+ * if (keyval == GDK_PLUS &&
+ * (event->state &amp; ~consumed &amp; ALL_ACCELS_MASK) == GDK_CONTROL_MASK)
+ * &sol;* Control was pressed *&sol;
+ * </programlisting></informalexample>
+ * <para>
+ * An older interpretation @consumed_modifiers was that it contained
+ * all modifiers that might affect the translation of the key;
+ * this allowed accelerators to be stored with irrelevant consumed
+ * modifiers, by doing:</para>
+ * <informalexample><programlisting>
+ * &sol;* XXX Don't do this XXX *&sol;
+ * if (keyval == accel_keyval &&
+ * (event->state &amp; ~consumed &amp; ALL_ACCELS_MASK) == (accel_mods &amp; ~consumed))
+ * &sol;* Accelerator was pressed *&sol;
+ * </programlisting></informalexample>
+ * <para>
+ * However, this did not work if multi-modifier combinations were
+ * used in the keymap, since, for instance, <literal>&lt;Control&gt;</literal>
+ * would be masked out even if only <literal>&lt;Control&gt;&lt;Alt&gt;</literal>
+ * was used in the keymap. To support this usage as well as well as
+ * possible, all <emphasis>single modifier</emphasis> combinations
+ * that could affect the key for any combination of modifiers will
+ * be returned in @consumed_modifiers; multi-modifier combinations
+ * are returned only when actually found in @state. When you store
+ * accelerators, you should always store them with consumed modifiers
+ * removed. Store <literal>&lt;Control&gt;plus</literal>,
+ * not <literal>&lt;Control&gt;&lt;Shift&gt;plus</literal>,
+ * </para></note>
+ *
+ * Return value: %TRUE if there was a keyval bound to the keycode/state/group
+ **/
+gboolean
+gdk_keymap_translate_keyboard_state (GdkKeymap *keymap,
+ guint hardware_keycode,
+ GdkModifierType state,
+ gint group,
+ guint *keyval,
+ gint *effective_group,
+ gint *level,
+ GdkModifierType *consumed_modifiers)
+{
+ GdkKeymapX11 *keymap_x11;
+ KeySym tmp_keyval = NoSymbol;
+ guint tmp_modifiers;
+
+ g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
+ g_return_val_if_fail (group < 4, FALSE);
+
+ keymap = GET_EFFECTIVE_KEYMAP (keymap);
+ keymap_x11 = GDK_KEYMAP_X11 (keymap);
+
+ if (keyval)
+ *keyval = NoSymbol;
+ if (effective_group)
+ *effective_group = 0;
+ if (level)
+ *level = 0;
+ if (consumed_modifiers)
+ *consumed_modifiers = 0;
+
+ update_keyrange (keymap_x11);
+
+ if (hardware_keycode < keymap_x11->min_keycode ||
+ hardware_keycode > keymap_x11->max_keycode)
+ return FALSE;
+
+#ifdef HAVE_XKB
+ if (KEYMAP_USE_XKB (keymap))
+ {
+ XkbDescRec *xkb = get_xkb (keymap_x11);
+
+ /* replace bits 13 and 14 with the provided group */
+ state &= ~(1 << 13 | 1 << 14);
+ state |= group << 13;
+
+ MyEnhancedXkbTranslateKeyCode (xkb,
+ hardware_keycode,
+ state,
+ &tmp_modifiers,
+ &tmp_keyval,
+ effective_group,
+ level);
+
+ if (state & ~tmp_modifiers & LockMask)
+ tmp_keyval = gdk_keyval_to_upper (tmp_keyval);
+
+ /* We need to augment the consumed modifiers with LockMask, since
+ * we handle that ourselves, and also with the group bits
+ */
+ tmp_modifiers |= LockMask | 1 << 13 | 1 << 14;
+ }
+ else
+#endif
+ {
+ GdkModifierType bit;
+
+ tmp_modifiers = 0;
+
+ /* We see what modifiers matter by trying the translation with
+ * and without each possible modifier
+ */
+ for (bit = GDK_SHIFT_MASK; bit < GDK_BUTTON1_MASK; bit <<= 1)
+ {
+ /* Handling of the group here is a bit funky; a traditional
+ * X keyboard map can have more than two groups, but no way
+ * of accessing the extra groups is defined. We allow a
+ * caller to pass in any group to this function, but we
+ * only can represent switching between group 0 and 1 in
+ * consumed modifiers.
+ */
+ if (translate_keysym (keymap_x11, hardware_keycode,
+ (bit == keymap_x11->group_switch_mask) ? 0 : group,
+ state & ~bit,
+ NULL, NULL) !=
+ translate_keysym (keymap_x11, hardware_keycode,
+ (bit == keymap_x11->group_switch_mask) ? 1 : group,
+ state | bit,
+ NULL, NULL))
+ tmp_modifiers |= bit;
+ }
+
+ tmp_keyval = translate_keysym (keymap_x11, hardware_keycode,
+ group, state,
+ level, effective_group);
+ }
+
+ if (consumed_modifiers)
+ *consumed_modifiers = tmp_modifiers;
+
+ if (keyval)
+ *keyval = tmp_keyval;
+
+ return tmp_keyval != NoSymbol;
+}
+
+
+/* Key handling not part of the keymap */
+/**
+ * gdk_keyval_name:
+ *
+ * Converts a key value into a symbolic name.
+ * The names are the same as those in the
+ * <filename>&lt;gdk/gdkkeysyms.h&gt;</filename> header file
+ * but without the leading "GDK_KEY_".
+ *
+ * @keyval: a key value.
+ *
+ * Return value: (transfer none): a string containing the name of the key, or
+ * %NULL if @keyval is not a valid key. The string should not be modified.
+ **/
+gchar*
+gdk_keyval_name (guint keyval)
+{
+ switch (keyval)
+ {
+ case GDK_KEY_Page_Up:
+ return "Page_Up";
+ case GDK_KEY_Page_Down:
+ return "Page_Down";
+ case GDK_KEY_KP_Page_Up:
+ return "KP_Page_Up";
+ case GDK_KEY_KP_Page_Down:
+ return "KP_Page_Down";
+ }
+
+ return XKeysymToString (keyval);
+}
+
+guint
+gdk_keyval_from_name (const gchar *keyval_name)
+{
+ g_return_val_if_fail (keyval_name != NULL, 0);
+
+ return XStringToKeysym (keyval_name);
+}
+
+#ifdef HAVE_XCONVERTCASE
+void
+gdk_keyval_convert_case (guint symbol,
+ guint *lower,
+ guint *upper)
+{
+ KeySym xlower = 0;
+ KeySym xupper = 0;
+
+ /* Check for directly encoded 24-bit UCS characters: */
+ if ((symbol & 0xff000000) == 0x01000000)
+ {
+ if (lower)
+ *lower = gdk_unicode_to_keyval (g_unichar_tolower (symbol & 0x00ffffff));
+ if (upper)
+ *upper = gdk_unicode_to_keyval (g_unichar_toupper (symbol & 0x00ffffff));
+ return;
+ }
+
+ if (symbol)
+ XConvertCase (symbol, &xlower, &xupper);
+
+ if (lower)
+ *lower = xlower;
+ if (upper)
+ *upper = xupper;
+}
+#endif /* HAVE_XCONVERTCASE */
+
+gint
+_gdk_x11_get_group_for_state (GdkDisplay *display,
+ GdkModifierType state)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+#ifdef HAVE_XKB
+ if (display_x11->use_xkb)
+ {
+ return XkbGroupForCoreState (state);
+ }
+ else
+#endif
+ {
+ GdkKeymapX11 *keymap_impl = GDK_KEYMAP_X11 (gdk_keymap_get_for_display (display));
+ update_keymaps (keymap_impl);
+ return (state & keymap_impl->group_switch_mask) ? 1 : 0;
+ }
+}
+
+void
+_gdk_keymap_add_virtual_modifiers_compat (GdkKeymap *keymap,
+ GdkModifierType *modifiers)
+{
+ GdkKeymapX11 *keymap_x11;
+ int i;
+
+ keymap = GET_EFFECTIVE_KEYMAP (keymap);
+ keymap_x11 = GDK_KEYMAP_X11 (keymap);
+
+ for (i = 3; i < 8; i++)
+ {
+ if ((1 << i) & *modifiers)
+ {
+ if (keymap_x11->modmap[i] & GDK_MOD1_MASK)
+ *modifiers |= GDK_MOD1_MASK;
+ else if (keymap_x11->modmap[i] & GDK_SUPER_MASK)
+ *modifiers |= GDK_SUPER_MASK;
+ else if (keymap_x11->modmap[i] & GDK_HYPER_MASK)
+ *modifiers |= GDK_HYPER_MASK;
+ else if (keymap_x11->modmap[i] & GDK_META_MASK)
+ *modifiers |= GDK_META_MASK;
+ }
+ }
+}
+
+/**
+ * gdk_keymap_add_virtual_modifiers:
+ * @keymap: a #GdkKeymap
+ * @state: pointer to the modifier mask to change
+ *
+ * Adds virtual modifiers (i.e. Super, Hyper and Meta) which correspond
+ * to the real modifiers (i.e Mod2, Mod3, ...) in @modifiers.
+ * are set in @state to their non-virtual counterparts (i.e. Mod2,
+ * Mod3,...) and set the corresponding bits in @state.
+ *
+ * GDK already does this before delivering key events, but for
+ * compatibility reasons, it only sets the first virtual modifier
+ * it finds, whereas this function sets all matching virtual modifiers.
+ *
+ * This function is useful when matching key events against
+ * accelerators.
+ *
+ * Since: 2.20
+ */
+void
+gdk_keymap_add_virtual_modifiers (GdkKeymap *keymap,
+ GdkModifierType *state)
+{
+ GdkKeymapX11 *keymap_x11;
+ int i;
+
+ keymap = GET_EFFECTIVE_KEYMAP (keymap);
+ keymap_x11 = GDK_KEYMAP_X11 (keymap);
+
+ for (i = 3; i < 8; i++)
+ {
+ if ((1 << i) & *state)
+ {
+ if (keymap_x11->modmap[i] & GDK_MOD1_MASK)
+ *state |= GDK_MOD1_MASK;
+ if (keymap_x11->modmap[i] & GDK_SUPER_MASK)
+ *state |= GDK_SUPER_MASK;
+ if (keymap_x11->modmap[i] & GDK_HYPER_MASK)
+ *state |= GDK_HYPER_MASK;
+ if (keymap_x11->modmap[i] & GDK_META_MASK)
+ *state |= GDK_META_MASK;
+ }
+ }
+}
+
+gboolean
+_gdk_keymap_key_is_modifier (GdkKeymap *keymap,
+ guint keycode)
+{
+ GdkKeymapX11 *keymap_x11;
+ gint i;
+
+ keymap = GET_EFFECTIVE_KEYMAP (keymap);
+ keymap_x11 = GDK_KEYMAP_X11 (keymap);
+
+ if (keycode < keymap_x11->min_keycode ||
+ keycode > keymap_x11->max_keycode)
+ return FALSE;
+
+#ifdef HAVE_XKB
+ if (KEYMAP_USE_XKB (keymap))
+ {
+ XkbDescRec *xkb = get_xkb (keymap_x11);
+
+ if (xkb->map->modmap && xkb->map->modmap[keycode] != 0)
+ return TRUE;
+ }
+ else
+#endif
+ {
+ for (i = 0; i < 8 * keymap_x11->mod_keymap->max_keypermod; i++)
+ {
+ if (keycode == keymap_x11->mod_keymap->modifiermap[i])
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * gdk_keymap_map_virtual_modifiers:
+ * @keymap: a #GdkKeymap
+ * @state: pointer to the modifier state to map
+ *
+ * Maps the virtual modifiers (i.e. Super, Hyper and Meta) which
+ * are set in @state to their non-virtual counterparts (i.e. Mod2,
+ * Mod3,...) and set the corresponding bits in @state.
+ *
+ * This function is useful when matching key events against
+ * accelerators.
+ *
+ * Returns: %TRUE if no virtual modifiers were mapped to the
+ * same non-virtual modifier. Note that %FALSE is also returned
+ * if a virtual modifier is mapped to a non-virtual modifier that
+ * was already set in @state.
+ *
+ * Since: 2.20
+ */
+gboolean
+gdk_keymap_map_virtual_modifiers (GdkKeymap *keymap,
+ GdkModifierType *state)
+{
+ GdkKeymapX11 *keymap_x11;
+ const guint vmods[] = {
+ GDK_SUPER_MASK, GDK_HYPER_MASK, GDK_META_MASK
+ };
+ int i, j;
+ gboolean retval;
+
+ keymap = GET_EFFECTIVE_KEYMAP (keymap);
+ keymap_x11 = GDK_KEYMAP_X11 (keymap);
+
+ if (KEYMAP_USE_XKB (keymap))
+ get_xkb (keymap_x11);
+
+ retval = TRUE;
+
+ for (j = 0; j < 3; j++)
+ {
+ if (*state & vmods[j])
+ {
+ for (i = 3; i < 8; i++)
+ {
+ if (keymap_x11->modmap[i] & vmods[j])
+ {
+ if (*state & (1 << i))
+ retval = FALSE;
+ else
+ *state |= 1 << i;
+ }
+ }
+ }
+ }
+
+ return retval;
+}
diff --git a/gdk/broadway/gdkmain-broadway.c b/gdk/broadway/gdkmain-broadway.c
new file mode 100644
index 0000000000..8dd7f1f8e3
--- /dev/null
+++ b/gdk/broadway/gdkmain-broadway.c
@@ -0,0 +1,698 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gdkx.h"
+#include "gdkasync.h"
+#include "gdkdisplay-broadway.h"
+#include "gdkinternals.h"
+#include "gdkprivate-broadway.h"
+#include "gdkintl.h"
+#include "gdkdeviceprivate.h"
+
+#include <glib/gprintf.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#ifdef HAVE_XKB
+#include <X11/XKBlib.h>
+#endif
+
+
+typedef struct _GdkPredicate GdkPredicate;
+typedef struct _GdkGlobalErrorTrap GdkGlobalErrorTrap;
+
+struct _GdkPredicate
+{
+ GdkEventFunc func;
+ gpointer data;
+};
+
+/* non-GDK previous error handler */
+typedef int (*GdkXErrorHandler) (Display *, XErrorEvent *);
+static GdkXErrorHandler _gdk_old_error_handler;
+/* number of times we've pushed the GDK error handler */
+static int _gdk_error_handler_push_count = 0;
+
+struct _GdkGlobalErrorTrap
+{
+ GSList *displays;
+};
+
+/*
+ * Private function declarations
+ */
+
+#ifndef HAVE_XCONVERTCASE
+static void gdkx_XConvertCase (KeySym symbol,
+ KeySym *lower,
+ KeySym *upper);
+#define XConvertCase gdkx_XConvertCase
+#endif
+
+static int gdk_x_error (Display *display,
+ XErrorEvent *error);
+static int gdk_x_io_error (Display *display);
+
+/* Private variable declarations
+ */
+static GQueue gdk_error_traps;
+
+const GOptionEntry _gdk_windowing_args[] = {
+ { "sync", 0, 0, G_OPTION_ARG_NONE, &_gdk_synchronize,
+ /* Description of --sync in --help output */ N_("Make X calls synchronous"), NULL },
+ { NULL }
+};
+
+void
+_gdk_windowing_init (void)
+{
+ _gdk_x11_initialize_locale ();
+
+ g_queue_init (&gdk_error_traps);
+ XSetErrorHandler (gdk_x_error);
+ XSetIOErrorHandler (gdk_x_io_error);
+
+ _gdk_selection_property = gdk_atom_intern_static_string ("GDK_SELECTION");
+}
+
+GdkGrabStatus
+_gdk_x11_convert_grab_status (gint status)
+{
+ switch (status)
+ {
+ case GrabSuccess:
+ return GDK_GRAB_SUCCESS;
+ case AlreadyGrabbed:
+ return GDK_GRAB_ALREADY_GRABBED;
+ case GrabInvalidTime:
+ return GDK_GRAB_INVALID_TIME;
+ case GrabNotViewable:
+ return GDK_GRAB_NOT_VIEWABLE;
+ case GrabFrozen:
+ return GDK_GRAB_FROZEN;
+ }
+
+ g_assert_not_reached();
+
+ return 0;
+}
+
+static void
+has_pointer_grab_callback (GdkDisplay *display,
+ gpointer data,
+ gulong serial)
+{
+ GdkDevice *device = data;
+
+ _gdk_display_device_grab_update (display, device, serial);
+}
+
+GdkGrabStatus
+_gdk_windowing_device_grab (GdkDevice *device,
+ GdkWindow *window,
+ GdkWindow *native,
+ gboolean owner_events,
+ GdkEventMask event_mask,
+ GdkWindow *confine_to,
+ GdkCursor *cursor,
+ guint32 time)
+{
+ GdkDisplay *display;
+ GdkGrabStatus status = GDK_GRAB_SUCCESS;
+
+ if (!window || GDK_WINDOW_DESTROYED (window))
+ return GDK_GRAB_NOT_VIEWABLE;
+
+ display = gdk_device_get_display (device);
+
+#ifdef G_ENABLE_DEBUG
+ if (_gdk_debug_flags & GDK_DEBUG_NOGRABS)
+ status = GrabSuccess;
+ else
+#endif
+ status = GDK_DEVICE_GET_CLASS (device)->grab (device,
+ native,
+ owner_events,
+ event_mask,
+ confine_to,
+ cursor,
+ time);
+ if (status == GDK_GRAB_SUCCESS)
+ _gdk_x11_roundtrip_async (display,
+ has_pointer_grab_callback,
+ device);
+ return status;
+}
+
+/**
+ * _gdk_xgrab_check_unmap:
+ * @window: a #GdkWindow
+ * @serial: serial from Unmap event (or from NextRequest(display)
+ * if the unmap is being done by this client.)
+ *
+ * Checks to see if an unmap request or event causes the current
+ * grab window to become not viewable, and if so, clear the
+ * the pointer we keep to it.
+ **/
+void
+_gdk_xgrab_check_unmap (GdkWindow *window,
+ gulong serial)
+{
+ GdkDisplay *display = gdk_window_get_display (window);
+ GdkDeviceManager *device_manager;
+ GList *devices, *d;
+
+ device_manager = gdk_display_get_device_manager (display);
+
+ /* Get all devices */
+ devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+ devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
+ devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_FLOATING));
+
+ /* End all grabs on the newly hidden window */
+ for (d = devices; d; d = d->next)
+ _gdk_display_end_device_grab (display, d->data, serial, window, TRUE);
+
+ g_list_free (devices);
+}
+
+/**
+ * _gdk_xgrab_check_destroy:
+ * @window: a #GdkWindow
+ *
+ * Checks to see if window is the current grab window, and if
+ * so, clear the current grab window.
+ **/
+void
+_gdk_xgrab_check_destroy (GdkWindow *window)
+{
+ GdkDisplay *display = gdk_window_get_display (window);
+ GdkDeviceManager *device_manager;
+ GdkDeviceGrabInfo *grab;
+ GList *devices, *d;
+
+ device_manager = gdk_display_get_device_manager (display);
+
+ /* Get all devices */
+ devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+ devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
+ devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_FLOATING));
+
+ for (d = devices; d; d = d->next)
+ {
+ /* Make sure there is no lasting grab in this native window */
+ grab = _gdk_display_get_last_device_grab (display, d->data);
+
+ if (grab && grab->native_window == window)
+ {
+ /* We don't know the actual serial to end, but it
+ doesn't really matter as this only happens
+ after we get told of the destroy from the
+ server so we know its ended in the server,
+ just make sure its ended. */
+ grab->serial_end = grab->serial_start;
+ grab->implicit_ungrab = TRUE;
+ }
+ }
+
+ g_list_free (devices);
+}
+
+void
+_gdk_windowing_display_set_sm_client_id (GdkDisplay *display,
+ const gchar *sm_client_id)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+ if (display->closed)
+ return;
+
+ if (sm_client_id && strcmp (sm_client_id, ""))
+ {
+ XChangeProperty (display_x11->xdisplay, display_x11->leader_window,
+ gdk_x11_get_xatom_by_name_for_display (display, "SM_CLIENT_ID"),
+ XA_STRING, 8, PropModeReplace, (guchar *)sm_client_id,
+ strlen (sm_client_id));
+ }
+ else
+ XDeleteProperty (display_x11->xdisplay, display_x11->leader_window,
+ gdk_x11_get_xatom_by_name_for_display (display, "SM_CLIENT_ID"));
+}
+
+/* Close all open displays
+ */
+void
+_gdk_windowing_exit (void)
+{
+ GSList *tmp_list = _gdk_displays;
+
+ while (tmp_list)
+ {
+ XCloseDisplay (GDK_DISPLAY_XDISPLAY (tmp_list->data));
+
+ tmp_list = tmp_list->next;
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ * gdk_x_io_error
+ *
+ * The X I/O error handling routine.
+ *
+ * Arguments:
+ * "display" is the X display the error orignated from.
+ *
+ * Results:
+ * An X I/O error basically means we lost our connection
+ * to the X server. There is not much we can do to
+ * continue, so simply print an error message and exit.
+ *
+ * Side effects:
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+gdk_x_io_error (Display *display)
+{
+ /* This is basically modelled after the code in XLib. We need
+ * an explicit error handler here, so we can disable our atexit()
+ * which would otherwise cause a nice segfault.
+ * We fprintf(stderr, instead of g_warning() because g_warning()
+ * could possibly be redirected to a dialog
+ */
+ if (errno == EPIPE)
+ {
+ g_warning ("The application '%s' lost its connection to the display %s;\n"
+ "most likely the X server was shut down or you killed/destroyed\n"
+ "the application.\n",
+ g_get_prgname (),
+ display ? DisplayString (display) : gdk_get_display_arg_name ());
+ }
+ else
+ {
+ g_warning ("%s: Fatal IO error %d (%s) on X server %s.\n",
+ g_get_prgname (),
+ errno, g_strerror (errno),
+ display ? DisplayString (display) : gdk_get_display_arg_name ());
+ }
+
+ exit(1);
+}
+
+/* X error handler. Keep the name the same because people are used to
+ * breaking on it in the debugger.
+ */
+static int
+gdk_x_error (Display *xdisplay,
+ XErrorEvent *error)
+{
+ if (error->error_code)
+ {
+ GdkDisplay *error_display;
+ GdkDisplayManager *manager;
+ GSList *displays;
+
+ /* Figure out which GdkDisplay if any got the error. */
+ error_display = NULL;
+ manager = gdk_display_manager_get ();
+ displays = gdk_display_manager_list_displays (manager);
+ while (displays != NULL)
+ {
+ GdkDisplayX11 *gdk_display = displays->data;
+
+ if (xdisplay == gdk_display->xdisplay)
+ {
+ error_display = GDK_DISPLAY_OBJECT (gdk_display);
+ g_slist_free (displays);
+ displays = NULL;
+ }
+ else
+ {
+ displays = g_slist_delete_link (displays, displays);
+ }
+ }
+
+ if (error_display == NULL)
+ {
+ /* Error on an X display not opened by GDK. Ignore. */
+
+ return 0;
+ }
+ else
+ {
+ _gdk_x11_display_error_event (error_display, error);
+ }
+ }
+
+ return 0;
+}
+
+void
+_gdk_x11_error_handler_push (void)
+{
+ GdkXErrorHandler previous;
+
+ previous = XSetErrorHandler (gdk_x_error);
+
+ if (_gdk_error_handler_push_count > 0)
+ {
+ if (previous != gdk_x_error)
+ g_warning ("XSetErrorHandler() called with a GDK error trap pushed. Don't do that.");
+ }
+ else
+ {
+ _gdk_old_error_handler = previous;
+ }
+
+ _gdk_error_handler_push_count += 1;
+}
+
+void
+_gdk_x11_error_handler_pop (void)
+{
+ g_return_if_fail (_gdk_error_handler_push_count > 0);
+
+ _gdk_error_handler_push_count -= 1;
+
+ if (_gdk_error_handler_push_count == 0)
+ {
+ XSetErrorHandler (_gdk_old_error_handler);
+ _gdk_old_error_handler = NULL;
+ }
+}
+
+/**
+ * gdk_error_trap_push:
+ *
+ * This function allows X errors to be trapped instead of the normal
+ * behavior of exiting the application. It should only be used if it
+ * is not possible to avoid the X error in any other way. Errors are
+ * ignored on all #GdkDisplay currently known to the
+ * #GdkDisplayManager. If you don't care which error happens and just
+ * want to ignore everything, pop with gdk_error_trap_pop_ignored().
+ * If you need the error code, use gdk_error_trap_pop() which may have
+ * to block and wait for the error to arrive from the X server.
+ *
+ * This API exists on all platforms but only does anything on X.
+ *
+ * You can use gdk_x11_display_error_trap_push() to ignore errors
+ * on only a single display.
+ *
+ * <example>
+ * <title>Trapping an X error</title>
+ * <programlisting>
+ * gdk_error_trap_push (<!-- -->);
+ *
+ * // ... Call the X function which may cause an error here ...
+ *
+ *
+ * if (gdk_error_trap_pop (<!-- -->))
+ * {
+ * // ... Handle the error here ...
+ * }
+ * </programlisting>
+ * </example>
+ *
+ */
+void
+gdk_error_trap_push (void)
+{
+ GdkGlobalErrorTrap *trap;
+ GdkDisplayManager *manager;
+ GSList *tmp_list;
+
+ trap = g_slice_new (GdkGlobalErrorTrap);
+ manager = gdk_display_manager_get ();
+ trap->displays = gdk_display_manager_list_displays (manager);
+
+ g_slist_foreach (trap->displays, (GFunc) g_object_ref, NULL);
+ for (tmp_list = trap->displays;
+ tmp_list != NULL;
+ tmp_list = tmp_list->next)
+ {
+ gdk_x11_display_error_trap_push (tmp_list->data);
+ }
+
+ g_queue_push_head (&gdk_error_traps, trap);
+}
+
+static gint
+gdk_error_trap_pop_internal (gboolean need_code)
+{
+ GdkGlobalErrorTrap *trap;
+ gint result;
+ GSList *tmp_list;
+
+ trap = g_queue_pop_head (&gdk_error_traps);
+
+ g_return_val_if_fail (trap != NULL, Success);
+
+ result = Success;
+ for (tmp_list = trap->displays;
+ tmp_list != NULL;
+ tmp_list = tmp_list->next)
+ {
+ gint code = Success;
+
+ if (need_code)
+ code = gdk_x11_display_error_trap_pop (tmp_list->data);
+ else
+ gdk_x11_display_error_trap_pop_ignored (tmp_list->data);
+
+ /* we use the error on the last display listed, why not. */
+ if (code != Success)
+ result = code;
+ }
+
+ g_slist_foreach (trap->displays, (GFunc) g_object_unref, NULL);
+ g_slist_free (trap->displays);
+
+ g_slice_free (GdkGlobalErrorTrap, trap);
+
+ return result;
+}
+
+/**
+ * gdk_error_trap_pop_ignored:
+ *
+ * Removes an error trap pushed with gdk_error_trap_push(), but
+ * without bothering to wait and see whether an error occurred. If an
+ * error arrives later asynchronously that was triggered while the
+ * trap was pushed, that error will be ignored.
+ *
+ * Since: 3.0
+ */
+void
+gdk_error_trap_pop_ignored (void)
+{
+ gdk_error_trap_pop_internal (FALSE);
+}
+
+/**
+ * gdk_error_trap_pop:
+ *
+ * Removes an error trap pushed with gdk_error_trap_push().
+ * May block until an error has been definitively received
+ * or not received from the X server. gdk_error_trap_pop_ignored()
+ * is preferred if you don't need to know whether an error
+ * occurred, because it never has to block. If you don't
+ * need the return value of gdk_error_trap_pop(), use
+ * gdk_error_trap_pop_ignored().
+ *
+ * Prior to GDK 3.0, this function would not automatically
+ * sync for you, so you had to gdk_flush() if your last
+ * call to Xlib was not a blocking round trip.
+ *
+ * Return value: X error code or 0 on success
+ */
+gint
+gdk_error_trap_pop (void)
+{
+ return gdk_error_trap_pop_internal (TRUE);
+}
+
+gchar *
+gdk_get_display (void)
+{
+ return g_strdup (gdk_display_get_name (gdk_display_get_default ()));
+}
+
+/**
+ * _gdk_send_xevent:
+ * @display: #GdkDisplay which @window is on
+ * @window: window ID to which to send the event
+ * @propagate: %TRUE if the event should be propagated if the target window
+ * doesn't handle it.
+ * @event_mask: event mask to match against, or 0 to send it to @window
+ * without regard to event masks.
+ * @event_send: #XEvent to send
+ *
+ * Send an event, like XSendEvent(), but trap errors and check
+ * the result.
+ *
+ * Return value: %TRUE if sending the event succeeded.
+ **/
+gint
+_gdk_send_xevent (GdkDisplay *display,
+ Window window,
+ gboolean propagate,
+ glong event_mask,
+ XEvent *event_send)
+{
+ gboolean result;
+
+ if (display->closed)
+ return FALSE;
+
+ gdk_error_trap_push ();
+ result = XSendEvent (GDK_DISPLAY_XDISPLAY (display), window,
+ propagate, event_mask, event_send);
+ XSync (GDK_DISPLAY_XDISPLAY (display), False);
+
+ if (gdk_error_trap_pop ())
+ return FALSE;
+
+ return result;
+}
+
+void
+_gdk_region_get_xrectangles (const cairo_region_t *region,
+ gint x_offset,
+ gint y_offset,
+ XRectangle **rects,
+ gint *n_rects)
+{
+ XRectangle *rectangles;
+ cairo_rectangle_int_t box;
+ gint i, n;
+
+ n = cairo_region_num_rectangles (region);
+ rectangles = g_new (XRectangle, n);
+
+ for (i = 0; i < n; i++)
+ {
+ cairo_region_get_rectangle (region, i, &box);
+ rectangles[i].x = CLAMP (box.x + x_offset, G_MINSHORT, G_MAXSHORT);
+ rectangles[i].y = CLAMP (box.y + y_offset, G_MINSHORT, G_MAXSHORT);
+ rectangles[i].width = CLAMP (box.width, G_MINSHORT, G_MAXSHORT);
+ rectangles[i].height = CLAMP (box.height, G_MINSHORT, G_MAXSHORT);
+ }
+
+ *n_rects = n;
+ *rects = rectangles;
+}
+
+/**
+ * gdk_x11_grab_server:
+ *
+ * Call gdk_x11_display_grab() on the default display.
+ * To ungrab the server again, use gdk_x11_ungrab_server().
+ *
+ * gdk_x11_grab_server()/gdk_x11_ungrab_server() calls can be nested.
+ **/
+void
+gdk_x11_grab_server (void)
+{
+ gdk_x11_display_grab (gdk_display_get_default ());
+}
+
+/**
+ * gdk_x11_ungrab_server:
+ *
+ * Ungrab the default display after it has been grabbed with
+ * gdk_x11_grab_server().
+ **/
+void
+gdk_x11_ungrab_server (void)
+{
+ gdk_x11_display_ungrab (gdk_display_get_default ());
+}
+
+/**
+ * gdk_x11_get_default_screen:
+ *
+ * Gets the default GTK+ screen number.
+ *
+ * Return value: returns the screen number specified by
+ * the --display command line option or the DISPLAY environment
+ * variable when gdk_init() calls XOpenDisplay().
+ **/
+gint
+gdk_x11_get_default_screen (void)
+{
+ return gdk_screen_get_number (gdk_screen_get_default ());
+}
+
+/**
+ * gdk_x11_get_default_root_xwindow:
+ *
+ * Gets the root window of the default screen
+ * (see gdk_x11_get_default_screen()).
+ *
+ * Return value: an Xlib <type>Window</type>.
+ **/
+Window
+gdk_x11_get_default_root_xwindow (void)
+{
+ return GDK_SCREEN_XROOTWIN (gdk_screen_get_default ());
+}
+
+/**
+ * gdk_x11_get_default_xdisplay:
+ *
+ * Gets the default GTK+ display.
+ *
+ * Return value: the Xlib <type>Display*</type> for the display
+ * specified in the <option>--display</option> command line option
+ * or the <envar>DISPLAY</envar> environment variable.
+ **/
+Display *
+gdk_x11_get_default_xdisplay (void)
+{
+ return GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+}
+
+void
+_gdk_windowing_event_data_copy (const GdkEvent *src,
+ GdkEvent *dst)
+{
+}
+
+void
+_gdk_windowing_event_data_free (GdkEvent *event)
+{
+}
diff --git a/gdk/broadway/gdkprivate-broadway.h b/gdk/broadway/gdkprivate-broadway.h
new file mode 100644
index 0000000000..0f957ac972
--- /dev/null
+++ b/gdk/broadway/gdkprivate-broadway.h
@@ -0,0 +1,162 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * Private uninstalled header defining things local to X windowing code
+ */
+
+#ifndef __GDK_PRIVATE_BROADWAY_H__
+#define __GDK_PRIVATE_BROADWAY_H__
+
+#include <gdk/gdkcursor.h>
+#include <gdk/gdkprivate.h>
+#include <gdk/gdkinternals.h>
+#include "gdkwindow-broadway.h"
+#include "gdkdisplay-broadway.h"
+#include <cairo-xlib.h>
+
+typedef struct _GdkCursorPrivate GdkCursorPrivate;
+
+struct _GdkCursorPrivate
+{
+ GdkCursor cursor;
+ Cursor xcursor;
+ GdkDisplay *display;
+ gchar *name;
+ guint serial;
+};
+
+void _gdk_x11_error_handler_push (void);
+void _gdk_x11_error_handler_pop (void);
+
+Colormap _gdk_visual_get_x11_colormap (GdkVisual *visual);
+
+void _gdk_xid_table_insert (GdkDisplay *display,
+ XID *xid,
+ gpointer data);
+void _gdk_xid_table_remove (GdkDisplay *display,
+ XID xid);
+gint _gdk_send_xevent (GdkDisplay *display,
+ Window window,
+ gboolean propagate,
+ glong event_mask,
+ XEvent *event_send);
+
+/* Routines from gdkgeometry-broadway.c */
+void _gdk_window_move_resize_child (GdkWindow *window,
+ gint x,
+ gint y,
+ gint width,
+ gint height);
+void _gdk_window_process_expose (GdkWindow *window,
+ gulong serial,
+ GdkRectangle *area);
+
+gboolean _gdk_x11_window_queue_antiexpose (GdkWindow *window,
+ cairo_region_t *area);
+void _gdk_x11_window_translate (GdkWindow *window,
+ cairo_region_t *area,
+ gint dx,
+ gint dy);
+
+void _gdk_selection_window_destroyed (GdkWindow *window);
+gboolean _gdk_selection_filter_clear_event (XSelectionClearEvent *event);
+
+cairo_region_t* _xwindow_get_shape (Display *xdisplay,
+ Window window,
+ gint shape_type);
+
+void _gdk_region_get_xrectangles (const cairo_region_t *region,
+ gint x_offset,
+ gint y_offset,
+ XRectangle **rects,
+ gint *n_rects);
+
+gboolean _gdk_moveresize_handle_event (XEvent *event);
+gboolean _gdk_moveresize_configure_done (GdkDisplay *display,
+ GdkWindow *window);
+
+void _gdk_keymap_state_changed (GdkDisplay *display,
+ XEvent *event);
+void _gdk_keymap_keys_changed (GdkDisplay *display);
+gint _gdk_x11_get_group_for_state (GdkDisplay *display,
+ GdkModifierType state);
+void _gdk_keymap_add_virtual_modifiers_compat (GdkKeymap *keymap,
+ GdkModifierType *modifiers);
+gboolean _gdk_keymap_key_is_modifier (GdkKeymap *keymap,
+ guint keycode);
+
+void _gdk_x11_initialize_locale (void);
+
+void _gdk_xgrab_check_unmap (GdkWindow *window,
+ gulong serial);
+void _gdk_xgrab_check_destroy (GdkWindow *window);
+
+gboolean _gdk_x11_display_is_root_window (GdkDisplay *display,
+ Window xroot_window);
+
+void _gdk_x11_precache_atoms (GdkDisplay *display,
+ const gchar * const *atom_names,
+ gint n_atoms);
+
+void _gdk_screen_x11_events_init (GdkScreen *screen);
+
+void _gdk_events_init (GdkDisplay *display);
+void _gdk_events_uninit (GdkDisplay *display);
+void _gdk_windowing_window_init (GdkScreen *screen);
+void _gdk_visual_init (GdkScreen *screen);
+void _gdk_dnd_init (GdkDisplay *display);
+
+void _gdk_x11_cursor_update_theme (GdkCursor *cursor);
+void _gdk_x11_cursor_display_finalize (GdkDisplay *display);
+
+gboolean _gdk_x11_get_xft_setting (GdkScreen *screen,
+ const gchar *name,
+ GValue *value);
+
+GdkGrabStatus _gdk_x11_convert_grab_status (gint status);
+
+cairo_surface_t * _gdk_x11_window_create_bitmap_surface (GdkWindow *window,
+ int width,
+ int height);
+
+extern GdkDrawableClass _gdk_x11_drawable_class;
+extern gboolean _gdk_use_xshm;
+extern const int _gdk_nenvent_masks;
+extern const int _gdk_event_mask_table[];
+extern GdkAtom _gdk_selection_property;
+extern gboolean _gdk_synchronize;
+
+#define GDK_DRAWABLE_XROOTWIN(win) (GDK_WINDOW_XROOTWIN (win))
+#define GDK_SCREEN_DISPLAY(screen) (GDK_SCREEN_X11 (screen)->display)
+#define GDK_SCREEN_XROOTWIN(screen) (GDK_SCREEN_X11 (screen)->xroot_window)
+#define GDK_WINDOW_SCREEN(win) (GDK_DRAWABLE_IMPL_X11 (((GdkWindowObject *)win)->impl)->screen)
+#define GDK_WINDOW_DISPLAY(win) (GDK_SCREEN_X11 (GDK_WINDOW_SCREEN (win))->display)
+#define GDK_WINDOW_XROOTWIN(win) (GDK_SCREEN_X11 (GDK_WINDOW_SCREEN (win))->xroot_window)
+#define GDK_GC_DISPLAY(gc) (GDK_SCREEN_DISPLAY (GDK_GC_X11(gc)->screen))
+#define GDK_WINDOW_IS_X11(win) (GDK_IS_WINDOW_IMPL_X11 (((GdkWindowObject *)win)->impl))
+
+#endif /* __GDK_PRIVATE_BROADWAY_H__ */
diff --git a/gdk/broadway/gdkproperty-broadway.c b/gdk/broadway/gdkproperty-broadway.c
new file mode 100644
index 0000000000..49a6e75fd8
--- /dev/null
+++ b/gdk/broadway/gdkproperty-broadway.c
@@ -0,0 +1,761 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gdkproperty.h"
+
+#include "gdkmain.h"
+#include "gdkx.h"
+#include "gdkprivate.h"
+#include "gdkinternals.h"
+#include "gdkdisplay-broadway.h"
+#include "gdkscreen-broadway.h"
+#include "gdkselection.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <string.h>
+
+
+static GPtrArray *virtual_atom_array;
+static GHashTable *virtual_atom_hash;
+
+static const gchar xatoms_string[] =
+ /* These are all the standard predefined X atoms */
+ "\0" /* leave a space for None, even though it is not a predefined atom */
+ "PRIMARY\0"
+ "SECONDARY\0"
+ "ARC\0"
+ "ATOM\0"
+ "BITMAP\0"
+ "CARDINAL\0"
+ "COLORMAP\0"
+ "CURSOR\0"
+ "CUT_BUFFER0\0"
+ "CUT_BUFFER1\0"
+ "CUT_BUFFER2\0"
+ "CUT_BUFFER3\0"
+ "CUT_BUFFER4\0"
+ "CUT_BUFFER5\0"
+ "CUT_BUFFER6\0"
+ "CUT_BUFFER7\0"
+ "DRAWABLE\0"
+ "FONT\0"
+ "INTEGER\0"
+ "PIXMAP\0"
+ "POINT\0"
+ "RECTANGLE\0"
+ "RESOURCE_MANAGER\0"
+ "RGB_COLOR_MAP\0"
+ "RGB_BEST_MAP\0"
+ "RGB_BLUE_MAP\0"
+ "RGB_DEFAULT_MAP\0"
+ "RGB_GRAY_MAP\0"
+ "RGB_GREEN_MAP\0"
+ "RGB_RED_MAP\0"
+ "STRING\0"
+ "VISUALID\0"
+ "WINDOW\0"
+ "WM_COMMAND\0"
+ "WM_HINTS\0"
+ "WM_CLIENT_MACHINE\0"
+ "WM_ICON_NAME\0"
+ "WM_ICON_SIZE\0"
+ "WM_NAME\0"
+ "WM_NORMAL_HINTS\0"
+ "WM_SIZE_HINTS\0"
+ "WM_ZOOM_HINTS\0"
+ "MIN_SPACE\0"
+ "NORM_SPACE\0"
+ "MAX_SPACE\0"
+ "END_SPACE\0"
+ "SUPERSCRIPT_X\0"
+ "SUPERSCRIPT_Y\0"
+ "SUBSCRIPT_X\0"
+ "SUBSCRIPT_Y\0"
+ "UNDERLINE_POSITION\0"
+ "UNDERLINE_THICKNESS\0"
+ "STRIKEOUT_ASCENT\0"
+ "STRIKEOUT_DESCENT\0"
+ "ITALIC_ANGLE\0"
+ "X_HEIGHT\0"
+ "QUAD_WIDTH\0"
+ "WEIGHT\0"
+ "POINT_SIZE\0"
+ "RESOLUTION\0"
+ "COPYRIGHT\0"
+ "NOTICE\0"
+ "FONT_NAME\0"
+ "FAMILY_NAME\0"
+ "FULL_NAME\0"
+ "CAP_HEIGHT\0"
+ "WM_CLASS\0"
+ "WM_TRANSIENT_FOR\0"
+ /* Below here, these are our additions. Increment N_CUSTOM_PREDEFINED
+ * if you add any.
+ */
+ "CLIPBOARD\0" /* = 69 */
+;
+
+static const gint xatoms_offset[] = {
+ 0, 1, 9, 19, 23, 28, 35, 44, 53, 60, 72, 84,
+ 96, 108, 120, 132, 144, 156, 165, 170, 178, 185, 189, 201,
+ 218, 232, 245, 258, 274, 287, 301, 313, 320, 329, 336, 347,
+ 356, 374, 387, 400, 408, 424, 438, 452, 462, 473, 483, 493,
+ 507, 521, 533, 545, 564, 584, 601, 619, 632, 641, 652, 659,
+ 670, 681, 691, 698, 708, 720, 730, 741, 750, 767
+};
+
+#define N_CUSTOM_PREDEFINED 1
+
+#define ATOM_TO_INDEX(atom) (GPOINTER_TO_UINT(atom))
+#define INDEX_TO_ATOM(atom) ((GdkAtom)GUINT_TO_POINTER(atom))
+
+static void
+insert_atom_pair (GdkDisplay *display,
+ GdkAtom virtual_atom,
+ Atom xatom)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+ if (!display_x11->atom_from_virtual)
+ {
+ display_x11->atom_from_virtual = g_hash_table_new (g_direct_hash, NULL);
+ display_x11->atom_to_virtual = g_hash_table_new (g_direct_hash, NULL);
+ }
+
+ g_hash_table_insert (display_x11->atom_from_virtual,
+ GDK_ATOM_TO_POINTER (virtual_atom),
+ GUINT_TO_POINTER (xatom));
+ g_hash_table_insert (display_x11->atom_to_virtual,
+ GUINT_TO_POINTER (xatom),
+ GDK_ATOM_TO_POINTER (virtual_atom));
+}
+
+static Atom
+lookup_cached_xatom (GdkDisplay *display,
+ GdkAtom atom)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+ if (ATOM_TO_INDEX (atom) < G_N_ELEMENTS (xatoms_offset) - N_CUSTOM_PREDEFINED)
+ return ATOM_TO_INDEX (atom);
+
+ if (display_x11->atom_from_virtual)
+ return GPOINTER_TO_UINT (g_hash_table_lookup (display_x11->atom_from_virtual,
+ GDK_ATOM_TO_POINTER (atom)));
+
+ return None;
+}
+
+/**
+ * gdk_x11_atom_to_xatom_for_display:
+ * @display: A #GdkDisplay
+ * @atom: A #GdkAtom, or %GDK_NONE
+ *
+ * Converts from a #GdkAtom to the X atom for a #GdkDisplay
+ * with the same string value. The special value %GDK_NONE
+ * is converted to %None.
+ *
+ * Return value: the X atom corresponding to @atom, or %None
+ *
+ * Since: 2.2
+ **/
+Atom
+gdk_x11_atom_to_xatom_for_display (GdkDisplay *display,
+ GdkAtom atom)
+{
+ Atom xatom = None;
+
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), None);
+
+ if (atom == GDK_NONE)
+ return None;
+
+ if (display->closed)
+ return None;
+
+ xatom = lookup_cached_xatom (display, atom);
+
+ if (!xatom)
+ {
+ char *name;
+
+ g_return_val_if_fail (ATOM_TO_INDEX (atom) < virtual_atom_array->len, None);
+
+ name = g_ptr_array_index (virtual_atom_array, ATOM_TO_INDEX (atom));
+
+ xatom = XInternAtom (GDK_DISPLAY_XDISPLAY (display), name, FALSE);
+ insert_atom_pair (display, atom, xatom);
+ }
+
+ return xatom;
+}
+
+void
+_gdk_x11_precache_atoms (GdkDisplay *display,
+ const gchar * const *atom_names,
+ gint n_atoms)
+{
+ Atom *xatoms;
+ GdkAtom *atoms;
+ const gchar **xatom_names;
+ gint n_xatoms;
+ gint i;
+
+ xatoms = g_new (Atom, n_atoms);
+ xatom_names = g_new (const gchar *, n_atoms);
+ atoms = g_new (GdkAtom, n_atoms);
+
+ n_xatoms = 0;
+ for (i = 0; i < n_atoms; i++)
+ {
+ GdkAtom atom = gdk_atom_intern_static_string (atom_names[i]);
+ if (lookup_cached_xatom (display, atom) == None)
+ {
+ atoms[n_xatoms] = atom;
+ xatom_names[n_xatoms] = atom_names[i];
+ n_xatoms++;
+ }
+ }
+
+ if (n_xatoms)
+ {
+#ifdef HAVE_XINTERNATOMS
+ XInternAtoms (GDK_DISPLAY_XDISPLAY (display),
+ (char **)xatom_names, n_xatoms, False, xatoms);
+#else
+ for (i = 0; i < n_xatoms; i++)
+ xatoms[i] = XInternAtom (GDK_DISPLAY_XDISPLAY (display),
+ xatom_names[i], False);
+#endif
+ }
+
+ for (i = 0; i < n_xatoms; i++)
+ insert_atom_pair (display, atoms[i], xatoms[i]);
+
+ g_free (xatoms);
+ g_free (xatom_names);
+ g_free (atoms);
+}
+
+/**
+ * gdk_x11_atom_to_xatom:
+ * @atom: A #GdkAtom
+ *
+ * Converts from a #GdkAtom to the X atom for the default GDK display
+ * with the same string value.
+ *
+ * Return value: the X atom corresponding to @atom.
+ **/
+Atom
+gdk_x11_atom_to_xatom (GdkAtom atom)
+{
+ return gdk_x11_atom_to_xatom_for_display (gdk_display_get_default (), atom);
+}
+
+/**
+ * gdk_x11_xatom_to_atom_for_display:
+ * @display: A #GdkDisplay
+ * @xatom: an X atom
+ *
+ * Convert from an X atom for a #GdkDisplay to the corresponding
+ * #GdkAtom.
+ *
+ * Return value: the corresponding #GdkAtom.
+ *
+ * Since: 2.2
+ **/
+GdkAtom
+gdk_x11_xatom_to_atom_for_display (GdkDisplay *display,
+ Atom xatom)
+{
+ GdkDisplayX11 *display_x11;
+ GdkAtom virtual_atom = GDK_NONE;
+
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), GDK_NONE);
+
+ if (xatom == None)
+ return GDK_NONE;
+
+ if (display->closed)
+ return GDK_NONE;
+
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ if (xatom < G_N_ELEMENTS (xatoms_offset) - N_CUSTOM_PREDEFINED)
+ return INDEX_TO_ATOM (xatom);
+
+ if (display_x11->atom_to_virtual)
+ virtual_atom = GDK_POINTER_TO_ATOM (g_hash_table_lookup (display_x11->atom_to_virtual,
+ GUINT_TO_POINTER (xatom)));
+
+ if (!virtual_atom)
+ {
+ /* If this atom doesn't exist, we'll die with an X error unless
+ * we take precautions
+ */
+ char *name;
+ gdk_error_trap_push ();
+ name = XGetAtomName (GDK_DISPLAY_XDISPLAY (display), xatom);
+ if (gdk_error_trap_pop ())
+ {
+ g_warning (G_STRLOC " invalid X atom: %ld", xatom);
+ }
+ else
+ {
+ virtual_atom = gdk_atom_intern (name, FALSE);
+ XFree (name);
+
+ insert_atom_pair (display, virtual_atom, xatom);
+ }
+ }
+
+ return virtual_atom;
+}
+
+/**
+ * gdk_x11_xatom_to_atom:
+ * @xatom: an X atom for the default GDK display
+ *
+ * Convert from an X atom for the default display to the corresponding
+ * #GdkAtom.
+ *
+ * Return value: the corresponding G#dkAtom.
+ **/
+GdkAtom
+gdk_x11_xatom_to_atom (Atom xatom)
+{
+ return gdk_x11_xatom_to_atom_for_display (gdk_display_get_default (), xatom);
+}
+
+static void
+virtual_atom_check_init (void)
+{
+ if (!virtual_atom_hash)
+ {
+ gint i;
+
+ virtual_atom_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ virtual_atom_array = g_ptr_array_new ();
+
+ for (i = 0; i < G_N_ELEMENTS (xatoms_offset); i++)
+ {
+ g_ptr_array_add (virtual_atom_array, (gchar *)(xatoms_string + xatoms_offset[i]));
+ g_hash_table_insert (virtual_atom_hash, (gchar *)(xatoms_string + xatoms_offset[i]),
+ GUINT_TO_POINTER (i));
+ }
+ }
+}
+
+static GdkAtom
+intern_atom (const gchar *atom_name,
+ gboolean dup)
+{
+ GdkAtom result;
+
+ virtual_atom_check_init ();
+
+ result = GDK_POINTER_TO_ATOM (g_hash_table_lookup (virtual_atom_hash, atom_name));
+ if (!result)
+ {
+ result = INDEX_TO_ATOM (virtual_atom_array->len);
+
+ g_ptr_array_add (virtual_atom_array, dup ? g_strdup (atom_name) : (gchar *)atom_name);
+ g_hash_table_insert (virtual_atom_hash,
+ g_ptr_array_index (virtual_atom_array,
+ ATOM_TO_INDEX (result)),
+ GDK_ATOM_TO_POINTER (result));
+ }
+
+ return result;
+}
+
+GdkAtom
+gdk_atom_intern (const gchar *atom_name,
+ gboolean only_if_exists)
+{
+ return intern_atom (atom_name, TRUE);
+}
+
+/**
+ * gdk_atom_intern_static_string:
+ * @atom_name: a static string
+ *
+ * Finds or creates an atom corresponding to a given string.
+ *
+ * Note that this function is identical to gdk_atom_intern() except
+ * that if a new #GdkAtom is created the string itself is used rather
+ * than a copy. This saves memory, but can only be used if the string
+ * will <emphasis>always</emphasis> exist. It can be used with statically
+ * allocated strings in the main program, but not with statically
+ * allocated memory in dynamically loaded modules, if you expect to
+ * ever unload the module again (e.g. do not use this function in
+ * GTK+ theme engines).
+ *
+ * Returns: the atom corresponding to @atom_name
+ *
+ * Since: 2.10
+ */
+GdkAtom
+gdk_atom_intern_static_string (const gchar *atom_name)
+{
+ return intern_atom (atom_name, FALSE);
+}
+
+static G_CONST_RETURN char *
+get_atom_name (GdkAtom atom)
+{
+ virtual_atom_check_init ();
+
+ if (ATOM_TO_INDEX (atom) < virtual_atom_array->len)
+ return g_ptr_array_index (virtual_atom_array, ATOM_TO_INDEX (atom));
+ else
+ return NULL;
+}
+
+gchar *
+gdk_atom_name (GdkAtom atom)
+{
+ return g_strdup (get_atom_name (atom));
+}
+
+/**
+ * gdk_x11_get_xatom_by_name_for_display:
+ * @display: a #GdkDisplay
+ * @atom_name: a string
+ *
+ * Returns the X atom for a #GdkDisplay corresponding to @atom_name.
+ * This function caches the result, so if called repeatedly it is much
+ * faster than XInternAtom(), which is a round trip to the server each time.
+ *
+ * Return value: a X atom for a #GdkDisplay
+ *
+ * Since: 2.2
+ **/
+Atom
+gdk_x11_get_xatom_by_name_for_display (GdkDisplay *display,
+ const gchar *atom_name)
+{
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), None);
+ return gdk_x11_atom_to_xatom_for_display (display,
+ gdk_atom_intern (atom_name, FALSE));
+}
+
+/**
+ * gdk_x11_get_xatom_by_name:
+ * @atom_name: a string
+ *
+ * Returns the X atom for GDK's default display corresponding to @atom_name.
+ * This function caches the result, so if called repeatedly it is much
+ * faster than XInternAtom(), which is a round trip to the server each time.
+ *
+ * Return value: a X atom for GDK's default display.
+ **/
+Atom
+gdk_x11_get_xatom_by_name (const gchar *atom_name)
+{
+ return gdk_x11_get_xatom_by_name_for_display (gdk_display_get_default (),
+ atom_name);
+}
+
+/**
+ * gdk_x11_get_xatom_name_for_display:
+ * @display: the #GdkDisplay where @xatom is defined
+ * @xatom: an X atom
+ *
+ * Returns the name of an X atom for its display. This
+ * function is meant mainly for debugging, so for convenience, unlike
+ * XAtomName() and gdk_atom_name(), the result doesn't need to
+ * be freed.
+ *
+ * Return value: name of the X atom; this string is owned by GDK,
+ * so it shouldn't be modifed or freed.
+ *
+ * Since: 2.2
+ **/
+G_CONST_RETURN gchar *
+gdk_x11_get_xatom_name_for_display (GdkDisplay *display,
+ Atom xatom)
+{
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
+
+ return get_atom_name (gdk_x11_xatom_to_atom_for_display (display, xatom));
+}
+
+/**
+ * gdk_x11_get_xatom_name:
+ * @xatom: an X atom for GDK's default display
+ *
+ * Returns the name of an X atom for GDK's default display. This
+ * function is meant mainly for debugging, so for convenience, unlike
+ * <function>XAtomName()</function> and gdk_atom_name(), the result
+ * doesn't need to be freed. Also, this function will never return %NULL,
+ * even if @xatom is invalid.
+ *
+ * Return value: name of the X atom; this string is owned by GTK+,
+ * so it shouldn't be modifed or freed.
+ **/
+G_CONST_RETURN gchar *
+gdk_x11_get_xatom_name (Atom xatom)
+{
+ return get_atom_name (gdk_x11_xatom_to_atom (xatom));
+}
+
+gboolean
+gdk_property_get (GdkWindow *window,
+ GdkAtom property,
+ GdkAtom type,
+ gulong offset,
+ gulong length,
+ gint pdelete,
+ GdkAtom *actual_property_type,
+ gint *actual_format_type,
+ gint *actual_length,
+ guchar **data)
+{
+ GdkDisplay *display;
+ Atom ret_prop_type;
+ gint ret_format;
+ gulong ret_nitems;
+ gulong ret_bytes_after;
+ gulong get_length;
+ gulong ret_length;
+ guchar *ret_data;
+ Atom xproperty;
+ Atom xtype;
+ int res;
+
+ g_return_val_if_fail (!window || GDK_WINDOW_IS_X11 (window), FALSE);
+
+ if (!window)
+ {
+ GdkScreen *screen = gdk_screen_get_default ();
+ window = gdk_screen_get_root_window (screen);
+
+ GDK_NOTE (MULTIHEAD, g_message ("gdk_property_get(): window is NULL\n"));
+ }
+ else if (!GDK_WINDOW_IS_X11 (window))
+ return FALSE;
+
+ if (GDK_WINDOW_DESTROYED (window))
+ return FALSE;
+
+ display = gdk_window_get_display (window);
+ xproperty = gdk_x11_atom_to_xatom_for_display (display, property);
+ if (type == GDK_NONE)
+ xtype = AnyPropertyType;
+ else
+ xtype = gdk_x11_atom_to_xatom_for_display (display, type);
+
+ ret_data = NULL;
+
+ /*
+ * Round up length to next 4 byte value. Some code is in the (bad?)
+ * habit of passing G_MAXLONG as the length argument, causing an
+ * overflow to negative on the add. In this case, we clamp the
+ * value to G_MAXLONG.
+ */
+ get_length = length + 3;
+ if (get_length > G_MAXLONG)
+ get_length = G_MAXLONG;
+
+ /* To fail, either the user passed 0 or G_MAXULONG */
+ get_length = get_length / 4;
+ if (get_length == 0)
+ {
+ g_warning ("gdk_propery-get(): invalid length 0");
+ return FALSE;
+ }
+
+ res = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XWINDOW (window), xproperty,
+ offset, get_length, pdelete,
+ xtype, &ret_prop_type, &ret_format,
+ &ret_nitems, &ret_bytes_after,
+ &ret_data);
+
+ if (res != Success || (ret_prop_type == None && ret_format == 0))
+ {
+ return FALSE;
+ }
+
+ if (actual_property_type)
+ *actual_property_type = gdk_x11_xatom_to_atom_for_display (display, ret_prop_type);
+ if (actual_format_type)
+ *actual_format_type = ret_format;
+
+ if ((xtype != AnyPropertyType) && (ret_prop_type != xtype))
+ {
+ XFree (ret_data);
+ g_warning ("Couldn't match property type %s to %s\n",
+ gdk_x11_get_xatom_name_for_display (display, ret_prop_type),
+ gdk_x11_get_xatom_name_for_display (display, xtype));
+ return FALSE;
+ }
+
+ /* FIXME: ignoring bytes_after could have very bad effects */
+
+ if (data)
+ {
+ if (ret_prop_type == XA_ATOM ||
+ ret_prop_type == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
+ {
+ /*
+ * data is an array of X atom, we need to convert it
+ * to an array of GDK Atoms
+ */
+ gint i;
+ GdkAtom *ret_atoms = g_new (GdkAtom, ret_nitems);
+ Atom *xatoms = (Atom *)ret_data;
+
+ *data = (guchar *)ret_atoms;
+
+ for (i = 0; i < ret_nitems; i++)
+ ret_atoms[i] = gdk_x11_xatom_to_atom_for_display (display, xatoms[i]);
+
+ if (actual_length)
+ *actual_length = ret_nitems * sizeof (GdkAtom);
+ }
+ else
+ {
+ switch (ret_format)
+ {
+ case 8:
+ ret_length = ret_nitems;
+ break;
+ case 16:
+ ret_length = sizeof(short) * ret_nitems;
+ break;
+ case 32:
+ ret_length = sizeof(long) * ret_nitems;
+ break;
+ default:
+ g_warning ("unknown property return format: %d", ret_format);
+ XFree (ret_data);
+ return FALSE;
+ }
+
+ *data = g_new (guchar, ret_length);
+ memcpy (*data, ret_data, ret_length);
+ if (actual_length)
+ *actual_length = ret_length;
+ }
+ }
+
+ XFree (ret_data);
+
+ return TRUE;
+}
+
+void
+gdk_property_change (GdkWindow *window,
+ GdkAtom property,
+ GdkAtom type,
+ gint format,
+ GdkPropMode mode,
+ const guchar *data,
+ gint nelements)
+{
+ GdkDisplay *display;
+ Window xwindow;
+ Atom xproperty;
+ Atom xtype;
+
+ g_return_if_fail (!window || GDK_WINDOW_IS_X11 (window));
+
+ if (!window)
+ {
+ GdkScreen *screen;
+
+ screen = gdk_screen_get_default ();
+ window = gdk_screen_get_root_window (screen);
+
+ GDK_NOTE (MULTIHEAD, g_message ("gdk_property_change(): window is NULL\n"));
+ }
+ else if (!GDK_WINDOW_IS_X11 (window))
+ return;
+
+ if (GDK_WINDOW_DESTROYED (window))
+ return;
+
+ gdk_window_ensure_native (window);
+
+ display = gdk_window_get_display (window);
+ xproperty = gdk_x11_atom_to_xatom_for_display (display, property);
+ xtype = gdk_x11_atom_to_xatom_for_display (display, type);
+ xwindow = GDK_WINDOW_XID (window);
+
+ if (xtype == XA_ATOM ||
+ xtype == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
+ {
+ /*
+ * data is an array of GdkAtom, we need to convert it
+ * to an array of X Atoms
+ */
+ gint i;
+ GdkAtom *atoms = (GdkAtom*) data;
+ Atom *xatoms;
+
+ xatoms = g_new (Atom, nelements);
+ for (i = 0; i < nelements; i++)
+ xatoms[i] = gdk_x11_atom_to_xatom_for_display (display, atoms[i]);
+
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow,
+ xproperty, xtype,
+ format, mode, (guchar *)xatoms, nelements);
+ g_free (xatoms);
+ }
+ else
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow, xproperty,
+ xtype, format, mode, (guchar *)data, nelements);
+}
+
+void
+gdk_property_delete (GdkWindow *window,
+ GdkAtom property)
+{
+ g_return_if_fail (!window || GDK_WINDOW_IS_X11 (window));
+
+ if (!window)
+ {
+ GdkScreen *screen = gdk_screen_get_default ();
+ window = gdk_screen_get_root_window (screen);
+
+ GDK_NOTE (MULTIHEAD,
+ g_message ("gdk_property_delete(): window is NULL\n"));
+ }
+ else if (!GDK_WINDOW_IS_X11 (window))
+ return;
+
+ if (GDK_WINDOW_DESTROYED (window))
+ return;
+
+ XDeleteProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XWINDOW (window),
+ gdk_x11_atom_to_xatom_for_display (GDK_WINDOW_DISPLAY (window),
+ property));
+}
diff --git a/gdk/broadway/gdkscreen-broadway.c b/gdk/broadway/gdkscreen-broadway.c
new file mode 100644
index 0000000000..9bb2259b89
--- /dev/null
+++ b/gdk/broadway/gdkscreen-broadway.c
@@ -0,0 +1,2003 @@
+ /*
+ * gdkscreen-broadway.c
+ *
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * 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 "gdkscreen-broadway.h"
+
+#include "gdkscreen.h"
+#include "gdkdisplay.h"
+#include "gdkdisplay-broadway.h"
+#include "gdkx.h"
+
+#include <glib.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <X11/Xatom.h>
+
+#ifdef HAVE_SOLARIS_XINERAMA
+#include <X11/extensions/xinerama.h>
+#endif
+#ifdef HAVE_XFREE_XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif
+
+#ifdef HAVE_RANDR
+#include <X11/extensions/Xrandr.h>
+#endif
+
+#ifdef HAVE_XFIXES
+#include <X11/extensions/Xfixes.h>
+#endif
+
+#include "gdksettings.c"
+
+static void gdk_screen_x11_dispose (GObject *object);
+static void gdk_screen_x11_finalize (GObject *object);
+static void init_randr_support (GdkScreen *screen);
+static void deinit_multihead (GdkScreen *screen);
+
+enum
+{
+ WINDOW_MANAGER_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GdkScreenX11, _gdk_screen_x11, GDK_TYPE_SCREEN)
+
+typedef struct _NetWmSupportedAtoms NetWmSupportedAtoms;
+
+struct _NetWmSupportedAtoms
+{
+ Atom *atoms;
+ gulong n_atoms;
+};
+
+struct _GdkX11Monitor
+{
+ GdkRectangle geometry;
+ XID output;
+ int width_mm;
+ int height_mm;
+ char * output_name;
+ char * manufacturer;
+};
+
+static void
+_gdk_screen_x11_class_init (GdkScreenX11Class *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gdk_screen_x11_dispose;
+ object_class->finalize = gdk_screen_x11_finalize;
+
+ signals[WINDOW_MANAGER_CHANGED] =
+ g_signal_new (g_intern_static_string ("window_manager_changed"),
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdkScreenX11Class, window_manager_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+}
+
+static void
+_gdk_screen_x11_init (GdkScreenX11 *screen)
+{
+}
+
+/**
+ * gdk_screen_get_display:
+ * @screen: a #GdkScreen
+ *
+ * Gets the display to which the @screen belongs.
+ *
+ * Returns: the display to which @screen belongs
+ *
+ * Since: 2.2
+ **/
+GdkDisplay *
+gdk_screen_get_display (GdkScreen *screen)
+{
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+
+ return GDK_SCREEN_X11 (screen)->display;
+}
+/**
+ * gdk_screen_get_width:
+ * @screen: a #GdkScreen
+ *
+ * Gets the width of @screen in pixels
+ *
+ * Returns: the width of @screen in pixels.
+ *
+ * Since: 2.2
+ **/
+gint
+gdk_screen_get_width (GdkScreen *screen)
+{
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
+
+ return WidthOfScreen (GDK_SCREEN_X11 (screen)->xscreen);
+}
+
+/**
+ * gdk_screen_get_height:
+ * @screen: a #GdkScreen
+ *
+ * Gets the height of @screen in pixels
+ *
+ * Returns: the height of @screen in pixels.
+ *
+ * Since: 2.2
+ **/
+gint
+gdk_screen_get_height (GdkScreen *screen)
+{
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
+
+ return HeightOfScreen (GDK_SCREEN_X11 (screen)->xscreen);
+}
+
+/**
+ * gdk_screen_get_width_mm:
+ * @screen: a #GdkScreen
+ *
+ * Gets the width of @screen in millimeters.
+ * Note that on some X servers this value will not be correct.
+ *
+ * Returns: the width of @screen in millimeters.
+ *
+ * Since: 2.2
+ **/
+gint
+gdk_screen_get_width_mm (GdkScreen *screen)
+{
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
+
+ return WidthMMOfScreen (GDK_SCREEN_X11 (screen)->xscreen);
+}
+
+/**
+ * gdk_screen_get_height_mm:
+ * @screen: a #GdkScreen
+ *
+ * Returns the height of @screen in millimeters.
+ * Note that on some X servers this value will not be correct.
+ *
+ * Returns: the heigth of @screen in millimeters.
+ *
+ * Since: 2.2
+ **/
+gint
+gdk_screen_get_height_mm (GdkScreen *screen)
+{
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
+
+ return HeightMMOfScreen (GDK_SCREEN_X11 (screen)->xscreen);
+}
+
+/**
+ * gdk_screen_get_number:
+ * @screen: a #GdkScreen
+ *
+ * Gets the index of @screen among the screens in the display
+ * to which it belongs. (See gdk_screen_get_display())
+ *
+ * Returns: the index
+ *
+ * Since: 2.2
+ **/
+gint
+gdk_screen_get_number (GdkScreen *screen)
+{
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
+
+ return GDK_SCREEN_X11 (screen)->screen_num;
+}
+
+/**
+ * gdk_screen_get_root_window:
+ * @screen: a #GdkScreen
+ *
+ * Gets the root window of @screen.
+ *
+ * Returns: (transfer none): the root window
+ *
+ * Since: 2.2
+ **/
+GdkWindow *
+gdk_screen_get_root_window (GdkScreen *screen)
+{
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+
+ return GDK_SCREEN_X11 (screen)->root_window;
+}
+
+static void
+_gdk_screen_x11_events_uninit (GdkScreen *screen)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+
+ if (screen_x11->xsettings_client)
+ {
+ xsettings_client_destroy (screen_x11->xsettings_client);
+ screen_x11->xsettings_client = NULL;
+ }
+}
+
+static void
+gdk_screen_x11_dispose (GObject *object)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (object);
+ int i;
+
+ for (i = 0; i < 32; ++i)
+ {
+ if (screen_x11->subwindow_gcs[i])
+ {
+ XFreeGC (screen_x11->xdisplay, screen_x11->subwindow_gcs[i]);
+ screen_x11->subwindow_gcs[i] = 0;
+ }
+ }
+
+ _gdk_screen_x11_events_uninit (GDK_SCREEN (object));
+
+ if (screen_x11->root_window)
+ _gdk_window_destroy (screen_x11->root_window, TRUE);
+
+ G_OBJECT_CLASS (_gdk_screen_x11_parent_class)->dispose (object);
+
+ screen_x11->xdisplay = NULL;
+ screen_x11->xscreen = NULL;
+ screen_x11->screen_num = -1;
+ screen_x11->xroot_window = None;
+ screen_x11->wmspec_check_window = None;
+}
+
+static void
+gdk_screen_x11_finalize (GObject *object)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (object);
+ gint i;
+
+ if (screen_x11->root_window)
+ g_object_unref (screen_x11->root_window);
+
+ /* Visual Part */
+ for (i = 0; i < screen_x11->nvisuals; i++)
+ g_object_unref (screen_x11->visuals[i]);
+ g_free (screen_x11->visuals);
+ g_hash_table_destroy (screen_x11->visual_hash);
+
+ g_free (screen_x11->window_manager_name);
+
+ deinit_multihead (GDK_SCREEN (object));
+
+ G_OBJECT_CLASS (_gdk_screen_x11_parent_class)->finalize (object);
+}
+
+/**
+ * gdk_screen_get_n_monitors:
+ * @screen: a #GdkScreen
+ *
+ * Returns the number of monitors which @screen consists of.
+ *
+ * Returns: number of monitors which @screen consists of
+ *
+ * Since: 2.2
+ */
+gint
+gdk_screen_get_n_monitors (GdkScreen *screen)
+{
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
+
+ return GDK_SCREEN_X11 (screen)->n_monitors;
+}
+
+/**
+ * gdk_screen_get_primary_monitor:
+ * @screen: a #GdkScreen.
+ *
+ * Gets the primary monitor for @screen. The primary monitor
+ * is considered the monitor where the 'main desktop' lives.
+ * While normal application windows typically allow the window
+ * manager to place the windows, specialized desktop applications
+ * such as panels should place themselves on the primary monitor.
+ *
+ * If no primary monitor is configured by the user, the return value
+ * will be 0, defaulting to the first monitor.
+ *
+ * Returns: An integer index for the primary monitor, or 0 if none is configured.
+ *
+ * Since: 2.20
+ */
+gint
+gdk_screen_get_primary_monitor (GdkScreen *screen)
+{
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
+
+ return GDK_SCREEN_X11 (screen)->primary_monitor;
+}
+
+/**
+ * gdk_screen_get_monitor_width_mm:
+ * @screen: a #GdkScreen
+ * @monitor_num: number of the monitor, between 0 and gdk_screen_get_n_monitors (screen)
+ *
+ * Gets the width in millimeters of the specified monitor, if available.
+ *
+ * Returns: the width of the monitor, or -1 if not available
+ *
+ * Since: 2.14
+ */
+gint
+gdk_screen_get_monitor_width_mm (GdkScreen *screen,
+ gint monitor_num)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), -1);
+ g_return_val_if_fail (monitor_num >= 0, -1);
+ g_return_val_if_fail (monitor_num < screen_x11->n_monitors, -1);
+
+ return screen_x11->monitors[monitor_num].width_mm;
+}
+
+/**
+ * gdk_screen_get_monitor_height_mm:
+ * @screen: a #GdkScreen
+ * @monitor_num: number of the monitor, between 0 and gdk_screen_get_n_monitors (screen)
+ *
+ * Gets the height in millimeters of the specified monitor.
+ *
+ * Returns: the height of the monitor, or -1 if not available
+ *
+ * Since: 2.14
+ */
+gint
+gdk_screen_get_monitor_height_mm (GdkScreen *screen,
+ gint monitor_num)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), -1);
+ g_return_val_if_fail (monitor_num >= 0, -1);
+ g_return_val_if_fail (monitor_num < screen_x11->n_monitors, -1);
+
+ return screen_x11->monitors[monitor_num].height_mm;
+}
+
+/**
+ * gdk_screen_get_monitor_plug_name:
+ * @screen: a #GdkScreen
+ * @monitor_num: number of the monitor, between 0 and gdk_screen_get_n_monitors (screen)
+ *
+ * Returns the output name of the specified monitor.
+ * Usually something like VGA, DVI, or TV, not the actual
+ * product name of the display device.
+ *
+ * Returns: a newly-allocated string containing the name of the monitor,
+ * or %NULL if the name cannot be determined
+ *
+ * Since: 2.14
+ */
+gchar *
+gdk_screen_get_monitor_plug_name (GdkScreen *screen,
+ gint monitor_num)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+ g_return_val_if_fail (monitor_num >= 0, NULL);
+ g_return_val_if_fail (monitor_num < screen_x11->n_monitors, NULL);
+
+ return g_strdup (screen_x11->monitors[monitor_num].output_name);
+}
+
+/**
+ * gdk_x11_screen_get_monitor_output:
+ * @screen: a #GdkScreen
+ * @monitor_num: number of the monitor, between 0 and gdk_screen_get_n_monitors (screen)
+ *
+ * Gets the XID of the specified output/monitor.
+ * If the X server does not support version 1.2 of the RANDR
+ * extension, 0 is returned.
+ *
+ * Returns: the XID of the monitor
+ *
+ * Since: 2.14
+ */
+XID
+gdk_x11_screen_get_monitor_output (GdkScreen *screen,
+ gint monitor_num)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), None);
+ g_return_val_if_fail (monitor_num >= 0, None);
+ g_return_val_if_fail (monitor_num < screen_x11->n_monitors, None);
+
+ return screen_x11->monitors[monitor_num].output;
+}
+
+/**
+ * gdk_screen_get_monitor_geometry:
+ * @screen: a #GdkScreen
+ * @monitor_num: the monitor number, between 0 and gdk_screen_get_n_monitors (screen)
+ * @dest: a #GdkRectangle to be filled with the monitor geometry
+ *
+ * Retrieves the #GdkRectangle representing the size and position of
+ * the individual monitor within the entire screen area.
+ *
+ * Note that the size of the entire screen area can be retrieved via
+ * gdk_screen_get_width() and gdk_screen_get_height().
+ *
+ * Since: 2.2
+ */
+void
+gdk_screen_get_monitor_geometry (GdkScreen *screen,
+ gint monitor_num,
+ GdkRectangle *dest)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+ g_return_if_fail (monitor_num >= 0);
+ g_return_if_fail (monitor_num < screen_x11->n_monitors);
+
+ if (dest)
+ *dest = screen_x11->monitors[monitor_num].geometry;
+}
+
+/**
+ * gdk_screen_get_rgba_visual:
+ * @screen: a #GdkScreen
+ *
+ * Gets a visual to use for creating windows with an alpha channel.
+ * The windowing system on which GTK+ is running
+ * may not support this capability, in which case %NULL will
+ * be returned. Even if a non-%NULL value is returned, its
+ * possible that the window's alpha channel won't be honored
+ * when displaying the window on the screen: in particular, for
+ * X an appropriate windowing manager and compositing manager
+ * must be running to provide appropriate display.
+ *
+ * This functionality is not implemented in the Windows backend.
+ *
+ * For setting an overall opacity for a top-level window, see
+ * gdk_window_set_opacity().
+ *
+ * Return value: (transfer none): a visual to use for windows with an
+ * alpha channel or %NULL if the capability is not available.
+ *
+ * Since: 2.8
+ **/
+GdkVisual *
+gdk_screen_get_rgba_visual (GdkScreen *screen)
+{
+ GdkScreenX11 *screen_x11;
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+
+ screen_x11 = GDK_SCREEN_X11 (screen);
+
+ return screen_x11->rgba_visual;
+}
+
+/**
+ * gdk_x11_screen_get_xscreen:
+ * @screen: a #GdkScreen.
+ * @returns: (transfer none): an Xlib <type>Screen*</type>
+ *
+ * Returns the screen of a #GdkScreen.
+ *
+ * Since: 2.2
+ */
+Screen *
+gdk_x11_screen_get_xscreen (GdkScreen *screen)
+{
+ return GDK_SCREEN_X11 (screen)->xscreen;
+}
+
+/**
+ * gdk_x11_screen_get_screen_number:
+ * @screen: a #GdkScreen.
+ * @returns: the position of @screen among the screens of
+ * its display.
+ *
+ * Returns the index of a #GdkScreen.
+ *
+ * Since: 2.2
+ */
+int
+gdk_x11_screen_get_screen_number (GdkScreen *screen)
+{
+ return GDK_SCREEN_X11 (screen)->screen_num;
+}
+
+static gboolean
+check_is_composited (GdkDisplay *display,
+ GdkScreenX11 *screen_x11)
+{
+ Atom xselection = gdk_x11_atom_to_xatom_for_display (display, screen_x11->cm_selection_atom);
+ Window xwindow;
+
+ xwindow = XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display), xselection);
+
+ return xwindow != None;
+}
+
+static GdkAtom
+make_cm_atom (int screen_number)
+{
+ gchar *name = g_strdup_printf ("_NET_WM_CM_S%d", screen_number);
+ GdkAtom atom = gdk_atom_intern (name, FALSE);
+ g_free (name);
+ return atom;
+}
+
+static void
+init_monitor_geometry (GdkX11Monitor *monitor,
+ int x, int y, int width, int height)
+{
+ monitor->geometry.x = x;
+ monitor->geometry.y = y;
+ monitor->geometry.width = width;
+ monitor->geometry.height = height;
+
+ monitor->output = None;
+ monitor->width_mm = -1;
+ monitor->height_mm = -1;
+ monitor->output_name = NULL;
+ monitor->manufacturer = NULL;
+}
+
+static gboolean
+init_fake_xinerama (GdkScreen *screen)
+{
+#ifdef G_ENABLE_DEBUG
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+ XSetWindowAttributes atts;
+ Window win;
+ gint w, h;
+
+ if (!(_gdk_debug_flags & GDK_DEBUG_XINERAMA))
+ return FALSE;
+
+ /* Fake Xinerama mode by splitting the screen into 4 monitors.
+ * Also draw a little cross to make the monitor boundaries visible.
+ */
+ w = WidthOfScreen (screen_x11->xscreen);
+ h = HeightOfScreen (screen_x11->xscreen);
+
+ screen_x11->n_monitors = 4;
+ screen_x11->monitors = g_new0 (GdkX11Monitor, 4);
+ init_monitor_geometry (&screen_x11->monitors[0], 0, 0, w / 2, h / 2);
+ init_monitor_geometry (&screen_x11->monitors[1], w / 2, 0, w / 2, h / 2);
+ init_monitor_geometry (&screen_x11->monitors[2], 0, h / 2, w / 2, h / 2);
+ init_monitor_geometry (&screen_x11->monitors[3], w / 2, h / 2, w / 2, h / 2);
+
+ atts.override_redirect = 1;
+ atts.background_pixel = WhitePixel(GDK_SCREEN_XDISPLAY (screen),
+ screen_x11->screen_num);
+ win = XCreateWindow(GDK_SCREEN_XDISPLAY (screen),
+ screen_x11->xroot_window, 0, h / 2, w, 1, 0,
+ DefaultDepth(GDK_SCREEN_XDISPLAY (screen),
+ screen_x11->screen_num),
+ InputOutput,
+ DefaultVisual(GDK_SCREEN_XDISPLAY (screen),
+ screen_x11->screen_num),
+ CWOverrideRedirect|CWBackPixel,
+ &atts);
+ XMapRaised(GDK_SCREEN_XDISPLAY (screen), win);
+ win = XCreateWindow(GDK_SCREEN_XDISPLAY (screen),
+ screen_x11->xroot_window, w/2 , 0, 1, h, 0,
+ DefaultDepth(GDK_SCREEN_XDISPLAY (screen),
+ screen_x11->screen_num),
+ InputOutput,
+ DefaultVisual(GDK_SCREEN_XDISPLAY (screen),
+ screen_x11->screen_num),
+ CWOverrideRedirect|CWBackPixel,
+ &atts);
+ XMapRaised(GDK_SCREEN_XDISPLAY (screen), win);
+ return TRUE;
+#endif
+
+ return FALSE;
+}
+
+static void
+free_monitors (GdkX11Monitor *monitors,
+ gint n_monitors)
+{
+ int i;
+
+ for (i = 0; i < n_monitors; ++i)
+ {
+ g_free (monitors[i].output_name);
+ g_free (monitors[i].manufacturer);
+ }
+
+ g_free (monitors);
+}
+
+#ifdef HAVE_RANDR
+static int
+monitor_compare_function (GdkX11Monitor *monitor1,
+ GdkX11Monitor *monitor2)
+{
+ /* Sort the leftmost/topmost monitors first.
+ * For "cloned" monitors, sort the bigger ones first
+ * (giving preference to taller monitors over wider
+ * monitors)
+ */
+
+ if (monitor1->geometry.x != monitor2->geometry.x)
+ return monitor1->geometry.x - monitor2->geometry.x;
+
+ if (monitor1->geometry.y != monitor2->geometry.y)
+ return monitor1->geometry.y - monitor2->geometry.y;
+
+ if (monitor1->geometry.height != monitor2->geometry.height)
+ return - (monitor1->geometry.height - monitor2->geometry.height);
+
+ if (monitor1->geometry.width != monitor2->geometry.width)
+ return - (monitor1->geometry.width - monitor2->geometry.width);
+
+ return 0;
+}
+#endif
+
+static gboolean
+init_randr13 (GdkScreen *screen)
+{
+#ifdef HAVE_RANDR
+ GdkDisplay *display = gdk_screen_get_display (screen);
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+ Display *dpy = GDK_SCREEN_XDISPLAY (screen);
+ XRRScreenResources *resources;
+ RROutput primary_output;
+ RROutput first_output = None;
+ int i;
+ GArray *monitors;
+ gboolean randr12_compat = FALSE;
+
+ if (!display_x11->have_randr13)
+ return FALSE;
+
+ resources = XRRGetScreenResourcesCurrent (screen_x11->xdisplay,
+ screen_x11->xroot_window);
+ if (!resources)
+ return FALSE;
+
+ monitors = g_array_sized_new (FALSE, TRUE, sizeof (GdkX11Monitor),
+ resources->noutput);
+
+ for (i = 0; i < resources->noutput; ++i)
+ {
+ XRROutputInfo *output =
+ XRRGetOutputInfo (dpy, resources, resources->outputs[i]);
+
+ /* Non RandR1.2 X driver have output name "default" */
+ randr12_compat |= !g_strcmp0 (output->name, "default");
+
+ if (output->connection == RR_Disconnected)
+ {
+ XRRFreeOutputInfo (output);
+ continue;
+ }
+
+ if (output->crtc)
+ {
+ GdkX11Monitor monitor;
+ XRRCrtcInfo *crtc = XRRGetCrtcInfo (dpy, resources, output->crtc);
+
+ monitor.geometry.x = crtc->x;
+ monitor.geometry.y = crtc->y;
+ monitor.geometry.width = crtc->width;
+ monitor.geometry.height = crtc->height;
+
+ monitor.output = resources->outputs[i];
+ monitor.width_mm = output->mm_width;
+ monitor.height_mm = output->mm_height;
+ monitor.output_name = g_strdup (output->name);
+ /* FIXME: need EDID parser */
+ monitor.manufacturer = NULL;
+
+ g_array_append_val (monitors, monitor);
+
+ XRRFreeCrtcInfo (crtc);
+ }
+
+ XRRFreeOutputInfo (output);
+ }
+
+ if (resources->noutput > 0)
+ first_output = resources->outputs[0];
+
+ XRRFreeScreenResources (resources);
+
+ /* non RandR 1.2 X driver doesn't return any usable multihead data */
+ if (randr12_compat)
+ {
+ guint n_monitors = monitors->len;
+
+ free_monitors ((GdkX11Monitor *)g_array_free (monitors, FALSE),
+ n_monitors);
+
+ return FALSE;
+ }
+
+ g_array_sort (monitors,
+ (GCompareFunc) monitor_compare_function);
+ screen_x11->n_monitors = monitors->len;
+ screen_x11->monitors = (GdkX11Monitor *)g_array_free (monitors, FALSE);
+
+ screen_x11->primary_monitor = 0;
+
+ primary_output = XRRGetOutputPrimary (screen_x11->xdisplay,
+ screen_x11->xroot_window);
+
+ for (i = 0; i < screen_x11->n_monitors; ++i)
+ {
+ if (screen_x11->monitors[i].output == primary_output)
+ {
+ screen_x11->primary_monitor = i;
+ break;
+ }
+
+ /* No RandR1.3+ available or no primary set, fall back to prefer LVDS as primary if present */
+ if (primary_output == None &&
+ g_ascii_strncasecmp (screen_x11->monitors[i].output_name, "LVDS", 4) == 0)
+ {
+ screen_x11->primary_monitor = i;
+ break;
+ }
+
+ /* No primary specified and no LVDS found */
+ if (screen_x11->monitors[i].output == first_output)
+ screen_x11->primary_monitor = i;
+ }
+
+ return screen_x11->n_monitors > 0;
+#endif
+
+ return FALSE;
+}
+
+static gboolean
+init_solaris_xinerama (GdkScreen *screen)
+{
+#ifdef HAVE_SOLARIS_XINERAMA
+ Display *dpy = GDK_SCREEN_XDISPLAY (screen);
+ int screen_no = gdk_screen_get_number (screen);
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+ XRectangle monitors[MAXFRAMEBUFFERS];
+ unsigned char hints[16];
+ gint result;
+ int n_monitors;
+ int i;
+
+ if (!XineramaGetState (dpy, screen_no))
+ return FALSE;
+
+ result = XineramaGetInfo (dpy, screen_no, monitors, hints, &n_monitors);
+
+ /* Yes I know it should be Success but the current implementation
+ * returns the num of monitor
+ */
+ if (result == 0)
+ {
+ return FALSE;
+ }
+
+ screen_x11->monitors = g_new0 (GdkX11Monitor, n_monitors);
+ screen_x11->n_monitors = n_monitors;
+
+ for (i = 0; i < n_monitors; i++)
+ {
+ init_monitor_geometry (&screen_x11->monitors[i],
+ monitors[i].x, monitors[i].y,
+ monitors[i].width, monitors[i].height);
+ }
+
+ screen_x11->primary_monitor = 0;
+
+ return TRUE;
+#endif /* HAVE_SOLARIS_XINERAMA */
+
+ return FALSE;
+}
+
+static gboolean
+init_xfree_xinerama (GdkScreen *screen)
+{
+#ifdef HAVE_XFREE_XINERAMA
+ Display *dpy = GDK_SCREEN_XDISPLAY (screen);
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+ XineramaScreenInfo *monitors;
+ int i, n_monitors;
+
+ if (!XineramaIsActive (dpy))
+ return FALSE;
+
+ monitors = XineramaQueryScreens (dpy, &n_monitors);
+
+ if (n_monitors <= 0 || monitors == NULL)
+ {
+ /* If Xinerama doesn't think we have any monitors, try acting as
+ * though we had no Xinerama. If the "no monitors" condition
+ * is because XRandR 1.2 is currently switching between CRTCs,
+ * we'll be notified again when we have our monitor back,
+ * and can go back into Xinerama-ish mode at that point.
+ */
+ if (monitors)
+ XFree (monitors);
+
+ return FALSE;
+ }
+
+ screen_x11->n_monitors = n_monitors;
+ screen_x11->monitors = g_new0 (GdkX11Monitor, n_monitors);
+
+ for (i = 0; i < n_monitors; ++i)
+ {
+ init_monitor_geometry (&screen_x11->monitors[i],
+ monitors[i].x_org, monitors[i].y_org,
+ monitors[i].width, monitors[i].height);
+ }
+
+ XFree (monitors);
+
+ screen_x11->primary_monitor = 0;
+
+ return TRUE;
+#endif /* HAVE_XFREE_XINERAMA */
+
+ return FALSE;
+}
+
+static void
+deinit_multihead (GdkScreen *screen)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+
+ free_monitors (screen_x11->monitors, screen_x11->n_monitors);
+
+ screen_x11->n_monitors = 0;
+ screen_x11->monitors = NULL;
+}
+
+static gboolean
+compare_monitor (GdkX11Monitor *m1,
+ GdkX11Monitor *m2)
+{
+ if (m1->geometry.x != m2->geometry.x ||
+ m1->geometry.y != m2->geometry.y ||
+ m1->geometry.width != m2->geometry.width ||
+ m1->geometry.height != m2->geometry.height)
+ return FALSE;
+
+ if (m1->width_mm != m2->width_mm ||
+ m1->height_mm != m2->height_mm)
+ return FALSE;
+
+ if (g_strcmp0 (m1->output_name, m2->output_name) != 0)
+ return FALSE;
+
+ if (g_strcmp0 (m1->manufacturer, m2->manufacturer) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+compare_monitors (GdkX11Monitor *monitors1, gint n_monitors1,
+ GdkX11Monitor *monitors2, gint n_monitors2)
+{
+ gint i;
+
+ if (n_monitors1 != n_monitors2)
+ return FALSE;
+
+ for (i = 0; i < n_monitors1; i++)
+ {
+ if (!compare_monitor (monitors1 + i, monitors2 + i))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+init_multihead (GdkScreen *screen)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+ int opcode, firstevent, firsterror;
+
+ /* There are four different implementations of multihead support:
+ *
+ * 1. Fake Xinerama for debugging purposes
+ * 2. RandR 1.2
+ * 3. Solaris Xinerama
+ * 4. XFree86/Xorg Xinerama
+ *
+ * We use them in that order.
+ */
+ if (init_fake_xinerama (screen))
+ return;
+
+ if (init_randr13 (screen))
+ return;
+
+ if (XQueryExtension (GDK_SCREEN_XDISPLAY (screen), "XINERAMA",
+ &opcode, &firstevent, &firsterror))
+ {
+ if (init_solaris_xinerama (screen))
+ return;
+
+ if (init_xfree_xinerama (screen))
+ return;
+ }
+
+ /* No multihead support of any kind for this screen */
+ screen_x11->n_monitors = 1;
+ screen_x11->monitors = g_new0 (GdkX11Monitor, 1);
+ screen_x11->primary_monitor = 0;
+
+ init_monitor_geometry (screen_x11->monitors, 0, 0,
+ WidthOfScreen (screen_x11->xscreen),
+ HeightOfScreen (screen_x11->xscreen));
+}
+
+GdkScreen *
+_gdk_x11_screen_new (GdkDisplay *display,
+ gint screen_number)
+{
+ GdkScreen *screen;
+ GdkScreenX11 *screen_x11;
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+ screen = g_object_new (GDK_TYPE_SCREEN_X11, NULL);
+
+ screen_x11 = GDK_SCREEN_X11 (screen);
+ screen_x11->display = display;
+ screen_x11->xdisplay = display_x11->xdisplay;
+ screen_x11->xscreen = ScreenOfDisplay (display_x11->xdisplay, screen_number);
+ screen_x11->screen_num = screen_number;
+ screen_x11->xroot_window = RootWindow (display_x11->xdisplay,screen_number);
+ screen_x11->wmspec_check_window = None;
+ /* we want this to be always non-null */
+ screen_x11->window_manager_name = g_strdup ("unknown");
+
+ init_multihead (screen);
+ init_randr_support (screen);
+
+ _gdk_visual_init (screen);
+ _gdk_windowing_window_init (screen);
+
+ return screen;
+}
+
+/*
+ * It is important that we first request the selection
+ * notification, and then setup the initial state of
+ * is_composited to avoid a race condition here.
+ */
+void
+_gdk_x11_screen_setup (GdkScreen *screen)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+
+ screen_x11->cm_selection_atom = make_cm_atom (screen_x11->screen_num);
+ gdk_display_request_selection_notification (screen_x11->display,
+ screen_x11->cm_selection_atom);
+ screen_x11->is_composited = check_is_composited (screen_x11->display, screen_x11);
+}
+
+/**
+ * gdk_screen_is_composited:
+ * @screen: a #GdkScreen
+ *
+ * Returns whether windows with an RGBA visual can reasonably
+ * be expected to have their alpha channel drawn correctly on
+ * the screen.
+ *
+ * On X11 this function returns whether a compositing manager is
+ * compositing @screen.
+ *
+ * Return value: Whether windows with RGBA visuals can reasonably be
+ * expected to have their alpha channels drawn correctly on the screen.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gdk_screen_is_composited (GdkScreen *screen)
+{
+ GdkScreenX11 *screen_x11;
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
+
+ screen_x11 = GDK_SCREEN_X11 (screen);
+
+ return screen_x11->is_composited;
+}
+
+static void
+init_randr_support (GdkScreen * screen)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+
+ XSelectInput (GDK_SCREEN_XDISPLAY (screen),
+ screen_x11->xroot_window,
+ StructureNotifyMask);
+
+#ifdef HAVE_RANDR
+ XRRSelectInput (GDK_SCREEN_XDISPLAY (screen),
+ screen_x11->xroot_window,
+ RRScreenChangeNotifyMask |
+ RRCrtcChangeNotifyMask |
+ RROutputPropertyNotifyMask);
+#endif
+}
+
+static void
+process_monitors_change (GdkScreen *screen)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+ gint n_monitors;
+ GdkX11Monitor *monitors;
+ gboolean changed;
+
+ n_monitors = screen_x11->n_monitors;
+ monitors = screen_x11->monitors;
+
+ screen_x11->n_monitors = 0;
+ screen_x11->monitors = NULL;
+
+ init_multihead (screen);
+
+ changed = !compare_monitors (monitors, n_monitors,
+ screen_x11->monitors, screen_x11->n_monitors);
+
+ free_monitors (monitors, n_monitors);
+
+ if (changed)
+ g_signal_emit_by_name (screen, "monitors-changed");
+}
+
+void
+_gdk_x11_screen_size_changed (GdkScreen *screen,
+ XEvent *event)
+{
+ gint width, height;
+#ifdef HAVE_RANDR
+ GdkDisplayX11 *display_x11;
+#endif
+
+ width = gdk_screen_get_width (screen);
+ height = gdk_screen_get_height (screen);
+
+#ifdef HAVE_RANDR
+ display_x11 = GDK_DISPLAY_X11 (gdk_screen_get_display (screen));
+
+ if (display_x11->have_randr13 && event->type == ConfigureNotify)
+ {
+ g_signal_emit_by_name (screen, "monitors-changed");
+ return;
+ }
+
+ XRRUpdateConfiguration (event);
+#else
+ if (event->type == ConfigureNotify)
+ {
+ XConfigureEvent *rcevent = (XConfigureEvent *) event;
+ Screen *xscreen = gdk_x11_screen_get_xscreen (screen);
+
+ xscreen->width = rcevent->width;
+ xscreen->height = rcevent->height;
+ }
+ else
+ return;
+#endif
+
+ process_monitors_change (screen);
+
+ if (width != gdk_screen_get_width (screen) ||
+ height != gdk_screen_get_height (screen))
+ g_signal_emit_by_name (screen, "size-changed");
+}
+
+void
+_gdk_x11_screen_window_manager_changed (GdkScreen *screen)
+{
+ g_signal_emit (screen, signals[WINDOW_MANAGER_CHANGED], 0);
+}
+
+void
+_gdk_x11_screen_process_owner_change (GdkScreen *screen,
+ XEvent *event)
+{
+#ifdef HAVE_XFIXES
+ XFixesSelectionNotifyEvent *selection_event = (XFixesSelectionNotifyEvent *)event;
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+ Atom xcm_selection_atom = gdk_x11_atom_to_xatom_for_display (screen_x11->display,
+ screen_x11->cm_selection_atom);
+
+ if (selection_event->selection == xcm_selection_atom)
+ {
+ gboolean composited = selection_event->owner != None;
+
+ if (composited != screen_x11->is_composited)
+ {
+ screen_x11->is_composited = composited;
+
+ g_signal_emit_by_name (screen, "composited-changed");
+ }
+ }
+#endif
+}
+
+/**
+ * _gdk_windowing_substitute_screen_number:
+ * @display_name: The name of a display, in the form used by
+ * gdk_display_open (). If %NULL a default value
+ * will be used. On X11, this is derived from the DISPLAY
+ * environment variable.
+ * @screen_number: The number of a screen within the display
+ * referred to by @display_name.
+ *
+ * Modifies a @display_name to make @screen_number the default
+ * screen when the display is opened.
+ *
+ * Return value: a newly allocated string holding the resulting
+ * display name. Free with g_free().
+ */
+gchar *
+_gdk_windowing_substitute_screen_number (const gchar *display_name,
+ gint screen_number)
+{
+ GString *str;
+ gchar *p;
+
+ if (!display_name)
+ display_name = getenv ("DISPLAY");
+
+ if (!display_name)
+ return NULL;
+
+ str = g_string_new (display_name);
+
+ p = strrchr (str->str, '.');
+ if (p && p > strchr (str->str, ':'))
+ g_string_truncate (str, p - str->str);
+
+ g_string_append_printf (str, ".%d", screen_number);
+
+ return g_string_free (str, FALSE);
+}
+
+/**
+ * gdk_screen_make_display_name:
+ * @screen: a #GdkScreen
+ *
+ * Determines the name to pass to gdk_display_open() to get
+ * a #GdkDisplay with this screen as the default screen.
+ *
+ * Return value: a newly allocated string, free with g_free()
+ *
+ * Since: 2.2
+ **/
+gchar *
+gdk_screen_make_display_name (GdkScreen *screen)
+{
+ const gchar *old_display;
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+
+ old_display = gdk_display_get_name (gdk_screen_get_display (screen));
+
+ return _gdk_windowing_substitute_screen_number (old_display,
+ gdk_screen_get_number (screen));
+}
+
+/**
+ * gdk_screen_get_active_window
+ * @screen: a #GdkScreen
+ *
+ * Returns the screen's currently active window.
+ *
+ * On X11, this is done by inspecting the _NET_ACTIVE_WINDOW property
+ * on the root window, as described in the <ulink
+ * url="http://www.freedesktop.org/Standards/wm-spec">Extended Window
+ * Manager Hints</ulink>. If there is no currently currently active
+ * window, or the window manager does not support the
+ * _NET_ACTIVE_WINDOW hint, this function returns %NULL.
+ *
+ * On other platforms, this function may return %NULL, depending on whether
+ * it is implementable on that platform.
+ *
+ * The returned window should be unrefed using g_object_unref() when
+ * no longer needed.
+ *
+ * Return value: the currently active window, or %NULL.
+ *
+ * Since: 2.10
+ **/
+GdkWindow *
+gdk_screen_get_active_window (GdkScreen *screen)
+{
+ GdkScreenX11 *screen_x11;
+ GdkWindow *ret = NULL;
+ Atom type_return;
+ gint format_return;
+ gulong nitems_return;
+ gulong bytes_after_return;
+ guchar *data = NULL;
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+
+ if (!gdk_x11_screen_supports_net_wm_hint (screen,
+ gdk_atom_intern_static_string ("_NET_ACTIVE_WINDOW")))
+ return NULL;
+
+ screen_x11 = GDK_SCREEN_X11 (screen);
+
+ if (XGetWindowProperty (screen_x11->xdisplay, screen_x11->xroot_window,
+ gdk_x11_get_xatom_by_name_for_display (screen_x11->display,
+ "_NET_ACTIVE_WINDOW"),
+ 0, 1, False, XA_WINDOW, &type_return,
+ &format_return, &nitems_return,
+ &bytes_after_return, &data)
+ == Success)
+ {
+ if ((type_return == XA_WINDOW) && (format_return == 32) && (data))
+ {
+ GdkNativeWindow window = *(GdkNativeWindow *) data;
+
+ if (window != None)
+ {
+ ret = gdk_window_foreign_new_for_display (screen_x11->display,
+ *(GdkNativeWindow *) data);
+ }
+ }
+ }
+
+ if (data)
+ XFree (data);
+
+ return ret;
+}
+
+/**
+ * gdk_screen_get_window_stack:
+ * @screen: a #GdkScreen
+ *
+ * Returns a #GList of #GdkWindow<!-- -->s representing the current
+ * window stack.
+ *
+ * On X11, this is done by inspecting the _NET_CLIENT_LIST_STACKING
+ * property on the root window, as described in the <ulink
+ * url="http://www.freedesktop.org/Standards/wm-spec">Extended Window
+ * Manager Hints</ulink>. If the window manager does not support the
+ * _NET_CLIENT_LIST_STACKING hint, this function returns %NULL.
+ *
+ * On other platforms, this function may return %NULL, depending on whether
+ * it is implementable on that platform.
+ *
+ * The returned list is newly allocated and owns references to the
+ * windows it contains, so it should be freed using g_list_free() and
+ * its windows unrefed using g_object_unref() when no longer needed.
+ *
+ * Return value: (transfer full) (element-type GdkWindow):
+ * a list of #GdkWindow<!-- -->s for the current window stack,
+ * or %NULL.
+ *
+ * Since: 2.10
+ **/
+GList *
+gdk_screen_get_window_stack (GdkScreen *screen)
+{
+ GdkScreenX11 *screen_x11;
+ GList *ret = NULL;
+ Atom type_return;
+ gint format_return;
+ gulong nitems_return;
+ gulong bytes_after_return;
+ guchar *data = NULL;
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+
+ if (!gdk_x11_screen_supports_net_wm_hint (screen,
+ gdk_atom_intern_static_string ("_NET_CLIENT_LIST_STACKING")))
+ return NULL;
+
+ screen_x11 = GDK_SCREEN_X11 (screen);
+
+ if (XGetWindowProperty (screen_x11->xdisplay, screen_x11->xroot_window,
+ gdk_x11_get_xatom_by_name_for_display (screen_x11->display,
+ "_NET_CLIENT_LIST_STACKING"),
+ 0, G_MAXLONG, False, XA_WINDOW, &type_return,
+ &format_return, &nitems_return,
+ &bytes_after_return, &data)
+ == Success)
+ {
+ if ((type_return == XA_WINDOW) && (format_return == 32) &&
+ (data) && (nitems_return > 0))
+ {
+ gulong *stack = (gulong *) data;
+ GdkWindow *win;
+ int i;
+
+ for (i = 0; i < nitems_return; i++)
+ {
+ win = gdk_window_foreign_new_for_display (screen_x11->display,
+ (GdkNativeWindow)stack[i]);
+
+ if (win != NULL)
+ ret = g_list_append (ret, win);
+ }
+ }
+ }
+
+ if (data)
+ XFree (data);
+
+ return ret;
+}
+
+/* Sends a ClientMessage to all toplevel client windows */
+static gboolean
+gdk_event_send_client_message_to_all_recurse (GdkDisplay *display,
+ XEvent *xev,
+ guint32 xid,
+ guint level)
+{
+ Atom type = None;
+ int format;
+ unsigned long nitems, after;
+ unsigned char *data;
+ Window *ret_children, ret_root, ret_parent;
+ unsigned int ret_nchildren;
+ gboolean send = FALSE;
+ gboolean found = FALSE;
+ gboolean result = FALSE;
+ int i;
+
+ gdk_error_trap_push ();
+
+ if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), xid,
+ gdk_x11_get_xatom_by_name_for_display (display, "WM_STATE"),
+ 0, 0, False, AnyPropertyType,
+ &type, &format, &nitems, &after, &data) != Success)
+ goto out;
+
+ if (type)
+ {
+ send = TRUE;
+ XFree (data);
+ }
+ else
+ {
+ /* OK, we're all set, now let's find some windows to send this to */
+ if (!XQueryTree (GDK_DISPLAY_XDISPLAY (display), xid,
+ &ret_root, &ret_parent,
+ &ret_children, &ret_nchildren))
+ goto out;
+
+ for(i = 0; i < ret_nchildren; i++)
+ if (gdk_event_send_client_message_to_all_recurse (display, xev, ret_children[i], level + 1))
+ found = TRUE;
+
+ XFree (ret_children);
+ }
+
+ if (send || (!found && (level == 1)))
+ {
+ xev->xclient.window = xid;
+ _gdk_send_xevent (display, xid, False, NoEventMask, xev);
+ }
+
+ result = send || found;
+
+ out:
+ gdk_error_trap_pop_ignored ();
+
+ return result;
+}
+
+/**
+ * gdk_screen_broadcast_client_message:
+ * @screen: the #GdkScreen where the event will be broadcasted.
+ * @event: the #GdkEvent.
+ *
+ * On X11, sends an X ClientMessage event to all toplevel windows on
+ * @screen.
+ *
+ * Toplevel windows are determined by checking for the WM_STATE property,
+ * as described in the Inter-Client Communication Conventions Manual (ICCCM).
+ * If no windows are found with the WM_STATE property set, the message is
+ * sent to all children of the root window.
+ *
+ * On Windows, broadcasts a message registered with the name
+ * GDK_WIN32_CLIENT_MESSAGE to all top-level windows. The amount of
+ * data is limited to one long, i.e. four bytes.
+ *
+ * Since: 2.2
+ */
+
+void
+gdk_screen_broadcast_client_message (GdkScreen *screen,
+ GdkEvent *event)
+{
+ XEvent sev;
+ GdkWindow *root_window;
+
+ g_return_if_fail (event != NULL);
+
+ root_window = gdk_screen_get_root_window (screen);
+
+ /* Set up our event to send, with the exception of its target window */
+ sev.xclient.type = ClientMessage;
+ sev.xclient.display = GDK_WINDOW_XDISPLAY (root_window);
+ sev.xclient.format = event->client.data_format;
+ memcpy(&sev.xclient.data, &event->client.data, sizeof (sev.xclient.data));
+ sev.xclient.message_type =
+ gdk_x11_atom_to_xatom_for_display (GDK_WINDOW_DISPLAY (root_window),
+ event->client.message_type);
+
+ gdk_event_send_client_message_to_all_recurse (gdk_screen_get_display (screen),
+ &sev,
+ GDK_WINDOW_XID (root_window),
+ 0);
+}
+
+static gboolean
+check_transform (const gchar *xsettings_name,
+ GType src_type,
+ GType dest_type)
+{
+ if (!g_value_type_transformable (src_type, dest_type))
+ {
+ g_warning ("Cannot transform xsetting %s of type %s to type %s\n",
+ xsettings_name,
+ g_type_name (src_type),
+ g_type_name (dest_type));
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+/**
+ * gdk_screen_get_setting:
+ * @screen: the #GdkScreen where the setting is located
+ * @name: the name of the setting
+ * @value: location to store the value of the setting
+ *
+ * Retrieves a desktop-wide setting such as double-click time
+ * for the #GdkScreen @screen.
+ *
+ * FIXME needs a list of valid settings here, or a link to
+ * more information.
+ *
+ * Returns: %TRUE if the setting existed and a value was stored
+ * in @value, %FALSE otherwise.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gdk_screen_get_setting (GdkScreen *screen,
+ const gchar *name,
+ GValue *value)
+{
+
+ const char *xsettings_name = NULL;
+ XSettingsResult result;
+ XSettingsSetting *setting = NULL;
+ GdkScreenX11 *screen_x11;
+ gboolean success = FALSE;
+ gint i;
+ GValue tmp_val = { 0, };
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
+
+ screen_x11 = GDK_SCREEN_X11 (screen);
+
+ for (i = 0; i < GDK_SETTINGS_N_ELEMENTS(); i++)
+ if (strcmp (GDK_SETTINGS_GDK_NAME (i), name) == 0)
+ {
+ xsettings_name = GDK_SETTINGS_X_NAME (i);
+ break;
+ }
+
+ if (!xsettings_name)
+ goto out;
+
+ result = xsettings_client_get_setting (screen_x11->xsettings_client,
+ xsettings_name, &setting);
+ if (result != XSETTINGS_SUCCESS)
+ goto out;
+
+ switch (setting->type)
+ {
+ case XSETTINGS_TYPE_INT:
+ if (check_transform (xsettings_name, G_TYPE_INT, G_VALUE_TYPE (value)))
+ {
+ g_value_init (&tmp_val, G_TYPE_INT);
+ g_value_set_int (&tmp_val, setting->data.v_int);
+ g_value_transform (&tmp_val, value);
+
+ success = TRUE;
+ }
+ break;
+ case XSETTINGS_TYPE_STRING:
+ if (check_transform (xsettings_name, G_TYPE_STRING, G_VALUE_TYPE (value)))
+ {
+ g_value_init (&tmp_val, G_TYPE_STRING);
+ g_value_set_string (&tmp_val, setting->data.v_string);
+ g_value_transform (&tmp_val, value);
+
+ success = TRUE;
+ }
+ break;
+ case XSETTINGS_TYPE_COLOR:
+ if (!check_transform (xsettings_name, GDK_TYPE_COLOR, G_VALUE_TYPE (value)))
+ {
+ GdkColor color;
+
+ g_value_init (&tmp_val, GDK_TYPE_COLOR);
+
+ color.pixel = 0;
+ color.red = setting->data.v_color.red;
+ color.green = setting->data.v_color.green;
+ color.blue = setting->data.v_color.blue;
+
+ g_value_set_boxed (&tmp_val, &color);
+
+ g_value_transform (&tmp_val, value);
+
+ success = TRUE;
+ }
+ break;
+ }
+
+ g_value_unset (&tmp_val);
+
+ out:
+ if (setting)
+ xsettings_setting_free (setting);
+
+ if (success)
+ return TRUE;
+ else
+ return _gdk_x11_get_xft_setting (screen, name, value);
+}
+
+static void
+cleanup_atoms(gpointer data)
+{
+ NetWmSupportedAtoms *supported_atoms = data;
+ if (supported_atoms->atoms)
+ XFree (supported_atoms->atoms);
+ g_free (supported_atoms);
+}
+
+static void
+fetch_net_wm_check_window (GdkScreen *screen)
+{
+ GdkScreenX11 *screen_x11;
+ GdkDisplay *display;
+ Atom type;
+ gint format;
+ gulong n_items;
+ gulong bytes_after;
+ guchar *data;
+ Window *xwindow;
+ GTimeVal tv;
+ gint error;
+
+ screen_x11 = GDK_SCREEN_X11 (screen);
+ display = screen_x11->display;
+
+ g_return_if_fail (GDK_DISPLAY_X11 (display)->trusted_client);
+
+ g_get_current_time (&tv);
+
+ if (ABS (tv.tv_sec - screen_x11->last_wmspec_check_time) < 15)
+ return; /* we've checked recently */
+
+ screen_x11->last_wmspec_check_time = tv.tv_sec;
+
+ data = NULL;
+ XGetWindowProperty (screen_x11->xdisplay, screen_x11->xroot_window,
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_SUPPORTING_WM_CHECK"),
+ 0, G_MAXLONG, False, XA_WINDOW, &type, &format,
+ &n_items, &bytes_after, &data);
+
+ if (type != XA_WINDOW)
+ {
+ if (data)
+ XFree (data);
+ return;
+ }
+
+ xwindow = (Window *)data;
+
+ if (screen_x11->wmspec_check_window == *xwindow)
+ {
+ XFree (xwindow);
+ return;
+ }
+
+ gdk_error_trap_push ();
+
+ /* Find out if this WM goes away, so we can reset everything. */
+ XSelectInput (screen_x11->xdisplay, *xwindow, StructureNotifyMask);
+
+ error = gdk_error_trap_pop ();
+ if (!error)
+ {
+ screen_x11->wmspec_check_window = *xwindow;
+ screen_x11->need_refetch_net_supported = TRUE;
+ screen_x11->need_refetch_wm_name = TRUE;
+
+ /* Careful, reentrancy */
+ _gdk_x11_screen_window_manager_changed (GDK_SCREEN (screen_x11));
+ }
+ else if (error == BadWindow)
+ {
+ /* Leftover property, try again immediately, new wm may be starting up */
+ screen_x11->last_wmspec_check_time = 0;
+ }
+
+ XFree (xwindow);
+}
+
+/**
+ * gdk_x11_screen_supports_net_wm_hint:
+ * @screen: the relevant #GdkScreen.
+ * @property: a property atom.
+ *
+ * This function is specific to the X11 backend of GDK, and indicates
+ * whether the window manager supports a certain hint from the
+ * Extended Window Manager Hints Specification. You can find this
+ * specification on
+ * <ulink url="http://www.freedesktop.org">http://www.freedesktop.org</ulink>.
+ *
+ * When using this function, keep in mind that the window manager
+ * can change over time; so you shouldn't use this function in
+ * a way that impacts persistent application state. A common bug
+ * is that your application can start up before the window manager
+ * does when the user logs in, and before the window manager starts
+ * gdk_x11_screen_supports_net_wm_hint() will return %FALSE for every property.
+ * You can monitor the window_manager_changed signal on #GdkScreen to detect
+ * a window manager change.
+ *
+ * Return value: %TRUE if the window manager supports @property
+ *
+ * Since: 2.2
+ **/
+gboolean
+gdk_x11_screen_supports_net_wm_hint (GdkScreen *screen,
+ GdkAtom property)
+{
+ gulong i;
+ GdkScreenX11 *screen_x11;
+ NetWmSupportedAtoms *supported_atoms;
+ GdkDisplay *display;
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
+
+ screen_x11 = GDK_SCREEN_X11 (screen);
+ display = screen_x11->display;
+
+ if (!G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client))
+ return FALSE;
+
+ supported_atoms = g_object_get_data (G_OBJECT (screen), "gdk-net-wm-supported-atoms");
+ if (!supported_atoms)
+ {
+ supported_atoms = g_new0 (NetWmSupportedAtoms, 1);
+ g_object_set_data_full (G_OBJECT (screen), "gdk-net-wm-supported-atoms", supported_atoms, cleanup_atoms);
+ }
+
+ fetch_net_wm_check_window (screen);
+
+ if (screen_x11->wmspec_check_window == None)
+ return FALSE;
+
+ if (screen_x11->need_refetch_net_supported)
+ {
+ /* WM has changed since we last got the supported list,
+ * refetch it.
+ */
+ Atom type;
+ gint format;
+ gulong bytes_after;
+
+ screen_x11->need_refetch_net_supported = FALSE;
+
+ if (supported_atoms->atoms)
+ XFree (supported_atoms->atoms);
+
+ supported_atoms->atoms = NULL;
+ supported_atoms->n_atoms = 0;
+
+ XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), screen_x11->xroot_window,
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_SUPPORTED"),
+ 0, G_MAXLONG, False, XA_ATOM, &type, &format,
+ &supported_atoms->n_atoms, &bytes_after,
+ (guchar **)&supported_atoms->atoms);
+
+ if (type != XA_ATOM)
+ return FALSE;
+ }
+
+ if (supported_atoms->atoms == NULL)
+ return FALSE;
+
+ i = 0;
+ while (i < supported_atoms->n_atoms)
+ {
+ if (supported_atoms->atoms[i] == gdk_x11_atom_to_xatom_for_display (display, property))
+ return TRUE;
+
+ ++i;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gdk_net_wm_supports:
+ * @property: a property atom.
+ *
+ * This function is specific to the X11 backend of GDK, and indicates
+ * whether the window manager for the default screen supports a certain
+ * hint from the Extended Window Manager Hints Specification. See
+ * gdk_x11_screen_supports_net_wm_hint() for complete details.
+ *
+ * Return value: %TRUE if the window manager supports @property
+ **/
+gboolean
+gdk_net_wm_supports (GdkAtom property)
+{
+ return gdk_x11_screen_supports_net_wm_hint (gdk_screen_get_default (), property);
+}
+
+static void
+refcounted_grab_server (Display *xdisplay)
+{
+ GdkDisplay *display = gdk_x11_lookup_xdisplay (xdisplay);
+
+ gdk_x11_display_grab (display);
+}
+
+static void
+refcounted_ungrab_server (Display *xdisplay)
+{
+ GdkDisplay *display = gdk_x11_lookup_xdisplay (xdisplay);
+
+ gdk_x11_display_ungrab (display);
+}
+
+static GdkFilterReturn
+gdk_xsettings_client_event_filter (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ GdkScreenX11 *screen = data;
+
+ if (xsettings_client_process_event (screen->xsettings_client, (XEvent *)xevent))
+ return GDK_FILTER_REMOVE;
+ else
+ return GDK_FILTER_CONTINUE;
+}
+
+static Bool
+gdk_xsettings_watch_cb (Window window,
+ Bool is_start,
+ long mask,
+ void *cb_data)
+{
+ GdkWindow *gdkwin;
+ GdkScreen *screen = cb_data;
+
+ gdkwin = gdk_window_lookup_for_display (gdk_screen_get_display (screen), window);
+
+ if (is_start)
+ {
+ if (gdkwin)
+ g_object_ref (gdkwin);
+ else
+ {
+ gdkwin = gdk_window_foreign_new_for_display (gdk_screen_get_display (screen), window);
+
+ /* gdk_window_foreign_new_for_display() can fail and return NULL if the
+ * window has already been destroyed.
+ */
+ if (!gdkwin)
+ return False;
+ }
+
+ gdk_window_add_filter (gdkwin, gdk_xsettings_client_event_filter, screen);
+ }
+ else
+ {
+ if (!gdkwin)
+ {
+ /* gdkwin should not be NULL here, since if starting the watch succeeded
+ * we have a reference on the window. It might mean that the caller didn't
+ * remove the watch when it got a DestroyNotify event. Or maybe the
+ * caller ignored the return value when starting the watch failed.
+ */
+ g_warning ("gdk_xsettings_watch_cb(): Couldn't find window to unwatch");
+ return False;
+ }
+
+ gdk_window_remove_filter (gdkwin, gdk_xsettings_client_event_filter, screen);
+ g_object_unref (gdkwin);
+ }
+
+ return True;
+}
+
+static void
+gdk_xsettings_notify_cb (const char *name,
+ XSettingsAction action,
+ XSettingsSetting *setting,
+ void *data)
+{
+ GdkEvent new_event;
+ GdkScreen *screen = data;
+ GdkScreenX11 *screen_x11 = data;
+ int i;
+
+ if (screen_x11->xsettings_in_init)
+ return;
+
+ new_event.type = GDK_SETTING;
+ new_event.setting.window = gdk_screen_get_root_window (screen);
+ new_event.setting.send_event = FALSE;
+ new_event.setting.name = NULL;
+
+ for (i = 0; i < GDK_SETTINGS_N_ELEMENTS() ; i++)
+ if (strcmp (GDK_SETTINGS_X_NAME (i), name) == 0)
+ {
+ new_event.setting.name = (char*) GDK_SETTINGS_GDK_NAME (i);
+ break;
+ }
+
+ if (!new_event.setting.name)
+ return;
+
+ switch (action)
+ {
+ case XSETTINGS_ACTION_NEW:
+ new_event.setting.action = GDK_SETTING_ACTION_NEW;
+ break;
+ case XSETTINGS_ACTION_CHANGED:
+ new_event.setting.action = GDK_SETTING_ACTION_CHANGED;
+ break;
+ case XSETTINGS_ACTION_DELETED:
+ new_event.setting.action = GDK_SETTING_ACTION_DELETED;
+ break;
+ }
+
+ gdk_event_put (&new_event);
+}
+
+void
+_gdk_screen_x11_events_init (GdkScreen *screen)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+
+ /* Keep a flag to avoid extra notifies that we don't need
+ */
+ screen_x11->xsettings_in_init = TRUE;
+ screen_x11->xsettings_client = xsettings_client_new_with_grab_funcs (screen_x11->xdisplay,
+ screen_x11->screen_num,
+ gdk_xsettings_notify_cb,
+ gdk_xsettings_watch_cb,
+ screen,
+ refcounted_grab_server,
+ refcounted_ungrab_server);
+ screen_x11->xsettings_in_init = FALSE;
+}
+
+/**
+ * gdk_x11_screen_get_window_manager_name:
+ * @screen: a #GdkScreen
+ *
+ * Returns the name of the window manager for @screen.
+ *
+ * Return value: the name of the window manager screen @screen, or
+ * "unknown" if the window manager is unknown. The string is owned by GDK
+ * and should not be freed.
+ *
+ * Since: 2.2
+ **/
+const char*
+gdk_x11_screen_get_window_manager_name (GdkScreen *screen)
+{
+ GdkScreenX11 *screen_x11;
+
+ screen_x11 = GDK_SCREEN_X11 (screen);
+
+ if (!G_LIKELY (GDK_DISPLAY_X11 (screen_x11->display)->trusted_client))
+ return screen_x11->window_manager_name;
+
+ fetch_net_wm_check_window (screen);
+
+ if (screen_x11->need_refetch_wm_name)
+ {
+ /* Get the name of the window manager */
+ screen_x11->need_refetch_wm_name = FALSE;
+
+ g_free (screen_x11->window_manager_name);
+ screen_x11->window_manager_name = g_strdup ("unknown");
+
+ if (screen_x11->wmspec_check_window != None)
+ {
+ Atom type;
+ gint format;
+ gulong n_items;
+ gulong bytes_after;
+ gchar *name;
+
+ name = NULL;
+
+ gdk_error_trap_push ();
+
+ XGetWindowProperty (GDK_DISPLAY_XDISPLAY (screen_x11->display),
+ screen_x11->wmspec_check_window,
+ gdk_x11_get_xatom_by_name_for_display (screen_x11->display,
+ "_NET_WM_NAME"),
+ 0, G_MAXLONG, False,
+ gdk_x11_get_xatom_by_name_for_display (screen_x11->display,
+ "UTF8_STRING"),
+ &type, &format,
+ &n_items, &bytes_after,
+ (guchar **)&name);
+
+ gdk_error_trap_pop_ignored ();
+
+ if (name != NULL)
+ {
+ g_free (screen_x11->window_manager_name);
+ screen_x11->window_manager_name = g_strdup (name);
+ XFree (name);
+ }
+ }
+ }
+
+ return GDK_SCREEN_X11 (screen)->window_manager_name;
+}
diff --git a/gdk/broadway/gdkscreen-broadway.h b/gdk/broadway/gdkscreen-broadway.h
new file mode 100644
index 0000000000..1b9d95e1d8
--- /dev/null
+++ b/gdk/broadway/gdkscreen-broadway.h
@@ -0,0 +1,129 @@
+/*
+ * gdkscreen-broadway.h
+ *
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * 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.
+ */
+
+#ifndef __GDK_SCREEN_BROADWAY_H__
+#define __GDK_SCREEN_BROADWAY_H__
+
+#include <gdk/gdkscreen.h>
+#include <gdk/gdkvisual.h>
+#include "gdkprivate-broadway.h"
+#include "xsettings-client.h"
+#include <X11/X.h>
+#include <X11/Xlib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GdkScreenX11 GdkScreenX11;
+typedef struct _GdkScreenX11Class GdkScreenX11Class;
+
+#define GDK_TYPE_SCREEN_X11 (_gdk_screen_x11_get_type ())
+#define GDK_SCREEN_X11(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_SCREEN_X11, GdkScreenX11))
+#define GDK_SCREEN_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_SCREEN_X11, GdkScreenX11Class))
+#define GDK_IS_SCREEN_X11(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_SCREEN_X11))
+#define GDK_IS_SCREEN_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_SCREEN_X11))
+#define GDK_SCREEN_X11_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_SCREEN_X11, GdkScreenX11Class))
+
+typedef struct _GdkX11Monitor GdkX11Monitor;
+
+struct _GdkScreenX11
+{
+ GdkScreen parent_instance;
+
+ GdkDisplay *display;
+ Display *xdisplay;
+ Screen *xscreen;
+ gint screen_num;
+ Window xroot_window;
+ GdkWindow *root_window;
+
+ /* Window manager */
+ long last_wmspec_check_time;
+ Window wmspec_check_window;
+ char *window_manager_name;
+ /* TRUE if wmspec_check_window has changed since last
+ * fetch of _NET_SUPPORTED
+ */
+ guint need_refetch_net_supported : 1;
+ /* TRUE if wmspec_check_window has changed since last
+ * fetch of window manager name
+ */
+ guint need_refetch_wm_name : 1;
+
+ /* Visual Part */
+ GdkVisual *system_visual;
+ GdkVisual **visuals;
+ gint nvisuals;
+ gint available_depths[7];
+ gint navailable_depths;
+ GdkVisualType available_types[6];
+ gint navailable_types;
+ GHashTable *visual_hash;
+ GdkVisual *rgba_visual;
+
+ /* X settings */
+ XSettingsClient *xsettings_client;
+ guint xsettings_in_init : 1;
+
+ /* Xinerama/RandR 1.2 */
+ gint n_monitors;
+ GdkX11Monitor *monitors;
+ gint primary_monitor;
+
+ /* cache for window->translate vfunc */
+ GC subwindow_gcs[32];
+
+ /* Xft resources for the display, used for default values for
+ * the Xft/ XSETTINGS
+ */
+ gboolean xft_init; /* Whether we've intialized these values yet */
+ gboolean xft_antialias;
+ gboolean xft_hinting;
+ gint xft_hintstyle;
+ gint xft_rgba;
+ gint xft_dpi;
+
+ GdkAtom cm_selection_atom;
+ gboolean is_composited;
+};
+
+struct _GdkScreenX11Class
+{
+ GdkScreenClass parent_class;
+
+ void (* window_manager_changed) (GdkScreenX11 *screen_x11);
+};
+
+GType _gdk_screen_x11_get_type (void);
+GdkScreen * _gdk_x11_screen_new (GdkDisplay *display,
+ gint screen_number);
+
+void _gdk_x11_screen_setup (GdkScreen *screen);
+void _gdk_x11_screen_window_manager_changed (GdkScreen *screen);
+void _gdk_x11_screen_size_changed (GdkScreen *screen,
+ XEvent *event);
+void _gdk_x11_screen_process_owner_change (GdkScreen *screen,
+ XEvent *event);
+
+G_END_DECLS
+
+#endif /* __GDK_SCREEN_BROADWAY_H__ */
diff --git a/gdk/broadway/gdkselection-broadway.c b/gdk/broadway/gdkselection-broadway.c
new file mode 100644
index 0000000000..10509bfb9e
--- /dev/null
+++ b/gdk/broadway/gdkselection-broadway.c
@@ -0,0 +1,891 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gdkselection.h"
+
+#include "gdkx.h"
+#include "gdkproperty.h"
+#include "gdkprivate.h"
+#include "gdkprivate-broadway.h"
+#include "gdkdisplay-broadway.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <string.h>
+
+
+typedef struct _OwnerInfo OwnerInfo;
+
+struct _OwnerInfo
+{
+ GdkAtom selection;
+ GdkWindow *owner;
+ gulong serial;
+};
+
+static GSList *owner_list;
+
+/* When a window is destroyed we check if it is the owner
+ * of any selections. This is somewhat inefficient, but
+ * owner_list is typically short, and it is a low memory,
+ * low code solution
+ */
+void
+_gdk_selection_window_destroyed (GdkWindow *window)
+{
+ GSList *tmp_list = owner_list;
+ while (tmp_list)
+ {
+ OwnerInfo *info = tmp_list->data;
+ tmp_list = tmp_list->next;
+
+ if (info->owner == window)
+ {
+ owner_list = g_slist_remove (owner_list, info);
+ g_free (info);
+ }
+ }
+}
+
+/* We only pass through those SelectionClear events that actually
+ * reflect changes to the selection owner that we didn't make ourself.
+ */
+gboolean
+_gdk_selection_filter_clear_event (XSelectionClearEvent *event)
+{
+ GSList *tmp_list = owner_list;
+ GdkDisplay *display = gdk_x11_lookup_xdisplay (event->display);
+
+ while (tmp_list)
+ {
+ OwnerInfo *info = tmp_list->data;
+
+ if (gdk_window_get_display (info->owner) == display &&
+ info->selection == gdk_x11_xatom_to_atom_for_display (display, event->selection))
+ {
+ if ((GDK_DRAWABLE_XID (info->owner) == event->window &&
+ event->serial >= info->serial))
+ {
+ owner_list = g_slist_remove (owner_list, info);
+ g_free (info);
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+ tmp_list = tmp_list->next;
+ }
+
+ return FALSE;
+}
+/**
+ * gdk_selection_owner_set_for_display:
+ * @display: the #GdkDisplay.
+ * @owner: a #GdkWindow or %NULL to indicate that the owner for
+ * the given should be unset.
+ * @selection: an atom identifying a selection.
+ * @time_: timestamp to use when setting the selection.
+ * If this is older than the timestamp given last time the owner was
+ * set for the given selection, the request will be ignored.
+ * @send_event: if %TRUE, and the new owner is different from the current
+ * owner, the current owner will be sent a SelectionClear event.
+ *
+ * Sets the #GdkWindow @owner as the current owner of the selection @selection.
+ *
+ * Returns: %TRUE if the selection owner was successfully changed to owner,
+ * otherwise %FALSE.
+ *
+ * Since: 2.2
+ */
+gboolean
+gdk_selection_owner_set_for_display (GdkDisplay *display,
+ GdkWindow *owner,
+ GdkAtom selection,
+ guint32 time,
+ gboolean send_event)
+{
+ Display *xdisplay;
+ Window xwindow;
+ Atom xselection;
+ GSList *tmp_list;
+ OwnerInfo *info;
+
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
+ g_return_val_if_fail (selection != GDK_NONE, FALSE);
+
+ if (display->closed)
+ return FALSE;
+
+ if (owner)
+ {
+ if (GDK_WINDOW_DESTROYED (owner) || !GDK_WINDOW_IS_X11 (owner))
+ return FALSE;
+
+ gdk_window_ensure_native (owner);
+ xdisplay = GDK_WINDOW_XDISPLAY (owner);
+ xwindow = GDK_WINDOW_XID (owner);
+ }
+ else
+ {
+ xdisplay = GDK_DISPLAY_XDISPLAY (display);
+ xwindow = None;
+ }
+
+ xselection = gdk_x11_atom_to_xatom_for_display (display, selection);
+
+ tmp_list = owner_list;
+ while (tmp_list)
+ {
+ info = tmp_list->data;
+ if (info->selection == selection)
+ {
+ owner_list = g_slist_remove (owner_list, info);
+ g_free (info);
+ break;
+ }
+ tmp_list = tmp_list->next;
+ }
+
+ if (owner)
+ {
+ info = g_new (OwnerInfo, 1);
+ info->owner = owner;
+ info->serial = NextRequest (GDK_WINDOW_XDISPLAY (owner));
+ info->selection = selection;
+
+ owner_list = g_slist_prepend (owner_list, info);
+ }
+
+ XSetSelectionOwner (xdisplay, xselection, xwindow, time);
+
+ return (XGetSelectionOwner (xdisplay, xselection) == xwindow);
+}
+
+/**
+ * gdk_selection_owner_get_for_display:
+ * @display: a #GdkDisplay.
+ * @selection: an atom indentifying a selection.
+ *
+ * Determine the owner of the given selection.
+ *
+ * Note that the return value may be owned by a different
+ * process if a foreign window was previously created for that
+ * window, but a new foreign window will never be created by this call.
+ *
+ * Returns: if there is a selection owner for this window, and it is a
+ * window known to the current process, the #GdkWindow that owns the
+ * selection, otherwise %NULL.
+ *
+ * Since: 2.2
+ */
+GdkWindow *
+gdk_selection_owner_get_for_display (GdkDisplay *display,
+ GdkAtom selection)
+{
+ Window xwindow;
+
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
+ g_return_val_if_fail (selection != GDK_NONE, NULL);
+
+ if (display->closed)
+ return NULL;
+
+ xwindow = XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
+ gdk_x11_atom_to_xatom_for_display (display,
+ selection));
+ if (xwindow == None)
+ return NULL;
+
+ return gdk_window_lookup_for_display (display, xwindow);
+}
+
+void
+gdk_selection_convert (GdkWindow *requestor,
+ GdkAtom selection,
+ GdkAtom target,
+ guint32 time)
+{
+ GdkDisplay *display;
+
+ g_return_if_fail (selection != GDK_NONE);
+
+ if (GDK_WINDOW_DESTROYED (requestor) || !GDK_WINDOW_IS_X11 (requestor))
+ return;
+
+ gdk_window_ensure_native (requestor);
+ display = GDK_WINDOW_DISPLAY (requestor);
+
+ XConvertSelection (GDK_WINDOW_XDISPLAY (requestor),
+ gdk_x11_atom_to_xatom_for_display (display, selection),
+ gdk_x11_atom_to_xatom_for_display (display, target),
+ gdk_x11_atom_to_xatom_for_display (display, _gdk_selection_property),
+ GDK_WINDOW_XID (requestor), time);
+}
+
+/**
+ * gdk_selection_property_get:
+ * @requestor: the window on which the data is stored
+ * @data: location to store a pointer to the retrieved data.
+ If the retrieval failed, %NULL we be stored here, otherwise, it
+ will be non-%NULL and the returned data should be freed with g_free()
+ when you are finished using it. The length of the
+ allocated memory is one more than the length
+ of the returned data, and the final byte will always
+ be zero, to ensure nul-termination of strings.
+ * @prop_type: location to store the type of the property.
+ * @prop_format: location to store the format of the property.
+ *
+ * Retrieves selection data that was stored by the selection
+ * data in response to a call to gdk_selection_convert(). This function
+ * will not be used by applications, who should use the #GtkClipboard
+ * API instead.
+ *
+ * Return value: the length of the retrieved data.
+ **/
+gint
+gdk_selection_property_get (GdkWindow *requestor,
+ guchar **data,
+ GdkAtom *ret_type,
+ gint *ret_format)
+{
+ gulong nitems;
+ gulong nbytes;
+ gulong length = 0; /* Quiet GCC */
+ Atom prop_type;
+ gint prop_format;
+ guchar *t = NULL;
+ GdkDisplay *display;
+
+ g_return_val_if_fail (requestor != NULL, 0);
+ g_return_val_if_fail (GDK_IS_WINDOW (requestor), 0);
+ g_return_val_if_fail (GDK_WINDOW_IS_X11 (requestor), 0);
+
+ display = GDK_WINDOW_DISPLAY (requestor);
+
+ if (GDK_WINDOW_DESTROYED (requestor) || !GDK_WINDOW_IS_X11 (requestor))
+ goto err;
+
+ t = NULL;
+
+ /* We can't delete the selection here, because it might be the INCR
+ protocol, in which case the client has to make sure they'll be
+ notified of PropertyChange events _before_ the property is deleted.
+ Otherwise there's no guarantee we'll win the race ... */
+ if (XGetWindowProperty (GDK_DRAWABLE_XDISPLAY (requestor),
+ GDK_DRAWABLE_XID (requestor),
+ gdk_x11_atom_to_xatom_for_display (display, _gdk_selection_property),
+ 0, 0x1FFFFFFF /* MAXINT32 / 4 */, False,
+ AnyPropertyType, &prop_type, &prop_format,
+ &nitems, &nbytes, &t) != Success)
+ goto err;
+
+ if (prop_type != None)
+ {
+ if (ret_type)
+ *ret_type = gdk_x11_xatom_to_atom_for_display (display, prop_type);
+ if (ret_format)
+ *ret_format = prop_format;
+
+ if (prop_type == XA_ATOM ||
+ prop_type == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
+ {
+ Atom* atoms = (Atom*) t;
+ GdkAtom* atoms_dest;
+ gint num_atom, i;
+
+ if (prop_format != 32)
+ goto err;
+
+ num_atom = nitems;
+ length = sizeof (GdkAtom) * num_atom + 1;
+
+ if (data)
+ {
+ *data = g_malloc (length);
+ (*data)[length - 1] = '\0';
+ atoms_dest = (GdkAtom *)(*data);
+
+ for (i=0; i < num_atom; i++)
+ atoms_dest[i] = gdk_x11_xatom_to_atom_for_display (display, atoms[i]);
+ }
+ }
+ else
+ {
+ switch (prop_format)
+ {
+ case 8:
+ length = nitems;
+ break;
+ case 16:
+ length = sizeof(short) * nitems;
+ break;
+ case 32:
+ length = sizeof(long) * nitems;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ /* Add on an extra byte to handle null termination. X guarantees
+ that t will be 1 longer than nitems and null terminated */
+ length += 1;
+
+ if (data)
+ *data = g_memdup (t, length);
+ }
+
+ if (t)
+ XFree (t);
+
+ return length - 1;
+ }
+
+ err:
+ if (ret_type)
+ *ret_type = GDK_NONE;
+ if (ret_format)
+ *ret_format = 0;
+ if (data)
+ *data = NULL;
+
+ return 0;
+}
+
+/**
+ * gdk_selection_send_notify_for_display:
+ * @display: the #GdkDisplay where @requestor is realized
+ * @requestor: window to which to deliver response.
+ * @selection: selection that was requested.
+ * @target: target that was selected.
+ * @property: property in which the selection owner stored the data,
+ * or %GDK_NONE to indicate that the request was rejected.
+ * @time_: timestamp.
+ *
+ * Send a response to SelectionRequest event.
+ *
+ * Since: 2.2
+ **/
+void
+gdk_selection_send_notify_for_display (GdkDisplay *display,
+ GdkNativeWindow requestor,
+ GdkAtom selection,
+ GdkAtom target,
+ GdkAtom property,
+ guint32 time)
+{
+ XSelectionEvent xevent;
+
+ g_return_if_fail (GDK_IS_DISPLAY (display));
+
+ xevent.type = SelectionNotify;
+ xevent.serial = 0;
+ xevent.send_event = True;
+ xevent.requestor = requestor;
+ xevent.selection = gdk_x11_atom_to_xatom_for_display (display, selection);
+ xevent.target = gdk_x11_atom_to_xatom_for_display (display, target);
+ if (property == GDK_NONE)
+ xevent.property = None;
+ else
+ xevent.property = gdk_x11_atom_to_xatom_for_display (display, property);
+ xevent.time = time;
+
+ _gdk_send_xevent (display, requestor, False, NoEventMask, (XEvent*) & xevent);
+}
+
+/**
+ * gdk_text_property_to_text_list_for_display:
+ * @display: The #GdkDisplay where the encoding is defined.
+ * @encoding: an atom representing the encoding. The most
+ * common values for this are STRING, or COMPOUND_TEXT.
+ * This is value used as the type for the property.
+ * @format: the format of the property.
+ * @text: The text data.
+ * @length: The number of items to transform.
+ * @list: location to store a terminated array of strings in
+ * the encoding of the current locale. This array should be
+ * freed using gdk_free_text_list().
+ *
+ * Convert a text string from the encoding as it is stored
+ * in a property into an array of strings in the encoding of
+ * the current locale. (The elements of the array represent the
+ * nul-separated elements of the original text string.)
+ *
+ * Returns: the number of strings stored in list, or 0,
+ * if the conversion failed.
+ *
+ * Since: 2.2
+ */
+gint
+gdk_text_property_to_text_list_for_display (GdkDisplay *display,
+ GdkAtom encoding,
+ gint format,
+ const guchar *text,
+ gint length,
+ gchar ***list)
+{
+ XTextProperty property;
+ gint count = 0;
+ gint res;
+ gchar **local_list;
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
+
+ if (list)
+ *list = NULL;
+
+ if (display->closed)
+ return 0;
+
+ property.value = (guchar *)text;
+ property.encoding = gdk_x11_atom_to_xatom_for_display (display, encoding);
+ property.format = format;
+ property.nitems = length;
+ res = XmbTextPropertyToTextList (GDK_DISPLAY_XDISPLAY (display), &property,
+ &local_list, &count);
+ if (res == XNoMemory || res == XLocaleNotSupported || res == XConverterNotFound)
+ return 0;
+ else
+ {
+ if (list)
+ *list = local_list;
+ else
+ XFreeStringList (local_list);
+
+ return count;
+ }
+}
+
+void
+gdk_free_text_list (gchar **list)
+{
+ g_return_if_fail (list != NULL);
+
+ XFreeStringList (list);
+}
+
+static gint
+make_list (const gchar *text,
+ gint length,
+ gboolean latin1,
+ gchar ***list)
+{
+ GSList *strings = NULL;
+ gint n_strings = 0;
+ gint i;
+ const gchar *p = text;
+ const gchar *q;
+ GSList *tmp_list;
+ GError *error = NULL;
+
+ while (p < text + length)
+ {
+ gchar *str;
+
+ q = p;
+ while (*q && q < text + length)
+ q++;
+
+ if (latin1)
+ {
+ str = g_convert (p, q - p,
+ "UTF-8", "ISO-8859-1",
+ NULL, NULL, &error);
+
+ if (!str)
+ {
+ g_warning ("Error converting selection from STRING: %s",
+ error->message);
+ g_error_free (error);
+ }
+ }
+ else
+ {
+ str = g_strndup (p, q - p);
+ if (!g_utf8_validate (str, -1, NULL))
+ {
+ g_warning ("Error converting selection from UTF8_STRING");
+ g_free (str);
+ str = NULL;
+ }
+ }
+
+ if (str)
+ {
+ strings = g_slist_prepend (strings, str);
+ n_strings++;
+ }
+
+ p = q + 1;
+ }
+
+ if (list)
+ {
+ *list = g_new (gchar *, n_strings + 1);
+ (*list)[n_strings] = NULL;
+ }
+
+ i = n_strings;
+ tmp_list = strings;
+ while (tmp_list)
+ {
+ if (list)
+ (*list)[--i] = tmp_list->data;
+ else
+ g_free (tmp_list->data);
+
+ tmp_list = tmp_list->next;
+ }
+
+ g_slist_free (strings);
+
+ return n_strings;
+}
+
+/**
+ * gdk_text_property_to_utf8_list_for_display:
+ * @display: a #GdkDisplay
+ * @encoding: an atom representing the encoding of the text
+ * @format: the format of the property
+ * @text: the text to convert
+ * @length: the length of @text, in bytes
+ * @list: location to store the list of strings or %NULL. The
+ * list should be freed with g_strfreev().
+ *
+ * Converts a text property in the given encoding to
+ * a list of UTF-8 strings.
+ *
+ * Return value: the number of strings in the resulting
+ * list.
+ *
+ * Since: 2.2
+ **/
+gint
+gdk_text_property_to_utf8_list_for_display (GdkDisplay *display,
+ GdkAtom encoding,
+ gint format,
+ const guchar *text,
+ gint length,
+ gchar ***list)
+{
+ g_return_val_if_fail (text != NULL, 0);
+ g_return_val_if_fail (length >= 0, 0);
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
+
+ if (encoding == GDK_TARGET_STRING)
+ {
+ return make_list ((gchar *)text, length, TRUE, list);
+ }
+ else if (encoding == gdk_atom_intern_static_string ("UTF8_STRING"))
+ {
+ return make_list ((gchar *)text, length, FALSE, list);
+ }
+ else
+ {
+ gchar **local_list;
+ gint local_count;
+ gint i;
+ const gchar *charset = NULL;
+ gboolean need_conversion = !g_get_charset (&charset);
+ gint count = 0;
+ GError *error = NULL;
+
+ /* Probably COMPOUND text, we fall back to Xlib routines
+ */
+ local_count = gdk_text_property_to_text_list_for_display (display,
+ encoding,
+ format,
+ text,
+ length,
+ &local_list);
+ if (list)
+ *list = g_new (gchar *, local_count + 1);
+
+ for (i=0; i<local_count; i++)
+ {
+ /* list contains stuff in our default encoding
+ */
+ if (need_conversion)
+ {
+ gchar *utf = g_convert (local_list[i], -1,
+ "UTF-8", charset,
+ NULL, NULL, &error);
+ if (utf)
+ {
+ if (list)
+ (*list)[count++] = utf;
+ else
+ g_free (utf);
+ }
+ else
+ {
+ g_warning ("Error converting to UTF-8 from '%s': %s",
+ charset, error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+ }
+ else
+ {
+ if (list)
+ {
+ if (g_utf8_validate (local_list[i], -1, NULL))
+ (*list)[count++] = g_strdup (local_list[i]);
+ else
+ g_warning ("Error converting selection");
+ }
+ }
+ }
+
+ if (local_count)
+ gdk_free_text_list (local_list);
+
+ if (list)
+ (*list)[count] = NULL;
+
+ return count;
+ }
+}
+
+/**
+ * gdk_string_to_compound_text_for_display:
+ * @display: the #GdkDisplay where the encoding is defined.
+ * @str: a nul-terminated string.
+ * @encoding: location to store the encoding atom
+ * (to be used as the type for the property).
+ * @format: location to store the format of the property
+ * @ctext: location to store newly allocated data for the property.
+ * @length: the length of @text, in bytes
+ *
+ * Convert a string from the encoding of the current
+ * locale into a form suitable for storing in a window property.
+ *
+ * Returns: 0 upon success, non-zero upon failure.
+ *
+ * Since: 2.2
+ **/
+gint
+gdk_string_to_compound_text_for_display (GdkDisplay *display,
+ const gchar *str,
+ GdkAtom *encoding,
+ gint *format,
+ guchar **ctext,
+ gint *length)
+{
+ gint res;
+ XTextProperty property;
+
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
+
+ if (display->closed)
+ res = XLocaleNotSupported;
+ else
+ res = XmbTextListToTextProperty (GDK_DISPLAY_XDISPLAY (display),
+ (char **)&str, 1, XCompoundTextStyle,
+ &property);
+ if (res != Success)
+ {
+ property.encoding = None;
+ property.format = None;
+ property.value = NULL;
+ property.nitems = 0;
+ }
+
+ if (encoding)
+ *encoding = gdk_x11_xatom_to_atom_for_display (display, property.encoding);
+ if (format)
+ *format = property.format;
+ if (ctext)
+ *ctext = property.value;
+ if (length)
+ *length = property.nitems;
+
+ return res;
+}
+
+/* The specifications for COMPOUND_TEXT and STRING specify that C0 and
+ * C1 are not allowed except for \n and \t, however the X conversions
+ * routines for COMPOUND_TEXT only enforce this in one direction,
+ * causing cut-and-paste of \r and \r\n separated text to fail.
+ * This routine strips out all non-allowed C0 and C1 characters
+ * from the input string and also canonicalizes \r, and \r\n to \n
+ */
+static gchar *
+sanitize_utf8 (const gchar *src,
+ gboolean return_latin1)
+{
+ gint len = strlen (src);
+ GString *result = g_string_sized_new (len);
+ const gchar *p = src;
+
+ while (*p)
+ {
+ if (*p == '\r')
+ {
+ p++;
+ if (*p == '\n')
+ p++;
+
+ g_string_append_c (result, '\n');
+ }
+ else
+ {
+ gunichar ch = g_utf8_get_char (p);
+
+ if (!((ch < 0x20 && ch != '\t' && ch != '\n') || (ch >= 0x7f && ch < 0xa0)))
+ {
+ if (return_latin1)
+ {
+ if (ch <= 0xff)
+ g_string_append_c (result, ch);
+ else
+ g_string_append_printf (result,
+ ch < 0x10000 ? "\\u%04x" : "\\U%08x",
+ ch);
+ }
+ else
+ {
+ char buf[7];
+ gint buflen;
+
+ buflen = g_unichar_to_utf8 (ch, buf);
+ g_string_append_len (result, buf, buflen);
+ }
+ }
+
+ p = g_utf8_next_char (p);
+ }
+ }
+
+ return g_string_free (result, FALSE);
+}
+
+/**
+ * gdk_utf8_to_string_target:
+ * @str: a UTF-8 string
+ *
+ * Converts an UTF-8 string into the best possible representation
+ * as a STRING. The representation of characters not in STRING
+ * is not specified; it may be as pseudo-escape sequences
+ * \x{ABCD}, or it may be in some other form of approximation.
+ *
+ * Return value: the newly-allocated string, or %NULL if the
+ * conversion failed. (It should not fail for
+ * any properly formed UTF-8 string unless system
+ * limits like memory or file descriptors are exceeded.)
+ **/
+gchar *
+gdk_utf8_to_string_target (const gchar *str)
+{
+ return sanitize_utf8 (str, TRUE);
+}
+
+/**
+ * gdk_utf8_to_compound_text_for_display:
+ * @display: a #GdkDisplay
+ * @str: a UTF-8 string
+ * @encoding: location to store resulting encoding
+ * @format: location to store format of the result
+ * @ctext: location to store the data of the result
+ * @length: location to store the length of the data
+ * stored in @ctext
+ *
+ * Converts from UTF-8 to compound text.
+ *
+ * Return value: %TRUE if the conversion succeeded, otherwise
+ * %FALSE.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gdk_utf8_to_compound_text_for_display (GdkDisplay *display,
+ const gchar *str,
+ GdkAtom *encoding,
+ gint *format,
+ guchar **ctext,
+ gint *length)
+{
+ gboolean need_conversion;
+ const gchar *charset;
+ gchar *locale_str, *tmp_str;
+ GError *error = NULL;
+ gboolean result;
+
+ g_return_val_if_fail (str != NULL, FALSE);
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
+
+ need_conversion = !g_get_charset (&charset);
+
+ tmp_str = sanitize_utf8 (str, FALSE);
+
+ if (need_conversion)
+ {
+ locale_str = g_convert (tmp_str, -1,
+ charset, "UTF-8",
+ NULL, NULL, &error);
+ g_free (tmp_str);
+
+ if (!locale_str)
+ {
+ if (!(error->domain = G_CONVERT_ERROR &&
+ error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
+ {
+ g_warning ("Error converting from UTF-8 to '%s': %s",
+ charset, error->message);
+ }
+ g_error_free (error);
+
+ if (encoding)
+ *encoding = None;
+ if (format)
+ *format = None;
+ if (ctext)
+ *ctext = NULL;
+ if (length)
+ *length = 0;
+
+ return FALSE;
+ }
+ }
+ else
+ locale_str = tmp_str;
+
+ result = gdk_string_to_compound_text_for_display (display, locale_str,
+ encoding, format,
+ ctext, length);
+ result = (result == Success? TRUE : FALSE);
+
+ g_free (locale_str);
+
+ return result;
+}
+
+void gdk_free_compound_text (guchar *ctext)
+{
+ if (ctext)
+ XFree (ctext);
+}
diff --git a/gdk/broadway/gdksettings.c b/gdk/broadway/gdksettings.c
new file mode 100644
index 0000000000..0e98816bf0
--- /dev/null
+++ b/gdk/broadway/gdksettings.c
@@ -0,0 +1,128 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+
+#define GDK_SETTINGS_N_ELEMENTS() G_N_ELEMENTS (gdk_settings_map)
+#define GDK_SETTINGS_X_NAME(nth) (gdk_settings_names + gdk_settings_map[nth].xsettings_offset)
+#define GDK_SETTINGS_GDK_NAME(nth) (gdk_settings_names + gdk_settings_map[nth].gdk_offset)
+
+static const char gdk_settings_names[] =
+ "Net/DoubleClickTime\0" "gtk-double-click-time\0"
+ "Net/DoubleClickDistance\0" "gtk-double-click-distance\0"
+ "Net/DndDragThreshold\0" "gtk-dnd-drag-threshold\0"
+ "Net/CursorBlink\0" "gtk-cursor-blink\0"
+ "Net/CursorBlinkTime\0" "gtk-cursor-blink-time\0"
+ "Net/ThemeName\0" "gtk-theme-name\0"
+ "Net/IconThemeName\0" "gtk-icon-theme-name\0"
+ "Gtk/CanChangeAccels\0" "gtk-can-change-accels\0"
+ "Gtk/ColorPalette\0" "gtk-color-palette\0"
+ "Gtk/FontName\0" "gtk-font-name\0"
+ "Gtk/IconSizes\0" "gtk-icon-sizes\0"
+ "Gtk/KeyThemeName\0" "gtk-key-theme-name\0"
+ "Gtk/ToolbarStyle\0" "gtk-toolbar-style\0"
+ "Gtk/ToolbarIconSize\0" "gtk-toolbar-icon-size\0"
+ "Gtk/IMPreeditStyle\0" "gtk-im-preedit-style\0"
+ "Gtk/IMStatusStyle\0" "gtk-im-status-style\0"
+ "Gtk/Modules\0" "gtk-modules\0"
+ "Gtk/FileChooserBackend\0" "gtk-file-chooser-backend\0"
+ "Gtk/ButtonImages\0" "gtk-button-images\0"
+ "Gtk/MenuImages\0" "gtk-menu-images\0"
+ "Gtk/MenuBarAccel\0" "gtk-menu-bar-accel\0"
+ "Gtk/CursorThemeName\0" "gtk-cursor-theme-name\0"
+ "Gtk/CursorThemeSize\0" "gtk-cursor-theme-size\0"
+ "Gtk/ShowInputMethodMenu\0" "gtk-show-input-method-menu\0"
+ "Gtk/ShowUnicodeMenu\0" "gtk-show-unicode-menu\0"
+ "Gtk/TimeoutInitial\0" "gtk-timeout-initial\0"
+ "Gtk/TimeoutRepeat\0" "gtk-timeout-repeat\0"
+ "Gtk/ColorScheme\0" "gtk-color-scheme\0"
+ "Gtk/EnableAnimations\0" "gtk-enable-animations\0"
+ "Xft/Antialias\0" "gtk-xft-antialias\0"
+ "Xft/Hinting\0" "gtk-xft-hinting\0"
+ "Xft/HintStyle\0" "gtk-xft-hintstyle\0"
+ "Xft/RGBA\0" "gtk-xft-rgba\0"
+ "Xft/DPI\0" "gtk-xft-dpi\0"
+ "Net/FallbackIconTheme\0" "gtk-fallback-icon-theme\0"
+ "Gtk/TouchscreenMode\0" "gtk-touchscreen-mode\0"
+ "Gtk/EnableAccels\0" "gtk-enable-accels\0"
+ "Gtk/EnableMnemonics\0" "gtk-enable-mnemonics\0"
+ "Gtk/ScrolledWindowPlacement\0" "gtk-scrolled-window-placement\0"
+ "Gtk/IMModule\0" "gtk-im-module\0"
+ "Fontconfig/Timestamp\0" "gtk-fontconfig-timestamp\0"
+ "Net/SoundThemeName\0" "gtk-sound-theme-name\0"
+ "Net/EnableInputFeedbackSounds\0" "gtk-enable-input-feedback-sounds\0"
+ "Net/EnableEventSounds\0" "gtk-enable-event-sounds\0";
+
+
+static const struct
+{
+ gint xsettings_offset;
+ gint gdk_offset;
+} gdk_settings_map[] = {
+ { 0, 20 },
+ { 42, 66 },
+ { 92, 113 },
+ { 136, 152 },
+ { 169, 189 },
+ { 211, 225 },
+ { 240, 258 },
+ { 278, 298 },
+ { 320, 337 },
+ { 355, 368 },
+ { 382, 396 },
+ { 411, 428 },
+ { 447, 464 },
+ { 482, 502 },
+ { 524, 543 },
+ { 564, 582 },
+ { 602, 614 },
+ { 626, 649 },
+ { 674, 691 },
+ { 709, 724 },
+ { 740, 757 },
+ { 776, 796 },
+ { 818, 838 },
+ { 860, 884 },
+ { 911, 931 },
+ { 953, 972 },
+ { 992, 1010 },
+ { 1029, 1045 },
+ { 1062, 1083 },
+ { 1105, 1119 },
+ { 1137, 1149 },
+ { 1165, 1179 },
+ { 1197, 1206 },
+ { 1219, 1227 },
+ { 1239, 1261 },
+ { 1285, 1305 },
+ { 1326, 1343 },
+ { 1361, 1381 },
+ { 1402, 1430 },
+ { 1460, 1473 },
+ { 1487, 1508 },
+ { 1533, 1552 },
+ { 1573, 1603 },
+ { 1636, 1658 }
+};
diff --git a/gdk/broadway/gdkspawn-broadway.c b/gdk/broadway/gdkspawn-broadway.c
new file mode 100644
index 0000000000..7f97b4e4f0
--- /dev/null
+++ b/gdk/broadway/gdkspawn-broadway.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2003 Sun Microsystems Inc.
+ *
+ * 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.
+ *
+ * Authors: Mark McLoughlin <mark@skynet.ie>
+ */
+
+#include "config.h"
+
+#include "gdkspawn.h"
+
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+
+
+typedef struct {
+ char *display;
+ GSpawnChildSetupFunc child_setup;
+ gpointer user_data;
+} UserChildSetup;
+
+/*
+ * Set the DISPLAY variable, and then call the user-specified child setup
+ * function. This is required so that applications can use gdk_spawn_* and
+ * call putenv() in their child_setup functions.
+ */
+static void
+set_environment (gpointer user_data)
+{
+ UserChildSetup *setup = user_data;
+
+ g_setenv ("DISPLAY", setup->display, TRUE);
+
+ if (setup->child_setup)
+ setup->child_setup (setup->user_data);
+}
+
+/**
+ * gdk_spawn_on_screen:
+ * @screen: a #GdkScreen
+ * @working_directory: child's current working directory, or %NULL to
+ * inherit parent's
+ * @argv: child's argument vector
+ * @envp: child's environment, or %NULL to inherit parent's
+ * @flags: flags from #GSpawnFlags
+ * @child_setup: function to run in the child just before exec()
+ * @user_data: user data for @child_setup
+ * @child_pid: return location for child process ID, or %NULL
+ * @error: return location for error
+ *
+ * Like g_spawn_async(), except the child process is spawned in such
+ * an environment that on calling gdk_display_open() it would be
+ * returned a #GdkDisplay with @screen as the default screen.
+ *
+ * This is useful for applications which wish to launch an application
+ * on a specific screen.
+ *
+ * Return value: %TRUE on success, %FALSE if error is set
+ *
+ * Since: 2.4
+ **/
+gboolean
+gdk_spawn_on_screen (GdkScreen *screen,
+ const gchar *working_directory,
+ gchar **argv,
+ gchar **envp,
+ GSpawnFlags flags,
+ GSpawnChildSetupFunc child_setup,
+ gpointer user_data,
+ GPid *child_pid,
+ GError **error)
+{
+ UserChildSetup setup_data;
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
+
+ setup_data.display = gdk_screen_make_display_name (screen);
+ setup_data.child_setup = child_setup;
+ setup_data.user_data = user_data;
+
+ return g_spawn_async (working_directory,
+ argv,
+ envp,
+ flags,
+ set_environment,
+ &setup_data,
+ child_pid,
+ error);
+}
+
+/**
+ * gdk_spawn_on_screen_with_pipes:
+ * @screen: a #GdkScreen
+ * @working_directory: child's current working directory, or %NULL to
+ * inherit parent's
+ * @argv: child's argument vector
+ * @envp: child's environment, or %NULL to inherit parent's
+ * @flags: flags from #GSpawnFlags
+ * @child_setup: function to run in the child just before exec()
+ * @user_data: user data for @child_setup
+ * @child_pid: return location for child process ID, or %NULL
+ * @standard_input: return location for file descriptor to write to
+ * child's stdin, or %NULL
+ * @standard_output: return location for file descriptor to read child's
+ * stdout, or %NULL
+ * @standard_error: return location for file descriptor to read child's
+ * stderr, or %NULL
+ * @error: return location for error
+ *
+ * Like g_spawn_async_with_pipes(), except the child process is
+ * spawned in such an environment that on calling gdk_display_open()
+ * it would be returned a #GdkDisplay with @screen as the default
+ * screen.
+ *
+ * This is useful for applications which wish to launch an application
+ * on a specific screen.
+ *
+ * Return value: %TRUE on success, %FALSE if an error was set
+ *
+ * Since: 2.4
+ **/
+gboolean
+gdk_spawn_on_screen_with_pipes (GdkScreen *screen,
+ const gchar *working_directory,
+ gchar **argv,
+ gchar **envp,
+ GSpawnFlags flags,
+ GSpawnChildSetupFunc child_setup,
+ gpointer user_data,
+ GPid *child_pid,
+ gint *standard_input,
+ gint *standard_output,
+ gint *standard_error,
+ GError **error)
+{
+ UserChildSetup setup_data;
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
+
+ setup_data.display = gdk_screen_make_display_name (screen);
+ setup_data.child_setup = child_setup;
+ setup_data.user_data = user_data;
+
+ return g_spawn_async_with_pipes (working_directory,
+ argv,
+ envp,
+ flags,
+ set_environment,
+ &setup_data,
+ child_pid,
+ standard_input,
+ standard_output,
+ standard_error,
+ error);
+
+}
+
+/**
+ * gdk_spawn_command_line_on_screen:
+ * @screen: a #GdkScreen
+ * @command_line: a command line
+ * @error: return location for errors
+ *
+ * Like g_spawn_command_line_async(), except the child process is
+ * spawned in such an environment that on calling gdk_display_open()
+ * it would be returned a #GdkDisplay with @screen as the default
+ * screen.
+ *
+ * This is useful for applications which wish to launch an application
+ * on a specific screen.
+ *
+ * Return value: %TRUE on success, %FALSE if error is set.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gdk_spawn_command_line_on_screen (GdkScreen *screen,
+ const gchar *command_line,
+ GError **error)
+{
+ gchar **argv = NULL;
+ gboolean retval;
+
+ g_return_val_if_fail (command_line != NULL, FALSE);
+
+ if (!g_shell_parse_argv (command_line,
+ NULL, &argv,
+ error))
+ return FALSE;
+
+ retval = gdk_spawn_on_screen (screen,
+ NULL, argv, NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL, NULL, NULL,
+ error);
+ g_strfreev (argv);
+
+ return retval;
+}
diff --git a/gdk/broadway/gdktestutils-broadway.c b/gdk/broadway/gdktestutils-broadway.c
new file mode 100644
index 0000000000..d4d47fbad7
--- /dev/null
+++ b/gdk/broadway/gdktestutils-broadway.c
@@ -0,0 +1,254 @@
+/* Gtk+ testing utilities
+ * Copyright (C) 2007 Imendio AB
+ * Authors: Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 "gdktestutils.h"
+
+#include "gdkkeysyms.h"
+#include "gdkx.h"
+
+#include <X11/Xlib.h>
+
+/**
+ * gdk_test_render_sync:
+ * @window: a mapped #GdkWindow
+ *
+ * This function retrieves a pixel from @window to force the windowing
+ * system to carry out any pending rendering commands.
+ * This function is intended to be used to syncronize with rendering
+ * pipelines, to benchmark windowing system rendering operations.
+ *
+ * Since: 2.14
+ **/
+void
+gdk_test_render_sync (GdkWindow *window)
+{
+ Display *display = gdk_x11_drawable_get_xdisplay (window);
+ XImage *ximage;
+
+ /* syncronize to X drawing queue, see:
+ * http://mail.gnome.org/archives/gtk-devel-list/2006-October/msg00103.html
+ */
+ ximage = XGetImage (display, DefaultRootWindow (display),
+ 0, 0, 1, 1, AllPlanes, ZPixmap);
+ if (ximage != NULL)
+ XDestroyImage (ximage);
+}
+
+/**
+ * gdk_test_simulate_key
+ * @window: a #GdkWindow to simulate a key event for.
+ * @x: x coordinate within @window for the key event.
+ * @y: y coordinate within @window for the key event.
+ * @keyval: A GDK keyboard value.
+ * @modifiers: Keyboard modifiers the event is setup with.
+ * @key_pressrelease: either %GDK_KEY_PRESS or %GDK_KEY_RELEASE
+ *
+ * This function is intended to be used in GTK+ test programs.
+ * If (@x,@y) are > (-1,-1), it will warp the mouse pointer to
+ * the given (@x,@y) corrdinates within @window and simulate a
+ * key press or release event.
+ *
+ * When the mouse pointer is warped to the target location, use
+ * of this function outside of test programs that run in their
+ * own virtual windowing system (e.g. Xvfb) is not recommended.
+ * If (@x,@y) are passed as (-1,-1), the mouse pointer will not
+ * be warped and @window origin will be used as mouse pointer
+ * location for the event.
+ *
+ * Also, gtk_test_simulate_key() is a fairly low level function,
+ * for most testing purposes, gtk_test_widget_send_key() is the
+ * right function to call which will generate a key press event
+ * followed by its accompanying key release event.
+ *
+ * Returns: whether all actions neccessary for a key event simulation
+ * were carried out successfully.
+ *
+ * Since: 2.14
+ **/
+gboolean
+gdk_test_simulate_key (GdkWindow *window,
+ gint x,
+ gint y,
+ guint keyval,
+ GdkModifierType modifiers,
+ GdkEventType key_pressrelease)
+{
+ GdkScreen *screen;
+ GdkKeymapKey *keys = NULL;
+ GdkWindowObject *priv;
+ gboolean success;
+ gint n_keys = 0;
+ XKeyEvent xev = {
+ 0, /* type */
+ 0, /* serial */
+ 1, /* send_event */
+ };
+ g_return_val_if_fail (key_pressrelease == GDK_KEY_PRESS || key_pressrelease == GDK_KEY_RELEASE, FALSE);
+ g_return_val_if_fail (window != NULL, FALSE);
+ if (!GDK_WINDOW_IS_MAPPED (window))
+ return FALSE;
+
+ screen = gdk_window_get_screen (window);
+ priv = (GdkWindowObject *)window;
+
+ if (x < 0 && y < 0)
+ {
+ x = priv->width / 2;
+ y = priv->height / 2;
+ }
+
+ /* Convert to impl coordinates */
+ x = x + priv->abs_x;
+ y = y + priv->abs_y;
+
+ xev.type = key_pressrelease == GDK_KEY_PRESS ? KeyPress : KeyRelease;
+ xev.display = GDK_DRAWABLE_XDISPLAY (window);
+ xev.window = GDK_WINDOW_XID (window);
+ xev.root = RootWindow (xev.display, GDK_SCREEN_XNUMBER (screen));
+ xev.subwindow = 0;
+ xev.time = 0;
+ xev.x = MAX (x, 0);
+ xev.y = MAX (y, 0);
+ xev.x_root = 0;
+ xev.y_root = 0;
+ xev.state = modifiers;
+ xev.keycode = 0;
+ success = gdk_keymap_get_entries_for_keyval (gdk_keymap_get_for_display (gdk_window_get_display (window)), keyval, &keys, &n_keys);
+ success &= n_keys > 0;
+ if (success)
+ {
+ gint i;
+ for (i = 0; i < n_keys; i++)
+ if (keys[i].group == 0 && keys[i].level == 0)
+ {
+ xev.keycode = keys[i].keycode;
+ break;
+ }
+ if (i >= n_keys) /* no match for group==0 and level==0 */
+ xev.keycode = keys[0].keycode;
+ }
+ g_free (keys);
+ if (!success)
+ return FALSE;
+ gdk_error_trap_push ();
+ xev.same_screen = XTranslateCoordinates (xev.display, xev.window, xev.root,
+ xev.x, xev.y, &xev.x_root, &xev.y_root,
+ &xev.subwindow);
+ if (!xev.subwindow)
+ xev.subwindow = xev.window;
+ success &= xev.same_screen;
+ if (x >= 0 && y >= 0)
+ success &= 0 != XWarpPointer (xev.display, None, xev.window, 0, 0, 0, 0, xev.x, xev.y);
+ success &= 0 != XSendEvent (xev.display, xev.window, True, key_pressrelease == GDK_KEY_PRESS ? KeyPressMask : KeyReleaseMask, (XEvent*) &xev);
+ XSync (xev.display, False);
+ success &= 0 == gdk_error_trap_pop();
+ return success;
+}
+
+/**
+ * gdk_test_simulate_button
+ * @window: a #GdkWindow to simulate a button event for.
+ * @x: x coordinate within @window for the button event.
+ * @y: y coordinate within @window for the button event.
+ * @button: Number of the pointer button for the event, usually 1, 2 or 3.
+ * @modifiers: Keyboard modifiers the event is setup with.
+ * @button_pressrelease: either %GDK_BUTTON_PRESS or %GDK_BUTTON_RELEASE
+ *
+ * This function is intended to be used in GTK+ test programs.
+ * It will warp the mouse pointer to the given (@x,@y) corrdinates
+ * within @window and simulate a button press or release event.
+ * Because the mouse pointer needs to be warped to the target
+ * location, use of this function outside of test programs that
+ * run in their own virtual windowing system (e.g. Xvfb) is not
+ * recommended.
+ *
+ * Also, gtk_test_simulate_button() is a fairly low level function,
+ * for most testing purposes, gtk_test_widget_click() is the right
+ * function to call which will generate a button press event followed
+ * by its accompanying button release event.
+ *
+ * Returns: whether all actions neccessary for a button event simulation
+ * were carried out successfully.
+ *
+ * Since: 2.14
+ **/
+gboolean
+gdk_test_simulate_button (GdkWindow *window,
+ gint x,
+ gint y,
+ guint button, /*1..3*/
+ GdkModifierType modifiers,
+ GdkEventType button_pressrelease)
+{
+ GdkScreen *screen;
+ XButtonEvent xev = {
+ 0, /* type */
+ 0, /* serial */
+ 1, /* send_event */
+ };
+ gboolean success;
+ GdkWindowObject *priv;
+
+ g_return_val_if_fail (button_pressrelease == GDK_BUTTON_PRESS || button_pressrelease == GDK_BUTTON_RELEASE, FALSE);
+ g_return_val_if_fail (window != NULL, FALSE);
+
+ if (!GDK_WINDOW_IS_MAPPED (window))
+ return FALSE;
+
+ screen = gdk_window_get_screen (window);
+ priv = (GdkWindowObject *)window;
+
+ if (x < 0 && y < 0)
+ {
+ x = priv->width / 2;
+ y = priv->height / 2;
+ }
+
+ /* Convert to impl coordinates */
+ x = x + priv->abs_x;
+ y = y + priv->abs_y;
+
+ xev.type = button_pressrelease == GDK_BUTTON_PRESS ? ButtonPress : ButtonRelease;
+ xev.display = GDK_DRAWABLE_XDISPLAY (window);
+ xev.window = GDK_WINDOW_XID (window);
+ xev.root = RootWindow (xev.display, GDK_SCREEN_XNUMBER (screen));
+ xev.subwindow = 0;
+ xev.time = 0;
+ xev.x = x;
+ xev.y = y;
+ xev.x_root = 0;
+ xev.y_root = 0;
+ xev.state = modifiers;
+ xev.button = button;
+ gdk_error_trap_push ();
+ xev.same_screen = XTranslateCoordinates (xev.display, xev.window, xev.root,
+ xev.x, xev.y, &xev.x_root, &xev.y_root,
+ &xev.subwindow);
+ if (!xev.subwindow)
+ xev.subwindow = xev.window;
+ success = xev.same_screen;
+ success &= 0 != XWarpPointer (xev.display, None, xev.window, 0, 0, 0, 0, xev.x, xev.y);
+ success &= 0 != XSendEvent (xev.display, xev.window, True, button_pressrelease == GDK_BUTTON_PRESS ? ButtonPressMask : ButtonReleaseMask, (XEvent*) &xev);
+ XSync (xev.display, False);
+ success &= 0 == gdk_error_trap_pop();
+ return success;
+}
diff --git a/gdk/broadway/gdkvisual-broadway.c b/gdk/broadway/gdkvisual-broadway.c
new file mode 100644
index 0000000000..350e8a2391
--- /dev/null
+++ b/gdk/broadway/gdkvisual-broadway.c
@@ -0,0 +1,730 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gdkvisual.h"
+
+#include "gdkx.h"
+#include "gdkprivate-broadway.h"
+#include "gdkscreen-broadway.h"
+#include "gdkinternals.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+struct _GdkVisualPrivate
+{
+ Visual *xvisual;
+ GdkScreen *screen;
+ Colormap colormap;
+};
+
+struct _GdkVisualClass
+{
+ GObjectClass parent_class;
+};
+
+static void gdk_visual_add (GdkVisual *visual);
+static void gdk_visual_decompose_mask (gulong mask,
+ gint *shift,
+ gint *prec);
+static guint gdk_visual_hash (Visual *key);
+static gboolean gdk_visual_equal (Visual *a,
+ Visual *b);
+
+
+#ifdef G_ENABLE_DEBUG
+
+static const gchar *const visual_names[] =
+{
+ "static gray",
+ "grayscale",
+ "static color",
+ "pseudo color",
+ "true color",
+ "direct color",
+};
+
+#endif /* G_ENABLE_DEBUG */
+
+G_DEFINE_TYPE (GdkVisual, gdk_visual, G_TYPE_OBJECT)
+
+static void
+gdk_visual_finalize (GObject *object)
+{
+ GdkVisualPrivate *priv = (GdkVisualPrivate *) object;
+
+ if (priv->colormap != None)
+ XFreeColormap (GDK_SCREEN_XDISPLAY (priv->screen),
+ priv->colormap);
+
+ G_OBJECT_CLASS (gdk_visual_parent_class)->finalize (object);
+}
+
+static void
+gdk_visual_class_init (GdkVisualClass *visual_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (visual_class);
+
+ g_type_class_add_private (object_class, sizeof (GdkVisualPrivate));
+
+ object_class->finalize = gdk_visual_finalize;
+}
+
+static void
+gdk_visual_init (GdkVisual *visual)
+{
+ visual->priv = G_TYPE_INSTANCE_GET_PRIVATE (visual,
+ GDK_TYPE_VISUAL,
+ GdkVisualPrivate);
+
+ visual->priv->colormap = None;
+}
+
+void
+_gdk_visual_init (GdkScreen *screen)
+{
+ static const gint possible_depths[8] = { 32, 30, 24, 16, 15, 8, 4, 1 };
+ static const GdkVisualType possible_types[6] =
+ {
+ GDK_VISUAL_DIRECT_COLOR,
+ GDK_VISUAL_TRUE_COLOR,
+ GDK_VISUAL_PSEUDO_COLOR,
+ GDK_VISUAL_STATIC_COLOR,
+ GDK_VISUAL_GRAYSCALE,
+ GDK_VISUAL_STATIC_GRAY
+ };
+
+ GdkScreenX11 *screen_x11;
+ XVisualInfo *visual_list;
+ XVisualInfo visual_template;
+ GdkVisual *temp_visual;
+ Visual *default_xvisual;
+ GdkVisual **visuals;
+ int nxvisuals;
+ int nvisuals;
+ int i, j;
+
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+ screen_x11 = GDK_SCREEN_X11 (screen);
+
+ nxvisuals = 0;
+ visual_template.screen = screen_x11->screen_num;
+ visual_list = XGetVisualInfo (screen_x11->xdisplay, VisualScreenMask, &visual_template, &nxvisuals);
+
+ visuals = g_new (GdkVisual *, nxvisuals);
+ for (i = 0; i < nxvisuals; i++)
+ visuals[i] = g_object_new (GDK_TYPE_VISUAL, NULL);
+
+ default_xvisual = DefaultVisual (screen_x11->xdisplay, screen_x11->screen_num);
+
+ nvisuals = 0;
+ for (i = 0; i < nxvisuals; i++)
+ {
+ visuals[nvisuals]->priv->screen = screen;
+
+ if (visual_list[i].depth >= 1)
+ {
+#ifdef __cplusplus
+ switch (visual_list[i].c_class)
+#else /* __cplusplus */
+ switch (visual_list[i].class)
+#endif /* __cplusplus */
+ {
+ case StaticGray:
+ visuals[nvisuals]->type = GDK_VISUAL_STATIC_GRAY;
+ break;
+ case GrayScale:
+ visuals[nvisuals]->type = GDK_VISUAL_GRAYSCALE;
+ break;
+ case StaticColor:
+ visuals[nvisuals]->type = GDK_VISUAL_STATIC_COLOR;
+ break;
+ case PseudoColor:
+ visuals[nvisuals]->type = GDK_VISUAL_PSEUDO_COLOR;
+ break;
+ case TrueColor:
+ visuals[nvisuals]->type = GDK_VISUAL_TRUE_COLOR;
+ break;
+ case DirectColor:
+ visuals[nvisuals]->type = GDK_VISUAL_DIRECT_COLOR;
+ break;
+ }
+
+ visuals[nvisuals]->depth = visual_list[i].depth;
+ visuals[nvisuals]->byte_order =
+ (ImageByteOrder(screen_x11->xdisplay) == LSBFirst) ?
+ GDK_LSB_FIRST : GDK_MSB_FIRST;
+ visuals[nvisuals]->red_mask = visual_list[i].red_mask;
+ visuals[nvisuals]->green_mask = visual_list[i].green_mask;
+ visuals[nvisuals]->blue_mask = visual_list[i].blue_mask;
+ visuals[nvisuals]->colormap_size = visual_list[i].colormap_size;
+ visuals[nvisuals]->bits_per_rgb = visual_list[i].bits_per_rgb;
+ visuals[nvisuals]->priv->xvisual = visual_list[i].visual;
+
+ if ((visuals[nvisuals]->type == GDK_VISUAL_TRUE_COLOR) ||
+ (visuals[nvisuals]->type == GDK_VISUAL_DIRECT_COLOR))
+ {
+ gdk_visual_decompose_mask (visuals[nvisuals]->red_mask,
+ &visuals[nvisuals]->red_shift,
+ &visuals[nvisuals]->red_prec);
+
+ gdk_visual_decompose_mask (visuals[nvisuals]->green_mask,
+ &visuals[nvisuals]->green_shift,
+ &visuals[nvisuals]->green_prec);
+
+ gdk_visual_decompose_mask (visuals[nvisuals]->blue_mask,
+ &visuals[nvisuals]->blue_shift,
+ &visuals[nvisuals]->blue_prec);
+ }
+ else
+ {
+ visuals[nvisuals]->red_mask = 0;
+ visuals[nvisuals]->red_shift = 0;
+ visuals[nvisuals]->red_prec = 0;
+
+ visuals[nvisuals]->green_mask = 0;
+ visuals[nvisuals]->green_shift = 0;
+ visuals[nvisuals]->green_prec = 0;
+
+ visuals[nvisuals]->blue_mask = 0;
+ visuals[nvisuals]->blue_shift = 0;
+ visuals[nvisuals]->blue_prec = 0;
+ }
+
+ nvisuals += 1;
+ }
+ }
+
+ if (visual_list)
+ XFree (visual_list);
+
+ for (i = 0; i < nvisuals; i++)
+ {
+ for (j = i+1; j < nvisuals; j++)
+ {
+ if (visuals[j]->depth >= visuals[i]->depth)
+ {
+ if ((visuals[j]->depth == 8) && (visuals[i]->depth == 8))
+ {
+ if (visuals[j]->type == GDK_VISUAL_PSEUDO_COLOR)
+ {
+ temp_visual = visuals[j];
+ visuals[j] = visuals[i];
+ visuals[i] = temp_visual;
+ }
+ else if ((visuals[i]->type != GDK_VISUAL_PSEUDO_COLOR) &&
+ visuals[j]->type > visuals[i]->type)
+ {
+ temp_visual = visuals[j];
+ visuals[j] = visuals[i];
+ visuals[i] = temp_visual;
+ }
+ }
+ else if ((visuals[j]->depth > visuals[i]->depth) ||
+ ((visuals[j]->depth == visuals[i]->depth) &&
+ (visuals[j]->type > visuals[i]->type)))
+ {
+ temp_visual = visuals[j];
+ visuals[j] = visuals[i];
+ visuals[i] = temp_visual;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < nvisuals; i++)
+ {
+ if (default_xvisual->visualid == visuals[i]->priv->xvisual->visualid)
+ {
+ screen_x11->system_visual = visuals[i];
+ visuals[i]->priv->colormap = DefaultColormap (screen_x11->xdisplay,
+ screen_x11->screen_num);
+ }
+
+ /* For now, we only support 8888 ARGB for the "rgba visual".
+ * Additional formats (like ABGR) could be added later if they
+ * turn up.
+ */
+ if (visuals[i]->depth == 32 &&
+ (visuals[i]->red_mask == 0xff0000 &&
+ visuals[i]->green_mask == 0x00ff00 &&
+ visuals[i]->blue_mask == 0x0000ff))
+ {
+ screen_x11->rgba_visual = GDK_VISUAL (visuals[i]);
+ }
+ }
+
+#ifdef G_ENABLE_DEBUG
+ if (_gdk_debug_flags & GDK_DEBUG_MISC)
+ {
+ static const gchar *const visual_names[] =
+ {
+ "static gray",
+ "grayscale",
+ "static color",
+ "pseudo color",
+ "true color",
+ "direct color",
+ };
+
+ for (i = 0; i < nvisuals; i++)
+ g_message ("visual: %s: %d",
+ visual_names[visuals[i]->type],
+ visuals[i]->depth);
+ }
+#endif /* G_ENABLE_DEBUG */
+
+ screen_x11->navailable_depths = 0;
+ for (i = 0; i < G_N_ELEMENTS (possible_depths); i++)
+ {
+ for (j = 0; j < nvisuals; j++)
+ {
+ if (visuals[j]->depth == possible_depths[i])
+ {
+ screen_x11->available_depths[screen_x11->navailable_depths++] = visuals[j]->depth;
+ break;
+ }
+ }
+ }
+
+ if (screen_x11->navailable_depths == 0)
+ g_error ("unable to find a usable depth");
+
+ screen_x11->navailable_types = 0;
+ for (i = 0; i < G_N_ELEMENTS (possible_types); i++)
+ {
+ for (j = 0; j < nvisuals; j++)
+ {
+ if (visuals[j]->type == possible_types[i])
+ {
+ screen_x11->available_types[screen_x11->navailable_types++] = visuals[j]->type;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < nvisuals; i++)
+ gdk_visual_add ((GdkVisual*) visuals[i]);
+
+ if (screen_x11->navailable_types == 0)
+ g_error ("unable to find a usable visual type");
+
+ screen_x11->visuals = visuals;
+ screen_x11->nvisuals = nvisuals;
+}
+
+/**
+ * gdk_visual_get_best_depth:
+ *
+ * Get the best available depth for the default GDK screen. "Best"
+ * means "largest," i.e. 32 preferred over 24 preferred over 8 bits
+ * per pixel.
+ *
+ * Return value: best available depth
+ **/
+gint
+gdk_visual_get_best_depth (void)
+{
+ GdkScreen *screen = gdk_screen_get_default();
+
+ return GDK_SCREEN_X11 (screen)->available_depths[0];
+}
+
+/**
+ * gdk_visual_get_best_type:
+ *
+ * Return the best available visual type for the default GDK screen.
+ *
+ * Return value: best visual type
+ **/
+GdkVisualType
+gdk_visual_get_best_type (void)
+{
+ GdkScreen *screen = gdk_screen_get_default();
+
+ return GDK_SCREEN_X11 (screen)->available_types[0];
+}
+
+/**
+ * gdk_screen_get_system_visual:
+ * @screen: a #GdkScreen.
+ *
+ * Get the system's default visual for @screen.
+ * This is the visual for the root window of the display.
+ * The return value should not be freed.
+ *
+ * Return value: (transfer none): the system visual
+ *
+ * Since: 2.2
+ **/
+GdkVisual *
+gdk_screen_get_system_visual (GdkScreen * screen)
+{
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+
+ return ((GdkVisual *) GDK_SCREEN_X11 (screen)->system_visual);
+}
+
+/**
+ * gdk_visual_get_best:
+ *
+ * Get the visual with the most available colors for the default
+ * GDK screen. The return value should not be freed.
+ *
+ * Return value: (transfer none): best visual
+ **/
+GdkVisual*
+gdk_visual_get_best (void)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (gdk_screen_get_default());
+
+ return (GdkVisual *)screen_x11->visuals[0];
+}
+
+/**
+ * gdk_visual_get_best_with_depth:
+ * @depth: a bit depth
+ *
+ * Get the best visual with depth @depth for the default GDK screen.
+ * Color visuals and visuals with mutable colormaps are preferred
+ * over grayscale or fixed-colormap visuals. The return value should not
+ * be freed. %NULL may be returned if no visual supports @depth.
+ *
+ * Return value: (transfer none): best visual for the given depth
+ **/
+GdkVisual*
+gdk_visual_get_best_with_depth (gint depth)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (gdk_screen_get_default ());
+ GdkVisual *return_val;
+ int i;
+
+ return_val = NULL;
+ for (i = 0; i < screen_x11->nvisuals; i++)
+ if (depth == screen_x11->visuals[i]->depth)
+ {
+ return_val = (GdkVisual *) screen_x11->visuals[i];
+ break;
+ }
+
+ return return_val;
+}
+
+/**
+ * gdk_visual_get_best_with_type:
+ * @visual_type: a visual type
+ *
+ * Get the best visual of the given @visual_type for the default GDK screen.
+ * Visuals with higher color depths are considered better. The return value
+ * should not be freed. %NULL may be returned if no visual has type
+ * @visual_type.
+ *
+ * Return value: (transfer none): best visual of the given type
+ **/
+GdkVisual*
+gdk_visual_get_best_with_type (GdkVisualType visual_type)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (gdk_screen_get_default ());
+ GdkVisual *return_val;
+ int i;
+
+ return_val = NULL;
+ for (i = 0; i < screen_x11->nvisuals; i++)
+ if (visual_type == screen_x11->visuals[i]->type)
+ {
+ return_val = (GdkVisual *) screen_x11->visuals[i];
+ break;
+ }
+
+ return return_val;
+}
+
+/**
+ * gdk_visual_get_best_with_both:
+ * @depth: a bit depth
+ * @visual_type: a visual type
+ *
+ * Combines gdk_visual_get_best_with_depth() and gdk_visual_get_best_with_type().
+ *
+ * Return value: (transfer none): best visual with both @depth and
+ * @visual_type, or %NULL if none
+ **/
+GdkVisual*
+gdk_visual_get_best_with_both (gint depth,
+ GdkVisualType visual_type)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (gdk_screen_get_default ());
+ GdkVisual *return_val;
+ int i;
+
+ return_val = NULL;
+ for (i = 0; i < screen_x11->nvisuals; i++)
+ if ((depth == screen_x11->visuals[i]->depth) &&
+ (visual_type == screen_x11->visuals[i]->type))
+ {
+ return_val = (GdkVisual *) screen_x11->visuals[i];
+ break;
+ }
+
+ return return_val;
+}
+
+/**
+ * gdk_query_depths:
+ * @depths: (out) (array): return location for available depths
+ * @count: (out): return location for number of available depths
+ *
+ * This function returns the available bit depths for the default
+ * screen. It's equivalent to listing the visuals
+ * (gdk_list_visuals()) and then looking at the depth field in each
+ * visual, removing duplicates.
+ *
+ * The array returned by this function should not be freed.
+ *
+ **/
+void
+gdk_query_depths (gint **depths,
+ gint *count)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (gdk_screen_get_default ());
+
+ *count = screen_x11->navailable_depths;
+ *depths = screen_x11->available_depths;
+}
+
+/**
+ * gdk_query_visual_types:
+ * @visual_types: return location for the available visual types
+ * @count: return location for the number of available visual types
+ *
+ * This function returns the available visual types for the default
+ * screen. It's equivalent to listing the visuals
+ * (gdk_list_visuals()) and then looking at the type field in each
+ * visual, removing duplicates.
+ *
+ * The array returned by this function should not be freed.
+ **/
+void
+gdk_query_visual_types (GdkVisualType **visual_types,
+ gint *count)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (gdk_screen_get_default ());
+
+ *count = screen_x11->navailable_types;
+ *visual_types = screen_x11->available_types;
+}
+
+/**
+ * gdk_screen_list_visuals:
+ * @screen: the relevant #GdkScreen.
+ *
+ * Lists the available visuals for the specified @screen.
+ * A visual describes a hardware image data format.
+ * For example, a visual might support 24-bit color, or 8-bit color,
+ * and might expect pixels to be in a certain format.
+ *
+ * Call g_list_free() on the return value when you're finished with it.
+ *
+ * Return value: (transfer container) (element-type GdkVisual):
+ * a list of visuals; the list must be freed, but not its contents
+ *
+ * Since: 2.2
+ **/
+GList *
+gdk_screen_list_visuals (GdkScreen *screen)
+{
+ GList *list;
+ GdkScreenX11 *screen_x11;
+ guint i;
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+ screen_x11 = GDK_SCREEN_X11 (screen);
+
+ list = NULL;
+
+ for (i = 0; i < screen_x11->nvisuals; ++i)
+ list = g_list_append (list, screen_x11->visuals[i]);
+
+ return list;
+}
+
+/**
+ * gdk_x11_screen_lookup_visual:
+ * @screen: a #GdkScreen.
+ * @xvisualid: an X Visual ID.
+ *
+ * Looks up the #GdkVisual for a particular screen and X Visual ID.
+ *
+ * Returns: the #GdkVisual (owned by the screen object), or %NULL
+ * if the visual ID wasn't found.
+ *
+ * Since: 2.2
+ */
+GdkVisual *
+gdk_x11_screen_lookup_visual (GdkScreen *screen,
+ VisualID xvisualid)
+{
+ int i;
+ GdkScreenX11 *screen_x11;
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+ screen_x11 = GDK_SCREEN_X11 (screen);
+
+ for (i = 0; i < screen_x11->nvisuals; i++)
+ if (xvisualid == screen_x11->visuals[i]->priv->xvisual->visualid)
+ return (GdkVisual *) screen_x11->visuals[i];
+
+ return NULL;
+}
+
+/**
+ * gdkx_visual_get:
+ * @xvisualid: a X visual id.
+ *
+ * Returns a #GdkVisual corresponding to a X visual.
+ *
+ * Return value: the #GdkVisual.
+ **/
+GdkVisual*
+gdkx_visual_get (VisualID xvisualid)
+{
+ return gdk_x11_screen_lookup_visual (gdk_screen_get_default (), xvisualid);
+}
+
+static void
+gdk_visual_add (GdkVisual *visual)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (visual->priv->screen);
+
+ if (!screen_x11->visual_hash)
+ screen_x11->visual_hash = g_hash_table_new ((GHashFunc) gdk_visual_hash,
+ (GEqualFunc) gdk_visual_equal);
+
+ g_hash_table_insert (screen_x11->visual_hash, visual->priv->xvisual, visual);
+}
+
+static void
+gdk_visual_decompose_mask (gulong mask,
+ gint *shift,
+ gint *prec)
+{
+ *shift = 0;
+ *prec = 0;
+
+ if (mask == 0)
+ {
+ g_warning ("Mask is 0 in visual. Server bug ?");
+ return;
+ }
+
+ while (!(mask & 0x1))
+ {
+ (*shift)++;
+ mask >>= 1;
+ }
+
+ while (mask & 0x1)
+ {
+ (*prec)++;
+ mask >>= 1;
+ }
+}
+
+static guint
+gdk_visual_hash (Visual *key)
+{
+ return key->visualid;
+}
+
+static gboolean
+gdk_visual_equal (Visual *a,
+ Visual *b)
+{
+ return (a->visualid == b->visualid);
+}
+
+/**
+ * _gdk_visual_get_x11_colormap:
+ * @visual: the visual to get the colormap from
+ *
+ * Gets the colormap to use
+ *
+ * Returns: the X Colormap to use for new windows using @visual
+ **/
+Colormap
+_gdk_visual_get_x11_colormap (GdkVisual *visual)
+{
+ GdkVisualPrivate *priv;
+
+ g_return_val_if_fail (GDK_IS_VISUAL (visual), None);
+
+ priv = visual->priv;
+
+ if (priv->colormap == None)
+ {
+ priv->colormap = XCreateColormap (GDK_SCREEN_XDISPLAY (priv->screen),
+ GDK_SCREEN_XROOTWIN (priv->screen),
+ GDK_VISUAL_XVISUAL (visual),
+ AllocNone);
+ }
+
+ return priv->colormap;
+}
+
+/**
+ * gdk_x11_visual_get_xvisual:
+ * @visual: a #GdkVisual.
+ *
+ * Returns the X visual belonging to a #GdkVisual.
+ *
+ * Return value: an Xlib <type>Visual*</type>.
+ **/
+Visual *
+gdk_x11_visual_get_xvisual (GdkVisual *visual)
+{
+ g_return_val_if_fail (visual != NULL, NULL);
+
+ return visual->priv->xvisual;
+}
+
+/**
+ * gdk_visual_get_screen:
+ * @visual: a #GdkVisual
+ *
+ * Gets the screen to which this visual belongs
+ *
+ * Return value: (transfer none): the screen to which this visual belongs.
+ *
+ * Since: 2.2
+ **/
+GdkScreen *
+gdk_visual_get_screen (GdkVisual *visual)
+{
+ g_return_val_if_fail (GDK_IS_VISUAL (visual), NULL);
+
+ return visual->priv->screen;
+}
diff --git a/gdk/broadway/gdkwindow-broadway.c b/gdk/broadway/gdkwindow-broadway.c
new file mode 100644
index 0000000000..cb87793ea0
--- /dev/null
+++ b/gdk/broadway/gdkwindow-broadway.c
@@ -0,0 +1,5583 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-2007 Peter Mattis, Spencer Kimball,
+ * Josh MacDonald, Ryan Lortie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gdkwindow-broadway.h"
+
+#include "gdkx.h"
+#include "gdkwindow.h"
+#include "gdkwindowimpl.h"
+#include "gdkasync.h"
+#include "gdkdisplay-broadway.h"
+#include "gdkprivate-broadway.h"
+#include "gdkinternals.h"
+#include "gdkdeviceprivate.h"
+#include "gdkeventsource.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <unistd.h>
+
+#include <cairo-xlib.h>
+
+#include "MwmUtil.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#include <X11/extensions/shape.h>
+
+#ifdef HAVE_XKB
+#include <X11/XKBlib.h>
+#endif
+
+#ifdef HAVE_XCOMPOSITE
+#include <X11/extensions/Xcomposite.h>
+#endif
+
+#ifdef HAVE_XFIXES
+#include <X11/extensions/Xfixes.h>
+#endif
+
+#ifdef HAVE_XDAMAGE
+#include <X11/extensions/Xdamage.h>
+#endif
+
+const int _gdk_event_mask_table[21] =
+{
+ ExposureMask,
+ PointerMotionMask,
+ PointerMotionHintMask,
+ ButtonMotionMask,
+ Button1MotionMask,
+ Button2MotionMask,
+ Button3MotionMask,
+ ButtonPressMask,
+ ButtonReleaseMask,
+ KeyPressMask,
+ KeyReleaseMask,
+ EnterWindowMask,
+ LeaveWindowMask,
+ FocusChangeMask,
+ StructureNotifyMask,
+ PropertyChangeMask,
+ VisibilityChangeMask,
+ 0, /* PROXIMITY_IN */
+ 0, /* PROXIMTY_OUT */
+ SubstructureNotifyMask,
+ ButtonPressMask /* SCROLL; on X mouse wheel events is treated as mouse button 4/5 */
+};
+const int _gdk_nenvent_masks = sizeof (_gdk_event_mask_table) / sizeof (int);
+
+/* Forward declarations */
+static void gdk_window_set_static_win_gravity (GdkWindow *window,
+ gboolean on);
+static gboolean gdk_window_icon_name_set (GdkWindow *window);
+static void set_wm_name (GdkDisplay *display,
+ Window xwindow,
+ const gchar *name);
+static void move_to_current_desktop (GdkWindow *window);
+static void gdk_window_x11_set_background (GdkWindow *window,
+ cairo_pattern_t *pattern);
+
+static void gdk_window_impl_x11_finalize (GObject *object);
+static void gdk_window_impl_iface_init (GdkWindowImplIface *iface);
+
+#define WINDOW_IS_TOPLEVEL_OR_FOREIGN(window) \
+ (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD && \
+ GDK_WINDOW_TYPE (window) != GDK_WINDOW_OFFSCREEN)
+
+#define WINDOW_IS_TOPLEVEL(window) \
+ (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD && \
+ GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN && \
+ GDK_WINDOW_TYPE (window) != GDK_WINDOW_OFFSCREEN)
+
+/* Return whether time1 is considered later than time2 as far as xserver
+ * time is concerned. Accounts for wraparound.
+ */
+#define XSERVER_TIME_IS_LATER(time1, time2) \
+ ( (( time1 > time2 ) && ( time1 - time2 < ((guint32)-1)/2 )) || \
+ (( time1 < time2 ) && ( time2 - time1 > ((guint32)-1)/2 )) \
+ )
+
+G_DEFINE_TYPE_WITH_CODE (GdkWindowImplX11,
+ gdk_window_impl_x11,
+ GDK_TYPE_DRAWABLE_IMPL_X11,
+ G_IMPLEMENT_INTERFACE (GDK_TYPE_WINDOW_IMPL,
+ gdk_window_impl_iface_init));
+
+GType
+_gdk_window_impl_get_type (void)
+{
+ return gdk_window_impl_x11_get_type ();
+}
+
+static void
+gdk_window_impl_x11_init (GdkWindowImplX11 *impl)
+{
+ impl->toplevel_window_type = -1;
+ impl->device_cursor = g_hash_table_new_full (NULL, NULL, NULL,
+ (GDestroyNotify) gdk_cursor_unref);
+}
+
+GdkToplevelX11 *
+_gdk_x11_window_get_toplevel (GdkWindow *window)
+{
+ GdkWindowObject *private;
+ GdkWindowImplX11 *impl;
+
+ g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
+
+ if (!WINDOW_IS_TOPLEVEL (window))
+ return NULL;
+
+ private = (GdkWindowObject *)window;
+ impl = GDK_WINDOW_IMPL_X11 (private->impl);
+
+ if (!impl->toplevel)
+ impl->toplevel = g_new0 (GdkToplevelX11, 1);
+
+ return impl->toplevel;
+}
+
+static void
+gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gdk_window_impl_x11_finalize;
+}
+
+static void
+gdk_window_impl_x11_finalize (GObject *object)
+{
+ GdkWindowObject *wrapper;
+ GdkDrawableImplX11 *draw_impl;
+ GdkWindowImplX11 *window_impl;
+
+ g_return_if_fail (GDK_IS_WINDOW_IMPL_X11 (object));
+
+ draw_impl = GDK_DRAWABLE_IMPL_X11 (object);
+ window_impl = GDK_WINDOW_IMPL_X11 (object);
+
+ wrapper = (GdkWindowObject*) draw_impl->wrapper;
+
+ _gdk_xgrab_check_destroy (GDK_WINDOW (wrapper));
+
+ if (!GDK_WINDOW_DESTROYED (wrapper))
+ {
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (wrapper);
+
+ _gdk_xid_table_remove (display, draw_impl->xid);
+ if (window_impl->toplevel && window_impl->toplevel->focus_window)
+ _gdk_xid_table_remove (display, window_impl->toplevel->focus_window);
+ }
+
+ g_free (window_impl->toplevel);
+
+ if (window_impl->cursor)
+ gdk_cursor_unref (window_impl->cursor);
+
+ g_hash_table_destroy (window_impl->device_cursor);
+
+ G_OBJECT_CLASS (gdk_window_impl_x11_parent_class)->finalize (object);
+}
+
+typedef struct {
+ GdkDisplay *display;
+ Pixmap pixmap;
+} FreePixmapData;
+
+static void
+free_pixmap (gpointer datap)
+{
+ FreePixmapData *data = datap;
+
+ if (!gdk_display_is_closed (data->display))
+ {
+ XFreePixmap (GDK_DISPLAY_XDISPLAY (data->display),
+ data->pixmap);
+ }
+
+ g_object_unref (data->display);
+ g_slice_free (FreePixmapData, data);
+}
+
+static void
+attach_free_pixmap_handler (cairo_surface_t *surface,
+ GdkDisplay *display,
+ Pixmap pixmap)
+{
+ static const cairo_user_data_key_t key;
+ FreePixmapData *data;
+
+ data = g_slice_new (FreePixmapData);
+ data->display = g_object_ref (display);
+ data->pixmap = pixmap;
+
+ cairo_surface_set_user_data (surface, &key, data, free_pixmap);
+}
+
+/* Cairo does not guarantee we get an xlib surface if we call
+ * cairo_surface_create_similar(). In some cases however, we must use a
+ * pixmap or bitmap in the X11 API.
+ * These functions ensure an Xlib surface.
+ */
+cairo_surface_t *
+_gdk_x11_window_create_bitmap_surface (GdkWindow *window,
+ int width,
+ int height)
+{
+ cairo_surface_t *surface;
+ Pixmap pixmap;
+
+ pixmap = XCreatePixmap (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ width, height, 1);
+ surface = cairo_xlib_surface_create_for_bitmap (GDK_WINDOW_XDISPLAY (window),
+ pixmap,
+ GDK_SCREEN_XSCREEN (GDK_WINDOW_SCREEN (window)),
+ width, height);
+ attach_free_pixmap_handler (surface, GDK_WINDOW_DISPLAY (window), pixmap);
+
+ return surface;
+}
+
+static cairo_surface_t *
+gdk_x11_window_create_pixmap_surface (GdkWindow *window,
+ int width,
+ int height)
+{
+ cairo_surface_t *surface;
+ Pixmap pixmap;
+
+ pixmap = XCreatePixmap (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ width, height,
+ DefaultDepthOfScreen (GDK_SCREEN_XSCREEN (GDK_WINDOW_SCREEN (window))));
+ surface = cairo_xlib_surface_create (GDK_WINDOW_XDISPLAY (window),
+ pixmap,
+ GDK_VISUAL_XVISUAL (gdk_window_get_visual (window)),
+ width, height);
+ attach_free_pixmap_handler (surface, GDK_WINDOW_DISPLAY (window), pixmap);
+
+ return surface;
+}
+
+static void
+tmp_unset_bg (GdkWindow *window)
+{
+ GdkWindowImplX11 *impl;
+ GdkWindowObject *obj;
+
+ obj = (GdkWindowObject *) window;
+ impl = GDK_WINDOW_IMPL_X11 (obj->impl);
+
+ impl->no_bg = TRUE;
+
+ XSetWindowBackgroundPixmap (GDK_DRAWABLE_XDISPLAY (window),
+ GDK_DRAWABLE_XID (window), None);
+}
+
+static void
+tmp_reset_bg (GdkWindow *window)
+{
+ GdkWindowImplX11 *impl;
+ GdkWindowObject *obj;
+
+ obj = (GdkWindowObject *) window;
+ impl = GDK_WINDOW_IMPL_X11 (obj->impl);
+
+ impl->no_bg = FALSE;
+
+ gdk_window_x11_set_background (window, obj->background);
+}
+
+/* Unsetting and resetting window backgrounds.
+ *
+ * In many cases it is possible to avoid flicker by unsetting the
+ * background of windows. For example if the background of the
+ * parent window is unset when a window is unmapped, a brief flicker
+ * of background painting is avoided.
+ */
+void
+_gdk_x11_window_tmp_unset_bg (GdkWindow *window,
+ gboolean recurse)
+{
+ GdkWindowObject *private;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ private = (GdkWindowObject *)window;
+
+ if (private->input_only || private->destroyed ||
+ (private->window_type != GDK_WINDOW_ROOT &&
+ !GDK_WINDOW_IS_MAPPED (window)))
+ return;
+
+ if (_gdk_window_has_impl (window) &&
+ GDK_WINDOW_IS_X11 (window) &&
+ private->window_type != GDK_WINDOW_ROOT &&
+ private->window_type != GDK_WINDOW_FOREIGN)
+ tmp_unset_bg (window);
+
+ if (recurse)
+ {
+ GList *l;
+
+ for (l = private->children; l != NULL; l = l->next)
+ _gdk_x11_window_tmp_unset_bg (l->data, TRUE);
+ }
+}
+
+void
+_gdk_x11_window_tmp_unset_parent_bg (GdkWindow *window)
+{
+ GdkWindowObject *private;
+ private = (GdkWindowObject*) window;
+
+ if (GDK_WINDOW_TYPE (private->parent) == GDK_WINDOW_ROOT)
+ return;
+
+ window = _gdk_window_get_impl_window ((GdkWindow *)private->parent);
+ _gdk_x11_window_tmp_unset_bg (window, FALSE);
+}
+
+void
+_gdk_x11_window_tmp_reset_bg (GdkWindow *window,
+ gboolean recurse)
+{
+ GdkWindowObject *private;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ private = (GdkWindowObject *)window;
+
+ if (private->input_only || private->destroyed ||
+ (private->window_type != GDK_WINDOW_ROOT &&
+ !GDK_WINDOW_IS_MAPPED (window)))
+ return;
+
+
+ if (_gdk_window_has_impl (window) &&
+ GDK_WINDOW_IS_X11 (window) &&
+ private->window_type != GDK_WINDOW_ROOT &&
+ private->window_type != GDK_WINDOW_FOREIGN)
+ tmp_reset_bg (window);
+
+ if (recurse)
+ {
+ GList *l;
+
+ for (l = private->children; l != NULL; l = l->next)
+ _gdk_x11_window_tmp_reset_bg (l->data, TRUE);
+ }
+}
+
+void
+_gdk_x11_window_tmp_reset_parent_bg (GdkWindow *window)
+{
+ GdkWindowObject *private;
+ private = (GdkWindowObject*) window;
+
+ if (GDK_WINDOW_TYPE (private->parent) == GDK_WINDOW_ROOT)
+ return;
+
+ window = _gdk_window_get_impl_window ((GdkWindow *)private->parent);
+
+ _gdk_x11_window_tmp_reset_bg (window, FALSE);
+}
+
+void
+_gdk_windowing_window_init (GdkScreen * screen)
+{
+ GdkWindowObject *private;
+ GdkDrawableImplX11 *draw_impl;
+ GdkScreenX11 *screen_x11;
+
+ screen_x11 = GDK_SCREEN_X11 (screen);
+
+ g_assert (screen_x11->root_window == NULL);
+
+ screen_x11->root_window = g_object_new (GDK_TYPE_WINDOW, NULL);
+
+ private = (GdkWindowObject *) screen_x11->root_window;
+ private->impl = g_object_new (_gdk_window_impl_get_type (), NULL);
+ private->impl_window = private;
+ private->visual = gdk_screen_get_system_visual (screen);
+
+ draw_impl = GDK_DRAWABLE_IMPL_X11 (private->impl);
+
+ draw_impl->screen = screen;
+ draw_impl->xid = screen_x11->xroot_window;
+ draw_impl->wrapper = GDK_DRAWABLE (private);
+
+ private->window_type = GDK_WINDOW_ROOT;
+ private->depth = DefaultDepthOfScreen (screen_x11->xscreen);
+
+ private->x = 0;
+ private->y = 0;
+ private->abs_x = 0;
+ private->abs_y = 0;
+ private->width = WidthOfScreen (screen_x11->xscreen);
+ private->height = HeightOfScreen (screen_x11->xscreen);
+ private->viewable = TRUE;
+
+ /* see init_randr_support() in gdkscreen-broadway.c */
+ private->event_mask = GDK_STRUCTURE_MASK;
+
+ _gdk_window_update_size (screen_x11->root_window);
+
+ _gdk_xid_table_insert (screen_x11->display,
+ &screen_x11->xroot_window,
+ screen_x11->root_window);
+}
+
+static void
+set_wm_protocols (GdkWindow *window)
+{
+ GdkDisplay *display = gdk_window_get_display (window);
+ Atom protocols[4];
+ int n = 0;
+
+ protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW");
+ protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS");
+ protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_PING");
+
+#ifdef HAVE_XSYNC
+ if (GDK_DISPLAY_X11 (display)->use_sync)
+ protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_SYNC_REQUEST");
+#endif
+
+ XSetWMProtocols (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window), protocols, n);
+}
+
+static const gchar *
+get_default_title (void)
+{
+ const char *title;
+
+ title = g_get_application_name ();
+ if (!title)
+ title = g_get_prgname ();
+ if (!title)
+ title = "";
+
+ return title;
+}
+
+static void
+check_leader_window_title (GdkDisplay *display)
+{
+ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+ if (display_x11->leader_window && !display_x11->leader_window_title_set)
+ {
+ set_wm_name (display,
+ display_x11->leader_window,
+ get_default_title ());
+
+ display_x11->leader_window_title_set = TRUE;
+ }
+}
+
+static Window
+create_focus_window (GdkDisplay *display,
+ XID parent)
+{
+ GdkDisplayX11 *display_x11;
+ GdkEventMask event_mask;
+ Display *xdisplay;
+ Window focus_window;
+
+ xdisplay = GDK_DISPLAY_XDISPLAY (display);
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ focus_window = XCreateSimpleWindow (xdisplay, parent,
+ -1, -1, 1, 1, 0,
+ 0, 0);
+
+ /* FIXME: probably better to actually track the requested event mask for the toplevel
+ */
+ event_mask = (GDK_KEY_PRESS_MASK |
+ GDK_KEY_RELEASE_MASK |
+ GDK_FOCUS_CHANGE_MASK);
+
+ gdk_event_source_select_events ((GdkEventSource *) display_x11->event_source,
+ focus_window,
+ event_mask, 0);
+
+ XMapWindow (xdisplay, focus_window);
+
+ return focus_window;
+}
+
+static void
+ensure_sync_counter (GdkWindow *window)
+{
+#ifdef HAVE_XSYNC
+ if (!GDK_WINDOW_DESTROYED (window))
+ {
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
+ GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
+ GdkWindowObject *private = (GdkWindowObject *)window;
+ GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (private->impl);
+
+ if (toplevel && impl->use_synchronized_configure &&
+ toplevel->update_counter == None &&
+ GDK_DISPLAY_X11 (display)->use_sync)
+ {
+ Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
+ XSyncValue value;
+ Atom atom;
+
+ XSyncIntToValue (&value, 0);
+
+ toplevel->update_counter = XSyncCreateCounter (xdisplay, value);
+
+ atom = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_WM_SYNC_REQUEST_COUNTER");
+
+ XChangeProperty (xdisplay, GDK_WINDOW_XID (window),
+ atom, XA_CARDINAL,
+ 32, PropModeReplace,
+ (guchar *)&toplevel->update_counter, 1);
+
+ XSyncIntToValue (&toplevel->current_counter_value, 0);
+ }
+ }
+#endif
+}
+
+static void
+setup_toplevel_window (GdkWindow *window,
+ GdkWindow *parent)
+{
+ GdkWindowObject *obj = (GdkWindowObject *)window;
+ GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
+ GdkDisplay *display = gdk_window_get_display (window);
+ Display *xdisplay = GDK_WINDOW_XDISPLAY (window);
+ XID xid = GDK_WINDOW_XID (window);
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (GDK_WINDOW_SCREEN (parent));
+ XSizeHints size_hints;
+ long pid;
+ Window leader_window;
+
+ set_wm_protocols (window);
+
+ if (!obj->input_only)
+ {
+ /* The focus window is off the visible area, and serves to receive key
+ * press events so they don't get sent to child windows.
+ */
+ toplevel->focus_window = create_focus_window (display, xid);
+ _gdk_xid_table_insert (screen_x11->display, &toplevel->focus_window, window);
+ }
+
+ check_leader_window_title (screen_x11->display);
+
+ /* FIXME: Is there any point in doing this? Do any WM's pay
+ * attention to PSize, and even if they do, is this the
+ * correct value???
+ */
+ size_hints.flags = PSize;
+ size_hints.width = obj->width;
+ size_hints.height = obj->height;
+
+ XSetWMNormalHints (xdisplay, xid, &size_hints);
+
+ /* This will set WM_CLIENT_MACHINE and WM_LOCALE_NAME */
+ XSetWMProperties (xdisplay, xid, NULL, NULL, NULL, 0, NULL, NULL, NULL);
+
+ pid = getpid ();
+ XChangeProperty (xdisplay, xid,
+ gdk_x11_get_xatom_by_name_for_display (screen_x11->display, "_NET_WM_PID"),
+ XA_CARDINAL, 32,
+ PropModeReplace,
+ (guchar *)&pid, 1);
+
+ leader_window = GDK_DISPLAY_X11 (screen_x11->display)->leader_window;
+ if (!leader_window)
+ leader_window = xid;
+ XChangeProperty (xdisplay, xid,
+ gdk_x11_get_xatom_by_name_for_display (screen_x11->display, "WM_CLIENT_LEADER"),
+ XA_WINDOW, 32, PropModeReplace,
+ (guchar *) &leader_window, 1);
+
+ if (toplevel->focus_window != None)
+ XChangeProperty (xdisplay, xid,
+ gdk_x11_get_xatom_by_name_for_display (screen_x11->display, "_NET_WM_USER_TIME_WINDOW"),
+ XA_WINDOW, 32, PropModeReplace,
+ (guchar *) &toplevel->focus_window, 1);
+
+ if (!obj->focus_on_map)
+ gdk_x11_window_set_user_time (window, 0);
+ else if (GDK_DISPLAY_X11 (screen_x11->display)->user_time != 0)
+ gdk_x11_window_set_user_time (window, GDK_DISPLAY_X11 (screen_x11->display)->user_time);
+
+ ensure_sync_counter (window);
+}
+
+void
+_gdk_window_impl_new (GdkWindow *window,
+ GdkWindow *real_parent,
+ GdkScreen *screen,
+ GdkEventMask event_mask,
+ GdkWindowAttr *attributes,
+ gint attributes_mask)
+{
+ GdkWindowObject *private;
+ GdkWindowImplX11 *impl;
+ GdkDrawableImplX11 *draw_impl;
+ GdkScreenX11 *screen_x11;
+ GdkDisplayX11 *display_x11;
+
+ Window xparent;
+ Visual *xvisual;
+ Display *xdisplay;
+ Window xid;
+
+ XSetWindowAttributes xattributes;
+ long xattributes_mask;
+ XClassHint *class_hint;
+
+ unsigned int class;
+ const char *title;
+
+ private = (GdkWindowObject *) window;
+
+ screen_x11 = GDK_SCREEN_X11 (screen);
+ xparent = GDK_WINDOW_XID (real_parent);
+ display_x11 = GDK_DISPLAY_X11 (GDK_SCREEN_DISPLAY (screen));
+
+ impl = g_object_new (_gdk_window_impl_get_type (), NULL);
+ private->impl = (GdkDrawable *)impl;
+ draw_impl = GDK_DRAWABLE_IMPL_X11 (impl);
+ draw_impl->wrapper = GDK_DRAWABLE (window);
+
+ draw_impl->screen = screen;
+ xdisplay = screen_x11->xdisplay;
+
+ xattributes_mask = 0;
+
+ xvisual = gdk_x11_visual_get_xvisual (private->visual);
+
+ if (attributes_mask & GDK_WA_NOREDIR)
+ {
+ xattributes.override_redirect =
+ (attributes->override_redirect == FALSE)?False:True;
+ xattributes_mask |= CWOverrideRedirect;
+ }
+ else
+ xattributes.override_redirect = False;
+
+ impl->override_redirect = xattributes.override_redirect;
+
+ if (private->parent && private->parent->guffaw_gravity)
+ {
+ xattributes.win_gravity = StaticGravity;
+ xattributes_mask |= CWWinGravity;
+ }
+
+ /* Sanity checks */
+ switch (private->window_type)
+ {
+ case GDK_WINDOW_TOPLEVEL:
+ case GDK_WINDOW_TEMP:
+ if (GDK_WINDOW_TYPE (private->parent) != GDK_WINDOW_ROOT)
+ {
+ /* The common code warns for this case */
+ xparent = GDK_SCREEN_XROOTWIN (screen);
+ }
+ }
+
+ if (!private->input_only)
+ {
+ class = InputOutput;
+
+ xattributes.background_pixel = BlackPixel (xdisplay, screen_x11->screen_num);
+
+ xattributes.border_pixel = BlackPixel (xdisplay, screen_x11->screen_num);
+ xattributes_mask |= CWBorderPixel | CWBackPixel;
+
+ if (private->guffaw_gravity)
+ xattributes.bit_gravity = StaticGravity;
+ else
+ xattributes.bit_gravity = NorthWestGravity;
+
+ xattributes_mask |= CWBitGravity;
+
+ xattributes.colormap = _gdk_visual_get_x11_colormap (private->visual);
+ xattributes_mask |= CWColormap;
+
+ if (private->window_type == GDK_WINDOW_TEMP)
+ {
+ xattributes.save_under = True;
+ xattributes.override_redirect = True;
+ xattributes.cursor = None;
+ xattributes_mask |= CWSaveUnder | CWOverrideRedirect;
+
+ impl->override_redirect = TRUE;
+ }
+ }
+ else
+ {
+ class = InputOnly;
+ }
+
+ if (private->width > 65535 ||
+ private->height > 65535)
+ {
+ g_warning ("Native Windows wider or taller than 65535 pixels are not supported");
+
+ if (private->width > 65535)
+ private->width = 65535;
+ if (private->height > 65535)
+ private->height = 65535;
+ }
+
+ xid = draw_impl->xid = XCreateWindow (xdisplay, xparent,
+ private->x + private->parent->abs_x,
+ private->y + private->parent->abs_y,
+ private->width, private->height,
+ 0, private->depth, class, xvisual,
+ xattributes_mask, &xattributes);
+
+ g_object_ref (window);
+ _gdk_xid_table_insert (screen_x11->display, &draw_impl->xid, window);
+
+ switch (GDK_WINDOW_TYPE (private))
+ {
+ case GDK_WINDOW_TOPLEVEL:
+ case GDK_WINDOW_TEMP:
+ if (attributes_mask & GDK_WA_TITLE)
+ title = attributes->title;
+ else
+ title = get_default_title ();
+
+ gdk_window_set_title (window, title);
+
+ if (attributes_mask & GDK_WA_WMCLASS)
+ {
+ class_hint = XAllocClassHint ();
+ class_hint->res_name = attributes->wmclass_name;
+ class_hint->res_class = attributes->wmclass_class;
+ XSetClassHint (xdisplay, xid, class_hint);
+ XFree (class_hint);
+ }
+
+ setup_toplevel_window (window, (GdkWindow *)private->parent);
+ break;
+
+ case GDK_WINDOW_CHILD:
+ default:
+ break;
+ }
+
+ if (attributes_mask & GDK_WA_TYPE_HINT)
+ gdk_window_set_type_hint (window, attributes->type_hint);
+
+ gdk_event_source_select_events ((GdkEventSource *) display_x11->event_source,
+ GDK_WINDOW_XWINDOW (window), event_mask,
+ StructureNotifyMask | PropertyChangeMask);
+}
+
+static GdkEventMask
+x_event_mask_to_gdk_event_mask (long mask)
+{
+ GdkEventMask event_mask = 0;
+ int i;
+
+ for (i = 0; i < _gdk_nenvent_masks; i++)
+ {
+ if (mask & _gdk_event_mask_table[i])
+ event_mask |= 1 << (i + 1);
+ }
+
+ return event_mask;
+}
+
+/**
+ * gdk_window_foreign_new_for_display:
+ * @display: the #GdkDisplay where the window handle comes from.
+ * @anid: a native window handle.
+ *
+ * Wraps a native window in a #GdkWindow.
+ * This may fail if the window has been destroyed. If the window
+ * was already known to GDK, a new reference to the existing
+ * #GdkWindow is returned.
+ *
+ * For example in the X backend, a native window handle is an Xlib
+ * <type>XID</type>.
+ *
+ * Return value: a #GdkWindow wrapper for the native window or
+ * %NULL if the window has been destroyed. The wrapper will be
+ * newly created, if one doesn't exist already.
+ *
+ * Since: 2.2
+ **/
+GdkWindow *
+gdk_window_foreign_new_for_display (GdkDisplay *display,
+ GdkNativeWindow anid)
+{
+ GdkScreen *screen;
+ GdkWindow *window;
+ GdkWindowObject *private;
+ GdkWindowImplX11 *impl;
+ GdkDrawableImplX11 *draw_impl;
+ GdkDisplayX11 *display_x11;
+ XWindowAttributes attrs;
+ Window root, parent;
+ Window *children = NULL;
+ guint nchildren;
+ gboolean result;
+
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
+
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ if ((window = gdk_xid_table_lookup_for_display (display, anid)) != NULL)
+ return g_object_ref (window);
+
+ gdk_error_trap_push ();
+ result = XGetWindowAttributes (display_x11->xdisplay, anid, &attrs);
+ if (gdk_error_trap_pop () || !result)
+ return NULL;
+
+ /* FIXME: This is pretty expensive. Maybe the caller should supply
+ * the parent */
+ gdk_error_trap_push ();
+ result = XQueryTree (display_x11->xdisplay, anid, &root, &parent, &children, &nchildren);
+ if (gdk_error_trap_pop () || !result)
+ return NULL;
+
+ if (children)
+ XFree (children);
+
+ screen = _gdk_x11_display_screen_for_xrootwin (display, root);
+
+ window = g_object_new (GDK_TYPE_WINDOW, NULL);
+
+ private = (GdkWindowObject *) window;
+ private->impl = g_object_new (_gdk_window_impl_get_type (), NULL);
+ private->impl_window = private;
+ private->visual = gdk_x11_screen_lookup_visual (screen,
+ XVisualIDFromVisual (attrs.visual));
+
+ impl = GDK_WINDOW_IMPL_X11 (private->impl);
+ draw_impl = GDK_DRAWABLE_IMPL_X11 (private->impl);
+ draw_impl->wrapper = GDK_DRAWABLE (window);
+ draw_impl->screen = screen;
+
+ private->parent = gdk_xid_table_lookup_for_display (display, parent);
+
+ if (!private->parent || GDK_WINDOW_TYPE (private->parent) == GDK_WINDOW_FOREIGN)
+ private->parent = (GdkWindowObject *) gdk_screen_get_root_window (draw_impl->screen);
+
+ private->parent->children = g_list_prepend (private->parent->children, window);
+
+ draw_impl->xid = anid;
+
+ private->x = attrs.x;
+ private->y = attrs.y;
+ private->width = attrs.width;
+ private->height = attrs.height;
+ private->window_type = GDK_WINDOW_FOREIGN;
+ private->destroyed = FALSE;
+
+ private->event_mask = x_event_mask_to_gdk_event_mask (attrs.your_event_mask);
+
+ if (attrs.map_state == IsUnmapped)
+ private->state = GDK_WINDOW_STATE_WITHDRAWN;
+ else
+ private->state = 0;
+ private->viewable = TRUE;
+
+ private->depth = attrs.depth;
+
+ g_object_ref (window);
+ _gdk_xid_table_insert (display, &GDK_WINDOW_XID (window), window);
+
+ /* Update the clip region, etc */
+ _gdk_window_update_size (window);
+
+ return window;
+}
+
+/**
+ * gdk_window_lookup_for_display:
+ * @display: the #GdkDisplay corresponding to the window handle
+ * @anid: a native window handle.
+ *
+ * Looks up the #GdkWindow that wraps the given native window handle.
+ *
+ * For example in the X backend, a native window handle is an Xlib
+ * <type>XID</type>.
+ *
+ * Return value: the #GdkWindow wrapper for the native window,
+ * or %NULL if there is none.
+ *
+ * Since: 2.2
+ **/
+GdkWindow *
+gdk_window_lookup_for_display (GdkDisplay *display, GdkNativeWindow anid)
+{
+ return (GdkWindow*) gdk_xid_table_lookup_for_display (display, anid);
+}
+
+/**
+ * gdk_window_lookup:
+ * @anid: a native window handle.
+ *
+ * Looks up the #GdkWindow that wraps the given native window handle.
+ *
+ * For example in the X backend, a native window handle is an Xlib
+ * <type>XID</type>.
+ *
+ * Return value: the #GdkWindow wrapper for the native window,
+ * or %NULL if there is none.
+ **/
+GdkWindow *
+gdk_window_lookup (GdkNativeWindow anid)
+{
+ return (GdkWindow*) gdk_xid_table_lookup (anid);
+}
+
+static void
+gdk_toplevel_x11_free_contents (GdkDisplay *display,
+ GdkToplevelX11 *toplevel)
+{
+ if (toplevel->icon_pixmap)
+ {
+ cairo_surface_destroy (toplevel->icon_pixmap);
+ toplevel->icon_pixmap = NULL;
+ }
+ if (toplevel->icon_mask)
+ {
+ cairo_surface_destroy (toplevel->icon_mask);
+ toplevel->icon_mask = NULL;
+ }
+ if (toplevel->group_leader)
+ {
+ g_object_unref (toplevel->group_leader);
+ toplevel->group_leader = NULL;
+ }
+#ifdef HAVE_XSYNC
+ if (toplevel->update_counter != None)
+ {
+ XSyncDestroyCounter (GDK_DISPLAY_XDISPLAY (display),
+ toplevel->update_counter);
+ toplevel->update_counter = None;
+
+ XSyncIntToValue (&toplevel->current_counter_value, 0);
+ }
+#endif
+}
+
+static void
+_gdk_x11_window_destroy (GdkWindow *window,
+ gboolean recursing,
+ gboolean foreign_destroy)
+{
+ GdkWindowObject *private = (GdkWindowObject *)window;
+ GdkToplevelX11 *toplevel;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ _gdk_selection_window_destroyed (window);
+
+ toplevel = _gdk_x11_window_get_toplevel (window);
+ if (toplevel)
+ gdk_toplevel_x11_free_contents (GDK_WINDOW_DISPLAY (window), toplevel);
+
+ _gdk_x11_drawable_finish (private->impl);
+
+ if (!recursing && !foreign_destroy)
+ {
+ XDestroyWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window));
+ }
+}
+
+static cairo_surface_t *
+gdk_window_x11_resize_cairo_surface (GdkWindow *window,
+ cairo_surface_t *surface,
+ gint width,
+ gint height)
+{
+ cairo_xlib_surface_set_size (surface, width, height);
+
+ return surface;
+}
+
+void
+_gdk_windowing_window_destroy_foreign (GdkWindow *window)
+{
+ /* It's somebody else's window, but in our hierarchy,
+ * so reparent it to the root window, and then send
+ * it a delete event, as if we were a WM
+ */
+ XClientMessageEvent xclient;
+
+ gdk_error_trap_push ();
+ gdk_window_hide (window);
+ gdk_window_reparent (window, NULL, 0, 0);
+
+ memset (&xclient, 0, sizeof (xclient));
+ xclient.type = ClientMessage;
+ xclient.window = GDK_WINDOW_XID (window);
+ xclient.message_type = gdk_x11_get_xatom_by_name_for_display (GDK_WINDOW_DISPLAY (window),
+ "WM_PROTOCOLS");
+ xclient.format = 32;
+ xclient.data.l[0] = gdk_x11_get_xatom_by_name_for_display (GDK_WINDOW_DISPLAY (window),
+ "WM_DELETE_WINDOW");
+ xclient.data.l[1] = CurrentTime;
+ xclient.data.l[2] = 0;
+ xclient.data.l[3] = 0;
+ xclient.data.l[4] = 0;
+
+ XSendEvent (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ False, 0, (XEvent *)&xclient);
+ gdk_error_trap_pop_ignored ();
+}
+
+static GdkWindow *
+get_root (GdkWindow *window)
+{
+ GdkScreen *screen = gdk_window_get_screen (window);
+
+ return gdk_screen_get_root_window (screen);
+}
+
+/* This function is called when the XWindow is really gone.
+ */
+void
+gdk_window_destroy_notify (GdkWindow *window)
+{
+ GdkWindowImplX11 *window_impl;
+
+ window_impl = GDK_WINDOW_IMPL_X11 (((GdkWindowObject *)window)->impl);
+
+ if (!GDK_WINDOW_DESTROYED (window))
+ {
+ if (GDK_WINDOW_TYPE(window) != GDK_WINDOW_FOREIGN)
+ g_warning ("GdkWindow %#lx unexpectedly destroyed", GDK_WINDOW_XID (window));
+
+ _gdk_window_destroy (window, TRUE);
+ }
+
+ _gdk_xid_table_remove (GDK_WINDOW_DISPLAY (window), GDK_WINDOW_XID (window));
+ if (window_impl->toplevel && window_impl->toplevel->focus_window)
+ _gdk_xid_table_remove (GDK_WINDOW_DISPLAY (window), window_impl->toplevel->focus_window);
+
+ _gdk_xgrab_check_destroy (window);
+
+ g_object_unref (window);
+}
+
+static void
+update_wm_hints (GdkWindow *window,
+ gboolean force)
+{
+ GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
+ GdkWindowObject *private = (GdkWindowObject *)window;
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
+ XWMHints wm_hints;
+
+ if (!force &&
+ !toplevel->is_leader &&
+ private->state & GDK_WINDOW_STATE_WITHDRAWN)
+ return;
+
+ wm_hints.flags = StateHint | InputHint;
+ wm_hints.input = private->accept_focus ? True : False;
+ wm_hints.initial_state = NormalState;
+
+ if (private->state & GDK_WINDOW_STATE_ICONIFIED)
+ {
+ wm_hints.flags |= StateHint;
+ wm_hints.initial_state = IconicState;
+ }
+
+ if (toplevel->icon_pixmap)
+ {
+ wm_hints.flags |= IconPixmapHint;
+ wm_hints.icon_pixmap = cairo_xlib_surface_get_drawable (toplevel->icon_pixmap);
+ }
+
+ if (toplevel->icon_mask)
+ {
+ wm_hints.flags |= IconMaskHint;
+ wm_hints.icon_mask = cairo_xlib_surface_get_drawable (toplevel->icon_mask);
+ }
+
+ wm_hints.flags |= WindowGroupHint;
+ if (toplevel->group_leader && !GDK_WINDOW_DESTROYED (toplevel->group_leader))
+ {
+ wm_hints.flags |= WindowGroupHint;
+ wm_hints.window_group = GDK_WINDOW_XID (toplevel->group_leader);
+ }
+ else
+ wm_hints.window_group = GDK_DISPLAY_X11 (display)->leader_window;
+
+ if (toplevel->urgency_hint)
+ wm_hints.flags |= XUrgencyHint;
+
+ XSetWMHints (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ &wm_hints);
+}
+
+static void
+set_initial_hints (GdkWindow *window)
+{
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
+ Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
+ Window xwindow = GDK_WINDOW_XID (window);
+ GdkWindowObject *private;
+ GdkToplevelX11 *toplevel;
+ Atom atoms[9];
+ gint i;
+
+ private = (GdkWindowObject*) window;
+ toplevel = _gdk_x11_window_get_toplevel (window);
+
+ if (!toplevel)
+ return;
+
+ update_wm_hints (window, TRUE);
+
+ /* We set the spec hints regardless of whether the spec is supported,
+ * since it can't hurt and it's kind of expensive to check whether
+ * it's supported.
+ */
+
+ i = 0;
+
+ if (private->state & GDK_WINDOW_STATE_MAXIMIZED)
+ {
+ atoms[i] = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_WM_STATE_MAXIMIZED_VERT");
+ ++i;
+ atoms[i] = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_WM_STATE_MAXIMIZED_HORZ");
+ ++i;
+ toplevel->have_maxhorz = toplevel->have_maxvert = TRUE;
+ }
+
+ if (private->state & GDK_WINDOW_STATE_ABOVE)
+ {
+ atoms[i] = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_WM_STATE_ABOVE");
+ ++i;
+ }
+
+ if (private->state & GDK_WINDOW_STATE_BELOW)
+ {
+ atoms[i] = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_WM_STATE_BELOW");
+ ++i;
+ }
+
+ if (private->state & GDK_WINDOW_STATE_STICKY)
+ {
+ atoms[i] = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_WM_STATE_STICKY");
+ ++i;
+ toplevel->have_sticky = TRUE;
+ }
+
+ if (private->state & GDK_WINDOW_STATE_FULLSCREEN)
+ {
+ atoms[i] = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_WM_STATE_FULLSCREEN");
+ ++i;
+ toplevel->have_fullscreen = TRUE;
+ }
+
+ if (private->modal_hint)
+ {
+ atoms[i] = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_WM_STATE_MODAL");
+ ++i;
+ }
+
+ if (toplevel->skip_taskbar_hint)
+ {
+ atoms[i] = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_WM_STATE_SKIP_TASKBAR");
+ ++i;
+ }
+
+ if (toplevel->skip_pager_hint)
+ {
+ atoms[i] = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_WM_STATE_SKIP_PAGER");
+ ++i;
+ }
+
+ if (i > 0)
+ {
+ XChangeProperty (xdisplay,
+ xwindow,
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"),
+ XA_ATOM, 32, PropModeReplace,
+ (guchar*) atoms, i);
+ }
+ else
+ {
+ XDeleteProperty (xdisplay,
+ xwindow,
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"));
+ }
+
+ if (private->state & GDK_WINDOW_STATE_STICKY)
+ {
+ atoms[0] = 0xFFFFFFFF;
+ XChangeProperty (xdisplay,
+ xwindow,
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP"),
+ XA_CARDINAL, 32, PropModeReplace,
+ (guchar*) atoms, 1);
+ toplevel->on_all_desktops = TRUE;
+ }
+ else
+ {
+ XDeleteProperty (xdisplay,
+ xwindow,
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP"));
+ }
+
+ toplevel->map_serial = NextRequest (xdisplay);
+}
+
+static void
+gdk_window_x11_show (GdkWindow *window, gboolean already_mapped)
+{
+ GdkWindowObject *private = (GdkWindowObject*) window;
+ GdkDisplay *display;
+ GdkDisplayX11 *display_x11;
+ GdkToplevelX11 *toplevel;
+ GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (private->impl);
+ Display *xdisplay = GDK_WINDOW_XDISPLAY (window);
+ Window xwindow = GDK_WINDOW_XID (window);
+ gboolean unset_bg;
+
+ if (!already_mapped)
+ set_initial_hints (window);
+
+ if (WINDOW_IS_TOPLEVEL (window))
+ {
+ display = gdk_window_get_display (window);
+ display_x11 = GDK_DISPLAY_X11 (display);
+ toplevel = _gdk_x11_window_get_toplevel (window);
+
+ if (toplevel->user_time != 0 &&
+ display_x11->user_time != 0 &&
+ XSERVER_TIME_IS_LATER (display_x11->user_time, toplevel->user_time))
+ gdk_x11_window_set_user_time (window, display_x11->user_time);
+ }
+
+ unset_bg = !private->input_only &&
+ (private->window_type == GDK_WINDOW_CHILD ||
+ impl->override_redirect) &&
+ gdk_window_is_viewable (window);
+
+ if (unset_bg)
+ _gdk_x11_window_tmp_unset_bg (window, TRUE);
+
+ XMapWindow (xdisplay, xwindow);
+
+ if (unset_bg)
+ _gdk_x11_window_tmp_reset_bg (window, TRUE);
+}
+
+static void
+pre_unmap (GdkWindow *window)
+{
+ GdkWindow *start_window = NULL;
+ GdkWindowObject *private = (GdkWindowObject *)window;
+
+ if (private->input_only)
+ return;
+
+ if (private->window_type == GDK_WINDOW_CHILD)
+ start_window = _gdk_window_get_impl_window ((GdkWindow *)private->parent);
+ else if (private->window_type == GDK_WINDOW_TEMP)
+ start_window = get_root (window);
+
+ if (start_window)
+ _gdk_x11_window_tmp_unset_bg (start_window, TRUE);
+}
+
+static void
+post_unmap (GdkWindow *window)
+{
+ GdkWindow *start_window = NULL;
+ GdkWindowObject *private = (GdkWindowObject *)window;
+
+ if (private->input_only)
+ return;
+
+ if (private->window_type == GDK_WINDOW_CHILD)
+ start_window = _gdk_window_get_impl_window ((GdkWindow *)private->parent);
+ else if (private->window_type == GDK_WINDOW_TEMP)
+ start_window = get_root (window);
+
+ if (start_window)
+ {
+ _gdk_x11_window_tmp_reset_bg (start_window, TRUE);
+
+ if (private->window_type == GDK_WINDOW_CHILD && private->parent)
+ {
+ GdkRectangle invalid_rect;
+
+ gdk_window_get_position (window, &invalid_rect.x, &invalid_rect.y);
+ invalid_rect.width = gdk_window_get_width (window);
+ invalid_rect.height = gdk_window_get_height (window);
+ gdk_window_invalidate_rect ((GdkWindow *)private->parent,
+ &invalid_rect, TRUE);
+ }
+ }
+}
+
+static void
+gdk_window_x11_hide (GdkWindow *window)
+{
+ GdkWindowObject *private;
+
+ private = (GdkWindowObject*) window;
+
+ /* We'll get the unmap notify eventually, and handle it then,
+ * but checking here makes things more consistent if we are
+ * just doing stuff ourself.
+ */
+ _gdk_xgrab_check_unmap (window,
+ NextRequest (GDK_WINDOW_XDISPLAY (window)));
+
+ /* You can't simply unmap toplevel windows. */
+ switch (private->window_type)
+ {
+ case GDK_WINDOW_TOPLEVEL:
+ case GDK_WINDOW_TEMP: /* ? */
+ gdk_window_withdraw (window);
+ return;
+
+ case GDK_WINDOW_FOREIGN:
+ case GDK_WINDOW_ROOT:
+ case GDK_WINDOW_CHILD:
+ break;
+ }
+
+ _gdk_window_clear_update_area (window);
+
+ pre_unmap (window);
+ XUnmapWindow (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window));
+ post_unmap (window);
+}
+
+static void
+gdk_window_x11_withdraw (GdkWindow *window)
+{
+ GdkWindowObject *private;
+
+ private = (GdkWindowObject*) window;
+ if (!private->destroyed)
+ {
+ if (GDK_WINDOW_IS_MAPPED (window))
+ gdk_synthesize_window_state (window,
+ 0,
+ GDK_WINDOW_STATE_WITHDRAWN);
+
+ g_assert (!GDK_WINDOW_IS_MAPPED (window));
+
+ pre_unmap (window);
+
+ XWithdrawWindow (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window), 0);
+
+ post_unmap (window);
+ }
+}
+
+static inline void
+window_x11_move (GdkWindow *window,
+ gint x,
+ gint y)
+{
+ GdkWindowObject *private = (GdkWindowObject *) window;
+ GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (private->impl);
+
+ if (GDK_WINDOW_TYPE (private) == GDK_WINDOW_CHILD)
+ {
+ _gdk_window_move_resize_child (window,
+ x, y,
+ private->width, private->height);
+ }
+ else
+ {
+ XMoveWindow (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ x, y);
+
+ if (impl->override_redirect)
+ {
+ private->x = x;
+ private->y = y;
+ }
+ }
+}
+
+static inline void
+window_x11_resize (GdkWindow *window,
+ gint width,
+ gint height)
+{
+ GdkWindowObject *private = (GdkWindowObject *) window;
+
+ if (width < 1)
+ width = 1;
+
+ if (height < 1)
+ height = 1;
+
+ if (GDK_WINDOW_TYPE (private) == GDK_WINDOW_CHILD)
+ {
+ _gdk_window_move_resize_child (window,
+ private->x, private->y,
+ width, height);
+ }
+ else
+ {
+ GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (private->impl);
+
+ XResizeWindow (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ width, height);
+
+ if (impl->override_redirect)
+ {
+ private->width = width;
+ private->height = height;
+ _gdk_x11_drawable_update_size (private->impl);
+ }
+ else
+ {
+ if (width != private->width || height != private->height)
+ private->resize_count += 1;
+ }
+ }
+
+ _gdk_x11_drawable_update_size (private->impl);
+}
+
+static inline void
+window_x11_move_resize (GdkWindow *window,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ GdkWindowObject *private = (GdkWindowObject *) window;;
+
+ if (width < 1)
+ width = 1;
+
+ if (height < 1)
+ height = 1;
+
+ if (GDK_WINDOW_TYPE (private) == GDK_WINDOW_CHILD)
+ {
+ _gdk_window_move_resize_child (window, x, y, width, height);
+ _gdk_x11_drawable_update_size (private->impl);
+ }
+ else
+ {
+ GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (private->impl);
+
+ XMoveResizeWindow (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ x, y, width, height);
+
+ if (impl->override_redirect)
+ {
+ private->x = x;
+ private->y = y;
+
+ private->width = width;
+ private->height = height;
+
+ _gdk_x11_drawable_update_size (private->impl);
+ }
+ else
+ {
+ if (width != private->width || height != private->height)
+ private->resize_count += 1;
+ }
+ }
+}
+
+static void
+gdk_window_x11_move_resize (GdkWindow *window,
+ gboolean with_move,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ if (with_move && (width < 0 && height < 0))
+ window_x11_move (window, x, y);
+ else
+ {
+ if (with_move)
+ window_x11_move_resize (window, x, y, width, height);
+ else
+ window_x11_resize (window, width, height);
+ }
+}
+
+static gboolean
+gdk_window_x11_reparent (GdkWindow *window,
+ GdkWindow *new_parent,
+ gint x,
+ gint y)
+{
+ GdkWindowObject *window_private;
+ GdkWindowObject *parent_private;
+ GdkWindowImplX11 *impl;
+
+ window_private = (GdkWindowObject*) window;
+ parent_private = (GdkWindowObject*) new_parent;
+ impl = GDK_WINDOW_IMPL_X11 (window_private->impl);
+
+ _gdk_x11_window_tmp_unset_bg (window, TRUE);
+ _gdk_x11_window_tmp_unset_parent_bg (window);
+ XReparentWindow (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ GDK_WINDOW_XID (new_parent),
+ parent_private->abs_x + x, parent_private->abs_y + y);
+ _gdk_x11_window_tmp_reset_parent_bg (window);
+ _gdk_x11_window_tmp_reset_bg (window, TRUE);
+
+ if (GDK_WINDOW_TYPE (new_parent) == GDK_WINDOW_FOREIGN)
+ new_parent = gdk_screen_get_root_window (GDK_WINDOW_SCREEN (window));
+
+ window_private->parent = parent_private;
+
+ /* Switch the window type as appropriate */
+
+ switch (GDK_WINDOW_TYPE (new_parent))
+ {
+ case GDK_WINDOW_ROOT:
+ case GDK_WINDOW_FOREIGN:
+ /* Reparenting to toplevel */
+
+ if (!WINDOW_IS_TOPLEVEL (window) &&
+ GDK_WINDOW_TYPE (new_parent) == GDK_WINDOW_FOREIGN)
+ {
+ /* This is also done in common code at a later stage, but we
+ need it in setup_toplevel, so do it here too */
+ if (window_private->toplevel_window_type != -1)
+ GDK_WINDOW_TYPE (window) = window_private->toplevel_window_type;
+ else if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
+ GDK_WINDOW_TYPE (window) = GDK_WINDOW_TOPLEVEL;
+
+ /* Wasn't a toplevel, set up */
+ setup_toplevel_window (window, new_parent);
+ }
+
+ break;
+
+ case GDK_WINDOW_TOPLEVEL:
+ case GDK_WINDOW_CHILD:
+ case GDK_WINDOW_TEMP:
+ if (WINDOW_IS_TOPLEVEL (window) &&
+ impl->toplevel)
+ {
+ if (impl->toplevel->focus_window)
+ {
+ XDestroyWindow (GDK_WINDOW_XDISPLAY (window), impl->toplevel->focus_window);
+ _gdk_xid_table_remove (GDK_WINDOW_DISPLAY (window), impl->toplevel->focus_window);
+ }
+
+ gdk_toplevel_x11_free_contents (GDK_WINDOW_DISPLAY (window),
+ impl->toplevel);
+ g_free (impl->toplevel);
+ impl->toplevel = NULL;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+gdk_window_x11_raise (GdkWindow *window)
+{
+ XRaiseWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window));
+}
+
+static void
+gdk_window_x11_restack_under (GdkWindow *window,
+ GList *native_siblings /* in requested order, first is bottom-most */)
+{
+ Window *windows;
+ int n_windows, i;
+ GList *l;
+
+ n_windows = g_list_length (native_siblings) + 1;
+ windows = g_new (Window, n_windows);
+
+ windows[0] = GDK_WINDOW_XID (window);
+ /* Reverse order, as input order is bottom-most first */
+ i = n_windows - 1;
+ for (l = native_siblings; l != NULL; l = l->next)
+ windows[i--] = GDK_WINDOW_XID (l->data);
+
+ XRestackWindows (GDK_WINDOW_XDISPLAY (window), windows, n_windows);
+
+ g_free (windows);
+}
+
+static void
+gdk_window_x11_restack_toplevel (GdkWindow *window,
+ GdkWindow *sibling,
+ gboolean above)
+{
+ XWindowChanges changes;
+
+ changes.sibling = GDK_WINDOW_XID (sibling);
+ changes.stack_mode = above ? Above : Below;
+ XReconfigureWMWindow (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ gdk_screen_get_number (GDK_WINDOW_SCREEN (window)),
+ CWStackMode | CWSibling, &changes);
+}
+
+static void
+gdk_window_x11_lower (GdkWindow *window)
+{
+ XLowerWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window));
+}
+
+/**
+ * gdk_x11_window_move_to_current_desktop:
+ * @window: a #GdkWindow
+ *
+ * Moves the window to the correct workspace when running under a
+ * window manager that supports multiple workspaces, as described
+ * in the <ulink url="http://www.freedesktop.org/Standards/wm-spec">Extended
+ * Window Manager Hints</ulink>. Will not do anything if the
+ * window is already on all workspaces.
+ *
+ * Since: 2.8
+ */
+void
+gdk_x11_window_move_to_current_desktop (GdkWindow *window)
+{
+ GdkToplevelX11 *toplevel;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+ g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD);
+
+ toplevel = _gdk_x11_window_get_toplevel (window);
+
+ if (toplevel->on_all_desktops)
+ return;
+
+ move_to_current_desktop (window);
+}
+
+static void
+move_to_current_desktop (GdkWindow *window)
+{
+ if (gdk_x11_screen_supports_net_wm_hint (GDK_WINDOW_SCREEN (window),
+ gdk_atom_intern_static_string ("_NET_WM_DESKTOP")))
+ {
+ Atom type;
+ gint format;
+ gulong nitems;
+ gulong bytes_after;
+ guchar *data;
+ gulong *current_desktop;
+ GdkDisplay *display;
+
+ display = gdk_window_get_display (window);
+
+ /* Get current desktop, then set it; this is a race, but not
+ * one that matters much in practice.
+ */
+ XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XROOTWIN (window),
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_CURRENT_DESKTOP"),
+ 0, G_MAXLONG,
+ False, XA_CARDINAL, &type, &format, &nitems,
+ &bytes_after, &data);
+
+ if (type == XA_CARDINAL)
+ {
+ XClientMessageEvent xclient;
+ current_desktop = (gulong *)data;
+
+ memset (&xclient, 0, sizeof (xclient));
+ xclient.type = ClientMessage;
+ xclient.serial = 0;
+ xclient.send_event = True;
+ xclient.window = GDK_WINDOW_XWINDOW (window);
+ xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP");
+ xclient.format = 32;
+
+ xclient.data.l[0] = *current_desktop;
+ xclient.data.l[1] = 0;
+ xclient.data.l[2] = 0;
+ xclient.data.l[3] = 0;
+ xclient.data.l[4] = 0;
+
+ XSendEvent (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XROOTWIN (window),
+ False,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ (XEvent *)&xclient);
+
+ XFree (current_desktop);
+ }
+ }
+}
+
+/**
+ * gdk_window_focus:
+ * @window: a #GdkWindow
+ * @timestamp: timestamp of the event triggering the window focus
+ *
+ * Sets keyboard focus to @window. In most cases, gtk_window_present()
+ * should be used on a #GtkWindow, rather than calling this function.
+ *
+ **/
+void
+gdk_window_focus (GdkWindow *window,
+ guint32 timestamp)
+{
+ GdkDisplay *display;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ display = GDK_WINDOW_DISPLAY (window);
+
+ if (gdk_x11_screen_supports_net_wm_hint (GDK_WINDOW_SCREEN (window),
+ gdk_atom_intern_static_string ("_NET_ACTIVE_WINDOW")))
+ {
+ XClientMessageEvent xclient;
+
+ memset (&xclient, 0, sizeof (xclient));
+ xclient.type = ClientMessage;
+ xclient.window = GDK_WINDOW_XWINDOW (window);
+ xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_ACTIVE_WINDOW");
+ xclient.format = 32;
+ xclient.data.l[0] = 1; /* requestor type; we're an app */
+ xclient.data.l[1] = timestamp;
+ xclient.data.l[2] = None; /* currently active window */
+ xclient.data.l[3] = 0;
+ xclient.data.l[4] = 0;
+
+ XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XROOTWIN (window), False,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ (XEvent *)&xclient);
+ }
+ else
+ {
+ XRaiseWindow (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window));
+
+ /* There is no way of knowing reliably whether we are viewable;
+ * _gdk_x11_set_input_focus_safe() traps errors asynchronously.
+ */
+ _gdk_x11_set_input_focus_safe (display, GDK_WINDOW_XID (window),
+ RevertToParent,
+ timestamp);
+ }
+}
+
+/**
+ * gdk_window_set_type_hint:
+ * @window: A toplevel #GdkWindow
+ * @hint: A hint of the function this window will have
+ *
+ * The application can use this call to provide a hint to the window
+ * manager about the functionality of a window. The window manager
+ * can use this information when determining the decoration and behaviour
+ * of the window.
+ *
+ * The hint must be set before the window is mapped.
+ **/
+void
+gdk_window_set_type_hint (GdkWindow *window,
+ GdkWindowTypeHint hint)
+{
+ GdkDisplay *display;
+ Atom atom;
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ display = gdk_window_get_display (window);
+
+ switch (hint)
+ {
+ case GDK_WINDOW_TYPE_HINT_DIALOG:
+ atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DIALOG");
+ break;
+ case GDK_WINDOW_TYPE_HINT_MENU:
+ atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_MENU");
+ break;
+ case GDK_WINDOW_TYPE_HINT_TOOLBAR:
+ atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_TOOLBAR");
+ break;
+ case GDK_WINDOW_TYPE_HINT_UTILITY:
+ atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_UTILITY");
+ break;
+ case GDK_WINDOW_TYPE_HINT_SPLASHSCREEN:
+ atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_SPLASH");
+ break;
+ case GDK_WINDOW_TYPE_HINT_DOCK:
+ atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DOCK");
+ break;
+ case GDK_WINDOW_TYPE_HINT_DESKTOP:
+ atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DESKTOP");
+ break;
+ case GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU:
+ atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU");
+ break;
+ case GDK_WINDOW_TYPE_HINT_POPUP_MENU:
+ atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_POPUP_MENU");
+ break;
+ case GDK_WINDOW_TYPE_HINT_TOOLTIP:
+ atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_TOOLTIP");
+ break;
+ case GDK_WINDOW_TYPE_HINT_NOTIFICATION:
+ atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_NOTIFICATION");
+ break;
+ case GDK_WINDOW_TYPE_HINT_COMBO:
+ atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_COMBO");
+ break;
+ case GDK_WINDOW_TYPE_HINT_DND:
+ atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DND");
+ break;
+ default:
+ g_warning ("Unknown hint %d passed to gdk_window_set_type_hint", hint);
+ /* Fall thru */
+ case GDK_WINDOW_TYPE_HINT_NORMAL:
+ atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_NORMAL");
+ break;
+ }
+
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window),
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE"),
+ XA_ATOM, 32, PropModeReplace,
+ (guchar *)&atom, 1);
+}
+
+/**
+ * gdk_window_get_type_hint:
+ * @window: A toplevel #GdkWindow
+ *
+ * This function returns the type hint set for a window.
+ *
+ * Return value: The type hint set for @window
+ *
+ * Since: 2.10
+ **/
+GdkWindowTypeHint
+gdk_window_get_type_hint (GdkWindow *window)
+{
+ GdkDisplay *display;
+ GdkWindowTypeHint type;
+ Atom type_return;
+ gint format_return;
+ gulong nitems_return;
+ gulong bytes_after_return;
+ guchar *data = NULL;
+
+ g_return_val_if_fail (GDK_IS_WINDOW (window), GDK_WINDOW_TYPE_HINT_NORMAL);
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return GDK_WINDOW_TYPE_HINT_NORMAL;
+
+ type = GDK_WINDOW_TYPE_HINT_NORMAL;
+
+ display = gdk_window_get_display (window);
+
+ if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window),
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE"),
+ 0, G_MAXLONG, False, XA_ATOM, &type_return,
+ &format_return, &nitems_return, &bytes_after_return,
+ &data) == Success)
+ {
+ if ((type_return == XA_ATOM) && (format_return == 32) &&
+ (data) && (nitems_return == 1))
+ {
+ Atom atom = *(Atom*)data;
+
+ if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DIALOG"))
+ type = GDK_WINDOW_TYPE_HINT_DIALOG;
+ else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_MENU"))
+ type = GDK_WINDOW_TYPE_HINT_MENU;
+ else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_TOOLBAR"))
+ type = GDK_WINDOW_TYPE_HINT_TOOLBAR;
+ else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_UTILITY"))
+ type = GDK_WINDOW_TYPE_HINT_UTILITY;
+ else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_SPLASH"))
+ type = GDK_WINDOW_TYPE_HINT_SPLASHSCREEN;
+ else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DOCK"))
+ type = GDK_WINDOW_TYPE_HINT_DOCK;
+ else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DESKTOP"))
+ type = GDK_WINDOW_TYPE_HINT_DESKTOP;
+ else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"))
+ type = GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU;
+ else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_POPUP_MENU"))
+ type = GDK_WINDOW_TYPE_HINT_POPUP_MENU;
+ else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_TOOLTIP"))
+ type = GDK_WINDOW_TYPE_HINT_TOOLTIP;
+ else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_NOTIFICATION"))
+ type = GDK_WINDOW_TYPE_HINT_NOTIFICATION;
+ else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_COMBO"))
+ type = GDK_WINDOW_TYPE_HINT_COMBO;
+ else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DND"))
+ type = GDK_WINDOW_TYPE_HINT_DND;
+ }
+
+ if (type_return != None && data != NULL)
+ XFree (data);
+ }
+
+ return type;
+}
+
+static void
+gdk_wmspec_change_state (gboolean add,
+ GdkWindow *window,
+ GdkAtom state1,
+ GdkAtom state2)
+{
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
+ XClientMessageEvent xclient;
+
+#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
+#define _NET_WM_STATE_ADD 1 /* add/set property */
+#define _NET_WM_STATE_TOGGLE 2 /* toggle property */
+
+ memset (&xclient, 0, sizeof (xclient));
+ xclient.type = ClientMessage;
+ xclient.window = GDK_WINDOW_XID (window);
+ xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE");
+ xclient.format = 32;
+ xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
+ xclient.data.l[1] = gdk_x11_atom_to_xatom_for_display (display, state1);
+ xclient.data.l[2] = gdk_x11_atom_to_xatom_for_display (display, state2);
+ xclient.data.l[3] = 0;
+ xclient.data.l[4] = 0;
+
+ XSendEvent (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XROOTWIN (window), False,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ (XEvent *)&xclient);
+}
+
+/**
+ * gdk_window_set_modal_hint:
+ * @window: A toplevel #GdkWindow
+ * @modal: %TRUE if the window is modal, %FALSE otherwise.
+ *
+ * The application can use this hint to tell the window manager
+ * that a certain window has modal behaviour. The window manager
+ * can use this information to handle modal windows in a special
+ * way.
+ *
+ * You should only use this on windows for which you have
+ * previously called gdk_window_set_transient_for()
+ **/
+void
+gdk_window_set_modal_hint (GdkWindow *window,
+ gboolean modal)
+{
+ GdkWindowObject *private;
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ private = (GdkWindowObject*) window;
+
+ private->modal_hint = modal;
+
+ if (GDK_WINDOW_IS_MAPPED (window))
+ gdk_wmspec_change_state (modal, window,
+ gdk_atom_intern_static_string ("_NET_WM_STATE_MODAL"),
+ GDK_NONE);
+}
+
+/**
+ * gdk_window_set_skip_taskbar_hint:
+ * @window: a toplevel #GdkWindow
+ * @skips_taskbar: %TRUE to skip the taskbar
+ *
+ * Toggles whether a window should appear in a task list or window
+ * list. If a window's semantic type as specified with
+ * gdk_window_set_type_hint() already fully describes the window, this
+ * function should <emphasis>not</emphasis> be called in addition,
+ * instead you should allow the window to be treated according to
+ * standard policy for its semantic type.
+ *
+ * Since: 2.2
+ **/
+void
+gdk_window_set_skip_taskbar_hint (GdkWindow *window,
+ gboolean skips_taskbar)
+{
+ GdkToplevelX11 *toplevel;
+
+ g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD);
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ toplevel = _gdk_x11_window_get_toplevel (window);
+ toplevel->skip_taskbar_hint = skips_taskbar;
+
+ if (GDK_WINDOW_IS_MAPPED (window))
+ gdk_wmspec_change_state (skips_taskbar, window,
+ gdk_atom_intern_static_string ("_NET_WM_STATE_SKIP_TASKBAR"),
+ GDK_NONE);
+}
+
+/**
+ * gdk_window_set_skip_pager_hint:
+ * @window: a toplevel #GdkWindow
+ * @skips_pager: %TRUE to skip the pager
+ *
+ * Toggles whether a window should appear in a pager (workspace
+ * switcher, or other desktop utility program that displays a small
+ * thumbnail representation of the windows on the desktop). If a
+ * window's semantic type as specified with gdk_window_set_type_hint()
+ * already fully describes the window, this function should
+ * <emphasis>not</emphasis> be called in addition, instead you should
+ * allow the window to be treated according to standard policy for
+ * its semantic type.
+ *
+ * Since: 2.2
+ **/
+void
+gdk_window_set_skip_pager_hint (GdkWindow *window,
+ gboolean skips_pager)
+{
+ GdkToplevelX11 *toplevel;
+
+ g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD);
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ toplevel = _gdk_x11_window_get_toplevel (window);
+ toplevel->skip_pager_hint = skips_pager;
+
+ if (GDK_WINDOW_IS_MAPPED (window))
+ gdk_wmspec_change_state (skips_pager, window,
+ gdk_atom_intern_static_string ("_NET_WM_STATE_SKIP_PAGER"),
+ GDK_NONE);
+}
+
+/**
+ * gdk_window_set_urgency_hint:
+ * @window: a toplevel #GdkWindow
+ * @urgent: %TRUE if the window is urgent
+ *
+ * Toggles whether a window needs the user's
+ * urgent attention.
+ *
+ * Since: 2.8
+ **/
+void
+gdk_window_set_urgency_hint (GdkWindow *window,
+ gboolean urgent)
+{
+ GdkToplevelX11 *toplevel;
+
+ g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD);
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ toplevel = _gdk_x11_window_get_toplevel (window);
+ toplevel->urgency_hint = urgent;
+
+ update_wm_hints (window, FALSE);
+}
+
+/**
+ * gdk_window_set_geometry_hints:
+ * @window: a toplevel #GdkWindow
+ * @geometry: geometry hints
+ * @geom_mask: bitmask indicating fields of @geometry to pay attention to
+ *
+ * Sets the geometry hints for @window. Hints flagged in @geom_mask
+ * are set, hints not flagged in @geom_mask are unset.
+ * To unset all hints, use a @geom_mask of 0 and a @geometry of %NULL.
+ *
+ * This function provides hints to the windowing system about
+ * acceptable sizes for a toplevel window. The purpose of
+ * this is to constrain user resizing, but the windowing system
+ * will typically (but is not required to) also constrain the
+ * current size of the window to the provided values and
+ * constrain programatic resizing via gdk_window_resize() or
+ * gdk_window_move_resize().
+ *
+ * Note that on X11, this effect has no effect on windows
+ * of type %GDK_WINDOW_TEMP or windows where override redirect
+ * has been turned on via gdk_window_set_override_redirect()
+ * since these windows are not resizable by the user.
+ *
+ * Since you can't count on the windowing system doing the
+ * constraints for programmatic resizes, you should generally
+ * call gdk_window_constrain_size() yourself to determine
+ * appropriate sizes.
+ *
+ **/
+void
+gdk_window_set_geometry_hints (GdkWindow *window,
+ const GdkGeometry *geometry,
+ GdkWindowHints geom_mask)
+{
+ XSizeHints size_hints;
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ size_hints.flags = 0;
+
+ if (geom_mask & GDK_HINT_POS)
+ {
+ size_hints.flags |= PPosition;
+ /* We need to initialize the following obsolete fields because KWM
+ * apparently uses these fields if they are non-zero.
+ * #@#!#!$!.
+ */
+ size_hints.x = 0;
+ size_hints.y = 0;
+ }
+
+ if (geom_mask & GDK_HINT_USER_POS)
+ {
+ size_hints.flags |= USPosition;
+ }
+
+ if (geom_mask & GDK_HINT_USER_SIZE)
+ {
+ size_hints.flags |= USSize;
+ }
+
+ if (geom_mask & GDK_HINT_MIN_SIZE)
+ {
+ size_hints.flags |= PMinSize;
+ size_hints.min_width = geometry->min_width;
+ size_hints.min_height = geometry->min_height;
+ }
+
+ if (geom_mask & GDK_HINT_MAX_SIZE)
+ {
+ size_hints.flags |= PMaxSize;
+ size_hints.max_width = MAX (geometry->max_width, 1);
+ size_hints.max_height = MAX (geometry->max_height, 1);
+ }
+
+ if (geom_mask & GDK_HINT_BASE_SIZE)
+ {
+ size_hints.flags |= PBaseSize;
+ size_hints.base_width = geometry->base_width;
+ size_hints.base_height = geometry->base_height;
+ }
+
+ if (geom_mask & GDK_HINT_RESIZE_INC)
+ {
+ size_hints.flags |= PResizeInc;
+ size_hints.width_inc = geometry->width_inc;
+ size_hints.height_inc = geometry->height_inc;
+ }
+
+ if (geom_mask & GDK_HINT_ASPECT)
+ {
+ size_hints.flags |= PAspect;
+ if (geometry->min_aspect <= 1)
+ {
+ size_hints.min_aspect.x = 65536 * geometry->min_aspect;
+ size_hints.min_aspect.y = 65536;
+ }
+ else
+ {
+ size_hints.min_aspect.x = 65536;
+ size_hints.min_aspect.y = 65536 / geometry->min_aspect;;
+ }
+ if (geometry->max_aspect <= 1)
+ {
+ size_hints.max_aspect.x = 65536 * geometry->max_aspect;
+ size_hints.max_aspect.y = 65536;
+ }
+ else
+ {
+ size_hints.max_aspect.x = 65536;
+ size_hints.max_aspect.y = 65536 / geometry->max_aspect;;
+ }
+ }
+
+ if (geom_mask & GDK_HINT_WIN_GRAVITY)
+ {
+ size_hints.flags |= PWinGravity;
+ size_hints.win_gravity = geometry->win_gravity;
+ }
+
+ /* FIXME: Would it be better to delete this property if
+ * geom_mask == 0? It would save space on the server
+ */
+ XSetWMNormalHints (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ &size_hints);
+}
+
+static void
+gdk_window_get_geometry_hints (GdkWindow *window,
+ GdkGeometry *geometry,
+ GdkWindowHints *geom_mask)
+{
+ XSizeHints *size_hints;
+ glong junk_supplied_mask = 0;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+ g_return_if_fail (geometry != NULL);
+ g_return_if_fail (geom_mask != NULL);
+
+ *geom_mask = 0;
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ size_hints = XAllocSizeHints ();
+ if (!size_hints)
+ return;
+
+ if (!XGetWMNormalHints (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ size_hints,
+ &junk_supplied_mask))
+ size_hints->flags = 0;
+
+ if (size_hints->flags & PMinSize)
+ {
+ *geom_mask |= GDK_HINT_MIN_SIZE;
+ geometry->min_width = size_hints->min_width;
+ geometry->min_height = size_hints->min_height;
+ }
+
+ if (size_hints->flags & PMaxSize)
+ {
+ *geom_mask |= GDK_HINT_MAX_SIZE;
+ geometry->max_width = MAX (size_hints->max_width, 1);
+ geometry->max_height = MAX (size_hints->max_height, 1);
+ }
+
+ if (size_hints->flags & PResizeInc)
+ {
+ *geom_mask |= GDK_HINT_RESIZE_INC;
+ geometry->width_inc = size_hints->width_inc;
+ geometry->height_inc = size_hints->height_inc;
+ }
+
+ if (size_hints->flags & PAspect)
+ {
+ *geom_mask |= GDK_HINT_ASPECT;
+
+ geometry->min_aspect = (gdouble) size_hints->min_aspect.x / (gdouble) size_hints->min_aspect.y;
+ geometry->max_aspect = (gdouble) size_hints->max_aspect.x / (gdouble) size_hints->max_aspect.y;
+ }
+
+ if (size_hints->flags & PWinGravity)
+ {
+ *geom_mask |= GDK_HINT_WIN_GRAVITY;
+ geometry->win_gravity = size_hints->win_gravity;
+ }
+
+ XFree (size_hints);
+}
+
+static gboolean
+utf8_is_latin1 (const gchar *str)
+{
+ const char *p = str;
+
+ while (*p)
+ {
+ gunichar ch = g_utf8_get_char (p);
+
+ if (ch > 0xff)
+ return FALSE;
+
+ p = g_utf8_next_char (p);
+ }
+
+ return TRUE;
+}
+
+/* Set the property to @utf8_str as STRING if the @utf8_str is fully
+ * convertable to STRING, otherwise, set it as compound text
+ */
+static void
+set_text_property (GdkDisplay *display,
+ Window xwindow,
+ Atom property,
+ const gchar *utf8_str)
+{
+ gchar *prop_text = NULL;
+ Atom prop_type;
+ gint prop_length;
+ gint prop_format;
+ gboolean is_compound_text;
+
+ if (utf8_is_latin1 (utf8_str))
+ {
+ prop_type = XA_STRING;
+ prop_text = gdk_utf8_to_string_target (utf8_str);
+ prop_length = prop_text ? strlen (prop_text) : 0;
+ prop_format = 8;
+ is_compound_text = FALSE;
+ }
+ else
+ {
+ GdkAtom gdk_type;
+
+ gdk_utf8_to_compound_text_for_display (display,
+ utf8_str, &gdk_type, &prop_format,
+ (guchar **)&prop_text, &prop_length);
+ prop_type = gdk_x11_atom_to_xatom_for_display (display, gdk_type);
+ is_compound_text = TRUE;
+ }
+
+ if (prop_text)
+ {
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ xwindow,
+ property,
+ prop_type, prop_format,
+ PropModeReplace, (guchar *)prop_text,
+ prop_length);
+
+ if (is_compound_text)
+ gdk_free_compound_text ((guchar *)prop_text);
+ else
+ g_free (prop_text);
+ }
+}
+
+/* Set WM_NAME and _NET_WM_NAME
+ */
+static void
+set_wm_name (GdkDisplay *display,
+ Window xwindow,
+ const gchar *name)
+{
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow,
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME"),
+ gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8,
+ PropModeReplace, (guchar *)name, strlen (name));
+
+ set_text_property (display, xwindow,
+ gdk_x11_get_xatom_by_name_for_display (display, "WM_NAME"),
+ name);
+}
+
+/**
+ * gdk_window_set_title:
+ * @window: a toplevel #GdkWindow
+ * @title: title of @window
+ *
+ * Sets the title of a toplevel window, to be displayed in the titlebar.
+ * If you haven't explicitly set the icon name for the window
+ * (using gdk_window_set_icon_name()), the icon name will be set to
+ * @title as well. @title must be in UTF-8 encoding (as with all
+ * user-readable strings in GDK/GTK+). @title may not be %NULL.
+ **/
+void
+gdk_window_set_title (GdkWindow *window,
+ const gchar *title)
+{
+ GdkDisplay *display;
+ Display *xdisplay;
+ Window xwindow;
+
+ g_return_if_fail (title != NULL);
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ display = gdk_window_get_display (window);
+ xdisplay = GDK_DISPLAY_XDISPLAY (display);
+ xwindow = GDK_WINDOW_XID (window);
+
+ set_wm_name (display, xwindow, title);
+
+ if (!gdk_window_icon_name_set (window))
+ {
+ XChangeProperty (xdisplay, xwindow,
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_ICON_NAME"),
+ gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8,
+ PropModeReplace, (guchar *)title, strlen (title));
+
+ set_text_property (display, xwindow,
+ gdk_x11_get_xatom_by_name_for_display (display, "WM_ICON_NAME"),
+ title);
+ }
+}
+
+/**
+ * gdk_window_set_role:
+ * @window: a toplevel #GdkWindow
+ * @role: a string indicating its role
+ *
+ * When using GTK+, typically you should use gtk_window_set_role() instead
+ * of this low-level function.
+ *
+ * The window manager and session manager use a window's role to
+ * distinguish it from other kinds of window in the same application.
+ * When an application is restarted after being saved in a previous
+ * session, all windows with the same title and role are treated as
+ * interchangeable. So if you have two windows with the same title
+ * that should be distinguished for session management purposes, you
+ * should set the role on those windows. It doesn't matter what string
+ * you use for the role, as long as you have a different role for each
+ * non-interchangeable kind of window.
+ *
+ **/
+void
+gdk_window_set_role (GdkWindow *window,
+ const gchar *role)
+{
+ GdkDisplay *display;
+
+ display = gdk_window_get_display (window);
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ if (role)
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window),
+ gdk_x11_get_xatom_by_name_for_display (display, "WM_WINDOW_ROLE"),
+ XA_STRING, 8, PropModeReplace, (guchar *)role, strlen (role));
+ else
+ XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window),
+ gdk_x11_get_xatom_by_name_for_display (display, "WM_WINDOW_ROLE"));
+}
+
+/**
+ * gdk_window_set_startup_id:
+ * @window: a toplevel #GdkWindow
+ * @startup_id: a string with startup-notification identifier
+ *
+ * When using GTK+, typically you should use gtk_window_set_startup_id()
+ * instead of this low-level function.
+ *
+ * Since: 2.12
+ *
+ **/
+void
+gdk_window_set_startup_id (GdkWindow *window,
+ const gchar *startup_id)
+{
+ GdkDisplay *display;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ display = gdk_window_get_display (window);
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ if (startup_id)
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (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, (unsigned char *)startup_id, strlen (startup_id));
+ else
+ XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window),
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_STARTUP_ID"));
+}
+
+/**
+ * gdk_window_set_transient_for:
+ * @window: a toplevel #GdkWindow
+ * @parent: another toplevel #GdkWindow
+ *
+ * Indicates to the window manager that @window is a transient dialog
+ * associated with the application window @parent. This allows the
+ * window manager to do things like center @window on @parent and
+ * keep @window above @parent.
+ *
+ * See gtk_window_set_transient_for() if you're using #GtkWindow or
+ * #GtkDialog.
+ **/
+void
+gdk_window_set_transient_for (GdkWindow *window,
+ GdkWindow *parent)
+{
+ if (!GDK_WINDOW_DESTROYED (window) && !GDK_WINDOW_DESTROYED (parent) &&
+ WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ XSetTransientForHint (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ GDK_WINDOW_XID (parent));
+}
+
+static gboolean
+gdk_window_x11_set_back_color (GdkWindow *window,
+ double red,
+ double green,
+ double blue,
+ double alpha)
+{
+ GdkVisual *visual = gdk_window_get_visual (window);
+
+ /* I suppose we could handle these, but that'd require fiddling with
+ * xrender formats... */
+ if (alpha != 1.0)
+ return FALSE;
+
+ switch (visual->type)
+ {
+ case GDK_VISUAL_DIRECT_COLOR:
+ case GDK_VISUAL_TRUE_COLOR:
+ {
+ /* If bits not used for color are used for something other than padding,
+ * it's likely alpha, so we set them to 1s.
+ */
+ guint padding, pixel;
+
+ /* Shifting by >= width-of-type isn't defined in C */
+ if (visual->depth >= 32)
+ padding = 0;
+ else
+ padding = ((~(guint32)0)) << visual->depth;
+
+ pixel = ~ (visual->red_mask | visual->green_mask | visual->blue_mask | padding);
+
+ pixel += (((int) (red * ((1 << visual->red_prec ) - 1))) << visual->red_shift ) +
+ (((int) (green * ((1 << visual->green_prec) - 1))) << visual->green_shift) +
+ (((int) (blue * ((1 << visual->blue_prec ) - 1))) << visual->blue_shift );
+
+ XSetWindowBackground (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window), pixel);
+ }
+ return TRUE;
+
+ /* These require fiddling with the colormap, and as they're essentially unused
+ * we're just gonna skip them for now.
+ */
+ case GDK_VISUAL_PSEUDO_COLOR:
+ case GDK_VISUAL_GRAYSCALE:
+ case GDK_VISUAL_STATIC_GRAY:
+ case GDK_VISUAL_STATIC_COLOR:
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+matrix_is_identity (cairo_matrix_t *matrix)
+{
+ return matrix->xx == 1.0 && matrix->yy == 1.0 &&
+ matrix->yx == 0.0 && matrix->xy == 0.0 &&
+ matrix->x0 == 0.0 && matrix->y0 == 0.0;
+}
+
+static void
+gdk_window_x11_set_background (GdkWindow *window,
+ cairo_pattern_t *pattern)
+{
+ double r, g, b, a;
+ cairo_surface_t *surface;
+ cairo_matrix_t matrix;
+
+ if (GDK_WINDOW_DESTROYED (window))
+ return;
+
+ if (pattern == NULL)
+ {
+ GdkWindow *parent;
+
+ /* X throws BadMatch if the parent has a different visual when
+ * using ParentRelative */
+ parent = gdk_window_get_parent (window);
+ if (parent && gdk_window_get_visual (parent) == gdk_window_get_visual (window))
+ XSetWindowBackgroundPixmap (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window), ParentRelative);
+ else
+ XSetWindowBackgroundPixmap (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window), None);
+ return;
+ }
+
+ switch (cairo_pattern_get_type (pattern))
+ {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ cairo_pattern_get_rgba (pattern, &r, &g, &b, &a);
+ if (gdk_window_x11_set_back_color (window, r, g, b, a))
+ return;
+ break;
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ cairo_pattern_get_matrix (pattern, &matrix);
+ if (cairo_pattern_get_surface (pattern, &surface) == CAIRO_STATUS_SUCCESS &&
+ matrix_is_identity (&matrix) &&
+ cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_XLIB &&
+ cairo_xlib_surface_get_visual (surface) == GDK_VISUAL_XVISUAL (gdk_window_get_visual ((window))) &&
+ cairo_xlib_surface_get_display (surface) == GDK_WINDOW_XDISPLAY (window))
+ {
+ double x, y;
+
+ cairo_surface_get_device_offset (surface, &x, &y);
+ /* XXX: This still bombs for non-pixmaps, but there's no way to
+ * detect we're not a pixmap in Cairo... */
+ if (x == 0.0 && y == 0.0)
+ {
+ XSetWindowBackgroundPixmap (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ cairo_xlib_surface_get_drawable (surface));
+ return;
+ }
+ }
+ /* fall through */
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ default:
+ /* fallback: just use black */
+ break;
+ }
+
+ XSetWindowBackgroundPixmap (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window), None);
+}
+
+static void
+gdk_window_x11_set_device_cursor (GdkWindow *window,
+ GdkDevice *device,
+ GdkCursor *cursor)
+{
+ GdkWindowObject *private;
+ GdkWindowImplX11 *impl;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+ g_return_if_fail (GDK_IS_DEVICE (device));
+
+ private = (GdkWindowObject *) window;
+ impl = GDK_WINDOW_IMPL_X11 (private->impl);
+
+ if (!cursor)
+ g_hash_table_remove (impl->device_cursor, device);
+ else
+ {
+ _gdk_x11_cursor_update_theme (cursor);
+ g_hash_table_replace (impl->device_cursor,
+ device, gdk_cursor_ref (cursor));
+ }
+
+ if (!GDK_WINDOW_DESTROYED (window))
+ GDK_DEVICE_GET_CLASS (device)->set_window_cursor (device, window, cursor);
+}
+
+GdkCursor *
+_gdk_x11_window_get_cursor (GdkWindow *window)
+{
+ GdkWindowObject *private;
+ GdkWindowImplX11 *impl;
+
+ g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
+
+ private = (GdkWindowObject *)window;
+ impl = GDK_WINDOW_IMPL_X11 (private->impl);
+
+ return impl->cursor;
+}
+
+static void
+gdk_window_x11_get_geometry (GdkWindow *window,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height,
+ gint *depth)
+{
+ Window root;
+ gint tx;
+ gint ty;
+ guint twidth;
+ guint theight;
+ guint tborder_width;
+ guint tdepth;
+
+ if (!GDK_WINDOW_DESTROYED (window))
+ {
+ XGetGeometry (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ &root, &tx, &ty, &twidth, &theight, &tborder_width, &tdepth);
+
+ if (x)
+ *x = tx;
+ if (y)
+ *y = ty;
+ if (width)
+ *width = twidth;
+ if (height)
+ *height = theight;
+ if (depth)
+ *depth = tdepth;
+ }
+}
+
+static gint
+gdk_window_x11_get_root_coords (GdkWindow *window,
+ gint x,
+ gint y,
+ gint *root_x,
+ gint *root_y)
+{
+ gint return_val;
+ Window child;
+ gint tx;
+ gint ty;
+
+ return_val = XTranslateCoordinates (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ GDK_WINDOW_XROOTWIN (window),
+ x, y, &tx, &ty,
+ &child);
+
+ if (root_x)
+ *root_x = tx;
+ if (root_y)
+ *root_y = ty;
+
+ return return_val;
+}
+
+/**
+ * gdk_window_get_root_origin:
+ * @window: a toplevel #GdkWindow
+ * @x: return location for X position of window frame
+ * @y: return location for Y position of window frame
+ *
+ * Obtains the top-left corner of the window manager frame in root
+ * window coordinates.
+ *
+ **/
+void
+gdk_window_get_root_origin (GdkWindow *window,
+ gint *x,
+ gint *y)
+{
+ GdkRectangle rect;
+
+ gdk_window_get_frame_extents (window, &rect);
+
+ if (x)
+ *x = rect.x;
+
+ if (y)
+ *y = rect.y;
+}
+
+/**
+ * gdk_window_get_frame_extents:
+ * @window: a toplevel #GdkWindow
+ * @rect: rectangle to fill with bounding box of the window frame
+ *
+ * Obtains the bounding box of the window, including window manager
+ * titlebar/borders if any. The frame position is given in root window
+ * coordinates. To get the position of the window itself (rather than
+ * the frame) in root window coordinates, use gdk_window_get_origin().
+ *
+ **/
+void
+gdk_window_get_frame_extents (GdkWindow *window,
+ GdkRectangle *rect)
+{
+ GdkDisplay *display;
+ GdkWindowObject *private;
+ GdkWindowImplX11 *impl;
+ Window xwindow;
+ Window xparent;
+ Window root;
+ Window child;
+ Window *children;
+ guchar *data;
+ Window *vroots;
+ Atom type_return;
+ guint nchildren;
+ guint nvroots;
+ gulong nitems_return;
+ gulong bytes_after_return;
+ gint format_return;
+ gint i;
+ guint ww, wh, wb, wd;
+ gint wx, wy;
+ gboolean got_frame_extents = FALSE;
+
+ g_return_if_fail (rect != NULL);
+
+ private = (GdkWindowObject*) window;
+
+ rect->x = 0;
+ rect->y = 0;
+ rect->width = 1;
+ rect->height = 1;
+
+ while (private->parent && ((GdkWindowObject*) private->parent)->parent)
+ private = (GdkWindowObject*) private->parent;
+
+ /* Refine our fallback answer a bit using local information */
+ rect->x = private->x;
+ rect->y = private->y;
+ rect->width = private->width;
+ rect->height = private->height;
+
+ impl = GDK_WINDOW_IMPL_X11 (private->impl);
+ if (GDK_WINDOW_DESTROYED (private) || impl->override_redirect)
+ return;
+
+ nvroots = 0;
+ vroots = NULL;
+
+ gdk_error_trap_push();
+
+ display = gdk_window_get_display (window);
+ xwindow = GDK_WINDOW_XID (window);
+
+ /* first try: use _NET_FRAME_EXTENTS */
+ if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), xwindow,
+ gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_FRAME_EXTENTS"),
+ 0, G_MAXLONG, False, XA_CARDINAL, &type_return,
+ &format_return, &nitems_return, &bytes_after_return,
+ &data)
+ == Success)
+ {
+ if ((type_return == XA_CARDINAL) && (format_return == 32) &&
+ (nitems_return == 4) && (data))
+ {
+ gulong *ldata = (gulong *) data;
+ got_frame_extents = TRUE;
+
+ /* try to get the real client window geometry */
+ if (XGetGeometry (GDK_DISPLAY_XDISPLAY (display), xwindow,
+ &root, &wx, &wy, &ww, &wh, &wb, &wd) &&
+ XTranslateCoordinates (GDK_DISPLAY_XDISPLAY (display),
+ xwindow, root, 0, 0, &wx, &wy, &child))
+ {
+ rect->x = wx;
+ rect->y = wy;
+ rect->width = ww;
+ rect->height = wh;
+ }
+
+ /* _NET_FRAME_EXTENTS format is left, right, top, bottom */
+ rect->x -= ldata[0];
+ rect->y -= ldata[2];
+ rect->width += ldata[0] + ldata[1];
+ rect->height += ldata[2] + ldata[3];
+ }
+
+ if (data)
+ XFree (data);
+ }
+
+ if (got_frame_extents)
+ goto out;
+
+ /* no frame extents property available, which means we either have a WM that
+ is not EWMH compliant or is broken - try fallback and walk up the window
+ tree to get our window's parent which hopefully is the window frame */
+
+ /* use NETWM_VIRTUAL_ROOTS if available */
+ root = GDK_WINDOW_XROOTWIN (window);
+
+ if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), root,
+ gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_VIRTUAL_ROOTS"),
+ 0, G_MAXLONG, False, XA_WINDOW, &type_return,
+ &format_return, &nitems_return, &bytes_after_return,
+ &data)
+ == Success)
+ {
+ if ((type_return == XA_WINDOW) && (format_return == 32) && (data))
+ {
+ nvroots = nitems_return;
+ vroots = (Window *)data;
+ }
+ }
+
+ xparent = GDK_WINDOW_XID (window);
+
+ do
+ {
+ xwindow = xparent;
+
+ if (!XQueryTree (GDK_DISPLAY_XDISPLAY (display), xwindow,
+ &root, &xparent,
+ &children, &nchildren))
+ goto out;
+
+ if (children)
+ XFree (children);
+
+ /* check virtual roots */
+ for (i = 0; i < nvroots; i++)
+ {
+ if (xparent == vroots[i])
+ {
+ root = xparent;
+ break;
+ }
+ }
+ }
+ while (xparent != root);
+
+ if (XGetGeometry (GDK_DISPLAY_XDISPLAY (display), xwindow,
+ &root, &wx, &wy, &ww, &wh, &wb, &wd))
+ {
+ rect->x = wx;
+ rect->y = wy;
+ rect->width = ww;
+ rect->height = wh;
+ }
+
+ out:
+ if (vroots)
+ XFree (vroots);
+
+ gdk_error_trap_pop_ignored ();
+}
+
+void
+_gdk_windowing_get_device_state (GdkDisplay *display,
+ GdkDevice *device,
+ GdkScreen **screen,
+ gint *x,
+ gint *y,
+ GdkModifierType *mask)
+{
+ GdkScreen *default_screen;
+
+ if (display->closed)
+ return;
+
+ default_screen = gdk_display_get_default_screen (display);
+
+
+ if (G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client))
+ {
+ GdkWindow *root;
+
+ GDK_DEVICE_GET_CLASS (device)->query_state (device,
+ gdk_screen_get_root_window (default_screen),
+ &root, NULL,
+ x, y,
+ NULL, NULL,
+ mask);
+ *screen = gdk_window_get_screen (root);
+ }
+ else
+ {
+ XSetWindowAttributes attributes;
+ Display *xdisplay;
+ Window xwindow, w, root, child;
+ int rootx, rooty, winx, winy;
+ unsigned int xmask;
+
+ /* FIXME: untrusted clients not multidevice-safe */
+
+ xdisplay = GDK_SCREEN_XDISPLAY (default_screen);
+ xwindow = GDK_SCREEN_XROOTWIN (default_screen);
+
+ w = XCreateWindow (xdisplay, xwindow, 0, 0, 1, 1, 0,
+ CopyFromParent, InputOnly, CopyFromParent,
+ 0, &attributes);
+ XQueryPointer (xdisplay, w,
+ &root, &child, &rootx, &rooty, &winx, &winy, &xmask);
+ XDestroyWindow (xdisplay, w);
+
+ if (root != None)
+ {
+ GdkWindow *gdk_root = gdk_window_lookup_for_display (display, root);
+ *screen = gdk_window_get_screen (gdk_root);
+ }
+
+ *x = rootx;
+ *y = rooty;
+ *mask = xmask;
+ }
+}
+
+static gboolean
+gdk_window_x11_get_device_state (GdkWindow *window,
+ GdkDevice *device,
+ gint *x,
+ gint *y,
+ GdkModifierType *mask)
+{
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
+ gboolean return_val;
+
+ g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), FALSE);
+
+ return_val = TRUE;
+
+ if (!GDK_WINDOW_DESTROYED (window))
+ {
+ if (G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client))
+ {
+ GdkWindow *child;
+
+ GDK_DEVICE_GET_CLASS (device)->query_state (device, window,
+ NULL, &child,
+ NULL, NULL,
+ x, y, mask);
+ return_val = (child != NULL);
+ }
+ else
+ {
+ GdkScreen *screen;
+ int originx, originy;
+ int rootx, rooty;
+ int winx = 0;
+ int winy = 0;
+ unsigned int xmask = 0;
+
+ _gdk_windowing_get_device_state (gdk_window_get_display (window), device,
+ &screen, &rootx, &rooty, &xmask);
+ gdk_window_get_origin (window, &originx, &originy);
+ winx = rootx - originx;
+ winy = rooty - originy;
+
+ *x = winx;
+ *y = winy;
+ *mask = xmask;
+ }
+ }
+
+ return return_val;
+}
+
+/**
+ * gdk_display_warp_pointer:
+ * @display: a #GdkDisplay
+ * @screen: the screen of @display to warp the pointer to
+ * @x: the x coordinate of the destination
+ * @y: the y coordinate of the destination
+ *
+ * Warps the pointer of @display to the point @x,@y on
+ * the screen @screen, unless the pointer is confined
+ * to a window by a grab, in which case it will be moved
+ * as far as allowed by the grab. Warping the pointer
+ * creates events as if the user had moved the mouse
+ * instantaneously to the destination.
+ *
+ * Note that the pointer should normally be under the
+ * control of the user. This function was added to cover
+ * some rare use cases like keyboard navigation support
+ * for the color picker in the #GtkColorSelectionDialog.
+ *
+ * Since: 2.8
+ *
+ * Deprecated: 3.0: Use gdk_display_warp_device() instead.
+ */
+void
+gdk_display_warp_pointer (GdkDisplay *display,
+ GdkScreen *screen,
+ gint x,
+ gint y)
+{
+ GdkDevice *device;
+
+ g_return_if_fail (GDK_IS_DISPLAY (display));
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+
+ device = display->core_pointer;
+ GDK_DEVICE_GET_CLASS (device)->warp (device, screen, x, y);
+}
+
+/**
+ * gdk_display_warp_device:
+ * @display: a #GdkDisplay.
+ * @device: a #GdkDevice.
+ * @screen: the screen of @display to warp @device to.
+ * @x: the X coordinate of the destination.
+ * @y: the Y coordinate of the destination.
+ *
+ * Warps @device in @display to the point @x,@y on
+ * the screen @screen, unless the device is confined
+ * to a window by a grab, in which case it will be moved
+ * as far as allowed by the grab. Warping the pointer
+ * creates events as if the user had moved the mouse
+ * instantaneously to the destination.
+ *
+ * Note that the pointer should normally be under the
+ * control of the user. This function was added to cover
+ * some rare use cases like keyboard navigation support
+ * for the color picker in the #GtkColorSelectionDialog.
+ *
+ * Since: 3.0
+ **/
+void
+gdk_display_warp_device (GdkDisplay *display,
+ GdkDevice *device,
+ GdkScreen *screen,
+ gint x,
+ gint y)
+{
+ g_return_if_fail (GDK_IS_DISPLAY (display));
+ g_return_if_fail (GDK_IS_DEVICE (device));
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+ g_return_if_fail (display == gdk_device_get_display (device));
+
+ GDK_DEVICE_GET_CLASS (device)->warp (device, screen, x, y);
+}
+
+GdkWindow*
+_gdk_windowing_window_at_device_position (GdkDisplay *display,
+ GdkDevice *device,
+ gint *win_x,
+ gint *win_y,
+ GdkModifierType *mask,
+ gboolean get_toplevel)
+{
+ GdkWindow *window;
+ GdkScreen *screen;
+
+ screen = gdk_display_get_default_screen (display);
+
+ /* This function really only works if the mouse pointer is held still
+ * during its operation. If it moves from one leaf window to another
+ * than we'll end up with inaccurate values for win_x, win_y
+ * and the result.
+ */
+ gdk_x11_display_grab (display);
+ if (G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client))
+ window = GDK_DEVICE_GET_CLASS (device)->window_at_position (device, win_x, win_y, mask, get_toplevel);
+ else
+ {
+ gint i, screens, width, height;
+ GList *toplevels, *list;
+ Window pointer_window, root, xwindow, child;
+ Window xwindow_last = 0;
+ Display *xdisplay;
+ int rootx = -1, rooty = -1;
+ int winx, winy;
+ unsigned int xmask;
+
+ /* FIXME: untrusted clients case not multidevice-safe */
+
+ xwindow = GDK_SCREEN_XROOTWIN (screen);
+ xdisplay = GDK_SCREEN_XDISPLAY (screen);
+
+ pointer_window = None;
+ screens = gdk_display_get_n_screens (display);
+ for (i = 0; i < screens; ++i) {
+ screen = gdk_display_get_screen (display, i);
+ toplevels = gdk_screen_get_toplevel_windows (screen);
+ for (list = toplevels; list != NULL; list = g_list_next (list)) {
+ window = GDK_WINDOW (list->data);
+ xwindow = GDK_WINDOW_XWINDOW (window);
+ gdk_error_trap_push ();
+ XQueryPointer (xdisplay, xwindow,
+ &root, &child, &rootx, &rooty, &winx, &winy, &xmask);
+ if (gdk_error_trap_pop ())
+ continue;
+ if (child != None)
+ {
+ pointer_window = child;
+ break;
+ }
+ gdk_window_get_geometry (window, NULL, NULL, &width, &height, NULL);
+ if (winx >= 0 && winy >= 0 && winx < width && winy < height)
+ {
+ /* A childless toplevel, or below another window? */
+ XSetWindowAttributes attributes;
+ Window w;
+
+ w = XCreateWindow (xdisplay, xwindow, winx, winy, 1, 1, 0,
+ CopyFromParent, InputOnly, CopyFromParent,
+ 0, &attributes);
+ XMapWindow (xdisplay, w);
+ XQueryPointer (xdisplay, xwindow,
+ &root, &child, &rootx, &rooty, &winx, &winy, &xmask);
+ XDestroyWindow (xdisplay, w);
+ if (child == w)
+ {
+ pointer_window = xwindow;
+ break;
+ }
+ }
+ }
+ g_list_free (toplevels);
+ if (pointer_window != None)
+ break;
+ }
+ xwindow = pointer_window;
+
+ while (xwindow)
+ {
+ xwindow_last = xwindow;
+ gdk_error_trap_push ();
+ XQueryPointer (xdisplay, xwindow,
+ &root, &xwindow, &rootx, &rooty, &winx, &winy, &xmask);
+ if (gdk_error_trap_pop ())
+ break;
+ if (get_toplevel && xwindow_last != root &&
+ (window = gdk_window_lookup_for_display (display, xwindow_last)) != NULL &&
+ GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
+ break;
+ }
+
+ window = gdk_window_lookup_for_display (display, xwindow_last);
+
+ *win_x = window ? winx : -1;
+ *win_y = window ? winy : -1;
+ if (mask)
+ *mask = xmask;
+ }
+
+ gdk_x11_display_ungrab (display);
+
+ return window;
+}
+
+static GdkEventMask
+gdk_window_x11_get_events (GdkWindow *window)
+{
+ XWindowAttributes attrs;
+ GdkEventMask event_mask;
+ GdkEventMask filtered;
+
+ if (GDK_WINDOW_DESTROYED (window))
+ return 0;
+ else
+ {
+ XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ &attrs);
+ event_mask = x_event_mask_to_gdk_event_mask (attrs.your_event_mask);
+ /* if property change was filtered out before, keep it filtered out */
+ filtered = GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK;
+ GDK_WINDOW_OBJECT (window)->event_mask = event_mask & ((GDK_WINDOW_OBJECT (window)->event_mask & filtered) | ~filtered);
+
+ return event_mask;
+ }
+}
+static void
+gdk_window_x11_set_events (GdkWindow *window,
+ GdkEventMask event_mask)
+{
+ long xevent_mask = 0;
+
+ if (!GDK_WINDOW_DESTROYED (window))
+ {
+ GdkDisplayX11 *display_x11;
+
+ if (GDK_WINDOW_XID (window) != GDK_WINDOW_XROOTWIN (window))
+ xevent_mask = StructureNotifyMask | PropertyChangeMask;
+
+ display_x11 = GDK_DISPLAY_X11 (gdk_window_get_display (window));
+ gdk_event_source_select_events ((GdkEventSource *) display_x11->event_source,
+ GDK_WINDOW_XWINDOW (window), event_mask,
+ xevent_mask);
+ }
+}
+
+static inline void
+do_shape_combine_region (GdkWindow *window,
+ const cairo_region_t *shape_region,
+ gint offset_x,
+ gint offset_y,
+ gint shape)
+{
+ if (GDK_WINDOW_DESTROYED (window))
+ return;
+
+ if (shape_region == NULL)
+ {
+ /* Use NULL mask to unset the shape */
+ if (shape == ShapeBounding
+ ? gdk_display_supports_shapes (GDK_WINDOW_DISPLAY (window))
+ : gdk_display_supports_input_shapes (GDK_WINDOW_DISPLAY (window)))
+ {
+ if (shape == ShapeBounding)
+ {
+ _gdk_x11_window_tmp_unset_parent_bg (window);
+ _gdk_x11_window_tmp_unset_bg (window, TRUE);
+ }
+ XShapeCombineMask (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ shape,
+ 0, 0,
+ None,
+ ShapeSet);
+ if (shape == ShapeBounding)
+ {
+ _gdk_x11_window_tmp_reset_parent_bg (window);
+ _gdk_x11_window_tmp_reset_bg (window, TRUE);
+ }
+ }
+ return;
+ }
+
+ if (shape == ShapeBounding
+ ? gdk_display_supports_shapes (GDK_WINDOW_DISPLAY (window))
+ : gdk_display_supports_input_shapes (GDK_WINDOW_DISPLAY (window)))
+ {
+ gint n_rects = 0;
+ XRectangle *xrects = NULL;
+
+ _gdk_region_get_xrectangles (shape_region,
+ 0, 0,
+ &xrects, &n_rects);
+
+ if (shape == ShapeBounding)
+ {
+ _gdk_x11_window_tmp_unset_parent_bg (window);
+ _gdk_x11_window_tmp_unset_bg (window, TRUE);
+ }
+ XShapeCombineRectangles (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ shape,
+ offset_x, offset_y,
+ xrects, n_rects,
+ ShapeSet,
+ YXBanded);
+
+ if (shape == ShapeBounding)
+ {
+ _gdk_x11_window_tmp_reset_parent_bg (window);
+ _gdk_x11_window_tmp_reset_bg (window, TRUE);
+ }
+
+ g_free (xrects);
+ }
+}
+
+static void
+gdk_window_x11_shape_combine_region (GdkWindow *window,
+ const cairo_region_t *shape_region,
+ gint offset_x,
+ gint offset_y)
+{
+ do_shape_combine_region (window, shape_region, offset_x, offset_y, ShapeBounding);
+}
+
+static void
+gdk_window_x11_input_shape_combine_region (GdkWindow *window,
+ const cairo_region_t *shape_region,
+ gint offset_x,
+ gint offset_y)
+{
+#ifdef ShapeInput
+ do_shape_combine_region (window, shape_region, offset_x, offset_y, ShapeInput);
+#endif
+}
+
+
+/**
+ * gdk_window_set_override_redirect:
+ * @window: a toplevel #GdkWindow
+ * @override_redirect: %TRUE if window should be override redirect
+ *
+ * An override redirect window is not under the control of the window manager.
+ * This means it won't have a titlebar, won't be minimizable, etc. - it will
+ * be entirely under the control of the application. The window manager
+ * can't see the override redirect window at all.
+ *
+ * Override redirect should only be used for short-lived temporary
+ * windows, such as popup menus. #GtkMenu uses an override redirect
+ * window in its implementation, for example.
+ *
+ **/
+void
+gdk_window_set_override_redirect (GdkWindow *window,
+ gboolean override_redirect)
+{
+ XSetWindowAttributes attr;
+
+ if (!GDK_WINDOW_DESTROYED (window) &&
+ WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ {
+ GdkWindowObject *private = (GdkWindowObject *)window;
+ GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (private->impl);
+
+ attr.override_redirect = (override_redirect? True : False);
+ XChangeWindowAttributes (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ CWOverrideRedirect,
+ &attr);
+
+ impl->override_redirect = attr.override_redirect;
+ }
+}
+
+/**
+ * gdk_window_set_accept_focus:
+ * @window: a toplevel #GdkWindow
+ * @accept_focus: %TRUE if the window should receive input focus
+ *
+ * Setting @accept_focus to %FALSE hints the desktop environment that the
+ * window doesn't want to receive input focus.
+ *
+ * On X, it is the responsibility of the window manager to interpret this
+ * hint. ICCCM-compliant window manager usually respect it.
+ *
+ * Since: 2.4
+ **/
+void
+gdk_window_set_accept_focus (GdkWindow *window,
+ gboolean accept_focus)
+{
+ GdkWindowObject *private;
+
+ private = (GdkWindowObject *)window;
+
+ accept_focus = accept_focus != FALSE;
+
+ if (private->accept_focus != accept_focus)
+ {
+ private->accept_focus = accept_focus;
+
+ if (!GDK_WINDOW_DESTROYED (window) &&
+ WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ update_wm_hints (window, FALSE);
+ }
+}
+
+/**
+ * gdk_window_set_focus_on_map:
+ * @window: a toplevel #GdkWindow
+ * @focus_on_map: %TRUE if the window should receive input focus when mapped
+ *
+ * Setting @focus_on_map to %FALSE hints the desktop environment that the
+ * window doesn't want to receive input focus when it is mapped.
+ * focus_on_map should be turned off for windows that aren't triggered
+ * interactively (such as popups from network activity).
+ *
+ * On X, it is the responsibility of the window manager to interpret
+ * this hint. Window managers following the freedesktop.org window
+ * manager extension specification should respect it.
+ *
+ * Since: 2.6
+ **/
+void
+gdk_window_set_focus_on_map (GdkWindow *window,
+ gboolean focus_on_map)
+{
+ GdkWindowObject *private;
+
+ private = (GdkWindowObject *)window;
+
+ focus_on_map = focus_on_map != FALSE;
+
+ if (private->focus_on_map != focus_on_map)
+ {
+ private->focus_on_map = focus_on_map;
+
+ if ((!GDK_WINDOW_DESTROYED (window)) &&
+ (!private->focus_on_map) &&
+ WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ gdk_x11_window_set_user_time (window, 0);
+ }
+}
+
+/**
+ * gdk_x11_window_set_user_time:
+ * @window: A toplevel #GdkWindow
+ * @timestamp: An XServer timestamp to which the property should be set
+ *
+ * The application can use this call to update the _NET_WM_USER_TIME
+ * property on a toplevel window. This property stores an Xserver
+ * time which represents the time of the last user input event
+ * received for this window. This property may be used by the window
+ * manager to alter the focus, stacking, and/or placement behavior of
+ * windows when they are mapped depending on whether the new window
+ * was created by a user action or is a "pop-up" window activated by a
+ * timer or some other event.
+ *
+ * Note that this property is automatically updated by GDK, so this
+ * function should only be used by applications which handle input
+ * events bypassing GDK.
+ *
+ * Since: 2.6
+ **/
+void
+gdk_x11_window_set_user_time (GdkWindow *window,
+ guint32 timestamp)
+{
+ GdkDisplay *display;
+ GdkDisplayX11 *display_x11;
+ GdkToplevelX11 *toplevel;
+ glong timestamp_long = (glong)timestamp;
+ Window xid;
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ display = gdk_window_get_display (window);
+ display_x11 = GDK_DISPLAY_X11 (display);
+ toplevel = _gdk_x11_window_get_toplevel (window);
+
+ if (!toplevel)
+ {
+ g_warning ("gdk_window_set_user_time called on non-toplevel\n");
+ return;
+ }
+
+ if (toplevel->focus_window != None &&
+ gdk_x11_screen_supports_net_wm_hint (GDK_WINDOW_SCREEN (window),
+ gdk_atom_intern_static_string ("_NET_WM_USER_TIME_WINDOW")))
+ xid = toplevel->focus_window;
+ else
+ xid = GDK_WINDOW_XID (window);
+
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xid,
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_USER_TIME"),
+ XA_CARDINAL, 32, PropModeReplace,
+ (guchar *)&timestamp_long, 1);
+
+ if (timestamp_long != GDK_CURRENT_TIME &&
+ (display_x11->user_time == GDK_CURRENT_TIME ||
+ XSERVER_TIME_IS_LATER (timestamp_long, display_x11->user_time)))
+ display_x11->user_time = timestamp_long;
+
+ if (toplevel)
+ toplevel->user_time = timestamp_long;
+}
+
+#define GDK_SELECTION_MAX_SIZE(display) \
+ MIN(262144, \
+ XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) == 0 \
+ ? XMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100 \
+ : XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100)
+
+static void
+gdk_window_update_icon (GdkWindow *window,
+ GList *icon_list)
+{
+ GdkToplevelX11 *toplevel;
+ GdkPixbuf *best_icon;
+ GList *tmp_list;
+ int best_size;
+
+ toplevel = _gdk_x11_window_get_toplevel (window);
+
+ if (toplevel->icon_pixmap != NULL)
+ {
+ cairo_surface_destroy (toplevel->icon_pixmap);
+ toplevel->icon_pixmap = NULL;
+ }
+
+ if (toplevel->icon_mask != NULL)
+ {
+ cairo_surface_destroy (toplevel->icon_mask);
+ toplevel->icon_mask = NULL;
+ }
+
+#define IDEAL_SIZE 48
+
+ best_size = G_MAXINT;
+ best_icon = NULL;
+ for (tmp_list = icon_list; tmp_list; tmp_list = tmp_list->next)
+ {
+ GdkPixbuf *pixbuf = tmp_list->data;
+ int this;
+
+ /* average width and height - if someone passes in a rectangular
+ * icon they deserve what they get.
+ */
+ this = gdk_pixbuf_get_width (pixbuf) + gdk_pixbuf_get_height (pixbuf);
+ this /= 2;
+
+ if (best_icon == NULL)
+ {
+ best_icon = pixbuf;
+ best_size = this;
+ }
+ else
+ {
+ /* icon is better if it's 32 pixels or larger, and closer to
+ * the ideal size than the current best.
+ */
+ if (this >= 32 &&
+ (ABS (best_size - IDEAL_SIZE) <
+ ABS (this - IDEAL_SIZE)))
+ {
+ best_icon = pixbuf;
+ best_size = this;
+ }
+ }
+ }
+
+ if (best_icon)
+ {
+ int width = gdk_pixbuf_get_width (best_icon);
+ int height = gdk_pixbuf_get_height (best_icon);
+ cairo_t *cr;
+
+ toplevel->icon_pixmap = gdk_x11_window_create_pixmap_surface (window,
+ width,
+ height);
+
+ cr = cairo_create (toplevel->icon_pixmap);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ gdk_cairo_set_source_pixbuf (cr, best_icon, 0, 0);
+ if (gdk_pixbuf_get_has_alpha (best_icon))
+ {
+ /* Saturate the image, so it has bilevel alpha */
+ cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
+ cairo_paint (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE);
+ cairo_paint (cr);
+ cairo_pop_group_to_source (cr);
+ }
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ if (gdk_pixbuf_get_has_alpha (best_icon))
+ {
+ toplevel->icon_mask = _gdk_x11_window_create_bitmap_surface (window,
+ width,
+ height);
+
+ cr = cairo_create (toplevel->icon_mask);
+ gdk_cairo_set_source_pixbuf (cr, best_icon, 0, 0);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+ }
+ }
+
+ update_wm_hints (window, FALSE);
+}
+
+/**
+ * gdk_window_set_icon_list:
+ * @window: The #GdkWindow toplevel window to set the icon of.
+ * @pixbufs: (transfer none) (element-type GdkPixbuf):
+ * A list of pixbufs, of different sizes.
+ *
+ * Sets a list of icons for the window. One of these will be used
+ * to represent the window when it has been iconified. The icon is
+ * usually shown in an icon box or some sort of task bar. Which icon
+ * size is shown depends on the window manager. The window manager
+ * can scale the icon but setting several size icons can give better
+ * image quality since the window manager may only need to scale the
+ * icon by a small amount or not at all.
+ *
+ **/
+void
+gdk_window_set_icon_list (GdkWindow *window,
+ GList *pixbufs)
+{
+ gulong *data;
+ guchar *pixels;
+ gulong *p;
+ gint size;
+ GList *l;
+ GdkPixbuf *pixbuf;
+ gint width, height, stride;
+ gint x, y;
+ gint n_channels;
+ GdkDisplay *display;
+ gint n;
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ display = gdk_window_get_display (window);
+
+ l = pixbufs;
+ size = 0;
+ n = 0;
+ while (l)
+ {
+ pixbuf = l->data;
+ g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+
+ /* silently ignore overlarge icons */
+ if (size + 2 + width * height > GDK_SELECTION_MAX_SIZE(display))
+ {
+ g_warning ("gdk_window_set_icon_list: icons too large");
+ break;
+ }
+
+ n++;
+ size += 2 + width * height;
+
+ l = g_list_next (l);
+ }
+
+ data = g_malloc (size * sizeof (gulong));
+
+ l = pixbufs;
+ p = data;
+ while (l && n > 0)
+ {
+ pixbuf = l->data;
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ stride = gdk_pixbuf_get_rowstride (pixbuf);
+ n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+
+ *p++ = width;
+ *p++ = height;
+
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+ for (y = 0; y < height; y++)
+ {
+ for (x = 0; x < width; x++)
+ {
+ guchar r, g, b, a;
+
+ r = pixels[y*stride + x*n_channels + 0];
+ g = pixels[y*stride + x*n_channels + 1];
+ b = pixels[y*stride + x*n_channels + 2];
+ if (n_channels >= 4)
+ a = pixels[y*stride + x*n_channels + 3];
+ else
+ a = 255;
+
+ *p++ = a << 24 | r << 16 | g << 8 | b ;
+ }
+ }
+
+ l = g_list_next (l);
+ n--;
+ }
+
+ if (size > 0)
+ {
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (window),
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_ICON"),
+ XA_CARDINAL, 32,
+ PropModeReplace,
+ (guchar*) data, size);
+ }
+ else
+ {
+ XDeleteProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (window),
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_ICON"));
+ }
+
+ g_free (data);
+
+ gdk_window_update_icon (window, pixbufs);
+}
+
+static gboolean
+gdk_window_icon_name_set (GdkWindow *window)
+{
+ return GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (window),
+ g_quark_from_static_string ("gdk-icon-name-set")));
+}
+
+/**
+ * gdk_window_set_icon_name:
+ * @window: a toplevel #GdkWindow
+ * @name: name of window while iconified (minimized)
+ *
+ * Windows may have a name used while minimized, distinct from the
+ * name they display in their titlebar. Most of the time this is a bad
+ * idea from a user interface standpoint. But you can set such a name
+ * with this function, if you like.
+ *
+ * After calling this with a non-%NULL @name, calls to gdk_window_set_title()
+ * will not update the icon title.
+ *
+ * Using %NULL for @name unsets the icon title; further calls to
+ * gdk_window_set_title() will again update the icon title as well.
+ **/
+void
+gdk_window_set_icon_name (GdkWindow *window,
+ const gchar *name)
+{
+ GdkDisplay *display;
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ display = gdk_window_get_display (window);
+
+ g_object_set_qdata (G_OBJECT (window), g_quark_from_static_string ("gdk-icon-name-set"),
+ GUINT_TO_POINTER (name != NULL));
+
+ if (name != NULL)
+ {
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (window),
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_ICON_NAME"),
+ gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8,
+ PropModeReplace, (guchar *)name, strlen (name));
+
+ set_text_property (display, GDK_WINDOW_XID (window),
+ gdk_x11_get_xatom_by_name_for_display (display, "WM_ICON_NAME"),
+ name);
+ }
+ else
+ {
+ XDeleteProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (window),
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_ICON_NAME"));
+ XDeleteProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (window),
+ gdk_x11_get_xatom_by_name_for_display (display, "WM_ICON_NAME"));
+ }
+}
+
+/**
+ * gdk_window_iconify:
+ * @window: a toplevel #GdkWindow
+ *
+ * Asks to iconify (minimize) @window. The window manager may choose
+ * to ignore the request, but normally will honor it. Using
+ * gtk_window_iconify() is preferred, if you have a #GtkWindow widget.
+ *
+ * This function only makes sense when @window is a toplevel window.
+ *
+ **/
+void
+gdk_window_iconify (GdkWindow *window)
+{
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ if (GDK_WINDOW_IS_MAPPED (window))
+ {
+ XIconifyWindow (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XWINDOW (window),
+ gdk_screen_get_number (GDK_WINDOW_SCREEN (window)));
+ }
+ else
+ {
+ /* Flip our client side flag, the real work happens on map. */
+ gdk_synthesize_window_state (window,
+ 0,
+ GDK_WINDOW_STATE_ICONIFIED);
+ }
+}
+
+/**
+ * gdk_window_deiconify:
+ * @window: a toplevel #GdkWindow
+ *
+ * Attempt to deiconify (unminimize) @window. On X11 the window manager may
+ * choose to ignore the request to deiconify. When using GTK+,
+ * use gtk_window_deiconify() instead of the #GdkWindow variant. Or better yet,
+ * you probably want to use gtk_window_present(), which raises the window, focuses it,
+ * unminimizes it, and puts it on the current desktop.
+ *
+ **/
+void
+gdk_window_deiconify (GdkWindow *window)
+{
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ if (GDK_WINDOW_IS_MAPPED (window))
+ {
+ gdk_window_show (window);
+ }
+ else
+ {
+ /* Flip our client side flag, the real work happens on map. */
+ gdk_synthesize_window_state (window,
+ GDK_WINDOW_STATE_ICONIFIED,
+ 0);
+ }
+}
+
+/**
+ * gdk_window_stick:
+ * @window: a toplevel #GdkWindow
+ *
+ * "Pins" a window such that it's on all workspaces and does not scroll
+ * with viewports, for window managers that have scrollable viewports.
+ * (When using #GtkWindow, gtk_window_stick() may be more useful.)
+ *
+ * On the X11 platform, this function depends on window manager
+ * support, so may have no effect with many window managers. However,
+ * GDK will do the best it can to convince the window manager to stick
+ * the window. For window managers that don't support this operation,
+ * there's nothing you can do to force it to happen.
+ *
+ **/
+void
+gdk_window_stick (GdkWindow *window)
+{
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ if (GDK_WINDOW_IS_MAPPED (window))
+ {
+ /* "stick" means stick to all desktops _and_ do not scroll with the
+ * viewport. i.e. glue to the monitor glass in all cases.
+ */
+
+ XClientMessageEvent xclient;
+
+ /* Request stick during viewport scroll */
+ gdk_wmspec_change_state (TRUE, window,
+ gdk_atom_intern_static_string ("_NET_WM_STATE_STICKY"),
+ GDK_NONE);
+
+ /* Request desktop 0xFFFFFFFF */
+ memset (&xclient, 0, sizeof (xclient));
+ xclient.type = ClientMessage;
+ xclient.window = GDK_WINDOW_XWINDOW (window);
+ xclient.display = GDK_WINDOW_XDISPLAY (window);
+ xclient.message_type = gdk_x11_get_xatom_by_name_for_display (GDK_WINDOW_DISPLAY (window),
+ "_NET_WM_DESKTOP");
+ xclient.format = 32;
+
+ xclient.data.l[0] = 0xFFFFFFFF;
+ xclient.data.l[1] = 0;
+ xclient.data.l[2] = 0;
+ xclient.data.l[3] = 0;
+ xclient.data.l[4] = 0;
+
+ XSendEvent (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XROOTWIN (window), False,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ (XEvent *)&xclient);
+ }
+ else
+ {
+ /* Flip our client side flag, the real work happens on map. */
+ gdk_synthesize_window_state (window,
+ 0,
+ GDK_WINDOW_STATE_STICKY);
+ }
+}
+
+/**
+ * gdk_window_unstick:
+ * @window: a toplevel #GdkWindow
+ *
+ * Reverse operation for gdk_window_stick(); see gdk_window_stick(),
+ * and gtk_window_unstick().
+ *
+ **/
+void
+gdk_window_unstick (GdkWindow *window)
+{
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ if (GDK_WINDOW_IS_MAPPED (window))
+ {
+ /* Request unstick from viewport */
+ gdk_wmspec_change_state (FALSE, window,
+ gdk_atom_intern_static_string ("_NET_WM_STATE_STICKY"),
+ GDK_NONE);
+
+ move_to_current_desktop (window);
+ }
+ else
+ {
+ /* Flip our client side flag, the real work happens on map. */
+ gdk_synthesize_window_state (window,
+ GDK_WINDOW_STATE_STICKY,
+ 0);
+
+ }
+}
+
+/**
+ * gdk_window_maximize:
+ * @window: a toplevel #GdkWindow
+ *
+ * Maximizes the window. If the window was already maximized, then
+ * this function does nothing.
+ *
+ * On X11, asks the window manager to maximize @window, if the window
+ * manager supports this operation. Not all window managers support
+ * this, and some deliberately ignore it or don't have a concept of
+ * "maximized"; so you can't rely on the maximization actually
+ * happening. But it will happen with most standard window managers,
+ * and GDK makes a best effort to get it to happen.
+ *
+ * On Windows, reliably maximizes the window.
+ *
+ **/
+void
+gdk_window_maximize (GdkWindow *window)
+{
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ if (GDK_WINDOW_IS_MAPPED (window))
+ gdk_wmspec_change_state (TRUE, window,
+ gdk_atom_intern_static_string ("_NET_WM_STATE_MAXIMIZED_VERT"),
+ gdk_atom_intern_static_string ("_NET_WM_STATE_MAXIMIZED_HORZ"));
+ else
+ gdk_synthesize_window_state (window,
+ 0,
+ GDK_WINDOW_STATE_MAXIMIZED);
+}
+
+/**
+ * gdk_window_unmaximize:
+ * @window: a toplevel #GdkWindow
+ *
+ * Unmaximizes the window. If the window wasn't maximized, then this
+ * function does nothing.
+ *
+ * On X11, asks the window manager to unmaximize @window, if the
+ * window manager supports this operation. Not all window managers
+ * support this, and some deliberately ignore it or don't have a
+ * concept of "maximized"; so you can't rely on the unmaximization
+ * actually happening. But it will happen with most standard window
+ * managers, and GDK makes a best effort to get it to happen.
+ *
+ * On Windows, reliably unmaximizes the window.
+ *
+ **/
+void
+gdk_window_unmaximize (GdkWindow *window)
+{
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ if (GDK_WINDOW_IS_MAPPED (window))
+ gdk_wmspec_change_state (FALSE, window,
+ gdk_atom_intern_static_string ("_NET_WM_STATE_MAXIMIZED_VERT"),
+ gdk_atom_intern_static_string ("_NET_WM_STATE_MAXIMIZED_HORZ"));
+ else
+ gdk_synthesize_window_state (window,
+ GDK_WINDOW_STATE_MAXIMIZED,
+ 0);
+}
+
+/**
+ * gdk_window_fullscreen:
+ * @window: a toplevel #GdkWindow
+ *
+ * Moves the window into fullscreen mode. This means the
+ * window covers the entire screen and is above any panels
+ * or task bars.
+ *
+ * If the window was already fullscreen, then this function does nothing.
+ *
+ * On X11, asks the window manager to put @window in a fullscreen
+ * state, if the window manager supports this operation. Not all
+ * window managers support this, and some deliberately ignore it or
+ * don't have a concept of "fullscreen"; so you can't rely on the
+ * fullscreenification actually happening. But it will happen with
+ * most standard window managers, and GDK makes a best effort to get
+ * it to happen.
+ *
+ * Since: 2.2
+ **/
+void
+gdk_window_fullscreen (GdkWindow *window)
+{
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ if (GDK_WINDOW_IS_MAPPED (window))
+ gdk_wmspec_change_state (TRUE, window,
+ gdk_atom_intern_static_string ("_NET_WM_STATE_FULLSCREEN"),
+ GDK_NONE);
+
+ else
+ gdk_synthesize_window_state (window,
+ 0,
+ GDK_WINDOW_STATE_FULLSCREEN);
+}
+
+/**
+ * gdk_window_unfullscreen:
+ * @window: a toplevel #GdkWindow
+ *
+ * Moves the window out of fullscreen mode. If the window was not
+ * fullscreen, does nothing.
+ *
+ * On X11, asks the window manager to move @window out of the fullscreen
+ * state, if the window manager supports this operation. Not all
+ * window managers support this, and some deliberately ignore it or
+ * don't have a concept of "fullscreen"; so you can't rely on the
+ * unfullscreenification actually happening. But it will happen with
+ * most standard window managers, and GDK makes a best effort to get
+ * it to happen.
+ *
+ * Since: 2.2
+ **/
+void
+gdk_window_unfullscreen (GdkWindow *window)
+{
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ if (GDK_WINDOW_IS_MAPPED (window))
+ gdk_wmspec_change_state (FALSE, window,
+ gdk_atom_intern_static_string ("_NET_WM_STATE_FULLSCREEN"),
+ GDK_NONE);
+
+ else
+ gdk_synthesize_window_state (window,
+ GDK_WINDOW_STATE_FULLSCREEN,
+ 0);
+}
+
+/**
+ * gdk_window_set_keep_above:
+ * @window: a toplevel #GdkWindow
+ * @setting: whether to keep @window above other windows
+ *
+ * Set if @window must be kept above other windows. If the
+ * window was already above, then this function does nothing.
+ *
+ * On X11, asks the window manager to keep @window above, if the window
+ * manager supports this operation. Not all window managers support
+ * this, and some deliberately ignore it or don't have a concept of
+ * "keep above"; so you can't rely on the window being kept above.
+ * But it will happen with most standard window managers,
+ * and GDK makes a best effort to get it to happen.
+ *
+ * Since: 2.4
+ **/
+void
+gdk_window_set_keep_above (GdkWindow *window,
+ gboolean setting)
+{
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ if (GDK_WINDOW_IS_MAPPED (window))
+ {
+ if (setting)
+ gdk_wmspec_change_state (FALSE, window,
+ gdk_atom_intern_static_string ("_NET_WM_STATE_BELOW"),
+ GDK_NONE);
+ gdk_wmspec_change_state (setting, window,
+ gdk_atom_intern_static_string ("_NET_WM_STATE_ABOVE"),
+ GDK_NONE);
+ }
+ else
+ gdk_synthesize_window_state (window,
+ setting ? GDK_WINDOW_STATE_BELOW : GDK_WINDOW_STATE_ABOVE,
+ setting ? GDK_WINDOW_STATE_ABOVE : 0);
+}
+
+/**
+ * gdk_window_set_keep_below:
+ * @window: a toplevel #GdkWindow
+ * @setting: whether to keep @window below other windows
+ *
+ * Set if @window must be kept below other windows. If the
+ * window was already below, then this function does nothing.
+ *
+ * On X11, asks the window manager to keep @window below, if the window
+ * manager supports this operation. Not all window managers support
+ * this, and some deliberately ignore it or don't have a concept of
+ * "keep below"; so you can't rely on the window being kept below.
+ * But it will happen with most standard window managers,
+ * and GDK makes a best effort to get it to happen.
+ *
+ * Since: 2.4
+ **/
+void
+gdk_window_set_keep_below (GdkWindow *window, gboolean setting)
+{
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ if (GDK_WINDOW_IS_MAPPED (window))
+ {
+ if (setting)
+ gdk_wmspec_change_state (FALSE, window,
+ gdk_atom_intern_static_string ("_NET_WM_STATE_ABOVE"),
+ GDK_NONE);
+ gdk_wmspec_change_state (setting, window,
+ gdk_atom_intern_static_string ("_NET_WM_STATE_BELOW"),
+ GDK_NONE);
+ }
+ else
+ gdk_synthesize_window_state (window,
+ setting ? GDK_WINDOW_STATE_ABOVE : GDK_WINDOW_STATE_BELOW,
+ setting ? GDK_WINDOW_STATE_BELOW : 0);
+}
+
+/**
+ * gdk_window_get_group:
+ * @window: a toplevel #GdkWindow
+ *
+ * Returns the group leader window for @window. See gdk_window_set_group().
+ *
+ * Return value: (transfer none): the group leader window for @window
+ *
+ * Since: 2.4
+ **/
+GdkWindow *
+gdk_window_get_group (GdkWindow *window)
+{
+ GdkToplevelX11 *toplevel;
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL (window))
+ return NULL;
+
+ toplevel = _gdk_x11_window_get_toplevel (window);
+
+ return toplevel->group_leader;
+}
+
+/**
+ * gdk_window_set_group:
+ * @window: a toplevel #GdkWindow
+ * @leader: group leader window, or %NULL to restore the default group leader window
+ *
+ * Sets the group leader window for @window. By default,
+ * GDK sets the group leader for all toplevel windows
+ * to a global window implicitly created by GDK. With this function
+ * you can override this default.
+ *
+ * The group leader window allows the window manager to distinguish
+ * all windows that belong to a single application. It may for example
+ * allow users to minimize/unminimize all windows belonging to an
+ * application at once. You should only set a non-default group window
+ * if your application pretends to be multiple applications.
+ **/
+void
+gdk_window_set_group (GdkWindow *window,
+ GdkWindow *leader)
+{
+ GdkToplevelX11 *toplevel;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+ g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD);
+ g_return_if_fail (leader == NULL || GDK_IS_WINDOW (leader));
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ (leader != NULL && GDK_WINDOW_DESTROYED (leader)) ||
+ !WINDOW_IS_TOPLEVEL (window))
+ return;
+
+ toplevel = _gdk_x11_window_get_toplevel (window);
+
+ if (leader == NULL)
+ leader = gdk_display_get_default_group (gdk_window_get_display (window));
+
+ if (toplevel->group_leader != leader)
+ {
+ if (toplevel->group_leader)
+ g_object_unref (toplevel->group_leader);
+ toplevel->group_leader = g_object_ref (leader);
+ (_gdk_x11_window_get_toplevel (leader))->is_leader = TRUE;
+ }
+
+ update_wm_hints (window, FALSE);
+}
+
+static MotifWmHints *
+gdk_window_get_mwm_hints (GdkWindow *window)
+{
+ GdkDisplay *display;
+ Atom hints_atom = None;
+ guchar *data;
+ Atom type;
+ gint format;
+ gulong nitems;
+ gulong bytes_after;
+
+ if (GDK_WINDOW_DESTROYED (window))
+ return NULL;
+
+ display = gdk_window_get_display (window);
+
+ hints_atom = gdk_x11_get_xatom_by_name_for_display (display, _XA_MOTIF_WM_HINTS);
+
+ XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window),
+ hints_atom, 0, sizeof (MotifWmHints)/sizeof (long),
+ False, AnyPropertyType, &type, &format, &nitems,
+ &bytes_after, &data);
+
+ if (type == None)
+ return NULL;
+
+ return (MotifWmHints *)data;
+}
+
+static void
+gdk_window_set_mwm_hints (GdkWindow *window,
+ MotifWmHints *new_hints)
+{
+ GdkDisplay *display;
+ Atom hints_atom = None;
+ guchar *data;
+ MotifWmHints *hints;
+ Atom type;
+ gint format;
+ gulong nitems;
+ gulong bytes_after;
+
+ if (GDK_WINDOW_DESTROYED (window))
+ return;
+
+ display = gdk_window_get_display (window);
+
+ hints_atom = gdk_x11_get_xatom_by_name_for_display (display, _XA_MOTIF_WM_HINTS);
+
+ XGetWindowProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window),
+ hints_atom, 0, sizeof (MotifWmHints)/sizeof (long),
+ False, AnyPropertyType, &type, &format, &nitems,
+ &bytes_after, &data);
+
+ if (type == None)
+ hints = new_hints;
+ else
+ {
+ hints = (MotifWmHints *)data;
+
+ if (new_hints->flags & MWM_HINTS_FUNCTIONS)
+ {
+ hints->flags |= MWM_HINTS_FUNCTIONS;
+ hints->functions = new_hints->functions;
+ }
+ if (new_hints->flags & MWM_HINTS_DECORATIONS)
+ {
+ hints->flags |= MWM_HINTS_DECORATIONS;
+ hints->decorations = new_hints->decorations;
+ }
+ }
+
+ XChangeProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window),
+ hints_atom, hints_atom, 32, PropModeReplace,
+ (guchar *)hints, sizeof (MotifWmHints)/sizeof (long));
+
+ if (hints != new_hints)
+ XFree (hints);
+}
+
+/**
+ * gdk_window_set_decorations:
+ * @window: a toplevel #GdkWindow
+ * @decorations: decoration hint mask
+ *
+ * "Decorations" are the features the window manager adds to a toplevel #GdkWindow.
+ * This function sets the traditional Motif window manager hints that tell the
+ * window manager which decorations you would like your window to have.
+ * Usually you should use gtk_window_set_decorated() on a #GtkWindow instead of
+ * using the GDK function directly.
+ *
+ * The @decorations argument is the logical OR of the fields in
+ * the #GdkWMDecoration enumeration. If #GDK_DECOR_ALL is included in the
+ * mask, the other bits indicate which decorations should be turned off.
+ * If #GDK_DECOR_ALL is not included, then the other bits indicate
+ * which decorations should be turned on.
+ *
+ * Most window managers honor a decorations hint of 0 to disable all decorations,
+ * but very few honor all possible combinations of bits.
+ *
+ **/
+void
+gdk_window_set_decorations (GdkWindow *window,
+ GdkWMDecoration decorations)
+{
+ MotifWmHints hints;
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ /* initialize to zero to avoid writing uninitialized data to socket */
+ memset(&hints, 0, sizeof(hints));
+ hints.flags = MWM_HINTS_DECORATIONS;
+ hints.decorations = decorations;
+
+ gdk_window_set_mwm_hints (window, &hints);
+}
+
+/**
+ * gdk_window_get_decorations:
+ * @window: The toplevel #GdkWindow to get the decorations from
+ * @decorations: The window decorations will be written here
+ *
+ * Returns the decorations set on the GdkWindow with
+ * gdk_window_set_decorations().
+ *
+ * Returns: %TRUE if the window has decorations set, %FALSE otherwise.
+ **/
+gboolean
+gdk_window_get_decorations(GdkWindow *window,
+ GdkWMDecoration *decorations)
+{
+ MotifWmHints *hints;
+ gboolean result = FALSE;
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return FALSE;
+
+ hints = gdk_window_get_mwm_hints (window);
+
+ if (hints)
+ {
+ if (hints->flags & MWM_HINTS_DECORATIONS)
+ {
+ if (decorations)
+ *decorations = hints->decorations;
+ result = TRUE;
+ }
+
+ XFree (hints);
+ }
+
+ return result;
+}
+
+/**
+ * gdk_window_set_functions:
+ * @window: a toplevel #GdkWindow
+ * @functions: bitmask of operations to allow on @window
+ *
+ * Sets hints about the window management functions to make available
+ * via buttons on the window frame.
+ *
+ * On the X backend, this function sets the traditional Motif window
+ * manager hint for this purpose. However, few window managers do
+ * anything reliable or interesting with this hint. Many ignore it
+ * entirely.
+ *
+ * The @functions argument is the logical OR of values from the
+ * #GdkWMFunction enumeration. If the bitmask includes #GDK_FUNC_ALL,
+ * then the other bits indicate which functions to disable; if
+ * it doesn't include #GDK_FUNC_ALL, it indicates which functions to
+ * enable.
+ *
+ **/
+void
+gdk_window_set_functions (GdkWindow *window,
+ GdkWMFunction functions)
+{
+ MotifWmHints hints;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ /* initialize to zero to avoid writing uninitialized data to socket */
+ memset(&hints, 0, sizeof(hints));
+ hints.flags = MWM_HINTS_FUNCTIONS;
+ hints.functions = functions;
+
+ gdk_window_set_mwm_hints (window, &hints);
+}
+
+cairo_region_t *
+_xwindow_get_shape (Display *xdisplay,
+ Window window,
+ gint shape_type)
+{
+ cairo_region_t *shape;
+ GdkRectangle *rl;
+ XRectangle *xrl;
+ gint rn, ord, i;
+
+ shape = NULL;
+ rn = 0;
+
+ xrl = XShapeGetRectangles (xdisplay,
+ window,
+ shape_type, &rn, &ord);
+
+ if (xrl == NULL || rn == 0)
+ return cairo_region_create (); /* Empty */
+
+ if (ord != YXBanded)
+ {
+ /* This really shouldn't happen with any xserver, as they
+ generally convert regions to YXBanded internally */
+ g_warning ("non YXBanded shape masks not supported");
+ XFree (xrl);
+ return NULL;
+ }
+
+ rl = g_new (GdkRectangle, rn);
+ for (i = 0; i < rn; i++)
+ {
+ rl[i].x = xrl[i].x;
+ rl[i].y = xrl[i].y;
+ rl[i].width = xrl[i].width;
+ rl[i].height = xrl[i].height;
+ }
+ XFree (xrl);
+
+ shape = cairo_region_create_rectangles (rl, rn);
+ g_free (rl);
+
+ return shape;
+}
+
+
+cairo_region_t *
+_gdk_windowing_window_get_shape (GdkWindow *window)
+{
+ if (!GDK_WINDOW_DESTROYED (window) &&
+ gdk_display_supports_shapes (GDK_WINDOW_DISPLAY (window)))
+ return _xwindow_get_shape (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window), ShapeBounding);
+
+ return NULL;
+}
+
+cairo_region_t *
+_gdk_windowing_window_get_input_shape (GdkWindow *window)
+{
+#if defined(ShapeInput)
+ if (!GDK_WINDOW_DESTROYED (window) &&
+ gdk_display_supports_shapes (GDK_WINDOW_DISPLAY (window)))
+ return _xwindow_get_shape (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ ShapeInput);
+#endif
+
+ return NULL;
+}
+
+static void
+gdk_window_set_static_bit_gravity (GdkWindow *window,
+ gboolean on)
+{
+ XSetWindowAttributes xattributes;
+ GdkWindowObject *private;
+ guint xattributes_mask = 0;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ private = GDK_WINDOW_OBJECT (window);
+ if (private->input_only)
+ return;
+
+ xattributes.bit_gravity = StaticGravity;
+ xattributes_mask |= CWBitGravity;
+ xattributes.bit_gravity = on ? StaticGravity : ForgetGravity;
+ XChangeWindowAttributes (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ CWBitGravity, &xattributes);
+}
+
+static void
+gdk_window_set_static_win_gravity (GdkWindow *window,
+ gboolean on)
+{
+ XSetWindowAttributes xattributes;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ xattributes.win_gravity = on ? StaticGravity : NorthWestGravity;
+
+ XChangeWindowAttributes (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ CWWinGravity, &xattributes);
+}
+
+static gboolean
+gdk_window_x11_set_static_gravities (GdkWindow *window,
+ gboolean use_static)
+{
+ GdkWindowObject *private = (GdkWindowObject *)window;
+ GList *tmp_list;
+
+ if (!use_static == !private->guffaw_gravity)
+ return TRUE;
+
+ private->guffaw_gravity = use_static;
+
+ if (!GDK_WINDOW_DESTROYED (window))
+ {
+ gdk_window_set_static_bit_gravity (window, use_static);
+
+ tmp_list = private->children;
+ while (tmp_list)
+ {
+ gdk_window_set_static_win_gravity (tmp_list->data, use_static);
+
+ tmp_list = tmp_list->next;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+wmspec_moveresize (GdkWindow *window,
+ gint direction,
+ gint root_x,
+ gint root_y,
+ guint32 timestamp)
+{
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
+
+ XClientMessageEvent xclient;
+
+ /* Release passive grab */
+ gdk_display_pointer_ungrab (display, timestamp);
+
+ memset (&xclient, 0, sizeof (xclient));
+ xclient.type = ClientMessage;
+ xclient.window = GDK_WINDOW_XID (window);
+ xclient.message_type =
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_MOVERESIZE");
+ xclient.format = 32;
+ xclient.data.l[0] = root_x;
+ xclient.data.l[1] = root_y;
+ xclient.data.l[2] = direction;
+ xclient.data.l[3] = 0;
+ xclient.data.l[4] = 0;
+
+ XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XROOTWIN (window), False,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ (XEvent *)&xclient);
+}
+
+typedef struct _MoveResizeData MoveResizeData;
+
+struct _MoveResizeData
+{
+ GdkDisplay *display;
+
+ GdkWindow *moveresize_window;
+ GdkWindow *moveresize_emulation_window;
+ gboolean is_resize;
+ GdkWindowEdge resize_edge;
+ gint moveresize_button;
+ gint moveresize_x;
+ gint moveresize_y;
+ gint moveresize_orig_x;
+ gint moveresize_orig_y;
+ gint moveresize_orig_width;
+ gint moveresize_orig_height;
+ GdkWindowHints moveresize_geom_mask;
+ GdkGeometry moveresize_geometry;
+ Time moveresize_process_time;
+ XEvent *moveresize_pending_event;
+};
+
+/* From the WM spec */
+#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
+#define _NET_WM_MOVERESIZE_SIZE_TOP 1
+#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
+#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
+#define _NET_WM_MOVERESIZE_SIZE_LEFT 7
+#define _NET_WM_MOVERESIZE_MOVE 8
+
+static void
+wmspec_resize_drag (GdkWindow *window,
+ GdkWindowEdge edge,
+ gint button,
+ gint root_x,
+ gint root_y,
+ guint32 timestamp)
+{
+ gint direction;
+
+ /* Let the compiler turn a switch into a table, instead
+ * of doing the table manually, this way is easier to verify.
+ */
+ switch (edge)
+ {
+ case GDK_WINDOW_EDGE_NORTH_WEST:
+ direction = _NET_WM_MOVERESIZE_SIZE_TOPLEFT;
+ break;
+
+ case GDK_WINDOW_EDGE_NORTH:
+ direction = _NET_WM_MOVERESIZE_SIZE_TOP;
+ break;
+
+ case GDK_WINDOW_EDGE_NORTH_EAST:
+ direction = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT;
+ break;
+
+ case GDK_WINDOW_EDGE_WEST:
+ direction = _NET_WM_MOVERESIZE_SIZE_LEFT;
+ break;
+
+ case GDK_WINDOW_EDGE_EAST:
+ direction = _NET_WM_MOVERESIZE_SIZE_RIGHT;
+ break;
+
+ case GDK_WINDOW_EDGE_SOUTH_WEST:
+ direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT;
+ break;
+
+ case GDK_WINDOW_EDGE_SOUTH:
+ direction = _NET_WM_MOVERESIZE_SIZE_BOTTOM;
+ break;
+
+ case GDK_WINDOW_EDGE_SOUTH_EAST:
+ direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT;
+ break;
+
+ default:
+ g_warning ("gdk_window_begin_resize_drag: bad resize edge %d!",
+ edge);
+ return;
+ }
+
+ wmspec_moveresize (window, direction, root_x, root_y, timestamp);
+}
+
+static MoveResizeData *
+get_move_resize_data (GdkDisplay *display,
+ gboolean create)
+{
+ MoveResizeData *mv_resize;
+ static GQuark move_resize_quark = 0;
+
+ if (!move_resize_quark)
+ move_resize_quark = g_quark_from_static_string ("gdk-window-moveresize");
+
+ mv_resize = g_object_get_qdata (G_OBJECT (display), move_resize_quark);
+
+ if (!mv_resize && create)
+ {
+ mv_resize = g_new0 (MoveResizeData, 1);
+ mv_resize->display = display;
+
+ g_object_set_qdata (G_OBJECT (display), move_resize_quark, mv_resize);
+ }
+
+ return mv_resize;
+}
+
+static void
+update_pos (MoveResizeData *mv_resize,
+ gint new_root_x,
+ gint new_root_y)
+{
+ gint dx, dy;
+
+ dx = new_root_x - mv_resize->moveresize_x;
+ dy = new_root_y - mv_resize->moveresize_y;
+
+ if (mv_resize->is_resize)
+ {
+ gint x, y, w, h;
+
+ x = mv_resize->moveresize_orig_x;
+ y = mv_resize->moveresize_orig_y;
+
+ w = mv_resize->moveresize_orig_width;
+ h = mv_resize->moveresize_orig_height;
+
+ switch (mv_resize->resize_edge)
+ {
+ case GDK_WINDOW_EDGE_NORTH_WEST:
+ x += dx;
+ y += dy;
+ w -= dx;
+ h -= dy;
+ break;
+ case GDK_WINDOW_EDGE_NORTH:
+ y += dy;
+ h -= dy;
+ break;
+ case GDK_WINDOW_EDGE_NORTH_EAST:
+ y += dy;
+ h -= dy;
+ w += dx;
+ break;
+ case GDK_WINDOW_EDGE_SOUTH_WEST:
+ h += dy;
+ x += dx;
+ w -= dx;
+ break;
+ case GDK_WINDOW_EDGE_SOUTH_EAST:
+ w += dx;
+ h += dy;
+ break;
+ case GDK_WINDOW_EDGE_SOUTH:
+ h += dy;
+ break;
+ case GDK_WINDOW_EDGE_EAST:
+ w += dx;
+ break;
+ case GDK_WINDOW_EDGE_WEST:
+ x += dx;
+ w -= dx;
+ break;
+ }
+
+ x = MAX (x, 0);
+ y = MAX (y, 0);
+ w = MAX (w, 1);
+ h = MAX (h, 1);
+
+ if (mv_resize->moveresize_geom_mask)
+ {
+ gdk_window_constrain_size (&mv_resize->moveresize_geometry,
+ mv_resize->moveresize_geom_mask,
+ w, h, &w, &h);
+ }
+
+ gdk_window_move_resize (mv_resize->moveresize_window, x, y, w, h);
+ }
+ else
+ {
+ gint x, y;
+
+ x = mv_resize->moveresize_orig_x + dx;
+ y = mv_resize->moveresize_orig_y + dy;
+
+ gdk_window_move (mv_resize->moveresize_window, x, y);
+ }
+}
+
+static void
+finish_drag (MoveResizeData *mv_resize)
+{
+ gdk_window_destroy (mv_resize->moveresize_emulation_window);
+ mv_resize->moveresize_emulation_window = NULL;
+ g_object_unref (mv_resize->moveresize_window);
+ mv_resize->moveresize_window = NULL;
+
+ if (mv_resize->moveresize_pending_event)
+ {
+ g_free (mv_resize->moveresize_pending_event);
+ mv_resize->moveresize_pending_event = NULL;
+ }
+}
+
+static int
+lookahead_motion_predicate (Display *xdisplay,
+ XEvent *event,
+ XPointer arg)
+{
+ gboolean *seen_release = (gboolean *)arg;
+ GdkDisplay *display = gdk_x11_lookup_xdisplay (xdisplay);
+ MoveResizeData *mv_resize = get_move_resize_data (display, FALSE);
+
+ if (*seen_release)
+ return False;
+
+ switch (event->xany.type)
+ {
+ case ButtonRelease:
+ *seen_release = TRUE;
+ break;
+ case MotionNotify:
+ mv_resize->moveresize_process_time = event->xmotion.time;
+ break;
+ default:
+ break;
+ }
+
+ return False;
+}
+
+static gboolean
+moveresize_lookahead (MoveResizeData *mv_resize,
+ XEvent *event)
+{
+ XEvent tmp_event;
+ gboolean seen_release = FALSE;
+
+ if (mv_resize->moveresize_process_time)
+ {
+ if (event->xmotion.time == mv_resize->moveresize_process_time)
+ {
+ mv_resize->moveresize_process_time = 0;
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+
+ XCheckIfEvent (event->xany.display, &tmp_event,
+ lookahead_motion_predicate, (XPointer) & seen_release);
+
+ return mv_resize->moveresize_process_time == 0;
+}
+
+gboolean
+_gdk_moveresize_handle_event (XEvent *event)
+{
+ guint button_mask = 0;
+ GdkWindowObject *window_private;
+ GdkDisplay *display = gdk_x11_lookup_xdisplay (event->xany.display);
+ MoveResizeData *mv_resize = get_move_resize_data (display, FALSE);
+
+ if (!mv_resize || !mv_resize->moveresize_window)
+ return FALSE;
+
+ window_private = (GdkWindowObject *) mv_resize->moveresize_window;
+
+ button_mask = GDK_BUTTON1_MASK << (mv_resize->moveresize_button - 1);
+
+ switch (event->xany.type)
+ {
+ case MotionNotify:
+ if (window_private->resize_count > 0)
+ {
+ if (mv_resize->moveresize_pending_event)
+ *mv_resize->moveresize_pending_event = *event;
+ else
+ mv_resize->moveresize_pending_event =
+ g_memdup (event, sizeof (XEvent));
+
+ break;
+ }
+ if (!moveresize_lookahead (mv_resize, event))
+ break;
+
+ update_pos (mv_resize,
+ event->xmotion.x_root,
+ event->xmotion.y_root);
+
+ /* This should never be triggered in normal cases, but in the
+ * case where the drag started without an implicit grab being
+ * in effect, we could miss the release if it occurs before
+ * we grab the pointer; this ensures that we will never
+ * get a permanently stuck grab.
+ */
+ if ((event->xmotion.state & button_mask) == 0)
+ finish_drag (mv_resize);
+ break;
+
+ case ButtonRelease:
+ update_pos (mv_resize,
+ event->xbutton.x_root,
+ event->xbutton.y_root);
+
+ if (event->xbutton.button == mv_resize->moveresize_button)
+ finish_drag (mv_resize);
+ break;
+ }
+ return TRUE;
+}
+
+gboolean
+_gdk_moveresize_configure_done (GdkDisplay *display,
+ GdkWindow *window)
+{
+ XEvent *tmp_event;
+ MoveResizeData *mv_resize = get_move_resize_data (display, FALSE);
+
+ if (!mv_resize || window != mv_resize->moveresize_window)
+ return FALSE;
+
+ if (mv_resize->moveresize_pending_event)
+ {
+ tmp_event = mv_resize->moveresize_pending_event;
+ mv_resize->moveresize_pending_event = NULL;
+ _gdk_moveresize_handle_event (tmp_event);
+ g_free (tmp_event);
+ }
+
+ return TRUE;
+}
+
+static void
+create_moveresize_window (MoveResizeData *mv_resize,
+ guint32 timestamp)
+{
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+ GdkGrabStatus status;
+
+ g_assert (mv_resize->moveresize_emulation_window == NULL);
+
+ attributes.x = -100;
+ attributes.y = -100;
+ attributes.width = 10;
+ attributes.height = 10;
+ attributes.window_type = GDK_WINDOW_TEMP;
+ attributes.wclass = GDK_INPUT_ONLY;
+ attributes.override_redirect = TRUE;
+ attributes.event_mask = 0;
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
+
+ mv_resize->moveresize_emulation_window =
+ gdk_window_new (gdk_screen_get_root_window (gdk_display_get_default_screen (mv_resize->display)),
+ &attributes,
+ attributes_mask);
+
+ gdk_window_show (mv_resize->moveresize_emulation_window);
+
+ status = gdk_pointer_grab (mv_resize->moveresize_emulation_window,
+ FALSE,
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK,
+ NULL,
+ NULL,
+ timestamp);
+
+ if (status != GDK_GRAB_SUCCESS)
+ {
+ /* If this fails, some other client has grabbed the window
+ * already.
+ */
+ finish_drag (mv_resize);
+ }
+
+ mv_resize->moveresize_process_time = 0;
+}
+
+/*
+ Calculate mv_resize->moveresize_orig_x and mv_resize->moveresize_orig_y
+ so that calling XMoveWindow with these coordinates will not move the
+ window.
+ Note that this depends on the WM to implement ICCCM-compliant reference
+ point handling.
+*/
+static void
+calculate_unmoving_origin (MoveResizeData *mv_resize)
+{
+ GdkRectangle rect;
+ gint width, height;
+
+ if (mv_resize->moveresize_geom_mask & GDK_HINT_WIN_GRAVITY &&
+ mv_resize->moveresize_geometry.win_gravity == GDK_GRAVITY_STATIC)
+ {
+ gdk_window_get_origin (mv_resize->moveresize_window,
+ &mv_resize->moveresize_orig_x,
+ &mv_resize->moveresize_orig_y);
+ }
+ else
+ {
+ gdk_window_get_frame_extents (mv_resize->moveresize_window, &rect);
+ gdk_window_get_geometry (mv_resize->moveresize_window,
+ NULL, NULL, &width, &height, NULL);
+
+ switch (mv_resize->moveresize_geometry.win_gravity)
+ {
+ case GDK_GRAVITY_NORTH_WEST:
+ mv_resize->moveresize_orig_x = rect.x;
+ mv_resize->moveresize_orig_y = rect.y;
+ break;
+ case GDK_GRAVITY_NORTH:
+ mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2;
+ mv_resize->moveresize_orig_y = rect.y;
+ break;
+ case GDK_GRAVITY_NORTH_EAST:
+ mv_resize->moveresize_orig_x = rect.x + rect.width - width;
+ mv_resize->moveresize_orig_y = rect.y;
+ break;
+ case GDK_GRAVITY_WEST:
+ mv_resize->moveresize_orig_x = rect.x;
+ mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2;
+ break;
+ case GDK_GRAVITY_CENTER:
+ mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2;
+ mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2;
+ break;
+ case GDK_GRAVITY_EAST:
+ mv_resize->moveresize_orig_x = rect.x + rect.width - width;
+ mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2;
+ break;
+ case GDK_GRAVITY_SOUTH_WEST:
+ mv_resize->moveresize_orig_x = rect.x;
+ mv_resize->moveresize_orig_y = rect.y + rect.height - height;
+ break;
+ case GDK_GRAVITY_SOUTH:
+ mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2;
+ mv_resize->moveresize_orig_y = rect.y + rect.height - height;
+ break;
+ case GDK_GRAVITY_SOUTH_EAST:
+ mv_resize->moveresize_orig_x = rect.x + rect.width - width;
+ mv_resize->moveresize_orig_y = rect.y + rect.height - height;
+ break;
+ default:
+ mv_resize->moveresize_orig_x = rect.x;
+ mv_resize->moveresize_orig_y = rect.y;
+ break;
+ }
+ }
+}
+
+static void
+emulate_resize_drag (GdkWindow *window,
+ GdkWindowEdge edge,
+ gint button,
+ gint root_x,
+ gint root_y,
+ guint32 timestamp)
+{
+ MoveResizeData *mv_resize = get_move_resize_data (GDK_WINDOW_DISPLAY (window), TRUE);
+
+ mv_resize->is_resize = TRUE;
+ mv_resize->moveresize_button = button;
+ mv_resize->resize_edge = edge;
+ mv_resize->moveresize_x = root_x;
+ mv_resize->moveresize_y = root_y;
+ mv_resize->moveresize_window = g_object_ref (window);
+
+ mv_resize->moveresize_orig_width = gdk_window_get_width (window);
+ mv_resize->moveresize_orig_height = gdk_window_get_height (window);
+
+ mv_resize->moveresize_geom_mask = 0;
+ gdk_window_get_geometry_hints (window,
+ &mv_resize->moveresize_geometry,
+ &mv_resize->moveresize_geom_mask);
+
+ calculate_unmoving_origin (mv_resize);
+
+ create_moveresize_window (mv_resize, timestamp);
+}
+
+static void
+emulate_move_drag (GdkWindow *window,
+ gint button,
+ gint root_x,
+ gint root_y,
+ guint32 timestamp)
+{
+ MoveResizeData *mv_resize = get_move_resize_data (GDK_WINDOW_DISPLAY (window), TRUE);
+
+ mv_resize->is_resize = FALSE;
+ mv_resize->moveresize_button = button;
+ mv_resize->moveresize_x = root_x;
+ mv_resize->moveresize_y = root_y;
+
+ mv_resize->moveresize_window = g_object_ref (window);
+
+ calculate_unmoving_origin (mv_resize);
+
+ create_moveresize_window (mv_resize, timestamp);
+}
+
+/**
+ * gdk_window_begin_resize_drag:
+ * @window: a toplevel #GdkWindow
+ * @edge: the edge or corner from which the drag is started
+ * @button: the button being used to drag
+ * @root_x: root window X coordinate of mouse click that began the drag
+ * @root_y: root window Y coordinate of mouse click that began the drag
+ * @timestamp: timestamp of mouse click that began the drag (use gdk_event_get_time())
+ *
+ * Begins a window resize operation (for a toplevel window).
+ * You might use this function to implement a "window resize grip," for
+ * example; in fact #GtkStatusbar uses it. The function works best
+ * with window managers that support the <ulink url="http://www.freedesktop.org/Standards/wm-spec">Extended Window Manager Hints</ulink>, but has a
+ * fallback implementation for other window managers.
+ *
+ **/
+void
+gdk_window_begin_resize_drag (GdkWindow *window,
+ GdkWindowEdge edge,
+ gint button,
+ gint root_x,
+ gint root_y,
+ guint32 timestamp)
+{
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+
+ if (gdk_x11_screen_supports_net_wm_hint (GDK_WINDOW_SCREEN (window),
+ gdk_atom_intern_static_string ("_NET_WM_MOVERESIZE")))
+ wmspec_resize_drag (window, edge, button, root_x, root_y, timestamp);
+ else
+ emulate_resize_drag (window, edge, button, root_x, root_y, timestamp);
+}
+
+/**
+ * gdk_window_begin_move_drag:
+ * @window: a toplevel #GdkWindow
+ * @button: the button being used to drag
+ * @root_x: root window X coordinate of mouse click that began the drag
+ * @root_y: root window Y coordinate of mouse click that began the drag
+ * @timestamp: timestamp of mouse click that began the drag
+ *
+ * Begins a window move operation (for a toplevel window). You might
+ * use this function to implement a "window move grip," for
+ * example. The function works best with window managers that support
+ * the <ulink url="http://www.freedesktop.org/Standards/wm-spec">Extended
+ * Window Manager Hints</ulink>, but has a fallback implementation for
+ * other window managers.
+ *
+ **/
+void
+gdk_window_begin_move_drag (GdkWindow *window,
+ gint button,
+ gint root_x,
+ gint root_y,
+ guint32 timestamp)
+{
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL (window))
+ return;
+
+ if (gdk_x11_screen_supports_net_wm_hint (GDK_WINDOW_SCREEN (window),
+ gdk_atom_intern_static_string ("_NET_WM_MOVERESIZE")))
+ wmspec_moveresize (window, _NET_WM_MOVERESIZE_MOVE, root_x, root_y,
+ timestamp);
+ else
+ emulate_move_drag (window, button, root_x, root_y, timestamp);
+}
+
+/**
+ * gdk_window_enable_synchronized_configure:
+ * @window: a toplevel #GdkWindow
+ *
+ * Indicates that the application will cooperate with the window
+ * system in synchronizing the window repaint with the window
+ * manager during resizing operations. After an application calls
+ * this function, it must call gdk_window_configure_finished() every
+ * time it has finished all processing associated with a set of
+ * Configure events. Toplevel GTK+ windows automatically use this
+ * protocol.
+ *
+ * On X, calling this function makes @window participate in the
+ * _NET_WM_SYNC_REQUEST window manager protocol.
+ *
+ * Since: 2.6
+ **/
+void
+gdk_window_enable_synchronized_configure (GdkWindow *window)
+{
+ GdkWindowObject *private = (GdkWindowObject *)window;
+ GdkWindowImplX11 *impl;
+
+ if (!GDK_IS_WINDOW_IMPL_X11 (private->impl))
+ return;
+
+ impl = GDK_WINDOW_IMPL_X11 (private->impl);
+
+ if (!impl->use_synchronized_configure)
+ {
+ /* This basically means you want to do fancy X specific stuff, so
+ ensure we have a native window */
+ gdk_window_ensure_native (window);
+
+ impl->use_synchronized_configure = TRUE;
+ ensure_sync_counter (window);
+ }
+}
+
+/**
+ * gdk_window_configure_finished:
+ * @window: a toplevel #GdkWindow
+ *
+ * Signal to the window system that the application has finished
+ * handling Configure events it has received. Window Managers can
+ * use this to better synchronize the frame repaint with the
+ * application. GTK+ applications will automatically call this
+ * function when appropriate.
+ *
+ * This function can only be called if gdk_window_enable_synchronized_configure()
+ * was called previously.
+ *
+ * Since: 2.6
+ **/
+void
+gdk_window_configure_finished (GdkWindow *window)
+{
+ GdkWindowImplX11 *impl;
+
+ if (!WINDOW_IS_TOPLEVEL (window))
+ return;
+
+ impl = GDK_WINDOW_IMPL_X11 (((GdkWindowObject *)window)->impl);
+ if (!impl->use_synchronized_configure)
+ return;
+
+#ifdef HAVE_XSYNC
+ if (!GDK_WINDOW_DESTROYED (window))
+ {
+ GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
+ GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
+
+ if (toplevel && toplevel->update_counter != None &&
+ GDK_DISPLAY_X11 (display)->use_sync &&
+ !XSyncValueIsZero (toplevel->current_counter_value))
+ {
+ XSyncSetCounter (GDK_WINDOW_XDISPLAY (window),
+ toplevel->update_counter,
+ toplevel->current_counter_value);
+
+ XSyncIntToValue (&toplevel->current_counter_value, 0);
+ }
+ }
+#endif
+}
+
+void
+_gdk_windowing_window_beep (GdkWindow *window)
+{
+ GdkDisplay *display;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ display = GDK_WINDOW_DISPLAY (window);
+
+#ifdef HAVE_XKB
+ if (GDK_DISPLAY_X11 (display)->use_xkb)
+ XkbBell (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (window),
+ 0,
+ None);
+ else
+#endif
+ gdk_display_beep (display);
+}
+
+/**
+ * gdk_window_set_opacity:
+ * @window: a top-level #GdkWindow
+ * @opacity: opacity
+ *
+ * Request the windowing system to make @window partially transparent,
+ * with opacity 0 being fully transparent and 1 fully opaque. (Values
+ * of the opacity parameter are clamped to the [0,1] range.)
+ *
+ * On X11, this works only on X screens with a compositing manager
+ * running.
+ *
+ * For setting up per-pixel alpha, see gdk_screen_get_rgba_visual().
+ * For making non-toplevel windows translucent, see
+ * gdk_window_set_composited().
+ *
+ * Since: 2.12
+ */
+void
+gdk_window_set_opacity (GdkWindow *window,
+ gdouble opacity)
+{
+ GdkDisplay *display;
+ guint32 cardinal;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL (window))
+ return;
+
+ display = gdk_window_get_display (window);
+
+ if (opacity < 0)
+ opacity = 0;
+ else if (opacity > 1)
+ opacity = 1;
+
+ cardinal = opacity * 0xffffffff;
+
+ if (cardinal == 0xffffffff)
+ XDeleteProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (window),
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_OPACITY"));
+ else
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (window),
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_OPACITY"),
+ XA_CARDINAL, 32,
+ PropModeReplace,
+ (guchar *) &cardinal, 1);
+}
+
+void
+_gdk_windowing_window_set_composited (GdkWindow *window,
+ gboolean composited)
+{
+#if defined(HAVE_XCOMPOSITE) && defined(HAVE_XDAMAGE) && defined (HAVE_XFIXES)
+ GdkWindowObject *private = (GdkWindowObject *) window;
+ GdkWindowImplX11 *impl;
+ GdkDisplay *display;
+ Display *dpy;
+ Window xid;
+
+ impl = GDK_WINDOW_IMPL_X11 (private->impl);
+
+ display = gdk_screen_get_display (GDK_DRAWABLE_IMPL_X11 (impl)->screen);
+ dpy = GDK_DISPLAY_XDISPLAY (display);
+ xid = GDK_WINDOW_XWINDOW (private);
+
+ if (composited)
+ {
+ XCompositeRedirectWindow (dpy, xid, CompositeRedirectManual);
+ impl->damage = XDamageCreate (dpy, xid, XDamageReportBoundingBox);
+ }
+ else
+ {
+ XCompositeUnredirectWindow (dpy, xid, CompositeRedirectManual);
+ XDamageDestroy (dpy, impl->damage);
+ impl->damage = None;
+ }
+#endif
+}
+
+void
+_gdk_windowing_window_process_updates_recurse (GdkWindow *window,
+ cairo_region_t *region)
+{
+ _gdk_window_process_updates_recurse (window, region);
+}
+
+void
+_gdk_windowing_before_process_all_updates (void)
+{
+}
+
+void
+_gdk_windowing_after_process_all_updates (void)
+{
+}
+
+static void
+gdk_window_impl_iface_init (GdkWindowImplIface *iface)
+{
+ iface->show = gdk_window_x11_show;
+ iface->hide = gdk_window_x11_hide;
+ iface->withdraw = gdk_window_x11_withdraw;
+ iface->set_events = gdk_window_x11_set_events;
+ iface->get_events = gdk_window_x11_get_events;
+ iface->raise = gdk_window_x11_raise;
+ iface->lower = gdk_window_x11_lower;
+ iface->restack_under = gdk_window_x11_restack_under;
+ iface->restack_toplevel = gdk_window_x11_restack_toplevel;
+ iface->move_resize = gdk_window_x11_move_resize;
+ iface->set_background = gdk_window_x11_set_background;
+ iface->reparent = gdk_window_x11_reparent;
+ iface->set_device_cursor = gdk_window_x11_set_device_cursor;
+ iface->get_geometry = gdk_window_x11_get_geometry;
+ iface->get_root_coords = gdk_window_x11_get_root_coords;
+ iface->get_device_state = gdk_window_x11_get_device_state;
+ iface->shape_combine_region = gdk_window_x11_shape_combine_region;
+ iface->input_shape_combine_region = gdk_window_x11_input_shape_combine_region;
+ iface->set_static_gravities = gdk_window_x11_set_static_gravities;
+ iface->queue_antiexpose = _gdk_x11_window_queue_antiexpose;
+ iface->translate = _gdk_x11_window_translate;
+ iface->destroy = _gdk_x11_window_destroy;
+ iface->resize_cairo_surface = gdk_window_x11_resize_cairo_surface;
+}
+
+static Bool
+timestamp_predicate (Display *display,
+ XEvent *xevent,
+ XPointer arg)
+{
+ Window xwindow = GPOINTER_TO_UINT (arg);
+ GdkDisplay *gdk_display = gdk_x11_lookup_xdisplay (display);
+
+ if (xevent->type == PropertyNotify &&
+ xevent->xproperty.window == xwindow &&
+ xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (gdk_display,
+ "GDK_TIMESTAMP_PROP"))
+ return True;
+
+ return False;
+}
+
+/**
+ * gdk_x11_get_server_time:
+ * @window: a #GdkWindow, used for communication with the server.
+ * The window must have GDK_PROPERTY_CHANGE_MASK in its
+ * events mask or a hang will result.
+ *
+ * Routine to get the current X server time stamp.
+ *
+ * Return value: the time stamp.
+ **/
+guint32
+gdk_x11_get_server_time (GdkWindow *window)
+{
+ Display *xdisplay;
+ Window xwindow;
+ guchar c = 'a';
+ XEvent xevent;
+ Atom timestamp_prop_atom;
+
+ g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
+ g_return_val_if_fail (!GDK_WINDOW_DESTROYED (window), 0);
+
+ xdisplay = GDK_WINDOW_XDISPLAY (window);
+ xwindow = GDK_WINDOW_XWINDOW (window);
+ timestamp_prop_atom =
+ gdk_x11_get_xatom_by_name_for_display (GDK_WINDOW_DISPLAY (window),
+ "GDK_TIMESTAMP_PROP");
+
+ XChangeProperty (xdisplay, xwindow, timestamp_prop_atom,
+ timestamp_prop_atom,
+ 8, PropModeReplace, &c, 1);
+
+ XIfEvent (xdisplay, &xevent,
+ timestamp_predicate, GUINT_TO_POINTER(xwindow));
+
+ return xevent.xproperty.time;
+}
diff --git a/gdk/broadway/gdkwindow-broadway.h b/gdk/broadway/gdkwindow-broadway.h
new file mode 100644
index 0000000000..392c35779c
--- /dev/null
+++ b/gdk/broadway/gdkwindow-broadway.h
@@ -0,0 +1,162 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __GDK_WINDOW_BROADWAY_H__
+#define __GDK_WINDOW_BROADWAY_H__
+
+#include <gdk/broadway/gdkdrawable-broadway.h>
+
+#ifdef HAVE_XDAMAGE
+#include <X11/extensions/Xdamage.h>
+#endif
+
+#ifdef HAVE_XSYNC
+#include <X11/extensions/sync.h>
+#endif
+
+G_BEGIN_DECLS
+
+typedef struct _GdkToplevelX11 GdkToplevelX11;
+typedef struct _GdkWindowImplX11 GdkWindowImplX11;
+typedef struct _GdkWindowImplX11Class GdkWindowImplX11Class;
+typedef struct _GdkXPositionInfo GdkXPositionInfo;
+
+/* Window implementation for X11
+ */
+
+#define GDK_TYPE_WINDOW_IMPL_X11 (gdk_window_impl_x11_get_type ())
+#define GDK_WINDOW_IMPL_X11(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WINDOW_IMPL_X11, GdkWindowImplX11))
+#define GDK_WINDOW_IMPL_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WINDOW_IMPL_X11, GdkWindowImplX11Class))
+#define GDK_IS_WINDOW_IMPL_X11(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WINDOW_IMPL_X11))
+#define GDK_IS_WINDOW_IMPL_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WINDOW_IMPL_X11))
+#define GDK_WINDOW_IMPL_X11_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WINDOW_IMPL_X11, GdkWindowImplX11Class))
+
+struct _GdkWindowImplX11
+{
+ GdkDrawableImplX11 parent_instance;
+
+ GdkToplevelX11 *toplevel; /* Toplevel-specific information */
+ GdkCursor *cursor;
+ GHashTable *device_cursor;
+
+ gint8 toplevel_window_type;
+ guint no_bg : 1; /* Set when the window background is temporarily
+ * unset during resizing and scaling */
+ guint override_redirect : 1;
+ guint use_synchronized_configure : 1;
+
+#if defined (HAVE_XCOMPOSITE) && defined(HAVE_XDAMAGE) && defined (HAVE_XFIXES)
+ Damage damage;
+#endif
+};
+
+struct _GdkWindowImplX11Class
+{
+ GdkDrawableImplX11Class parent_class;
+};
+
+struct _GdkToplevelX11
+{
+
+ /* Set if the window, or any descendent of it, is the server's focus window
+ */
+ guint has_focus_window : 1;
+
+ /* Set if window->has_focus_window and the focus isn't grabbed elsewhere.
+ */
+ guint has_focus : 1;
+
+ /* Set if the pointer is inside this window. (This is needed for
+ * for focus tracking)
+ */
+ guint has_pointer : 1;
+
+ /* Set if the window is a descendent of the focus window and the pointer is
+ * inside it. (This is the case where the window will receive keystroke
+ * events even window->has_focus_window is FALSE)
+ */
+ guint has_pointer_focus : 1;
+
+ /* Set if we are requesting these hints */
+ guint skip_taskbar_hint : 1;
+ guint skip_pager_hint : 1;
+ guint urgency_hint : 1;
+
+ guint on_all_desktops : 1; /* _NET_WM_STICKY == 0xFFFFFFFF */
+
+ guint have_sticky : 1; /* _NET_WM_STATE_STICKY */
+ guint have_maxvert : 1; /* _NET_WM_STATE_MAXIMIZED_VERT */
+ guint have_maxhorz : 1; /* _NET_WM_STATE_MAXIMIZED_HORZ */
+ guint have_fullscreen : 1; /* _NET_WM_STATE_FULLSCREEN */
+
+ guint is_leader : 1;
+
+ gulong map_serial; /* Serial of last transition from unmapped */
+
+ cairo_surface_t *icon_pixmap;
+ cairo_surface_t *icon_mask;
+ GdkWindow *group_leader;
+
+ /* Time of most recent user interaction. */
+ gulong user_time;
+
+ /* We use an extra X window for toplevel windows that we XSetInputFocus()
+ * to in order to avoid getting keyboard events redirected to subwindows
+ * that might not even be part of this app
+ */
+ Window focus_window;
+
+#ifdef HAVE_XSYNC
+ XID update_counter;
+ XSyncValue pending_counter_value; /* latest _NET_WM_SYNC_REQUEST value received */
+ XSyncValue current_counter_value; /* Latest _NET_WM_SYNC_REQUEST value received
+ * where we have also seen the corresponding
+ * ConfigureNotify
+ */
+#endif
+};
+
+GType gdk_window_impl_x11_get_type (void);
+
+void gdk_x11_window_set_user_time (GdkWindow *window,
+ guint32 timestamp);
+
+GdkToplevelX11 *_gdk_x11_window_get_toplevel (GdkWindow *window);
+void _gdk_x11_window_tmp_unset_bg (GdkWindow *window,
+ gboolean recurse);
+void _gdk_x11_window_tmp_reset_bg (GdkWindow *window,
+ gboolean recurse);
+void _gdk_x11_window_tmp_unset_parent_bg (GdkWindow *window);
+void _gdk_x11_window_tmp_reset_parent_bg (GdkWindow *window);
+
+GdkCursor *_gdk_x11_window_get_cursor (GdkWindow *window);
+void _gdk_x11_window_get_offsets (GdkWindow *window,
+ gint *x_offset,
+ gint *y_offset);
+
+G_END_DECLS
+
+#endif /* __GDK_WINDOW_BROADWAY_H__ */
diff --git a/gdk/broadway/gdkx.h b/gdk/broadway/gdkx.h
new file mode 100644
index 0000000000..34917dd990
--- /dev/null
+++ b/gdk/broadway/gdkx.h
@@ -0,0 +1,169 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __GDK_X_H__
+#define __GDK_X_H__
+
+#include <gdk/gdkprivate.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+G_BEGIN_DECLS
+
+Display *gdk_x11_drawable_get_xdisplay (GdkDrawable *drawable);
+XID gdk_x11_drawable_get_xid (GdkDrawable *drawable);
+GdkDrawable *gdk_x11_window_get_drawable_impl (GdkWindow *window);
+Display *gdk_x11_cursor_get_xdisplay (GdkCursor *cursor);
+Cursor gdk_x11_cursor_get_xcursor (GdkCursor *cursor);
+Display *gdk_x11_display_get_xdisplay (GdkDisplay *display);
+Visual * gdk_x11_visual_get_xvisual (GdkVisual *visual);
+Screen * gdk_x11_screen_get_xscreen (GdkScreen *screen);
+int gdk_x11_screen_get_screen_number (GdkScreen *screen);
+void gdk_x11_window_set_user_time (GdkWindow *window,
+ guint32 timestamp);
+void gdk_x11_window_move_to_current_desktop (GdkWindow *window);
+
+const char* gdk_x11_screen_get_window_manager_name (GdkScreen *screen);
+
+#ifndef GDK_MULTIHEAD_SAFE
+Window gdk_x11_get_default_root_xwindow (void);
+Display *gdk_x11_get_default_xdisplay (void);
+gint gdk_x11_get_default_screen (void);
+#endif
+
+#define GDK_CURSOR_XDISPLAY(cursor) (gdk_x11_cursor_get_xdisplay (cursor))
+#define GDK_CURSOR_XCURSOR(cursor) (gdk_x11_cursor_get_xcursor (cursor))
+
+#ifdef GDK_COMPILATION
+
+#include "gdkprivate-broadway.h"
+#include "gdkscreen-broadway.h"
+
+#define GDK_DISPLAY_XDISPLAY(display) (GDK_DISPLAY_X11(display)->xdisplay)
+
+#define GDK_WINDOW_XDISPLAY(win) (GDK_SCREEN_X11 (GDK_WINDOW_SCREEN (win))->xdisplay)
+#define GDK_WINDOW_XID(win) (GDK_DRAWABLE_IMPL_X11(((GdkWindowObject *)win)->impl)->xid)
+#define GDK_DRAWABLE_XDISPLAY(win) (GDK_WINDOW_XDISPLAY (win))
+#define GDK_DRAWABLE_XID(win) (GDK_WINDOW_XID (win))
+#define GDK_SCREEN_XDISPLAY(screen) (GDK_SCREEN_X11 (screen)->xdisplay)
+#define GDK_SCREEN_XSCREEN(screen) (GDK_SCREEN_X11 (screen)->xscreen)
+#define GDK_SCREEN_XNUMBER(screen) (GDK_SCREEN_X11 (screen)->screen_num)
+#define GDK_WINDOW_XWINDOW GDK_DRAWABLE_XID
+
+#else /* GDK_COMPILATION */
+
+#ifndef GDK_MULTIHEAD_SAFE
+#define GDK_ROOT_WINDOW() (gdk_x11_get_default_root_xwindow ())
+#endif
+
+#define GDK_DISPLAY_XDISPLAY(display) (gdk_x11_display_get_xdisplay (display))
+
+#define GDK_WINDOW_XDISPLAY(win) (gdk_x11_drawable_get_xdisplay (gdk_x11_window_get_drawable_impl (win)))
+#define GDK_WINDOW_XID(win) (gdk_x11_drawable_get_xid (win))
+#define GDK_WINDOW_XWINDOW(win) (gdk_x11_drawable_get_xid (win))
+#define GDK_DRAWABLE_XDISPLAY(win) (gdk_x11_drawable_get_xdisplay (win))
+#define GDK_DRAWABLE_XID(win) (gdk_x11_drawable_get_xid (win))
+#define GDK_SCREEN_XDISPLAY(screen) (gdk_x11_display_get_xdisplay (gdk_screen_get_display (screen)))
+#define GDK_SCREEN_XSCREEN(screen) (gdk_x11_screen_get_xscreen (screen))
+#define GDK_SCREEN_XNUMBER(screen) (gdk_x11_screen_get_screen_number (screen))
+
+#endif /* GDK_COMPILATION */
+
+#define GDK_VISUAL_XVISUAL(visual) (gdk_x11_visual_get_xvisual (visual))
+
+GdkVisual* gdk_x11_screen_lookup_visual (GdkScreen *screen,
+ VisualID xvisualid);
+#ifndef GDK_MULTIHEAD_SAFE
+GdkVisual* gdkx_visual_get (VisualID xvisualid);
+#endif
+
+ /* Return the Gdk* for a particular XID */
+gpointer gdk_xid_table_lookup_for_display (GdkDisplay *display,
+ XID xid);
+guint32 gdk_x11_get_server_time (GdkWindow *window);
+guint32 gdk_x11_display_get_user_time (GdkDisplay *display);
+
+G_CONST_RETURN gchar *gdk_x11_display_get_startup_notification_id (GdkDisplay *display);
+void gdk_x11_display_set_startup_notification_id (GdkDisplay *display,
+ const gchar *startup_id);
+
+void gdk_x11_display_set_cursor_theme (GdkDisplay *display,
+ const gchar *theme,
+ const gint size);
+
+void gdk_x11_display_broadcast_startup_message (GdkDisplay *display,
+ const char *message_type,
+ ...) G_GNUC_NULL_TERMINATED;
+
+/* returns TRUE if we support the given WM spec feature */
+gboolean gdk_x11_screen_supports_net_wm_hint (GdkScreen *screen,
+ GdkAtom property);
+
+XID gdk_x11_screen_get_monitor_output (GdkScreen *screen,
+ gint monitor_num);
+
+#ifndef GDK_MULTIHEAD_SAFE
+gpointer gdk_xid_table_lookup (XID xid);
+gboolean gdk_net_wm_supports (GdkAtom property);
+void gdk_x11_grab_server (void);
+void gdk_x11_ungrab_server (void);
+#endif
+
+GdkDisplay *gdk_x11_lookup_xdisplay (Display *xdisplay);
+
+
+/* Functions to get the X Atom equivalent to the GdkAtom */
+Atom gdk_x11_atom_to_xatom_for_display (GdkDisplay *display,
+ GdkAtom atom);
+GdkAtom gdk_x11_xatom_to_atom_for_display (GdkDisplay *display,
+ Atom xatom);
+Atom gdk_x11_get_xatom_by_name_for_display (GdkDisplay *display,
+ const gchar *atom_name);
+G_CONST_RETURN gchar *gdk_x11_get_xatom_name_for_display (GdkDisplay *display,
+ Atom xatom);
+#ifndef GDK_MULTIHEAD_SAFE
+Atom gdk_x11_atom_to_xatom (GdkAtom atom);
+GdkAtom gdk_x11_xatom_to_atom (Atom xatom);
+Atom gdk_x11_get_xatom_by_name (const gchar *atom_name);
+G_CONST_RETURN gchar *gdk_x11_get_xatom_name (Atom xatom);
+#endif
+
+void gdk_x11_display_grab (GdkDisplay *display);
+void gdk_x11_display_ungrab (GdkDisplay *display);
+
+void gdk_x11_display_error_trap_push (GdkDisplay *display);
+/* warn unused because you could use pop_ignored otherwise */
+G_GNUC_WARN_UNUSED_RESULT gint gdk_x11_display_error_trap_pop (GdkDisplay *display);
+void gdk_x11_display_error_trap_pop_ignored (GdkDisplay *display);
+
+void gdk_x11_register_standard_event_type (GdkDisplay *display,
+ gint event_base,
+ gint n_events);
+
+
+G_END_DECLS
+
+#endif /* __GDK_X_H__ */
diff --git a/gdk/broadway/gdkxftdefaults.c b/gdk/broadway/gdkxftdefaults.c
new file mode 100644
index 0000000000..9ce1f12645
--- /dev/null
+++ b/gdk/broadway/gdkxftdefaults.c
@@ -0,0 +1,268 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ *
+ * Based on code from xftdpy.c
+ *
+ * Copyright © 2000 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Keith Packard not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission. Keith Packard makes no
+ * representations about the suitability of this software for any purpose. It
+ * is provided "as is" without express or implied warranty.
+ *
+ * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <fontconfig/fontconfig.h>
+
+#ifndef FC_HINT_STYLE
+#define FC_HINT_NONE 0
+#define FC_HINT_SLIGHT 1
+#define FC_HINT_MEDIUM 2
+#define FC_HINT_FULL 3
+#endif
+
+#include <gdkscreen-broadway.h>
+#include <gdkx.h>
+
+static gint
+parse_boolean (char *v)
+{
+ gchar c0, c1;
+
+ c0 = *v;
+ if (g_ascii_isupper ((int)c0))
+ c0 = g_ascii_tolower (c0);
+ if (c0 == 't' || c0 == 'y' || c0 == '1')
+ return 1;
+ if (c0 == 'f' || c0 == 'n' || c0 == '0')
+ return 0;
+ if (c0 == 'o')
+ {
+ c1 = v[1];
+ if (g_ascii_isupper ((int)c1))
+ c1 = g_ascii_tolower (c1);
+ if (c1 == 'n')
+ return 1;
+ if (c1 == 'f')
+ return 0;
+ }
+
+ return -1;
+}
+
+static gboolean
+get_boolean_default (Display *dpy,
+ gchar *option,
+ gboolean *value)
+{
+ gchar *v;
+ gint i;
+
+ v = XGetDefault (dpy, "Xft", option);
+ if (v)
+ {
+ i = parse_boolean (v);
+ if (i >= 0)
+ {
+ *value = i;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+get_double_default (Display *dpy,
+ gchar *option,
+ gdouble *value)
+{
+ gchar *v, *e;
+
+ v = XGetDefault (dpy, "Xft", option);
+ if (v)
+ {
+ /* Xft uses strtod, though localization probably wasn't
+ * desired. For compatibility, we use the conservative
+ * g_strtod() that accepts either localized or non-localized
+ * decimal separator.
+ */
+ *value = g_strtod (v, &e);
+ if (e != v)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+get_integer_default (Display *dpy,
+ gchar *option,
+ gint *value)
+{
+ gchar *v, *e;
+
+ v = XGetDefault (dpy, "Xft", option);
+ if (v)
+ {
+ if (FcNameConstant ((FcChar8 *) v, value))
+ return TRUE;
+
+ *value = strtol (v, &e, 0);
+ if (e != v)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+init_xft_settings (GdkScreen *screen)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+ Display *xdisplay = GDK_SCREEN_XDISPLAY (screen);
+ int xscreen = GDK_SCREEN_XNUMBER (screen);
+ double dpi_double;
+
+ if (screen_x11->xft_init)
+ return;
+
+ screen_x11->xft_init = TRUE;
+
+ if (!get_boolean_default (xdisplay, "antialias", &screen_x11->xft_antialias))
+ screen_x11->xft_antialias = TRUE;
+
+ if (!get_boolean_default (xdisplay, "hinting", &screen_x11->xft_hinting))
+ screen_x11->xft_hinting = TRUE;
+
+ if (!get_integer_default (xdisplay, "hintstyle", &screen_x11->xft_hintstyle))
+ screen_x11->xft_hintstyle = FC_HINT_FULL;
+
+ if (!get_integer_default (xdisplay, "rgba", &screen_x11->xft_rgba))
+ screen_x11->xft_rgba = FC_RGBA_UNKNOWN;
+
+ if (!get_double_default (xdisplay, "dpi", &dpi_double))
+ dpi_double = (((double) DisplayHeight (xdisplay, xscreen) * 25.4) /
+ (double) DisplayHeightMM (xdisplay, xscreen));
+
+ screen_x11->xft_dpi = (int)(0.5 + PANGO_SCALE * dpi_double);
+}
+
+gboolean
+_gdk_x11_get_xft_setting (GdkScreen *screen,
+ const gchar *name,
+ GValue *value)
+{
+ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+
+ if (strncmp (name, "gtk-xft-", 8) != 0)
+ return FALSE;
+
+ name += 8;
+
+ init_xft_settings (screen);
+
+ if (strcmp (name, "antialias") == 0)
+ {
+ g_value_set_int (value, screen_x11->xft_antialias);
+ return TRUE;
+ }
+ else if (strcmp (name, "hinting") == 0)
+ {
+ g_value_set_int (value, screen_x11->xft_hinting);
+ return TRUE;
+ }
+ else if (strcmp (name, "hintstyle") == 0)
+ {
+ const char *str;
+
+ switch (screen_x11->xft_hintstyle)
+ {
+ case FC_HINT_NONE:
+ str = "hintnone";
+ break;
+ case FC_HINT_SLIGHT:
+ str = "hintslight";
+ break;
+ case FC_HINT_MEDIUM:
+ str = "hintmedium";
+ break;
+ case FC_HINT_FULL:
+ str = "hintfull";
+ break;
+ default:
+ return FALSE;
+ }
+
+ g_value_set_string (value, str);
+ return TRUE;
+ }
+ else if (strcmp (name, "rgba") == 0)
+ {
+ const char *str;
+
+ switch (screen_x11->xft_rgba)
+ {
+ case FC_RGBA_NONE:
+ str = "none";
+ break;
+ case FC_RGBA_RGB:
+ str = "rgb";
+ break;
+ case FC_RGBA_BGR:
+ str = "bgr";
+ break;
+ case FC_RGBA_VRGB:
+ str = "vrgb";
+ break;
+ case FC_RGBA_VBGR:
+ str = "vbgr";
+ break;
+ case FC_RGBA_UNKNOWN:
+ default:
+ return FALSE;
+ }
+
+ g_value_set_string (value, str);
+ return TRUE;
+ }
+ else if (strcmp (name, "dpi") == 0)
+ {
+ g_value_set_int (value, screen_x11->xft_dpi);
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/gdk/broadway/gdkxid.c b/gdk/broadway/gdkxid.c
new file mode 100644
index 0000000000..ef2dc641a0
--- /dev/null
+++ b/gdk/broadway/gdkxid.c
@@ -0,0 +1,133 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gdkx.h"
+#include "gdkprivate-broadway.h"
+#include "gdkdisplay-broadway.h"
+
+#include <stdio.h>
+
+static guint gdk_xid_hash (XID *xid);
+static gboolean gdk_xid_equal (XID *a,
+ XID *b);
+
+
+void
+_gdk_xid_table_insert (GdkDisplay *display,
+ XID *xid,
+ gpointer data)
+{
+ GdkDisplayX11 *display_x11;
+
+ g_return_if_fail (xid != NULL);
+ g_return_if_fail (GDK_IS_DISPLAY (display));
+
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ if (!display_x11->xid_ht)
+ display_x11->xid_ht = g_hash_table_new ((GHashFunc) gdk_xid_hash,
+ (GEqualFunc) gdk_xid_equal);
+
+ if (g_hash_table_lookup (display_x11->xid_ht, xid))
+ g_warning ("XID collision, trouble ahead");
+
+ g_hash_table_insert (display_x11->xid_ht, xid, data);
+}
+
+void
+_gdk_xid_table_remove (GdkDisplay *display,
+ XID xid)
+{
+ GdkDisplayX11 *display_x11;
+
+ g_return_if_fail (GDK_IS_DISPLAY (display));
+
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ if (display_x11->xid_ht)
+ g_hash_table_remove (display_x11->xid_ht, &xid);
+}
+
+/**
+ * gdk_xid_table_lookup_for_display:
+ * @display: the #GdkDisplay.
+ * @xid: an X id.
+ *
+ * Returns the GDK object associated with the given X id.
+ *
+ * Return value: the associated #GdkWindow or %NULL if no
+ * object is associated with the X id.
+ *
+ * Since: 2.2
+ */
+gpointer
+gdk_xid_table_lookup_for_display (GdkDisplay *display,
+ XID xid)
+{
+ GdkDisplayX11 *display_x11;
+ gpointer data = NULL;
+
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
+
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ if (display_x11->xid_ht)
+ data = g_hash_table_lookup (display_x11->xid_ht, &xid);
+
+ return data;
+}
+
+
+/**
+ * gdk_xid_table_lookup:
+ * @xid: an X id.
+ *
+ * Returns the Gdk object associated with the given X id for the default
+ * display.
+ *
+ * Return value: the associated #GdkWindow or %NULL if no
+ * object is associated with the X id.
+ */
+gpointer
+gdk_xid_table_lookup (XID xid)
+{
+ return gdk_xid_table_lookup_for_display (gdk_display_get_default (), xid);
+}
+
+static guint
+gdk_xid_hash (XID *xid)
+{
+ return *xid;
+}
+
+static gboolean
+gdk_xid_equal (XID *a,
+ XID *b)
+{
+ return (*a == *b);
+}
diff --git a/gdk/broadway/xsettings-client.c b/gdk/broadway/xsettings-client.c
new file mode 100644
index 0000000000..67e3b6b020
--- /dev/null
+++ b/gdk/broadway/xsettings-client.c
@@ -0,0 +1,611 @@
+/*
+ * Copyright © 2001, 2007 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Red Hat not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. Red Hat makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Owen Taylor, Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include "xsettings-client.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xmd.h> /* For CARD16 */
+
+struct _XSettingsClient
+{
+ Display *display;
+ int screen;
+ XSettingsNotifyFunc notify;
+ XSettingsWatchFunc watch;
+ void *cb_data;
+
+ XSettingsGrabFunc grab;
+ XSettingsGrabFunc ungrab;
+
+ Window manager_window;
+ Atom manager_atom;
+ Atom selection_atom;
+ Atom xsettings_atom;
+
+ XSettingsList *settings;
+};
+
+static void
+notify_changes (XSettingsClient *client,
+ XSettingsList *old_list)
+{
+ XSettingsList *old_iter = old_list;
+ XSettingsList *new_iter = client->settings;
+
+ if (!client->notify)
+ return;
+
+ while (old_iter || new_iter)
+ {
+ int cmp;
+
+ if (old_iter && new_iter)
+ cmp = strcmp (old_iter->setting->name, new_iter->setting->name);
+ else if (old_iter)
+ cmp = -1;
+ else
+ cmp = 1;
+
+ if (cmp < 0)
+ {
+ client->notify (old_iter->setting->name,
+ XSETTINGS_ACTION_DELETED,
+ NULL,
+ client->cb_data);
+ }
+ else if (cmp == 0)
+ {
+ if (!xsettings_setting_equal (old_iter->setting,
+ new_iter->setting))
+ client->notify (old_iter->setting->name,
+ XSETTINGS_ACTION_CHANGED,
+ new_iter->setting,
+ client->cb_data);
+ }
+ else
+ {
+ client->notify (new_iter->setting->name,
+ XSETTINGS_ACTION_NEW,
+ new_iter->setting,
+ client->cb_data);
+ }
+
+ if (old_iter)
+ old_iter = old_iter->next;
+ if (new_iter)
+ new_iter = new_iter->next;
+ }
+}
+
+static int
+ignore_errors (Display *display, XErrorEvent *event)
+{
+ return True;
+}
+
+static char local_byte_order = '\0';
+
+#define BYTES_LEFT(buffer) ((buffer)->data + (buffer)->len - (buffer)->pos)
+
+static XSettingsResult
+fetch_card16 (XSettingsBuffer *buffer,
+ CARD16 *result)
+{
+ CARD16 x;
+
+ if (BYTES_LEFT (buffer) < 2)
+ return XSETTINGS_ACCESS;
+
+ x = *(CARD16 *)buffer->pos;
+ buffer->pos += 2;
+
+ if (buffer->byte_order == local_byte_order)
+ *result = x;
+ else
+ *result = (x << 8) | (x >> 8);
+
+ return XSETTINGS_SUCCESS;
+}
+
+static XSettingsResult
+fetch_ushort (XSettingsBuffer *buffer,
+ unsigned short *result)
+{
+ CARD16 x;
+ XSettingsResult r;
+
+ r = fetch_card16 (buffer, &x);
+ if (r == XSETTINGS_SUCCESS)
+ *result = x;
+
+ return r;
+}
+
+static XSettingsResult
+fetch_card32 (XSettingsBuffer *buffer,
+ CARD32 *result)
+{
+ CARD32 x;
+
+ if (BYTES_LEFT (buffer) < 4)
+ return XSETTINGS_ACCESS;
+
+ x = *(CARD32 *)buffer->pos;
+ buffer->pos += 4;
+
+ if (buffer->byte_order == local_byte_order)
+ *result = x;
+ else
+ *result = (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);
+
+ return XSETTINGS_SUCCESS;
+}
+
+static XSettingsResult
+fetch_card8 (XSettingsBuffer *buffer,
+ CARD8 *result)
+{
+ if (BYTES_LEFT (buffer) < 1)
+ return XSETTINGS_ACCESS;
+
+ *result = *(CARD8 *)buffer->pos;
+ buffer->pos += 1;
+
+ return XSETTINGS_SUCCESS;
+}
+
+#define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1)))
+
+static XSettingsList *
+parse_settings (unsigned char *data,
+ size_t len)
+{
+ XSettingsBuffer buffer;
+ XSettingsResult result = XSETTINGS_SUCCESS;
+ XSettingsList *settings = NULL;
+ CARD32 serial;
+ CARD32 n_entries;
+ CARD32 i;
+ XSettingsSetting *setting = NULL;
+
+ local_byte_order = xsettings_byte_order ();
+
+ buffer.pos = buffer.data = data;
+ buffer.len = len;
+
+ result = fetch_card8 (&buffer, (unsigned char *)&buffer.byte_order);
+ if (buffer.byte_order != MSBFirst &&
+ buffer.byte_order != LSBFirst)
+ {
+ fprintf (stderr, "Invalid byte order in XSETTINGS property\n");
+ result = XSETTINGS_FAILED;
+ goto out;
+ }
+
+ buffer.pos += 3;
+
+ result = fetch_card32 (&buffer, &serial);
+ if (result != XSETTINGS_SUCCESS)
+ goto out;
+
+ result = fetch_card32 (&buffer, &n_entries);
+ if (result != XSETTINGS_SUCCESS)
+ goto out;
+
+ for (i = 0; i < n_entries; i++)
+ {
+ CARD8 type;
+ CARD16 name_len;
+ CARD32 v_int;
+ size_t pad_len;
+
+ result = fetch_card8 (&buffer, &type);
+ if (result != XSETTINGS_SUCCESS)
+ goto out;
+
+ buffer.pos += 1;
+
+ result = fetch_card16 (&buffer, &name_len);
+ if (result != XSETTINGS_SUCCESS)
+ goto out;
+
+ pad_len = XSETTINGS_PAD(name_len, 4);
+ if (BYTES_LEFT (&buffer) < pad_len)
+ {
+ result = XSETTINGS_ACCESS;
+ goto out;
+ }
+
+ setting = malloc (sizeof *setting);
+ if (!setting)
+ {
+ result = XSETTINGS_NO_MEM;
+ goto out;
+ }
+ setting->type = XSETTINGS_TYPE_INT; /* No allocated memory */
+
+ setting->name = malloc (name_len + 1);
+ if (!setting->name)
+ {
+ result = XSETTINGS_NO_MEM;
+ goto out;
+ }
+
+ memcpy (setting->name, buffer.pos, name_len);
+ setting->name[name_len] = '\0';
+ buffer.pos += pad_len;
+
+ result = fetch_card32 (&buffer, &v_int);
+ if (result != XSETTINGS_SUCCESS)
+ goto out;
+ setting->last_change_serial = v_int;
+
+ switch (type)
+ {
+ case XSETTINGS_TYPE_INT:
+ result = fetch_card32 (&buffer, &v_int);
+ if (result != XSETTINGS_SUCCESS)
+ goto out;
+
+ setting->data.v_int = (INT32)v_int;
+ break;
+ case XSETTINGS_TYPE_STRING:
+ result = fetch_card32 (&buffer, &v_int);
+ if (result != XSETTINGS_SUCCESS)
+ goto out;
+
+ pad_len = XSETTINGS_PAD (v_int, 4);
+ if (v_int + 1 == 0 || /* Guard against wrap-around */
+ BYTES_LEFT (&buffer) < pad_len)
+ {
+ result = XSETTINGS_ACCESS;
+ goto out;
+ }
+
+ setting->data.v_string = malloc (v_int + 1);
+ if (!setting->data.v_string)
+ {
+ result = XSETTINGS_NO_MEM;
+ goto out;
+ }
+
+ memcpy (setting->data.v_string, buffer.pos, v_int);
+ setting->data.v_string[v_int] = '\0';
+ buffer.pos += pad_len;
+
+ break;
+ case XSETTINGS_TYPE_COLOR:
+ result = fetch_ushort (&buffer, &setting->data.v_color.red);
+ if (result != XSETTINGS_SUCCESS)
+ goto out;
+ result = fetch_ushort (&buffer, &setting->data.v_color.green);
+ if (result != XSETTINGS_SUCCESS)
+ goto out;
+ result = fetch_ushort (&buffer, &setting->data.v_color.blue);
+ if (result != XSETTINGS_SUCCESS)
+ goto out;
+ result = fetch_ushort (&buffer, &setting->data.v_color.alpha);
+ if (result != XSETTINGS_SUCCESS)
+ goto out;
+
+ break;
+ default:
+ /* Quietly ignore unknown types */
+ break;
+ }
+
+ setting->type = type;
+
+ result = xsettings_list_insert (&settings, setting);
+ if (result != XSETTINGS_SUCCESS)
+ goto out;
+
+ setting = NULL;
+ }
+
+ out:
+
+ if (result != XSETTINGS_SUCCESS)
+ {
+ switch (result)
+ {
+ case XSETTINGS_NO_MEM:
+ fprintf(stderr, "Out of memory reading XSETTINGS property\n");
+ break;
+ case XSETTINGS_ACCESS:
+ fprintf(stderr, "Invalid XSETTINGS property (read off end)\n");
+ break;
+ case XSETTINGS_DUPLICATE_ENTRY:
+ fprintf (stderr, "Duplicate XSETTINGS entry for '%s'\n", setting->name);
+ case XSETTINGS_FAILED:
+ case XSETTINGS_SUCCESS:
+ case XSETTINGS_NO_ENTRY:
+ break;
+ }
+
+ if (setting)
+ xsettings_setting_free (setting);
+
+ xsettings_list_free (settings);
+ settings = NULL;
+
+ }
+
+ return settings;
+}
+
+static void
+read_settings (XSettingsClient *client)
+{
+ Atom type;
+ int format;
+ unsigned long n_items;
+ unsigned long bytes_after;
+ unsigned char *data;
+ int result;
+
+ int (*old_handler) (Display *, XErrorEvent *);
+
+ XSettingsList *old_list = client->settings;
+
+ client->settings = NULL;
+
+ if (client->manager_window)
+ {
+ old_handler = XSetErrorHandler (ignore_errors);
+ result = XGetWindowProperty (client->display, client->manager_window,
+ client->xsettings_atom, 0, LONG_MAX,
+ False, client->xsettings_atom,
+ &type, &format, &n_items, &bytes_after, &data);
+ XSetErrorHandler (old_handler);
+
+ if (result == Success && type != None)
+ {
+ if (type != client->xsettings_atom)
+ {
+ fprintf (stderr, "Invalid type for XSETTINGS property");
+ }
+ else if (format != 8)
+ {
+ fprintf (stderr, "Invalid format for XSETTINGS property %d", format);
+ }
+ else
+ client->settings = parse_settings (data, n_items);
+
+ XFree (data);
+ }
+ }
+
+ notify_changes (client, old_list);
+ xsettings_list_free (old_list);
+}
+
+static void
+add_events (Display *display,
+ Window window,
+ long mask)
+{
+ XWindowAttributes attr;
+
+ XGetWindowAttributes (display, window, &attr);
+ XSelectInput (display, window, attr.your_event_mask | mask);
+}
+
+static void
+check_manager_window (XSettingsClient *client)
+{
+ if (client->manager_window && client->watch)
+ client->watch (client->manager_window, False, 0, client->cb_data);
+
+ if (client->grab)
+ client->grab (client->display);
+ else
+ XGrabServer (client->display);
+
+ client->manager_window = XGetSelectionOwner (client->display,
+ client->selection_atom);
+ if (client->manager_window)
+ XSelectInput (client->display, client->manager_window,
+ PropertyChangeMask | StructureNotifyMask);
+
+ if (client->ungrab)
+ client->ungrab (client->display);
+ else
+ XUngrabServer (client->display);
+
+ XFlush (client->display);
+
+ if (client->manager_window && client->watch)
+ {
+ if (!client->watch (client->manager_window, True,
+ PropertyChangeMask | StructureNotifyMask,
+ client->cb_data))
+ {
+ /* Inability to watch the window probably means that it was destroyed
+ * after we ungrabbed
+ */
+ client->manager_window = None;
+ return;
+ }
+ }
+
+
+ read_settings (client);
+}
+
+XSettingsClient *
+xsettings_client_new (Display *display,
+ int screen,
+ XSettingsNotifyFunc notify,
+ XSettingsWatchFunc watch,
+ void *cb_data)
+{
+ return xsettings_client_new_with_grab_funcs (display, screen, notify, watch, cb_data,
+ NULL, NULL);
+}
+
+XSettingsClient *
+xsettings_client_new_with_grab_funcs (Display *display,
+ int screen,
+ XSettingsNotifyFunc notify,
+ XSettingsWatchFunc watch,
+ void *cb_data,
+ XSettingsGrabFunc grab,
+ XSettingsGrabFunc ungrab)
+{
+ XSettingsClient *client;
+ char buffer[256];
+ char *atom_names[3];
+ Atom atoms[3];
+
+ client = malloc (sizeof *client);
+ if (!client)
+ return NULL;
+
+ client->display = display;
+ client->screen = screen;
+ client->notify = notify;
+ client->watch = watch;
+ client->cb_data = cb_data;
+ client->grab = grab;
+ client->ungrab = ungrab;
+
+ client->manager_window = None;
+ client->settings = NULL;
+
+ sprintf(buffer, "_XSETTINGS_S%d", screen);
+ atom_names[0] = buffer;
+ atom_names[1] = "_XSETTINGS_SETTINGS";
+ atom_names[2] = "MANAGER";
+
+#ifdef HAVE_XINTERNATOMS
+ XInternAtoms (display, atom_names, 3, False, atoms);
+#else
+ atoms[0] = XInternAtom (display, atom_names[0], False);
+ atoms[1] = XInternAtom (display, atom_names[1], False);
+ atoms[2] = XInternAtom (display, atom_names[2], False);
+#endif
+
+ client->selection_atom = atoms[0];
+ client->xsettings_atom = atoms[1];
+ client->manager_atom = atoms[2];
+
+ /* Select on StructureNotify so we get MANAGER events
+ */
+ add_events (display, RootWindow (display, screen), StructureNotifyMask);
+
+ if (client->watch)
+ client->watch (RootWindow (display, screen), True, StructureNotifyMask,
+ client->cb_data);
+
+ check_manager_window (client);
+
+ return client;
+}
+
+
+void
+xsettings_client_set_grab_func (XSettingsClient *client,
+ XSettingsGrabFunc grab)
+{
+ client->grab = grab;
+}
+
+void
+xsettings_client_set_ungrab_func (XSettingsClient *client,
+ XSettingsGrabFunc ungrab)
+{
+ client->ungrab = ungrab;
+}
+
+void
+xsettings_client_destroy (XSettingsClient *client)
+{
+ if (client->watch)
+ client->watch (RootWindow (client->display, client->screen),
+ False, 0, client->cb_data);
+ if (client->manager_window && client->watch)
+ client->watch (client->manager_window, False, 0, client->cb_data);
+
+ xsettings_list_free (client->settings);
+ free (client);
+}
+
+XSettingsResult
+xsettings_client_get_setting (XSettingsClient *client,
+ const char *name,
+ XSettingsSetting **setting)
+{
+ XSettingsSetting *search = xsettings_list_lookup (client->settings, name);
+ if (search)
+ {
+ *setting = xsettings_setting_copy (search);
+ return *setting ? XSETTINGS_SUCCESS : XSETTINGS_NO_MEM;
+ }
+ else
+ return XSETTINGS_NO_ENTRY;
+}
+
+Bool
+xsettings_client_process_event (XSettingsClient *client,
+ XEvent *xev)
+{
+ /* The checks here will not unlikely cause us to reread
+ * the properties from the manager window a number of
+ * times when the manager changes from A->B. But manager changes
+ * are going to be pretty rare.
+ */
+ if (xev->xany.window == RootWindow (client->display, client->screen))
+ {
+ if (xev->xany.type == ClientMessage &&
+ xev->xclient.message_type == client->manager_atom &&
+ xev->xclient.data.l[1] == client->selection_atom)
+ {
+ check_manager_window (client);
+ return True;
+ }
+ }
+ else if (xev->xany.window == client->manager_window)
+ {
+ if (xev->xany.type == DestroyNotify)
+ {
+ check_manager_window (client);
+ /* let GDK do its cleanup */
+ return False;
+ }
+ else if (xev->xany.type == PropertyNotify)
+ {
+ read_settings (client);
+ return True;
+ }
+ }
+
+ return False;
+}
diff --git a/gdk/broadway/xsettings-client.h b/gdk/broadway/xsettings-client.h
new file mode 100644
index 0000000000..710ed12482
--- /dev/null
+++ b/gdk/broadway/xsettings-client.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2001, 2007 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Red Hat not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. Red Hat makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Owen Taylor, Red Hat, Inc.
+ */
+#ifndef XSETTINGS_CLIENT_H
+#define XSETTINGS_CLIENT_H
+
+#include <X11/Xlib.h>
+#include "xsettings-common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _XSettingsClient XSettingsClient;
+
+typedef enum
+{
+ XSETTINGS_ACTION_NEW,
+ XSETTINGS_ACTION_CHANGED,
+ XSETTINGS_ACTION_DELETED
+} XSettingsAction;
+
+typedef void (*XSettingsNotifyFunc) (const char *name,
+ XSettingsAction action,
+ XSettingsSetting *setting,
+ void *cb_data);
+typedef Bool (*XSettingsWatchFunc) (Window window,
+ Bool is_start,
+ long mask,
+ void *cb_data);
+typedef void (*XSettingsGrabFunc) (Display *display);
+
+XSettingsClient *xsettings_client_new (Display *display,
+ int screen,
+ XSettingsNotifyFunc notify,
+ XSettingsWatchFunc watch,
+ void *cb_data);
+XSettingsClient *xsettings_client_new_with_grab_funcs (Display *display,
+ int screen,
+ XSettingsNotifyFunc notify,
+ XSettingsWatchFunc watch,
+ void *cb_data,
+ XSettingsGrabFunc grab,
+ XSettingsGrabFunc ungrab);
+void xsettings_client_set_grab_func (XSettingsClient *client,
+ XSettingsGrabFunc grab);
+void xsettings_client_set_ungrab_func (XSettingsClient *client,
+ XSettingsGrabFunc ungrab);
+void xsettings_client_destroy (XSettingsClient *client);
+Bool xsettings_client_process_event (XSettingsClient *client,
+ XEvent *xev);
+XSettingsResult xsettings_client_get_setting (XSettingsClient *client,
+ const char *name,
+ XSettingsSetting **setting);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* XSETTINGS_CLIENT_H */
diff --git a/gdk/broadway/xsettings-common.c b/gdk/broadway/xsettings-common.c
new file mode 100644
index 0000000000..cc3a8d0306
--- /dev/null
+++ b/gdk/broadway/xsettings-common.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright © 2001 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Red Hat not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. Red Hat makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Owen Taylor, Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include "xsettings-common.h"
+
+#include "string.h"
+#include "stdlib.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xmd.h> /* For CARD32 */
+
+XSettingsSetting *
+xsettings_setting_copy (XSettingsSetting *setting)
+{
+ XSettingsSetting *result;
+ size_t str_len;
+
+ result = malloc (sizeof *result);
+ if (!result)
+ return NULL;
+
+ str_len = strlen (setting->name);
+ result->name = malloc (str_len + 1);
+ if (!result->name)
+ goto err;
+
+ memcpy (result->name, setting->name, str_len + 1);
+
+ result->type = setting->type;
+
+ switch (setting->type)
+ {
+ case XSETTINGS_TYPE_INT:
+ result->data.v_int = setting->data.v_int;
+ break;
+ case XSETTINGS_TYPE_COLOR:
+ result->data.v_color = setting->data.v_color;
+ break;
+ case XSETTINGS_TYPE_STRING:
+ str_len = strlen (setting->data.v_string);
+ result->data.v_string = malloc (str_len + 1);
+ if (!result->data.v_string)
+ goto err;
+
+ memcpy (result->data.v_string, setting->data.v_string, str_len + 1);
+ break;
+ }
+
+ result->last_change_serial = setting->last_change_serial;
+
+ return result;
+
+ err:
+ if (result->name)
+ free (result->name);
+ free (result);
+
+ return NULL;
+}
+
+XSettingsList *
+xsettings_list_copy (XSettingsList *list)
+{
+ XSettingsList *new = NULL;
+ XSettingsList *old_iter = list;
+ XSettingsList *new_iter = NULL;
+
+ while (old_iter)
+ {
+ XSettingsList *new_node;
+
+ new_node = malloc (sizeof *new_node);
+ if (!new_node)
+ goto error;
+
+ new_node->setting = xsettings_setting_copy (old_iter->setting);
+ if (!new_node->setting)
+ {
+ free (new_node);
+ goto error;
+ }
+
+ if (new_iter)
+ new_iter->next = new_node;
+ else
+ new = new_node;
+
+ new_iter = new_node;
+
+ old_iter = old_iter->next;
+ }
+
+ return new;
+
+ error:
+ xsettings_list_free (new);
+ return NULL;
+}
+
+int
+xsettings_setting_equal (XSettingsSetting *setting_a,
+ XSettingsSetting *setting_b)
+{
+ if (setting_a->type != setting_b->type)
+ return 0;
+
+ if (strcmp (setting_a->name, setting_b->name) != 0)
+ return 0;
+
+ switch (setting_a->type)
+ {
+ case XSETTINGS_TYPE_INT:
+ return setting_a->data.v_int == setting_b->data.v_int;
+ case XSETTINGS_TYPE_COLOR:
+ return (setting_a->data.v_color.red == setting_b->data.v_color.red &&
+ setting_a->data.v_color.green == setting_b->data.v_color.green &&
+ setting_a->data.v_color.blue == setting_b->data.v_color.blue &&
+ setting_a->data.v_color.alpha == setting_b->data.v_color.alpha);
+ case XSETTINGS_TYPE_STRING:
+ return strcmp (setting_a->data.v_string, setting_b->data.v_string) == 0;
+ }
+
+ return 0;
+}
+
+void
+xsettings_setting_free (XSettingsSetting *setting)
+{
+ if (setting->type == XSETTINGS_TYPE_STRING)
+ free (setting->data.v_string);
+
+ if (setting->name)
+ free (setting->name);
+
+ free (setting);
+}
+
+void
+xsettings_list_free (XSettingsList *list)
+{
+ while (list)
+ {
+ XSettingsList *next = list->next;
+
+ xsettings_setting_free (list->setting);
+ free (list);
+
+ list = next;
+ }
+}
+
+XSettingsResult
+xsettings_list_insert (XSettingsList **list,
+ XSettingsSetting *setting)
+{
+ XSettingsList *node;
+ XSettingsList *iter;
+ XSettingsList *last = NULL;
+
+ node = malloc (sizeof *node);
+ if (!node)
+ return XSETTINGS_NO_MEM;
+ node->setting = setting;
+
+ iter = *list;
+ while (iter)
+ {
+ int cmp = strcmp (setting->name, iter->setting->name);
+
+ if (cmp < 0)
+ break;
+ else if (cmp == 0)
+ {
+ free (node);
+ return XSETTINGS_DUPLICATE_ENTRY;
+ }
+
+ last = iter;
+ iter = iter->next;
+ }
+
+ if (last)
+ last->next = node;
+ else
+ *list = node;
+
+ node->next = iter;
+
+ return XSETTINGS_SUCCESS;
+}
+
+XSettingsResult
+xsettings_list_delete (XSettingsList **list,
+ const char *name)
+{
+ XSettingsList *iter;
+ XSettingsList *last = NULL;
+
+ iter = *list;
+ while (iter)
+ {
+ if (strcmp (name, iter->setting->name) == 0)
+ {
+ if (last)
+ last->next = iter->next;
+ else
+ *list = iter->next;
+
+ xsettings_setting_free (iter->setting);
+ free (iter);
+
+ return XSETTINGS_SUCCESS;
+ }
+
+ last = iter;
+ iter = iter->next;
+ }
+
+ return XSETTINGS_FAILED;
+}
+
+XSettingsSetting *
+xsettings_list_lookup (XSettingsList *list,
+ const char *name)
+{
+ XSettingsList *iter;
+
+ iter = list;
+ while (iter)
+ {
+ if (strcmp (name, iter->setting->name) == 0)
+ return iter->setting;
+
+ iter = iter->next;
+ }
+
+ return NULL;
+}
+
+char
+xsettings_byte_order (void)
+{
+ CARD32 myint = 0x01020304;
+ return (*(char *)&myint == 1) ? MSBFirst : LSBFirst;
+}
diff --git a/gdk/broadway/xsettings-common.h b/gdk/broadway/xsettings-common.h
new file mode 100644
index 0000000000..de7367a6c2
--- /dev/null
+++ b/gdk/broadway/xsettings-common.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright © 2001 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Red Hat not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. Red Hat makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Owen Taylor, Red Hat, Inc.
+ */
+#ifndef XSETTINGS_COMMON_H
+#define XSETTINGS_COMMON_H
+
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Renames for GDK inclusion */
+
+#define xsettings_byte_order _gdk_xsettings_byte_order
+#define xsettings_client_destroy _gdk_xsettings_client_destroy
+#define xsettings_client_get_setting _gdk_xsettings_client_get_setting
+#define xsettings_client_new _gdk_xsettings_client_new
+#define xsettings_client_new_with_grab_funcs _gdk_xsettings_client_new_with_grab_funcs
+#define xsettings_client_set_grab_func _gdk_xsettings_client_set_grab_func
+#define xsettings_client_set_ungrab_func _gdk_xsettings_client_set_ungrab_func
+#define xsettings_client_process_event _gdk_xsettings_client_process_event
+#define xsettings_list_copy _gdk_xsettings_list_copy
+#define xsettings_list_delete _gdk_xsettings_list_delete
+#define xsettings_list_free _gdk_xsettings_list_free
+#define xsettings_list_insert _gdk_xsettings_list_insert
+#define xsettings_list_lookup _gdk_xsettings_list_lookup
+#define xsettings_setting_copy _gdk_xsettings_setting_copy
+#define xsettings_setting_equal _gdk_xsettings_setting_equal
+#define xsettings_setting_free _gdk_xsettings_setting_free
+
+
+typedef struct _XSettingsBuffer XSettingsBuffer;
+typedef struct _XSettingsColor XSettingsColor;
+typedef struct _XSettingsList XSettingsList;
+typedef struct _XSettingsSetting XSettingsSetting;
+
+/* Types of settings possible. Enum values correspond to
+ * protocol values.
+ */
+typedef enum
+{
+ XSETTINGS_TYPE_INT = 0,
+ XSETTINGS_TYPE_STRING = 1,
+ XSETTINGS_TYPE_COLOR = 2
+} XSettingsType;
+
+typedef enum
+{
+ XSETTINGS_SUCCESS,
+ XSETTINGS_NO_MEM,
+ XSETTINGS_ACCESS,
+ XSETTINGS_FAILED,
+ XSETTINGS_NO_ENTRY,
+ XSETTINGS_DUPLICATE_ENTRY
+} XSettingsResult;
+
+struct _XSettingsBuffer
+{
+ char byte_order;
+ size_t len;
+ unsigned char *data;
+ unsigned char *pos;
+};
+
+struct _XSettingsColor
+{
+ unsigned short red, green, blue, alpha;
+};
+
+struct _XSettingsList
+{
+ XSettingsSetting *setting;
+ XSettingsList *next;
+};
+
+struct _XSettingsSetting
+{
+ char *name;
+ XSettingsType type;
+
+ union {
+ int v_int;
+ char *v_string;
+ XSettingsColor v_color;
+ } data;
+
+ unsigned long last_change_serial;
+};
+
+XSettingsSetting *xsettings_setting_copy (XSettingsSetting *setting);
+void xsettings_setting_free (XSettingsSetting *setting);
+int xsettings_setting_equal (XSettingsSetting *setting_a,
+ XSettingsSetting *setting_b);
+
+void xsettings_list_free (XSettingsList *list);
+XSettingsList *xsettings_list_copy (XSettingsList *list);
+XSettingsResult xsettings_list_insert (XSettingsList **list,
+ XSettingsSetting *setting);
+XSettingsSetting *xsettings_list_lookup (XSettingsList *list,
+ const char *name);
+XSettingsResult xsettings_list_delete (XSettingsList **list,
+ const char *name);
+
+char xsettings_byte_order (void);
+
+#define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1)))
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* XSETTINGS_COMMON_H */
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index b6a92a982d..edef52711d 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -812,18 +812,22 @@ gtkinclude_HEADERS = $(gtk_public_h_sources) $(gtk_semi_private_h_sources) $(gtk
gtkunixprintincludedir = $(includedir)/gtk-3.0/unix-print/gtk
gtkunixprintinclude_HEADERS = $(gtk_unix_print_public_h_sources)
+libgtk_broadway_3_0_la_SOURCES = $(gtk_c_sources)
libgtk_x11_3_0_la_SOURCES = $(gtk_c_sources)
libgtk_win32_3_0_la_SOURCES = $(gtk_c_sources)
libgtk_quartz_3_0_la_SOURCES = $(gtk_c_sources)
+libgtk_broadway_3_0_la_LDFLAGS = $(libtool_opts)
libgtk_x11_3_0_la_LDFLAGS = $(libtool_opts)
libgtk_win32_3_0_la_LDFLAGS = $(libtool_opts) -Wl,-luuid
libgtk_quartz_3_0_la_LDFLAGS = $(libtool_opts)
+libgtk_broadway_3_0_la_LIBADD = $(libadd)
libgtk_x11_3_0_la_LIBADD = $(libadd)
libgtk_win32_3_0_la_LIBADD = $(libadd) -lole32 -lgdi32 -lcomdlg32 -lwinspool -lcomctl32
libgtk_quartz_3_0_la_LIBADD = $(libadd)
+libgtk_broadway_3_0_la_DEPENDENCIES = $(deps)
libgtk_x11_3_0_la_DEPENDENCIES = $(deps)
libgtk_win32_3_0_la_DEPENDENCIES = $(gtk_def) $(gtk_win32_res) $(deps)
libgtk_quartz_3_0_la_DEPENDENCIES = $(deps)
@@ -831,7 +835,7 @@ libgtk_quartz_3_0_la_DEPENDENCIES = $(deps)
if USE_WIN32
libgtk_target_ldflags = $(gtk_win32_res_ldflag) $(gtk_win32_symbols)
endif
-EXTRA_LTLIBRARIES = libgtk-x11-3.0.la libgtk-win32-3.0.la libgtk-quartz-3.0.la
+EXTRA_LTLIBRARIES = libgtk-broadway-3.0.la libgtk-x11-3.0.la libgtk-win32-3.0.la libgtk-quartz-3.0.la
install-exec-hook:
if DISABLE_EXPLICIT_DEPS
diff --git a/tests/testsocket_common.c b/tests/testsocket_common.c
index 995dbf9ee4..6dcabb35df 100644
--- a/tests/testsocket_common.c
+++ b/tests/testsocket_common.c
@@ -280,6 +280,8 @@ create_child_plug (guint32 xid,
return GDK_WINDOW_XID (gtk_widget_get_window (window));
#elif defined (GDK_WINDOWING_WIN32)
return (guint32) GDK_WINDOW_HWND (gtk_widget_get_window (window));
+#elif defined (GDK_WINDOWING_BROADWAY)
+ return (guint32) 0; /* Child windows not supported */
#endif
else
return 0;