+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;
+}
+
+static gboolean
+gdk_x11_screen_get_setting (GdkScreen *screen,
+ const gchar *name,
+ GValue *value)
+{
+
+ const char *xsettings_name = NULL;
+ XSettingsResult result;
+ XSettingsSetting *setting = NULL;
+ GdkX11Screen *x11_screen;
+ gboolean success = FALSE;
+ gint i;
+ GValue tmp_val = { 0, };
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
+
+ x11_screen = GDK_X11_SCREEN (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 (x11_screen->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)
+{
+ GdkX11Screen *x11_screen;
+ GdkDisplay *display;
+ Atom type;
+ gint format;
+ gulong n_items;
+ gulong bytes_after;
+ guchar *data;
+ Window *xwindow;
+ GTimeVal tv;
+ gint error;
+
+ x11_screen = GDK_X11_SCREEN (screen);
+ display = x11_screen->display;
+
+ g_return_if_fail (GDK_X11_DISPLAY (display)->trusted_client);
+
+ g_get_current_time (&tv);
+
+ if (ABS (tv.tv_sec - x11_screen->last_wmspec_check_time) < 15)
+ return; /* we've checked recently */
+
+ x11_screen->last_wmspec_check_time = tv.tv_sec;
+
+ data = NULL;
+ XGetWindowProperty (x11_screen->xdisplay, x11_screen->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 (x11_screen->wmspec_check_window == *xwindow)
+ {
+ XFree (xwindow);
+ return;
+ }
+
+ gdk_x11_display_error_trap_push (display);
+
+ /* Find out if this WM goes away, so we can reset everything. */
+ XSelectInput (x11_screen->xdisplay, *xwindow, StructureNotifyMask);
+
+ error = gdk_x11_display_error_trap_pop (display);
+ if (!error)
+ {
+ x11_screen->wmspec_check_window = *xwindow;
+ x11_screen->need_refetch_net_supported = TRUE;
+ x11_screen->need_refetch_wm_name = TRUE;
+
+ /* Careful, reentrancy */
+ _gdk_x11_screen_window_manager_changed (GDK_SCREEN (x11_screen));
+ }
+ else if (error == BadWindow)
+ {
+ /* Leftover property, try again immediately, new wm may be starting up */
+ x11_screen->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;
+ GdkX11Screen *x11_screen;
+ NetWmSupportedAtoms *supported_atoms;
+ GdkDisplay *display;
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
+
+ x11_screen = GDK_X11_SCREEN (screen);
+ display = x11_screen->display;
+
+ if (!G_LIKELY (GDK_X11_DISPLAY (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 (x11_screen->wmspec_check_window == None)
+ return FALSE;
+
+ if (x11_screen->need_refetch_net_supported)
+ {
+ /* WM has changed since we last got the supported list,
+ * refetch it.
+ */
+ Atom type;
+ gint format;
+ gulong bytes_after;
+
+ x11_screen->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), x11_screen->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;
+}
+
+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)
+{
+ GdkX11Screen *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_x11_window_lookup_for_display (gdk_screen_get_display (screen), window);
+
+ if (is_start)
+ {
+ if (gdkwin)
+ g_object_ref (gdkwin);
+ else
+ {
+ gdkwin = gdk_x11_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;
+ GdkX11Screen *x11_screen = data;
+ int i;
+
+ if (x11_screen->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_x11_screen_init_events (GdkScreen *screen)
+{
+ GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
+
+ /* Keep a flag to avoid extra notifies that we don't need
+ */
+ x11_screen->xsettings_in_init = TRUE;
+ x11_screen->xsettings_client = xsettings_client_new_with_grab_funcs (x11_screen->xdisplay,
+ x11_screen->screen_num,
+ gdk_xsettings_notify_cb,
+ gdk_xsettings_watch_cb,
+ screen,
+ refcounted_grab_server,
+ refcounted_ungrab_server);
+ x11_screen->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)
+{
+ GdkX11Screen *x11_screen;
+ GdkDisplay *display;
+
+ x11_screen = GDK_X11_SCREEN (screen);
+ display = x11_screen->display;
+
+ if (!G_LIKELY (GDK_X11_DISPLAY (display)->trusted_client))
+ return x11_screen->window_manager_name;
+
+ fetch_net_wm_check_window (screen);
+
+ if (x11_screen->need_refetch_wm_name)
+ {
+ /* Get the name of the window manager */
+ x11_screen->need_refetch_wm_name = FALSE;
+
+ g_free (x11_screen->window_manager_name);
+ x11_screen->window_manager_name = g_strdup ("unknown");
+
+ if (x11_screen->wmspec_check_window != None)
+ {
+ Atom type;
+ gint format;
+ gulong n_items;
+ gulong bytes_after;
+ gchar *name;
+
+ name = NULL;
+
+ gdk_x11_display_error_trap_push (display);
+
+ XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
+ x11_screen->wmspec_check_window,
+ gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_WM_NAME"),
+ 0, G_MAXLONG, False,
+ gdk_x11_get_xatom_by_name_for_display (display,
+ "UTF8_STRING"),
+ &type, &format,
+ &n_items, &bytes_after,
+ (guchar **)&name);
+
+ gdk_x11_display_error_trap_pop_ignored (display);
+
+ if (name != NULL)
+ {
+ g_free (x11_screen->window_manager_name);
+ x11_screen->window_manager_name = g_strdup (name);
+ XFree (name);
+ }
+ }
+ }
+
+ return GDK_X11_SCREEN (screen)->window_manager_name;
+}
+
+static void
+gdk_x11_screen_class_init (GdkX11ScreenClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GdkScreenClass *screen_class = GDK_SCREEN_CLASS (klass);
+
+ object_class->dispose = gdk_x11_screen_dispose;
+ object_class->finalize = gdk_x11_screen_finalize;
+
+ screen_class->get_display = gdk_x11_screen_get_display;
+ screen_class->get_width = gdk_x11_screen_get_width;
+ screen_class->get_height = gdk_x11_screen_get_height;
+ screen_class->get_width_mm = gdk_x11_screen_get_width_mm;
+ screen_class->get_height_mm = gdk_x11_screen_get_height_mm;
+ screen_class->get_number = gdk_x11_screen_get_number;
+ screen_class->get_root_window = gdk_x11_screen_get_root_window;
+ screen_class->get_n_monitors = gdk_x11_screen_get_n_monitors;
+ screen_class->get_primary_monitor = gdk_x11_screen_get_primary_monitor;
+ screen_class->get_monitor_width_mm = gdk_x11_screen_get_monitor_width_mm;
+ screen_class->get_monitor_height_mm = gdk_x11_screen_get_monitor_height_mm;
+ screen_class->get_monitor_plug_name = gdk_x11_screen_get_monitor_plug_name;
+ screen_class->get_monitor_geometry = gdk_x11_screen_get_monitor_geometry;
+ screen_class->get_system_visual = _gdk_x11_screen_get_system_visual;
+ screen_class->get_rgba_visual = gdk_x11_screen_get_rgba_visual;
+ screen_class->is_composited = gdk_x11_screen_is_composited;
+ screen_class->make_display_name = gdk_x11_screen_make_display_name;
+ screen_class->get_active_window = gdk_x11_screen_get_active_window;
+ screen_class->get_window_stack = gdk_x11_screen_get_window_stack;
+ screen_class->get_setting = gdk_x11_screen_get_setting;
+ screen_class->visual_get_best_depth = _gdk_x11_screen_visual_get_best_depth;
+ screen_class->visual_get_best_type = _gdk_x11_screen_visual_get_best_type;
+ screen_class->visual_get_best = _gdk_x11_screen_visual_get_best;
+ screen_class->visual_get_best_with_depth = _gdk_x11_screen_visual_get_best_with_depth;
+ screen_class->visual_get_best_with_type = _gdk_x11_screen_visual_get_best_with_type;
+ screen_class->visual_get_best_with_both = _gdk_x11_screen_visual_get_best_with_both;
+ screen_class->query_depths = _gdk_x11_screen_query_depths;
+ screen_class->query_visual_types = _gdk_x11_screen_query_visual_types;
+ screen_class->list_visuals = _gdk_x11_screen_list_visuals;
+
+ 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 (GdkX11ScreenClass, window_manager_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+}