diff options
| author | Perberos <[email protected]> | 2011-12-01 23:52:01 -0300 | 
|---|---|---|
| committer | Perberos <[email protected]> | 2011-12-01 23:52:01 -0300 | 
| commit | 28a029a4990d2a84f9d6a0b890eba812ea503998 (patch) | |
| tree | 7a69477d0dd6bf351801fa9698d95224e4fe47b6 /src/core/window-props.c | |
| download | marco-28a029a4990d2a84f9d6a0b890eba812ea503998.tar.bz2 marco-28a029a4990d2a84f9d6a0b890eba812ea503998.tar.xz | |
moving from https://github.com/perberos/mate-desktop-environment
Diffstat (limited to 'src/core/window-props.c')
| -rw-r--r-- | src/core/window-props.c | 1553 | 
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)); +} | 
