]> Pileus Git - ~andy/gtk/blobdiff - gdk/x11/gdkscreen-x11.c
gdk/: fully remove gdkalias hacks
[~andy/gtk] / gdk / x11 / gdkscreen-x11.c
index 9b06e7deae012d8eaa9b4c7e6e241ca4738b8c70..8aac0759ffc901c2bb6fa198856f79c11f9bab84 100644 (file)
@@ -1,4 +1,4 @@
-/*
+ /*
  * gdkscreen-x11.c
  * 
  * Copyright 2001 Sun Microsystems Inc. 
@@ -32,7 +32,6 @@
 #include "gdkdisplay.h"
 #include "gdkdisplay-x11.h"
 #include "gdkx.h"
-#include "gdkalias.h"
 
 #include <X11/Xatom.h>
 
@@ -51,6 +50,8 @@
 #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);
@@ -66,6 +67,14 @@ 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;
@@ -205,7 +214,7 @@ gdk_screen_get_height_mm (GdkScreen *screen)
 gint
 gdk_screen_get_number (GdkScreen *screen)
 {
-  g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);  
+  g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
   
   return GDK_SCREEN_X11 (screen)->screen_num;
 }
@@ -214,9 +223,9 @@ gdk_screen_get_number (GdkScreen *screen)
  * gdk_screen_get_root_window:
  * @screen: a #GdkScreen
  *
- * Gets the root window of @screen. 
- * 
- * Returns: the root window
+ * Gets the root window of @screen.
+ *
+ * Returns: (transfer none): the root window
  *
  * Since: 2.2
  **/
@@ -234,7 +243,7 @@ gdk_screen_get_root_window (GdkScreen *screen)
  *
  * Gets the default colormap for @screen.
  * 
- * Returns: the default #GdkColormap.
+ * Returns: (transfer none): the default #GdkColormap.
  *
  * Since: 2.2
  **/
@@ -272,12 +281,24 @@ gdk_screen_set_default_colormap (GdkScreen   *screen,
     g_object_unref (old_colormap);
 }
 
+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);
 
-  _gdk_x11_events_uninit_screen (GDK_SCREEN (object));
+  _gdk_screen_x11_events_uninit (GDK_SCREEN (object));
 
   if (screen_x11->default_colormap)
     {
@@ -338,46 +359,55 @@ gdk_screen_x11_finalize (GObject *object)
 
 /**
  * gdk_screen_get_n_monitors:
- * @screen: a #GdkScreen.
+ * @screen: a #GdkScreen
  *
  * Returns the number of monitors which @screen consists of.
  *
- * Returns: number of monitors which @screen consists of.
+ * Returns: number of monitors which @screen consists of
  *
  * Since: 2.2
- **/
-gint 
+ */
+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;
 }
 
-static GdkX11Monitor *
-get_monitor (GdkScreen *screen,
-            int        monitor_num)
+/**
+ * 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)
 {
-  GdkScreenX11 *screen_x11;
+  g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
 
-  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
-  
-  screen_x11 = GDK_SCREEN_X11 (screen);
-  
-  g_return_val_if_fail (monitor_num < screen_x11->n_monitors, NULL);
-  g_return_val_if_fail (monitor_num >= 0, NULL);
-  
-  return &(screen_x11->monitors[monitor_num]);
+  return GDK_SCREEN_X11 (screen)->primary_monitor;
 }
 
 /**
  * gdk_screen_get_monitor_width_mm:
  * @screen: a #GdkScreen
- * @monitor_num: number of the monitor
+ * @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
+ * Returns: the width of the monitor, or -1 if not available
  *
  * Since: 2.14
  */
