summaryrefslogtreecommitdiff
path: root/src/core/window-props.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/window-props.c')
-rw-r--r--src/core/window-props.c1553
1 files changed, 1553 insertions, 0 deletions
diff --git a/src/core/window-props.c b/src/core/window-props.c
new file mode 100644
index 00000000..b7b3e12d
--- /dev/null
+++ b/src/core/window-props.c
@@ -0,0 +1,1553 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/**
+ * \file window-props.c MetaWindow property handling
+ *
+ * A system which can inspect sets of properties of given windows
+ * and take appropriate action given their values.
+ *
+ * Note that all the meta_window_reload_propert* functions require a
+ * round trip to the server.
+ *
+ * The guts of this system are in meta_display_init_window_prop_hooks().
+ * Reading this function will give you insight into how this all fits
+ * together.
+ */
+
+/*
+ * Copyright (C) 2001, 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2004, 2005 Elijah Newren
+ * Copyright (C) 2009 Thomas Thurman
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#define _GNU_SOURCE
+#define _SVID_SOURCE /* for gethostname() */
+
+#include <config.h>
+#include "window-props.h"
+#include "errors.h"
+#include "xprops.h"
+#include "frame-private.h"
+#include "group.h"
+#include <X11/Xatom.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#ifdef HAVE_GTOP
+#include <glibtop/procuid.h>
+#include <errno.h>
+#include <pwd.h>
+#endif /* HAVE_GTOP */
+
+#ifndef HOST_NAME_MAX
+/* Solaris headers apparently don't define this so do so manually; #326745 */
+#define HOST_NAME_MAX 255
+#endif
+
+typedef void (* ReloadValueFunc) (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial);
+
+typedef struct MetaWindowPropHooks
+{
+ Atom property;
+ MetaPropValueType type;
+ ReloadValueFunc reload_func;
+} MetaWindowPropHooks;
+
+static MetaWindowPropHooks* find_hooks (MetaDisplay *display,
+ Atom property);
+
+
+void
+meta_window_reload_property (MetaWindow *window,
+ Atom property,
+ gboolean initial)
+{
+ meta_window_reload_properties (window, &property, 1, initial);
+}
+
+void
+meta_window_reload_properties (MetaWindow *window,
+ const Atom *properties,
+ int n_properties,
+ gboolean initial)
+{
+ meta_window_reload_properties_from_xwindow (window,
+ window->xwindow,
+ properties,
+ n_properties,
+ initial);
+}
+
+void
+meta_window_reload_property_from_xwindow (MetaWindow *window,
+ Window xwindow,
+ Atom property,
+ gboolean initial)
+{
+ meta_window_reload_properties_from_xwindow (window, xwindow, &property, 1,
+ initial);
+}
+
+void
+meta_window_reload_properties_from_xwindow (MetaWindow *window,
+ Window xwindow,
+ const Atom *properties,
+ int n_properties,
+ gboolean initial)
+{
+ int i;
+ MetaPropValue *values;
+
+ g_return_if_fail (properties != NULL);
+ g_return_if_fail (n_properties > 0);
+
+ values = g_new0 (MetaPropValue, n_properties);
+
+ for (i=0; i<n_properties; i++)
+ {
+ MetaWindowPropHooks *hooks = find_hooks (window->display,
+ properties[i]);
+
+ if (!hooks || hooks->type == META_PROP_VALUE_INVALID)
+ {
+ values[i].type = META_PROP_VALUE_INVALID;
+ values[i].atom = None;
+ }
+ else
+ {
+ values[i].type = hooks->type;
+ values[i].atom = properties[i];
+ }
+ }
+
+ meta_prop_get_values (window->display, xwindow,
+ values, n_properties);
+
+ for (i=0; i<n_properties; i++)
+ {
+ MetaWindowPropHooks *hooks = find_hooks (window->display,
+ properties[i]);
+
+ if (hooks && hooks->reload_func != NULL)
+ (* hooks->reload_func) (window, &values[i], initial);
+ }
+
+ meta_prop_free_values (values, n_properties);
+
+ g_free (values);
+}
+
+static void
+reload_wm_client_machine (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ g_free (window->wm_client_machine);
+ window->wm_client_machine = NULL;
+
+ if (value->type != META_PROP_VALUE_INVALID)
+ window->wm_client_machine = g_strdup (value->v.str);
+
+ meta_verbose ("Window has client machine \"%s\"\n",
+ window->wm_client_machine ? window->wm_client_machine : "unset");
+}
+
+static void
+complain_about_broken_client (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ meta_warning ("Broken client! Window %s changed client leader window or SM client ID\n",
+ window->desc);
+}
+
+static void
+reload_net_wm_window_type (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ meta_window_update_net_wm_type (window);
+}
+
+static void
+reload_icon (MetaWindow *window,
+ Atom atom)
+{
+ meta_icon_cache_property_changed (&window->icon_cache,
+ window->display,
+ atom);
+ meta_window_queue(window, META_QUEUE_UPDATE_ICON);
+}
+
+static void
+reload_net_wm_icon (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ reload_icon (window, window->display->atom__NET_WM_ICON);
+}
+
+static void
+reload_kwm_win_icon (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ reload_icon (window, window->display->atom__KWM_WIN_ICON);
+}
+
+static void
+reload_struts (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ meta_window_update_struts (window);
+}
+
+static void
+reload_wm_window_role (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ meta_window_update_role (window);
+}
+
+static void
+reload_net_wm_pid (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ if (value->type != META_PROP_VALUE_INVALID)
+ {
+ gulong cardinal = (int) value->v.cardinal;
+
+ if (cardinal <= 0)
+ meta_warning (_("Application set a bogus _NET_WM_PID %lu\n"),
+ cardinal);
+ else
+ {
+ window->net_wm_pid = cardinal;
+ meta_verbose ("Window has _NET_WM_PID %d\n",
+ window->net_wm_pid);
+ }
+ }
+}
+
+static void
+reload_net_wm_user_time (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ if (value->type != META_PROP_VALUE_INVALID)
+ {
+ gulong cardinal = value->v.cardinal;
+ meta_window_set_user_time (window, cardinal);
+ }
+}
+
+static void
+reload_net_wm_user_time_window (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ if (value->type != META_PROP_VALUE_INVALID)
+ {
+ /* Unregister old NET_WM_USER_TIME_WINDOW */
+ if (window->user_time_window != None)
+ {
+ /* See the comment to the meta_display_register_x_window call below. */
+ meta_display_unregister_x_window (window->display,
+ window->user_time_window);
+ /* Don't get events on not-managed windows */
+ XSelectInput (window->display->xdisplay,
+ window->user_time_window,
+ NoEventMask);
+ }
+
+
+ /* Obtain the new NET_WM_USER_TIME_WINDOW and register it */
+ window->user_time_window = value->v.xwindow;
+ if (window->user_time_window != None)
+ {
+ /* Kind of a hack; display.c:event_callback() ignores events
+ * for unknown windows. We make window->user_time_window
+ * known by registering it with window (despite the fact
+ * that window->xwindow is already registered with window).
+ * This basically means that property notifies to either the
+ * window->user_time_window or window->xwindow will be
+ * treated identically and will result in functions for
+ * window being called to update it. Maybe we should ignore
+ * any property notifies to window->user_time_window other
+ * than atom__NET_WM_USER_TIME ones, but I just don't care
+ * and it's not specified in the spec anyway.
+ */
+ meta_display_register_x_window (window->display,
+ &window->user_time_window,
+ window);
+ /* Just listen for property notify events */
+ XSelectInput (window->display->xdisplay,
+ window->user_time_window,
+ PropertyChangeMask);
+
+ /* Manually load the _NET_WM_USER_TIME field from the given window
+ * at this time as well. If the user_time_window ever broadens in
+ * scope, we'll probably want to load all relevant properties here.
+ */
+ meta_window_reload_property_from_xwindow (
+ window,
+ window->user_time_window,
+ window->display->atom__NET_WM_USER_TIME,
+ initial);
+ }
+ }
+}
+
+/**
+ * Finds who owns a particular process, if we can.
+ *
+ * \param process The process's ID.
+ * \result Set to the ID of the user, if we returned true.
+ *
+ * \result True if we could tell.
+ */
+static gboolean
+owner_of_process (pid_t process, uid_t *result)
+{
+#ifdef HAVE_GTOP
+ glibtop_proc_uid process_details;
+
+ glibtop_get_proc_uid (&process_details, process);
+
+ *result = process_details.uid;
+ return TRUE;
+#else
+ /* I don't know, maybe we could do something hairy like see whether
+ * /proc/$PID exists and who owns it, in case they have procfs.
+ */
+ return FALSE;
+#endif /* HAVE_GTOP */
+}
+
+#define MAX_TITLE_LENGTH 512
+
+/**
+ * Called by set_window_title and set_icon_title to set the value of
+ * *target to title. It required and atom is set, it will update the
+ * appropriate property.
+ *
+ * Returns TRUE if a new title was set.
+ */
+static gboolean
+set_title_text (MetaWindow *window,
+ gboolean previous_was_modified,
+ const char *title,
+ Atom atom,
+ char **target)
+{
+ char hostname[HOST_NAME_MAX + 1];
+ gboolean modified = FALSE;
+
+ if (!target)
+ return FALSE;
+
+ g_free (*target);
+
+ if (!title)
+ *target = g_strdup ("");
+ else if (g_utf8_strlen (title, MAX_TITLE_LENGTH + 1) > MAX_TITLE_LENGTH)
+ {
+ *target = meta_g_utf8_strndup (title, MAX_TITLE_LENGTH);
+ modified = TRUE;
+ }
+ /* if WM_CLIENT_MACHINE indicates this machine is on a remote host
+ * let's place that hostname in the title */
+ else if (window->wm_client_machine &&
+ !gethostname (hostname, HOST_NAME_MAX + 1) &&
+ strcmp (hostname, window->wm_client_machine))
+ {
+ /* Translators: the title of a window from another machine */
+ *target = g_strdup_printf (_("%s (on %s)"),
+ title, window->wm_client_machine);
+ modified = TRUE;
+ }
+ else if (window->net_wm_pid != -1)
+ {
+ /* We know the process which owns this window; perhaps we can
+ * find out the name of its owner (if it's not us).
+ */
+
+ char *found_name = NULL;
+
+ uid_t window_owner = 0;
+ gboolean window_owner_known =
+ owner_of_process (window->net_wm_pid, &window_owner);
+
+ /* Assume a window with unknown ownership is ours (call it usufruct!) */
+ gboolean window_owner_is_us =
+ !window_owner_known || window_owner==getuid ();
+
+ if (window_owner_is_us)
+ {
+ /* we own it, so fall back to the simple case */
+ *target = g_strdup (title);
+ }
+ else
+ {
+ /* it belongs to window_owner. So what's their name? */
+
+ if (window_owner==0)
+ {
+ /* Simple case-- don't bother to look it up. It's root. */
+ *target = g_strdup_printf (_("%s (as superuser)"),
+ title);
+ }
+ else
+ {
+ /* Okay, let's look up the name. */
+ struct passwd *pwd;
+
+ errno = 0;
+ pwd = getpwuid (window_owner);
+ if (errno==0 && pwd!=NULL)
+ {
+ found_name = pwd->pw_name;
+ }
+
+ if (found_name)
+ /* Translators: the title of a window owned by another user
+ * on this machine */
+ *target = g_strdup_printf (_("%s (as %s)"),
+ title,
+ found_name);
+ else
+ /* Translators: the title of a window owned by another user
+ * on this machine, whose name we don't know */
+ *target = g_strdup_printf (_("%s (as another user)"),
+ title);
+ }
+ /* either way we changed it */
+ modified = TRUE;
+
+ }
+ }
+ else
+ *target = g_strdup (title);
+
+ if (modified && atom != None)
+ meta_prop_set_utf8_string_hint (window->display,
+ window->xwindow,
+ atom, *target);
+
+ /* Bug 330671 -- Don't forget to clear _NET_WM_VISIBLE_(ICON_)NAME */
+ if (!modified && previous_was_modified)
+ {
+ meta_error_trap_push (window->display);
+ XDeleteProperty (window->display->xdisplay,
+ window->xwindow,
+ atom);
+ meta_error_trap_pop (window->display, FALSE);
+ }
+
+ return modified;
+}
+
+static void
+set_window_title (MetaWindow *window,
+ const char *title)
+{
+ char *str;
+
+ gboolean modified =
+ set_title_text (window,
+ window->using_net_wm_visible_name,
+ title,
+ window->display->atom__NET_WM_VISIBLE_NAME,
+ &window->title);
+ window->using_net_wm_visible_name = modified;
+
+ /* strndup is a hack since GNU libc has broken %.10s */
+ str = g_strndup (window->title, 10);
+ g_free (window->desc);
+ window->desc = g_strdup_printf ("0x%lx (%s)", window->xwindow, str);
+ g_free (str);
+
+ if (window->frame)
+ meta_ui_set_frame_title (window->screen->ui,
+ window->frame->xwindow,
+ window->title);
+}
+
+static void
+reload_net_wm_name (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ if (value->type != META_PROP_VALUE_INVALID)
+ {
+ set_window_title (window, value->v.str);
+ window->using_net_wm_name = TRUE;
+
+ meta_verbose ("Using _NET_WM_NAME for new title of %s: \"%s\"\n",
+ window->desc, window->title);
+ }
+ else
+ {
+ set_window_title (window, NULL);
+ window->using_net_wm_name = FALSE;
+ if (!initial)
+ meta_window_reload_property (window, XA_WM_NAME, FALSE);
+ }
+}
+
+static void
+reload_wm_name (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ if (window->using_net_wm_name)
+ {
+ meta_verbose ("Ignoring WM_NAME \"%s\" as _NET_WM_NAME is set\n",
+ value->v.str);
+ return;
+ }
+
+ if (value->type != META_PROP_VALUE_INVALID)
+ {
+ set_window_title (window, value->v.str);
+
+ meta_verbose ("Using WM_NAME for new title of %s: \"%s\"\n",
+ window->desc, window->title);
+ }
+ else
+ {
+ set_window_title (window, NULL);
+ }
+}
+
+static void
+set_icon_title (MetaWindow *window,
+ const char *title)
+{
+ gboolean modified =
+ set_title_text (window,
+ window->using_net_wm_visible_icon_name,
+ title,
+ window->display->atom__NET_WM_VISIBLE_ICON_NAME,
+ &window->icon_name);
+ window->using_net_wm_visible_icon_name = modified;
+}
+
+static void
+reload_net_wm_icon_name (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ if (value->type != META_PROP_VALUE_INVALID)
+ {
+ set_icon_title (window, value->v.str);
+ window->using_net_wm_icon_name = TRUE;
+
+ meta_verbose ("Using _NET_WM_ICON_NAME for new title of %s: \"%s\"\n",
+ window->desc, window->title);
+ }
+ else
+ {
+ set_icon_title (window, NULL);
+ window->using_net_wm_icon_name = FALSE;
+ if (!initial)
+ meta_window_reload_property (window, XA_WM_ICON_NAME, FALSE);
+ }
+}
+
+static void
+reload_wm_icon_name (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ if (window->using_net_wm_icon_name)
+ {
+ meta_verbose ("Ignoring WM_ICON_NAME \"%s\" as _NET_WM_ICON_NAME is set\n",
+ value->v.str);
+ return;
+ }
+
+ if (value->type != META_PROP_VALUE_INVALID)
+ {
+ set_icon_title (window, value->v.str);
+
+ meta_verbose ("Using WM_ICON_NAME for new title of %s: \"%s\"\n",
+ window->desc, window->title);
+ }
+ else
+ {
+ set_icon_title (window, NULL);
+ }
+}
+
+static void
+reload_net_wm_state (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ int i;
+
+ /* We know this is only an initial window creation,
+ * clients don't change the property.
+ */
+
+ if (!initial) {
+ /* no, they DON'T change the property */
+ meta_verbose ("Ignoring _NET_WM_STATE: we should be the one who set "
+ "the property in the first place\n");
+ return;
+ }
+
+ window->shaded = FALSE;
+ window->maximized_horizontally = FALSE;
+ window->maximized_vertically = FALSE;
+ window->fullscreen = FALSE;
+ window->wm_state_modal = FALSE;
+ window->wm_state_skip_taskbar = FALSE;
+ window->wm_state_skip_pager = FALSE;
+ window->wm_state_above = FALSE;
+ window->wm_state_below = FALSE;
+ window->wm_state_demands_attention = FALSE;
+
+ if (value->type == META_PROP_VALUE_INVALID)
+ return;
+
+ i = 0;
+ while (i < value->v.atom_list.n_atoms)
+ {
+ if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_SHADED)
+ window->shaded = TRUE;
+ else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_MAXIMIZED_HORZ)
+ window->maximize_horizontally_after_placement = TRUE;
+ else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_MAXIMIZED_VERT)
+ window->maximize_vertically_after_placement = TRUE;
+ else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_HIDDEN)
+ window->minimize_after_placement = TRUE;
+ else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_MODAL)
+ window->wm_state_modal = TRUE;
+ else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_SKIP_TASKBAR)
+ window->wm_state_skip_taskbar = TRUE;
+ else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_SKIP_PAGER)
+ window->wm_state_skip_pager = TRUE;
+ else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_FULLSCREEN)
+ window->fullscreen_after_placement = TRUE;
+ else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_ABOVE)
+ window->wm_state_above = TRUE;
+ else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_BELOW)
+ window->wm_state_below = TRUE;
+ else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_DEMANDS_ATTENTION)
+ window->wm_state_demands_attention = TRUE;
+ else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_STICKY)
+ window->on_all_workspaces = TRUE;
+
+ ++i;
+ }
+
+ meta_verbose ("Reloaded _NET_WM_STATE for %s\n",
+ window->desc);
+
+ meta_window_recalc_window_type (window);
+}
+
+static void
+reload_mwm_hints (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ MotifWmHints *hints;
+
+ window->mwm_decorated = TRUE;
+ window->mwm_border_only = FALSE;
+ window->mwm_has_close_func = TRUE;
+ window->mwm_has_minimize_func = TRUE;
+ window->mwm_has_maximize_func = TRUE;
+ window->mwm_has_move_func = TRUE;
+ window->mwm_has_resize_func = TRUE;
+
+ if (value->type == META_PROP_VALUE_INVALID)
+ {
+ meta_verbose ("Window %s has no MWM hints\n", window->desc);
+ meta_window_recalc_features (window);
+ return;
+ }
+
+ hints = value->v.motif_hints;
+
+ /* We support those MWM hints deemed non-stupid */
+
+ meta_verbose ("Window %s has MWM hints\n",
+ window->desc);
+
+ if (hints->flags & MWM_HINTS_DECORATIONS)
+ {
+ meta_verbose ("Window %s sets MWM_HINTS_DECORATIONS 0x%lx\n",
+ window->desc, hints->decorations);
+
+ if (hints->decorations == 0)
+ window->mwm_decorated = FALSE;
+ /* some input methods use this */
+ else if (hints->decorations == MWM_DECOR_BORDER)
+ window->mwm_border_only = TRUE;
+ }
+ else
+ meta_verbose ("Decorations flag unset\n");
+
+ if (hints->flags & MWM_HINTS_FUNCTIONS)
+ {
+ gboolean toggle_value;
+
+ meta_verbose ("Window %s sets MWM_HINTS_FUNCTIONS 0x%lx\n",
+ window->desc, hints->functions);
+
+ /* If _ALL is specified, then other flags indicate what to turn off;
+ * if ALL is not specified, flags are what to turn on.
+ * at least, I think so
+ */
+
+ if ((hints->functions & MWM_FUNC_ALL) == 0)
+ {
+ toggle_value = TRUE;
+
+ meta_verbose ("Window %s disables all funcs then reenables some\n",
+ window->desc);
+ window->mwm_has_close_func = FALSE;
+ window->mwm_has_minimize_func = FALSE;
+ window->mwm_has_maximize_func = FALSE;
+ window->mwm_has_move_func = FALSE;
+ window->mwm_has_resize_func = FALSE;
+ }
+ else
+ {
+ meta_verbose ("Window %s enables all funcs then disables some\n",
+ window->desc);
+ toggle_value = FALSE;
+ }
+
+ if ((hints->functions & MWM_FUNC_CLOSE) != 0)
+ {
+ meta_verbose ("Window %s toggles close via MWM hints\n",
+ window->desc);
+ window->mwm_has_close_func = toggle_value;
+ }
+ if ((hints->functions & MWM_FUNC_MINIMIZE) != 0)
+ {
+ meta_verbose ("Window %s toggles minimize via MWM hints\n",
+ window->desc);
+ window->mwm_has_minimize_func = toggle_value;
+ }
+ if ((hints->functions & MWM_FUNC_MAXIMIZE) != 0)
+ {
+ meta_verbose ("Window %s toggles maximize via MWM hints\n",
+ window->desc);
+ window->mwm_has_maximize_func = toggle_value;
+ }
+ if ((hints->functions & MWM_FUNC_MOVE) != 0)
+ {
+ meta_verbose ("Window %s toggles move via MWM hints\n",
+ window->desc);
+ window->mwm_has_move_func = toggle_value;
+ }
+ if ((hints->functions & MWM_FUNC_RESIZE) != 0)
+ {
+ meta_verbose ("Window %s toggles resize via MWM hints\n",
+ window->desc);
+ window->mwm_has_resize_func = toggle_value;
+ }
+ }
+ else
+ meta_verbose ("Functions flag unset\n");
+
+ meta_window_recalc_features (window);
+
+ /* We do all this anyhow at the end of meta_window_new() */
+ if (!window->constructing)
+ {
+ if (window->decorated)
+ meta_window_ensure_frame (window);
+ else
+ meta_window_destroy_frame (window);
+
+ meta_window_queue (window,
+ META_QUEUE_MOVE_RESIZE |
+ /* because ensure/destroy frame may unmap: */
+ META_QUEUE_CALC_SHOWING);
+ }
+}
+
+static void
+reload_wm_class (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ if (window->res_class)
+ g_free (window->res_class);
+ if (window->res_name)
+ g_free (window->res_name);
+
+ window->res_class = NULL;
+ window->res_name = NULL;
+
+ if (value->type != META_PROP_VALUE_INVALID)
+ {
+ if (value->v.class_hint.res_name)
+ window->res_name = g_strdup (value->v.class_hint.res_name);
+
+ if (value->v.class_hint.res_class)
+ window->res_class = g_strdup (value->v.class_hint.res_class);
+ }
+
+ meta_verbose ("Window %s class: '%s' name: '%s'\n",
+ window->desc,
+ window->res_class ? window->res_class : "none",
+ window->res_name ? window->res_name : "none");
+}
+
+static void
+reload_net_wm_desktop (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ if (value->type != META_PROP_VALUE_INVALID)
+ {
+ window->initial_workspace_set = TRUE;
+ window->initial_workspace = value->v.cardinal;
+ meta_topic (META_DEBUG_PLACEMENT,
+ "Read initial workspace prop %d for %s\n",
+ window->initial_workspace, window->desc);
+ }
+}
+
+static void
+reload_net_startup_id (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ guint32 timestamp = window->net_wm_user_time;
+ MetaWorkspace *workspace = NULL;
+
+ g_free (window->startup_id);
+
+ if (value->type != META_PROP_VALUE_INVALID)
+ window->startup_id = g_strdup (value->v.str);
+ else
+ window->startup_id = NULL;
+
+ /* Update timestamp and workspace on a running window */
+ if (!window->constructing)
+ {
+ window->initial_timestamp_set = 0;
+ window->initial_workspace_set = 0;
+
+ if (meta_screen_apply_startup_properties (window->screen, window))
+ {
+
+ if (window->initial_timestamp_set)
+ timestamp = window->initial_timestamp;
+ if (window->initial_workspace_set)
+ workspace = meta_screen_get_workspace_by_index (window->screen, window->initial_workspace);
+
+ meta_window_activate_with_workspace (window, timestamp, workspace);
+ }
+ }
+
+ meta_verbose ("New _NET_STARTUP_ID \"%s\" for %s\n",
+ window->startup_id ? window->startup_id : "unset",
+ window->desc);
+}
+
+static void
+reload_update_counter (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ if (value->type != META_PROP_VALUE_INVALID)
+ {
+#ifdef HAVE_XSYNC
+ XSyncCounter counter = value->v.xcounter;
+
+ window->sync_request_counter = counter;
+ meta_verbose ("Window has _NET_WM_SYNC_REQUEST_COUNTER 0x%lx\n",
+ window->sync_request_counter);
+#endif
+ }
+}
+
+#define FLAG_TOGGLED_ON(old,new,flag) \
+ (((old)->flags & (flag)) == 0 && \
+ ((new)->flags & (flag)) != 0)
+
+#define FLAG_TOGGLED_OFF(old,new,flag) \
+ (((old)->flags & (flag)) != 0 && \
+ ((new)->flags & (flag)) == 0)
+
+#define FLAG_CHANGED(old,new,flag) \
+ (FLAG_TOGGLED_ON(old,new,flag) || FLAG_TOGGLED_OFF(old,new,flag))
+
+static void
+spew_size_hints_differences (const XSizeHints *old,
+ const XSizeHints *new)
+{
+ if (FLAG_CHANGED (old, new, USPosition))
+ meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: USPosition now %s\n",
+ FLAG_TOGGLED_ON (old, new, USPosition) ? "set" : "unset");
+ if (FLAG_CHANGED (old, new, USSize))
+ meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: USSize now %s\n",
+ FLAG_TOGGLED_ON (old, new, USSize) ? "set" : "unset");
+ if (FLAG_CHANGED (old, new, PPosition))
+ meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: PPosition now %s\n",
+ FLAG_TOGGLED_ON (old, new, PPosition) ? "set" : "unset");
+ if (FLAG_CHANGED (old, new, PSize))
+ meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: PSize now %s\n",
+ FLAG_TOGGLED_ON (old, new, PSize) ? "set" : "unset");
+ if (FLAG_CHANGED (old, new, PMinSize))
+ meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: PMinSize now %s (%d x %d -> %d x %d)\n",
+ FLAG_TOGGLED_ON (old, new, PMinSize) ? "set" : "unset",
+ old->min_width, old->min_height,
+ new->min_width, new->min_height);
+ if (FLAG_CHANGED (old, new, PMaxSize))
+ meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: PMaxSize now %s (%d x %d -> %d x %d)\n",
+ FLAG_TOGGLED_ON (old, new, PMaxSize) ? "set" : "unset",
+ old->max_width, old->max_height,
+ new->max_width, new->max_height);
+ if (FLAG_CHANGED (old, new, PResizeInc))
+ meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: PResizeInc now %s (width_inc %d -> %d height_inc %d -> %d)\n",
+ FLAG_TOGGLED_ON (old, new, PResizeInc) ? "set" : "unset",
+ old->width_inc, new->width_inc,
+ old->height_inc, new->height_inc);
+ if (FLAG_CHANGED (old, new, PAspect))
+ meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: PAspect now %s (min %d/%d -> %d/%d max %d/%d -> %d/%d)\n",
+ FLAG_TOGGLED_ON (old, new, PAspect) ? "set" : "unset",
+ old->min_aspect.x, old->min_aspect.y,
+ new->min_aspect.x, new->min_aspect.y,
+ old->max_aspect.x, old->max_aspect.y,
+ new->max_aspect.x, new->max_aspect.y);
+ if (FLAG_CHANGED (old, new, PBaseSize))
+ meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: PBaseSize now %s (%d x %d -> %d x %d)\n",
+ FLAG_TOGGLED_ON (old, new, PBaseSize) ? "set" : "unset",
+ old->base_width, old->base_height,
+ new->base_width, new->base_height);
+ if (FLAG_CHANGED (old, new, PWinGravity))
+ meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: PWinGravity now %s (%d -> %d)\n",
+ FLAG_TOGGLED_ON (old, new, PWinGravity) ? "set" : "unset",
+ old->win_gravity, new->win_gravity);
+}
+
+void
+meta_set_normal_hints (MetaWindow *window,
+ XSizeHints *hints)
+{
+ int x, y, w, h;
+ double minr, maxr;
+ /* Some convenience vars */
+ int minw, minh, maxw, maxh; /* min/max width/height */
+ int basew, baseh, winc, hinc; /* base width/height, width/height increment */
+
+ /* Save the last ConfigureRequest, which we put here.
+ * Values here set in the hints are supposed to
+ * be ignored.
+ */
+ x = window->size_hints.x;
+ y = window->size_hints.y;
+ w = window->size_hints.width;
+ h = window->size_hints.height;
+
+ /* as far as I can tell, value->v.size_hints.flags is just to
+ * check whether we had old-style normal hints without gravity,
+ * base size as returned by XGetNormalHints(), so we don't
+ * really use it as we fixup window->size_hints to have those
+ * fields if they're missing.
+ */
+
+ /*
+ * When the window is first created, NULL hints will
+ * be passed in which will initialize all of the fields
+ * as if flags were zero
+ */
+ if (hints)
+ window->size_hints = *hints;
+ else
+ window->size_hints.flags = 0;
+
+ /* Put back saved ConfigureRequest. */
+ window->size_hints.x = x;
+ window->size_hints.y = y;
+ window->size_hints.width = w;
+ window->size_hints.height = h;
+
+ /* Get base size hints */
+ if (window->size_hints.flags & PBaseSize)
+ {
+ meta_topic (META_DEBUG_GEOMETRY, "Window %s sets base size %d x %d\n",
+ window->desc,
+ window->size_hints.base_width,
+ window->size_hints.base_height);
+ }
+ else if (window->size_hints.flags & PMinSize)
+ {
+ window->size_hints.base_width = window->size_hints.min_width;
+ window->size_hints.base_height = window->size_hints.min_height;
+ }
+ else
+ {
+ window->size_hints.base_width = 0;
+ window->size_hints.base_height = 0;
+ }
+ window->size_hints.flags |= PBaseSize;
+
+ /* Get min size hints */
+ if (window->size_hints.flags & PMinSize)
+ {
+ meta_topic (META_DEBUG_GEOMETRY, "Window %s sets min size %d x %d\n",
+ window->desc,
+ window->size_hints.min_width,
+ window->size_hints.min_height);
+ }
+ else if (window->size_hints.flags & PBaseSize)
+ {
+ window->size_hints.min_width = window->size_hints.base_width;
+ window->size_hints.min_height = window->size_hints.base_height;
+ }
+ else
+ {
+ window->size_hints.min_width = 0;
+ window->size_hints.min_height = 0;
+ }
+ window->size_hints.flags |= PMinSize;
+
+ /* Get max size hints */
+ if (window->size_hints.flags & PMaxSize)
+ {
+ meta_topic (META_DEBUG_GEOMETRY, "Window %s sets max size %d x %d\n",
+ window->desc,
+ window->size_hints.max_width,
+ window->size_hints.max_height);
+ }
+ else
+ {
+ window->size_hints.max_width = G_MAXINT;
+ window->size_hints.max_height = G_MAXINT;
+ window->size_hints.flags |= PMaxSize;
+ }
+
+ /* Get resize increment hints */
+ if (window->size_hints.flags & PResizeInc)
+ {
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Window %s sets resize width inc: %d height inc: %d\n",
+ window->desc,
+ window->size_hints.width_inc,
+ window->size_hints.height_inc);
+ }
+ else
+ {
+ window->size_hints.width_inc = 1;
+ window->size_hints.height_inc = 1;
+ window->size_hints.flags |= PResizeInc;
+ }
+
+ /* Get aspect ratio hints */
+ if (window->size_hints.flags & PAspect)
+ {
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Window %s sets min_aspect: %d/%d max_aspect: %d/%d\n",
+ window->desc,
+ window->size_hints.min_aspect.x,
+ window->size_hints.min_aspect.y,
+ window->size_hints.max_aspect.x,
+ window->size_hints.max_aspect.y);
+ }
+ else
+ {
+ window->size_hints.min_aspect.x = 1;
+ window->size_hints.min_aspect.y = G_MAXINT;
+ window->size_hints.max_aspect.x = G_MAXINT;
+ window->size_hints.max_aspect.y = 1;
+ window->size_hints.flags |= PAspect;
+ }
+
+ /* Get gravity hint */
+ if (window->size_hints.flags & PWinGravity)
+ {
+ meta_topic (META_DEBUG_GEOMETRY, "Window %s sets gravity %d\n",
+ window->desc,
+ window->size_hints.win_gravity);
+ }
+ else
+ {
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Window %s doesn't set gravity, using NW\n",
+ window->desc);
+ window->size_hints.win_gravity = NorthWestGravity;
+ window->size_hints.flags |= PWinGravity;
+ }
+
+ /*** Lots of sanity checking ***/
+
+ /* Verify all min & max hints are at least 1 pixel */
+ if (window->size_hints.min_width < 1)
+ {
+ /* someone is on crack */
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Window %s sets min width to 0, which makes no sense\n",
+ window->desc);
+ window->size_hints.min_width = 1;
+ }
+ if (window->size_hints.max_width < 1)
+ {
+ /* another cracksmoker */
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Window %s sets max width to 0, which makes no sense\n",
+ window->desc);
+ window->size_hints.max_width = 1;
+ }
+ if (window->size_hints.min_height < 1)
+ {
+ /* another cracksmoker */
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Window %s sets min height to 0, which makes no sense\n",
+ window->desc);
+ window->size_hints.min_height = 1;
+ }
+ if (window->size_hints.max_height < 1)
+ {
+ /* another cracksmoker */
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Window %s sets max height to 0, which makes no sense\n",
+ window->desc);
+ window->size_hints.max_height = 1;
+ }
+
+ /* Verify size increment hints are at least 1 pixel */
+ if (window->size_hints.width_inc < 1)
+ {
+ /* app authors find so many ways to smoke crack */
+ window->size_hints.width_inc = 1;
+ meta_topic (META_DEBUG_GEOMETRY, "Corrected 0 width_inc to 1\n");
+ }
+ if (window->size_hints.height_inc < 1)
+ {
+ /* another cracksmoker */
+ window->size_hints.height_inc = 1;
+ meta_topic (META_DEBUG_GEOMETRY, "Corrected 0 height_inc to 1\n");
+ }
+ /* divide by 0 cracksmokers; note that x & y in (min|max)_aspect are
+ * numerator & denominator
+ */
+ if (window->size_hints.min_aspect.y < 1)
+ window->size_hints.min_aspect.y = 1;
+ if (window->size_hints.max_aspect.y < 1)
+ window->size_hints.max_aspect.y = 1;
+
+ minw = window->size_hints.min_width; minh = window->size_hints.min_height;
+ maxw = window->size_hints.max_width; maxh = window->size_hints.max_height;
+ basew = window->size_hints.base_width; baseh = window->size_hints.base_height;
+ winc = window->size_hints.width_inc; hinc = window->size_hints.height_inc;
+
+ /* Make sure min and max size hints are consistent with the base + increment
+ * size hints. If they're not, it's not a real big deal, but it means the
+ * effective min and max size are more restrictive than the application
+ * specified values.
+ */
+ if ((minw - basew) % winc != 0)
+ {
+ /* Take advantage of integer division throwing away the remainder... */
+ window->size_hints.min_width = basew + ((minw - basew)/winc + 1)*winc;
+
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Window %s has width_inc (%d) that does not evenly divide "
+ "min_width - base_width (%d - %d); thus effective "
+ "min_width is really %d\n",
+ window->desc,
+ winc, minw, basew, window->size_hints.min_width);
+ minw = window->size_hints.min_width;
+ }
+ if (maxw != G_MAXINT && (maxw - basew) % winc != 0)
+ {
+ /* Take advantage of integer division throwing away the remainder... */
+ window->size_hints.max_width = basew + ((maxw - basew)/winc)*winc;
+
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Window %s has width_inc (%d) that does not evenly divide "
+ "max_width - base_width (%d - %d); thus effective "
+ "max_width is really %d\n",
+ window->desc,
+ winc, maxw, basew, window->size_hints.max_width);
+ maxw = window->size_hints.max_width;
+ }
+ if ((minh - baseh) % hinc != 0)
+ {
+ /* Take advantage of integer division throwing away the remainder... */
+ window->size_hints.min_height = baseh + ((minh - baseh)/hinc + 1)*hinc;
+
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Window %s has height_inc (%d) that does not evenly divide "
+ "min_height - base_height (%d - %d); thus effective "
+ "min_height is really %d\n",
+ window->desc,
+ hinc, minh, baseh, window->size_hints.min_height);
+ minh = window->size_hints.min_height;
+ }
+ if (maxh != G_MAXINT && (maxh - baseh) % hinc != 0)
+ {
+ /* Take advantage of integer division throwing away the remainder... */
+ window->size_hints.max_height = baseh + ((maxh - baseh)/hinc)*hinc;
+
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Window %s has height_inc (%d) that does not evenly divide "
+ "max_height - base_height (%d - %d); thus effective "
+ "max_height is really %d\n",
+ window->desc,
+ hinc, maxh, baseh, window->size_hints.max_height);
+ maxh = window->size_hints.max_height;
+ }
+
+ /* make sure maximum size hints are compatible with minimum size hints; min
+ * size hints take precedence.
+ */
+ if (window->size_hints.max_width < window->size_hints.min_width)
+ {
+ /* another cracksmoker */
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Window %s sets max width %d less than min width %d, "
+ "disabling resize\n",
+ window->desc,
+ window->size_hints.max_width,
+ window->size_hints.min_width);
+ maxw = window->size_hints.max_width = window->size_hints.min_width;
+ }
+ if (window->size_hints.max_height < window->size_hints.min_height)
+ {
+ /* another cracksmoker */
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Window %s sets max height %d less than min height %d, "
+ "disabling resize\n",
+ window->desc,
+ window->size_hints.max_height,
+ window->size_hints.min_height);
+ maxh = window->size_hints.max_height = window->size_hints.min_height;
+ }
+
+ /* Make sure the aspect ratio hints are sane. */
+ minr = window->size_hints.min_aspect.x /
+ (double)window->size_hints.min_aspect.y;
+ maxr = window->size_hints.max_aspect.x /
+ (double)window->size_hints.max_aspect.y;
+ if (minr > maxr)
+ {
+ /* another cracksmoker; not even minimally (self) consistent */
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Window %s sets min aspect ratio larger than max aspect "
+ "ratio; disabling aspect ratio constraints.\n",
+ window->desc);
+ window->size_hints.min_aspect.x = 1;
+ window->size_hints.min_aspect.y = G_MAXINT;
+ window->size_hints.max_aspect.x = G_MAXINT;
+ window->size_hints.max_aspect.y = 1;
+ }
+ else /* check consistency of aspect ratio hints with other hints */
+ {
+ if (minh > 0 && minr > (maxw / (double)minh))
+ {
+ /* another cracksmoker */
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Window %s sets min aspect ratio larger than largest "
+ "aspect ratio possible given min/max size constraints; "
+ "disabling min aspect ratio constraint.\n",
+ window->desc);
+ window->size_hints.min_aspect.x = 1;
+ window->size_hints.min_aspect.y = G_MAXINT;
+ }
+ if (maxr < (minw / (double)maxh))
+ {
+ /* another cracksmoker */
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Window %s sets max aspect ratio smaller than smallest "
+ "aspect ratio possible given min/max size constraints; "
+ "disabling max aspect ratio constraint.\n",
+ window->desc);
+ window->size_hints.max_aspect.x = G_MAXINT;
+ window->size_hints.max_aspect.y = 1;
+ }
+ /* FIXME: Would be nice to check that aspect ratios are
+ * consistent with base and size increment constraints.
+ */
+ }
+}
+
+static void
+reload_normal_hints (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ if (value->type != META_PROP_VALUE_INVALID)
+ {
+ XSizeHints old_hints;
+
+ meta_topic (META_DEBUG_GEOMETRY, "Updating WM_NORMAL_HINTS for %s\n", window->desc);
+
+ old_hints = window->size_hints;
+
+ meta_set_normal_hints (window, value->v.size_hints.hints);
+
+ spew_size_hints_differences (&old_hints, &window->size_hints);
+
+ meta_window_recalc_features (window);
+
+ if (!initial)
+ meta_window_queue(window, META_QUEUE_MOVE_RESIZE);
+ }
+}
+
+static void
+reload_wm_protocols (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ int i;
+
+ window->take_focus = FALSE;
+ window->delete_window = FALSE;
+ window->net_wm_ping = FALSE;
+
+ if (value->type == META_PROP_VALUE_INVALID)
+ return;
+
+ i = 0;
+ while (i < value->v.atom_list.n_atoms)
+ {
+ if (value->v.atom_list.atoms[i] ==
+ window->display->atom_WM_TAKE_FOCUS)
+ window->take_focus = TRUE;
+ else if (value->v.atom_list.atoms[i] ==
+ window->display->atom_WM_DELETE_WINDOW)
+ window->delete_window = TRUE;
+ else if (value->v.atom_list.atoms[i] ==
+ window->display->atom__NET_WM_PING)
+ window->net_wm_ping = TRUE;
+ ++i;
+ }
+
+ meta_verbose ("New _NET_STARTUP_ID \"%s\" for %s\n",
+ window->startup_id ? window->startup_id : "unset",
+ window->desc);
+}
+
+static void
+reload_wm_hints (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ Window old_group_leader;
+
+ old_group_leader = window->xgroup_leader;
+
+ /* Fill in defaults */
+ window->input = TRUE;
+ window->initially_iconic = FALSE;
+ window->xgroup_leader = None;
+ window->wm_hints_pixmap = None;
+ window->wm_hints_mask = None;
+
+ if (value->type != META_PROP_VALUE_INVALID)
+ {
+ const XWMHints *hints = value->v.wm_hints;
+
+ if (hints->flags & InputHint)
+ window->input = hints->input;
+
+ if (hints->flags & StateHint)
+ window->initially_iconic = (hints->initial_state == IconicState);
+
+ if (hints->flags & WindowGroupHint)
+ window->xgroup_leader = hints->window_group;
+
+ if (hints->flags & IconPixmapHint)
+ window->wm_hints_pixmap = hints->icon_pixmap;
+
+ if (hints->flags & IconMaskHint)
+ window->wm_hints_mask = hints->icon_mask;
+
+ meta_verbose ("Read WM_HINTS input: %d iconic: %d group leader: 0x%lx pixmap: 0x%lx mask: 0x%lx\n",
+ window->input, window->initially_iconic,
+ window->xgroup_leader,
+ window->wm_hints_pixmap,
+ window->wm_hints_mask);
+ }
+
+ if (window->xgroup_leader != old_group_leader)
+ {
+ meta_verbose ("Window %s changed its group leader to 0x%lx\n",
+ window->desc, window->xgroup_leader);
+
+ meta_window_group_leader_changed (window);
+ }
+
+ meta_icon_cache_property_changed (&window->icon_cache,
+ window->display,
+ XA_WM_HINTS);
+
+ meta_window_queue (window, META_QUEUE_UPDATE_ICON | META_QUEUE_MOVE_RESIZE);
+}
+
+static void
+reload_transient_for (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ window->xtransient_for = None;
+
+ if (value->type != META_PROP_VALUE_INVALID)
+ window->xtransient_for = value->v.xwindow;
+
+ /* Make sure transient_for is valid */
+ if (window->xtransient_for != None &&
+ meta_display_lookup_x_window (window->display,
+ window->xtransient_for) == NULL)
+ {
+ meta_warning (_("Invalid WM_TRANSIENT_FOR window 0x%lx specified "
+ "for %s.\n"),
+ window->xtransient_for, window->desc);
+ window->xtransient_for = None;
+ }
+
+ window->transient_parent_is_root_window =
+ window->xtransient_for == window->screen->xroot;
+
+ if (window->xtransient_for != None)
+ meta_verbose ("Window %s transient for 0x%lx (root = %d)\n", window->desc,
+ window->xtransient_for, window->transient_parent_is_root_window);
+ else
+ meta_verbose ("Window %s is not transient\n", window->desc);
+
+ /* may now be a dialog */
+ meta_window_recalc_window_type (window);
+
+ /* update stacking constraints */
+ meta_stack_update_transient (window->screen->stack, window);
+
+ /* possibly change its group. We treat being a window's transient as
+ * equivalent to making it your group leader, to work around shortcomings
+ * in programs such as xmms-- see #328211.
+ */
+ if (window->xtransient_for != None &&
+ window->xgroup_leader != None &&
+ window->xtransient_for != window->xgroup_leader)
+ meta_window_group_leader_changed (window);
+
+ if (!window->constructing)
+ meta_window_queue (window, META_QUEUE_MOVE_RESIZE);
+}
+
+/**
+ * Initialises the property hooks system. Each row in the table named "hooks"
+ * represents an action to take when a property is found on a newly-created
+ * window, or when a property changes its value.
+ *
+ * The first column shows which atom the row concerns.
+ * The second gives the type of the property data. The property will be
+ * queried for its new value, unless the type is given as
+ * META_PROP_VALUE_INVALID, in which case nothing will be queried.
+ * The third column gives the name of a callback which gets called with the
+ * new value. (If the new value was not retrieved because the second column
+ * was META_PROP_VALUE_INVALID, the callback still gets called anyway.)
+ * This value may be NULL, in which case no callback will be called.
+ */
+void
+meta_display_init_window_prop_hooks (MetaDisplay *display)
+{
+ MetaWindowPropHooks hooks[] = {
+ { display->atom_WM_STATE, META_PROP_VALUE_INVALID, NULL },
+ { display->atom_WM_CLIENT_MACHINE, META_PROP_VALUE_STRING, reload_wm_client_machine },
+ { display->atom__NET_WM_PID, META_PROP_VALUE_CARDINAL, reload_net_wm_pid },
+ { display->atom__NET_WM_USER_TIME, META_PROP_VALUE_CARDINAL, reload_net_wm_user_time },
+ { display->atom__NET_WM_NAME, META_PROP_VALUE_UTF8, reload_net_wm_name },
+ { XA_WM_NAME, META_PROP_VALUE_TEXT_PROPERTY, reload_wm_name },
+ { display->atom__NET_WM_ICON, META_PROP_VALUE_INVALID, reload_net_wm_icon },
+ { display->atom__KWM_WIN_ICON, META_PROP_VALUE_INVALID, reload_kwm_win_icon },
+ { display->atom__NET_WM_ICON_NAME, META_PROP_VALUE_UTF8, reload_net_wm_icon_name },
+ { XA_WM_ICON_NAME, META_PROP_VALUE_TEXT_PROPERTY, reload_wm_icon_name },
+ { display->atom__NET_WM_STATE, META_PROP_VALUE_ATOM_LIST, reload_net_wm_state },
+ { display->atom__MOTIF_WM_HINTS, META_PROP_VALUE_MOTIF_HINTS, reload_mwm_hints },
+ { display->atom__NET_WM_ICON_GEOMETRY, META_PROP_VALUE_INVALID, NULL },
+ { XA_WM_CLASS, META_PROP_VALUE_CLASS_HINT, reload_wm_class },
+ { display->atom_WM_CLIENT_LEADER, META_PROP_VALUE_INVALID, complain_about_broken_client },
+ { display->atom_SM_CLIENT_ID, META_PROP_VALUE_INVALID, complain_about_broken_client },
+ { display->atom_WM_WINDOW_ROLE, META_PROP_VALUE_INVALID, reload_wm_window_role },
+ { display->atom__NET_WM_WINDOW_TYPE, META_PROP_VALUE_INVALID, reload_net_wm_window_type },
+ { display->atom__NET_WM_DESKTOP, META_PROP_VALUE_CARDINAL, reload_net_wm_desktop },
+ { display->atom__NET_WM_STRUT, META_PROP_VALUE_INVALID, reload_struts },
+ { display->atom__NET_WM_STRUT_PARTIAL, META_PROP_VALUE_INVALID, reload_struts },
+ { display->atom__NET_STARTUP_ID, META_PROP_VALUE_UTF8, reload_net_startup_id },
+ { display->atom__NET_WM_SYNC_REQUEST_COUNTER, META_PROP_VALUE_SYNC_COUNTER, reload_update_counter },
+ { XA_WM_NORMAL_HINTS, META_PROP_VALUE_SIZE_HINTS, reload_normal_hints },
+ { display->atom_WM_PROTOCOLS, META_PROP_VALUE_ATOM_LIST, reload_wm_protocols },
+ { XA_WM_HINTS, META_PROP_VALUE_WM_HINTS, reload_wm_hints },
+ { XA_WM_TRANSIENT_FOR, META_PROP_VALUE_WINDOW, reload_transient_for },
+ { display->atom__NET_WM_USER_TIME_WINDOW, META_PROP_VALUE_WINDOW, reload_net_wm_user_time_window },
+ { 0 },
+ };
+
+ MetaWindowPropHooks *table = g_memdup (hooks, sizeof (hooks)),
+ *cursor = table;
+
+ g_assert (display->prop_hooks == NULL);
+
+ display->prop_hooks_table = (gpointer) table;
+ display->prop_hooks = g_hash_table_new (NULL, NULL);
+
+ while (cursor->property)
+ {
+ /* Atoms are safe to use with GINT_TO_POINTER because it's safe with
+ * anything 32 bits or less, and atoms are 32 bits with the top three
+ * bits clear. (Scheifler & Gettys, 2e, p372)
+ */
+ g_hash_table_insert (display->prop_hooks,
+ GINT_TO_POINTER (cursor->property),
+ cursor);
+ cursor++;
+ }
+}
+
+void
+meta_display_free_window_prop_hooks (MetaDisplay *display)
+{
+ g_hash_table_unref (display->prop_hooks);
+ display->prop_hooks = NULL;
+
+ g_free (display->prop_hooks_table);
+ display->prop_hooks_table = NULL;
+}
+
+/**
+ * Finds the hooks for a particular property.
+ */
+static MetaWindowPropHooks*
+find_hooks (MetaDisplay *display,
+ Atom property)
+{
+ return g_hash_table_lookup (display->prop_hooks,
+ GINT_TO_POINTER (property));
+}