mutter/src/core/window-props.c
Colin Walters 01581dc61c [MetaWindow]: Always notify on title property change
The previous notification code was attempting to use the "modified"
boolean returned from set_title_text, but "that boolean doesn't mean
what you think it means".  It actually means "I truncated the title".

Just always notify, it's far simpler than trying to compute
when we don't need to, and callers can compress if they really need
to.
2009-08-21 12:38:30 -04:00

1561 lines
53 KiB
C

/* -*- 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>
#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);
struct _MetaWindowPropHooks
{
Atom property;
MetaPropValueType type;
ReloadValueFunc reload_func;
gboolean load_initially;
gboolean include_override_redirect;
};
static void init_prop_value (MetaWindow *window,
MetaWindowPropHooks *hooks,
MetaPropValue *value);
static void reload_prop_value (MetaWindow *window,
MetaWindowPropHooks *hooks,
MetaPropValue *value,
gboolean initial);
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);
i = 0;
while (i < n_properties)
{
MetaWindowPropHooks *hooks = find_hooks (window->display, properties[i]);
init_prop_value (window, hooks, &values[i]);
++i;
}
meta_prop_get_values (window->display, xwindow,
values, n_properties);
i = 0;
while (i < n_properties)
{
MetaWindowPropHooks *hooks = find_hooks (window->display, properties[i]);
reload_prop_value (window, hooks, &values[i], initial);
++i;
}
meta_prop_free_values (values, n_properties);
g_free (values);
}
void
meta_window_load_initial_properties (MetaWindow *window)
{
int i, j;
MetaPropValue *values;
int n_properties = 0;
values = g_new0 (MetaPropValue, window->display->n_prop_hooks);
j = 0;
for (i = 0; i < window->display->n_prop_hooks; i++)
{
MetaWindowPropHooks *hooks = &window->display->prop_hooks_table[i];
if (hooks->load_initially)
{
init_prop_value (window, hooks, &values[j]);
++j;
}
}
n_properties = j;
meta_prop_get_values (window->display, window->xwindow,
values, n_properties);
j = 0;
for (i = 0; i < window->display->n_prop_hooks; i++)
{
MetaWindowPropHooks *hooks = &window->display->prop_hooks_table[i];
if (hooks->load_initially)
{
/* If we didn't actually manage to load anything then we don't need
* to call the reload function; this is different from a notification
* where disappearance of a previously present value is significant.
*/
if (values[j].type != META_PROP_VALUE_INVALID)
reload_prop_value (window, hooks, &values[j], TRUE);
++j;
}
}
meta_prop_free_values (values, n_properties);
g_free (values);
}
/* Fill in the MetaPropValue used to get the value of "property" */
static void
init_prop_value (MetaWindow *window,
MetaWindowPropHooks *hooks,
MetaPropValue *value)
{
if (!hooks || hooks->type == META_PROP_VALUE_INVALID ||
(window->override_redirect && !hooks->include_override_redirect))
{
value->type = META_PROP_VALUE_INVALID;
value->atom = None;
}
else
{
value->type = hooks->type;
value->atom = hooks->property;
}
}
static void
reload_prop_value (MetaWindow *window,
MetaWindowPropHooks *hooks,
MetaPropValue *value,
gboolean initial)
{
if (hooks && hooks->reload_func != NULL &&
!(window->override_redirect && !hooks->include_override_redirect))
(* hooks->reload_func) (window, value, initial);
}
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);
}
}
}
#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
* lets place that hostname in the title */
else if (window->wm_client_machine &&
!gethostname (hostname, HOST_NAME_MAX + 1) &&
strcmp (hostname, window->wm_client_machine))
{
*target = g_strdup_printf (_("%s (on %s)"),
title, window->wm_client_machine);
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);
g_object_notify (G_OBJECT (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->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)
{
if (!window->fullscreen)
{
window->fullscreen = TRUE;
g_object_notify (G_OBJECT (window), "fullscreen");
}
}
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;
gboolean old_decorated = window->decorated;
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);
if (old_decorated != window->decorated)
g_object_notify (G_OBJECT (window), "decorated");
}
}
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 */
if (!window->override_redirect)
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 && !window->override_redirect)
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)
{
/* INIT: load initially
* O-R: fetch for override-redirect windows
*
* The ordering here is significant for the properties we load
* initially: they are roughly ordered in the order we want them to
* be gotten. We want to get window name and class first so we can
* use them in error messages and such. However, name is modified
* depending on wm_client_machine, so push it slightly sooner.
*
* For override-redirect windows, we pay attention to:
*
* - properties that identify the window: useful for debugging
* purposes.
* - NET_WM_WINDOW_TYPE: can be used to do appropriate handling
* for different types of override-redirect windows.
*/
MetaWindowPropHooks hooks[] = { /* INIT O-R */
{ display->atom_WM_CLIENT_MACHINE, META_PROP_VALUE_STRING, reload_wm_client_machine, TRUE, TRUE },
{ display->atom__NET_WM_NAME, META_PROP_VALUE_UTF8, reload_net_wm_name, TRUE, TRUE },
{ XA_WM_CLASS, META_PROP_VALUE_CLASS_HINT, reload_wm_class, TRUE, TRUE },
{ display->atom__NET_WM_PID, META_PROP_VALUE_CARDINAL, reload_net_wm_pid, TRUE, TRUE },
{ XA_WM_NAME, META_PROP_VALUE_TEXT_PROPERTY, reload_wm_name, TRUE, TRUE },
{ display->atom__NET_WM_ICON_NAME, META_PROP_VALUE_UTF8, reload_net_wm_icon_name, TRUE, FALSE },
{ XA_WM_ICON_NAME, META_PROP_VALUE_TEXT_PROPERTY, reload_wm_icon_name, TRUE, FALSE },
{ display->atom__NET_WM_DESKTOP, META_PROP_VALUE_CARDINAL, reload_net_wm_desktop, TRUE, FALSE },
{ display->atom__NET_STARTUP_ID, META_PROP_VALUE_UTF8, reload_net_startup_id, TRUE, FALSE },
{ display->atom__NET_WM_SYNC_REQUEST_COUNTER, META_PROP_VALUE_SYNC_COUNTER, reload_update_counter, TRUE, FALSE },
{ XA_WM_NORMAL_HINTS, META_PROP_VALUE_SIZE_HINTS, reload_normal_hints, TRUE, FALSE },
{ display->atom_WM_PROTOCOLS, META_PROP_VALUE_ATOM_LIST, reload_wm_protocols, TRUE, FALSE },
{ XA_WM_HINTS, META_PROP_VALUE_WM_HINTS, reload_wm_hints, TRUE, FALSE },
{ display->atom__NET_WM_USER_TIME, META_PROP_VALUE_CARDINAL, reload_net_wm_user_time, TRUE, FALSE },
{ display->atom__NET_WM_STATE, META_PROP_VALUE_ATOM_LIST, reload_net_wm_state, TRUE, FALSE },
{ display->atom__MOTIF_WM_HINTS, META_PROP_VALUE_MOTIF_HINTS, reload_mwm_hints, TRUE, FALSE },
{ XA_WM_TRANSIENT_FOR, META_PROP_VALUE_WINDOW, reload_transient_for, TRUE, FALSE },
{ display->atom__NET_WM_USER_TIME_WINDOW, META_PROP_VALUE_WINDOW, reload_net_wm_user_time_window, TRUE, FALSE },
{ display->atom_WM_STATE, META_PROP_VALUE_INVALID, NULL, FALSE, FALSE },
{ display->atom__NET_WM_ICON, META_PROP_VALUE_INVALID, reload_net_wm_icon, FALSE, FALSE },
{ display->atom__KWM_WIN_ICON, META_PROP_VALUE_INVALID, reload_kwm_win_icon, FALSE, FALSE },
{ display->atom__NET_WM_ICON_GEOMETRY, META_PROP_VALUE_INVALID, NULL, FALSE, FALSE },
{ display->atom_WM_CLIENT_LEADER, META_PROP_VALUE_INVALID, complain_about_broken_client, FALSE, FALSE },
{ display->atom_SM_CLIENT_ID, META_PROP_VALUE_INVALID, complain_about_broken_client, FALSE, FALSE },
{ display->atom_WM_WINDOW_ROLE, META_PROP_VALUE_INVALID, reload_wm_window_role, FALSE, FALSE },
{ display->atom__NET_WM_WINDOW_TYPE, META_PROP_VALUE_INVALID, reload_net_wm_window_type, FALSE, TRUE },
{ display->atom__NET_WM_STRUT, META_PROP_VALUE_INVALID, reload_struts, FALSE, FALSE },
{ display->atom__NET_WM_STRUT_PARTIAL, META_PROP_VALUE_INVALID, reload_struts, FALSE, FALSE },
{ 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)
{
/* Doing initial loading doesn't make sense if we just want notification */
g_assert (!(hooks->load_initially && hooks->type == META_PROP_VALUE_INVALID));
/* 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++;
}
display->n_prop_hooks = cursor - table;
}
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;
}
static MetaWindowPropHooks*
find_hooks (MetaDisplay *display,
Atom property)
{
return g_hash_table_lookup (display->prop_hooks,
GINT_TO_POINTER (property));
}