@@ -385,15 +415,21 @@ gint
 gdk_screen_get_monitor_width_mm        (GdkScreen *screen,
                                 gint       monitor_num)
 {
-  return get_monitor (screen, monitor_num)->width_mm;
+  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
+ * @monitor_num: number of the monitor, between 0 and gdk_screen_get_n_monitors (screen)
  *
- * Gets the height in millimeters of the specified monitor. 
+ * Gets the height in millimeters of the specified monitor.
  *
  * Returns: the height of the monitor, or -1 if not available
  *
@@ -403,18 +439,24 @@ gint
 gdk_screen_get_monitor_height_mm (GdkScreen *screen,
                                   gint       monitor_num)
 {
-  return get_monitor (screen, monitor_num)->height_mm;
+  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
+ * @monitor_num: number of the monitor, between 0 and gdk_screen_get_n_monitors (screen)
  *
- * Returns the output name of the specified monitor. 
+ * 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
  *
@@ -424,16 +466,22 @@ gchar *
 gdk_screen_get_monitor_plug_name (GdkScreen *screen,
                                  gint       monitor_num)
 {
-  return g_strdup (get_monitor (screen, monitor_num)->output_name);
+  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 
+ * @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 
+ * If the X server does not support version 1.2 of the RANDR
  * extension, 0 is returned.
  *
  * Returns: the XID of the monitor
@@ -444,34 +492,42 @@ XID
 gdk_x11_screen_get_monitor_output (GdkScreen *screen,
                                    gint       monitor_num)
 {
-  return get_monitor (screen, monitor_num)->output;
+  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
+ * @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 
+ * 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 
+ *
+ * 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 
+ */
+void
 gdk_screen_get_monitor_geometry (GdkScreen    *screen,
                                 gint          monitor_num,
                                 GdkRectangle *dest)
 {
-  if (dest) 
-    {
-      GdkX11Monitor *monitor = get_monitor (screen, monitor_num);
+  GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
 
-      *dest = monitor->geometry;
-    }
+  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;
 }
 
 /**
@@ -492,8 +548,8 @@ gdk_screen_get_monitor_geometry (GdkScreen    *screen,
  * For setting an overall opacity for a top-level window, see
  * gdk_window_set_opacity().
 
- * Return value: a colormap to use for windows with an alpha channel
- *   or %NULL if the capability is not available.
+ * Return value: (transfer none): a colormap to use for windows with
+ *     an alpha channel or %NULL if the capability is not available.
  *
  * Since: 2.8
  **/
@@ -524,8 +580,8 @@ gdk_screen_get_rgba_colormap (GdkScreen *screen)
  * alpha channel. See the docs for gdk_screen_get_rgba_colormap()
  * for caveats.
  * 
- * Return value: a visual to use for windows with an alpha channel
- *   or %NULL if the capability is not available.
+ * 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
  **/
@@ -544,7 +600,7 @@ gdk_screen_get_rgba_visual (GdkScreen *screen)
 /**
  * gdk_x11_screen_get_xscreen:
  * @screen: a #GdkScreen.
- * @returns: an Xlib <type>Screen*</type>
+ * @returns: (transfer none): an Xlib <type>Screen*</type>
  *
  * Returns the screen of a #GdkScreen.
  *
@@ -663,8 +719,50 @@ init_fake_xinerama (GdkScreen *screen)
   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_randr12 (GdkScreen *screen)
+init_randr13 (GdkScreen *screen)
 {
 #ifdef HAVE_RANDR
   GdkDisplay *display = gdk_screen_get_display (screen);
@@ -672,17 +770,20 @@ init_randr12 (GdkScreen *screen)
   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_randr12)
+  if (!display_x11->have_randr13)
       return FALSE;
 
-  resources = XRRGetScreenResources (screen_x11->xdisplay,
-                                    screen_x11->xroot_window);
+  resources = XRRGetScreenResourcesCurrent (screen_x11->xdisplay,
+                                           screen_x11->xroot_window);
   if (!resources)
     return FALSE;
-  
+
   monitors = g_array_sized_new (FALSE, TRUE, sizeof (GdkX11Monitor),
                                 resources->noutput);
 
@@ -691,6 +792,15 @@ init_randr12 (GdkScreen *screen)
       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;
@@ -701,11 +811,11 @@ init_randr12 (GdkScreen *screen)
          monitor.geometry.width = crtc->width;
          monitor.geometry.height = crtc->height;
 
-         /* FIXME: fill this out properly - need EDID parser */
          monitor.output = resources->outputs[i];
-         monitor.width_mm = -1;
-         monitor.height_mm = -1;
-         monitor.output_name = NULL;
+         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);
@@ -716,14 +826,56 @@ init_randr12 (GdkScreen *screen)
       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);
 
-  return TRUE;
+  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;
 }
 
@@ -750,22 +902,21 @@ init_solaris_xinerama (GdkScreen *screen)
    */
   if (result == 0)
     {
-      /* FIXME: We need to trap errors, since
-       * XINERAMA isn't always XINERAMA.
-       */ 
-      g_error ("error while retrieving Xinerama information");
+      return FALSE;
     }
 
-  screen_x11->monitors = g_new0 (GdkX11Monitor, n_rects);
-  screen_x11->n_monitors = n_rects;
+  screen_x11->monitors = g_new0 (GdkX11Monitor, n_monitors);
+  screen_x11->n_monitors = n_monitors;
 
-  for (i = 0; i < n_rects; i++)
+  for (i = 0; i < n_monitors; i++)
     {
       init_monitor_geometry (&screen_x11->monitors[i],
-                            rects[i].x, rects[i].y,
-                            rects[i].width, rects[i].height);
+                            monitors[i].x, monitors[i].y,
+                            monitors[i].width, monitors[i].height);
     }
-  
+
+  screen_x11->primary_monitor = 0;
+
   return TRUE;
 #endif /* HAVE_SOLARIS_XINERAMA */
 
@@ -812,6 +963,8 @@ init_xfree_xinerama (GdkScreen *screen)
   
   XFree (monitors);
   
+  screen_x11->primary_monitor = 0;
+
   return TRUE;
 #endif /* HAVE_XFREE_XINERAMA */
   
@@ -822,30 +975,60 @@ static void
 deinit_multihead (GdkScreen *screen)
 {
   GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
-  int i;
-
-  for (i = 0; i < screen_x11->n_monitors; ++i)
-    {
-      GdkX11Monitor *monitor = get_monitor (screen, i);
-
-      g_free (monitor->output_name);
-      g_free (monitor->manufacturer);
-    }
 
-  g_free (screen_x11->monitors);
+  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;
 
-  deinit_multihead (screen);
-  
   /* There are four different implementations of multihead support: 
    *
    *  1. Fake Xinerama for debugging purposes
@@ -858,7 +1041,7 @@ init_multihead (GdkScreen *screen)
   if (init_fake_xinerama (screen))
     return;
 
-  if (init_randr12 (screen))
+  if (init_randr13 (screen))
     return;
 
   if (XQueryExtension (GDK_SCREEN_XDISPLAY (screen), "XINERAMA",
@@ -874,6 +1057,7 @@ init_multihead (GdkScreen *screen)
   /* 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),
@@ -899,8 +1083,6 @@ _gdk_x11_screen_new (GdkDisplay *display,
   screen_x11->wmspec_check_window = None;
   /* we want this to be always non-null */
   screen_x11->window_manager_name = g_strdup ("unknown");
-  screen_x11->cm_selection_atom = make_cm_atom (screen_number);
-  screen_x11->is_composited = check_is_composited (display, screen_x11);
   
   init_multihead (screen);
   init_randr_support (screen);
@@ -911,6 +1093,22 @@ _gdk_x11_screen_new (GdkDisplay *display,
   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
@@ -957,36 +1155,71 @@ init_randr_support (GdkScreen * screen)
 #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
-  if (!XRRUpdateConfiguration (event))
-    return;
+  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
-  
-  init_multihead (screen);
-  g_signal_emit_by_name (screen, "size_changed");
-}
 
-void
-_gdk_x11_screen_process_monitors_change (GdkScreen *screen)
-{
-  init_multihead (screen);
+  process_monitors_change (screen);
 
-  g_signal_emit_by_name (screen, "monitors_changed");
+  if (width != gdk_screen_get_width (screen) ||
+      height != gdk_screen_get_height (screen))
+    g_signal_emit_by_name (screen, "size-changed");
 }
 
 void
@@ -1013,7 +1246,7 @@ _gdk_x11_screen_process_owner_change (GdkScreen *screen,
        {
          screen_x11->is_composited = composited;
 
-         g_signal_emit_by_name (screen, "composited_changed");
+         g_signal_emit_by_name (screen, "composited-changed");
        }
     }
 #endif
@@ -1205,14 +1438,14 @@ gdk_screen_get_window_stack (GdkScreen *screen)
       if ((type_return == XA_WINDOW) && (format_return == 32) &&
           (data) && (nitems_return > 0))
         {
-          GdkNativeWindow *stack = (GdkNativeWindow *) data;
+          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,
-                                                        stack[i]);
+                                                        (GdkNativeWindow)stack[i]);
 
               if (win != NULL)
                 ret = g_list_append (ret, win);
@@ -1226,5 +1459,634 @@ gdk_screen_get_window_stack (GdkScreen *screen)
   return ret;
 }
 
-#define __GDK_SCREEN_X11_C__
-#include "gdkaliasdef.c"
+/* 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 ();
+
+  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);
+  gdk_display_sync (display);
+
+  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_display_sync (screen_x11->display);
+
+          gdk_error_trap_pop ();
+
+          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;
+}