You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

12053 lines
379 KiB

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2001 Havoc Pennington, Anders Carlsson
* Copyright (C) 2002, 2003 Red Hat, Inc.
* Copyright (C) 2003 Rob Adams
* Copyright (C) 2004-2006 Elijah Newren
*
* 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., 51 Franklin Street - Suite 500, Boston, MA
* 02110-1335, USA.
*/
/**
* SECTION:window
* @title: MetaWindow
* @short_description: Muffin X managed windows
*/
#include <config.h>
#include "window-private.h"
#include "boxes-private.h"
#include "edge-resistance.h"
#include <meta/util.h>
#include "frame.h"
#include <meta/errors.h>
#include "workspace-private.h"
#include "stack.h"
#include "keybindings-private.h"
#include "ui.h"
#include "place.h"
#include "session.h"
#include <meta/prefs.h>
#include "resizepopup.h"
#include "xprops.h"
#include <meta/group.h>
#include "window-props.h"
#include "constraints.h"
#include "muffin-enum-types.h"
#include <clutter/clutter.h>
#include <X11/Xatom.h>
#include <X11/Xlibint.h> /* For display->resource_mask */
#include <string.h>
#include <math.h>
#ifdef HAVE_SHAPE
#include <X11/extensions/shape.h>
#endif
#include <X11/XKBlib.h>
#include <X11/extensions/Xcomposite.h>
#include <gdk/gdkx.h>
static int destroying_windows_disallowed = 0;
static void update_sm_hints (MetaWindow *window);
static void update_net_frame_extents (MetaWindow *window);
static void recalc_window_type (MetaWindow *window);
static void recalc_window_features (MetaWindow *window);
static void invalidate_work_areas (MetaWindow *window);
static void recalc_window_type (MetaWindow *window);
static void set_wm_state_on_xwindow (MetaDisplay *display,
Window xwindow,
int state);
static void set_wm_state (MetaWindow *window,
int state);
static void set_net_wm_state (MetaWindow *window);
static void meta_window_set_above (MetaWindow *window,
gboolean new_value);
static void send_configure_notify (MetaWindow *window);
static gboolean process_property_notify (MetaWindow *window,
XPropertyEvent *event);
static void meta_window_force_placement (MetaWindow *window);
static void meta_window_show (MetaWindow *window);
static void meta_window_hide (MetaWindow *window);
static gboolean meta_window_same_client (MetaWindow *window,
MetaWindow *other_window);
static void meta_window_save_rect (MetaWindow *window);
static void save_user_window_placement (MetaWindow *window);
static void force_save_user_window_placement (MetaWindow *window);
static void meta_window_move_resize_internal (MetaWindow *window,
MetaMoveResizeFlags flags,
int resize_gravity,
int root_x_nw,
int root_y_nw,
int w,
int h);
static void ensure_mru_position_after (MetaWindow *window,
MetaWindow *after_this_one);
static void meta_window_move_resize_now (MetaWindow *window);
static void meta_window_unqueue (MetaWindow *window, guint queuebits);
static void update_move (MetaWindow *window,
gboolean legacy_snap,
gboolean snap_mode,
int x,
int y);
static gboolean update_move_timeout (gpointer data);
static void update_resize (MetaWindow *window,
gboolean snap,
int x,
int y,
gboolean force);
static gboolean update_resize_timeout (gpointer data);
static gboolean should_be_on_all_workspaces (MetaWindow *window);
static void meta_window_flush_calc_showing (MetaWindow *window);
static gboolean queue_calc_showing_func (MetaWindow *window,
void *data);
static void meta_window_apply_session_info (MetaWindow *window,
const MetaWindowSessionInfo *info);
static void meta_window_move_between_rects (MetaWindow *window,
const MetaRectangle *old_area,
const MetaRectangle *new_area);
static void unmaximize_window_before_freeing (MetaWindow *window);
static void unminimize_window_and_all_transient_parents (MetaWindow *window);
static void notify_tile_type (MetaWindow *window);
static void normalize_tile_state (MetaWindow *window);
static unsigned int get_mask_from_snap_keysym (MetaWindow *window);
/* Idle handlers for the three queues (run with meta_later_add()). The
* "data" parameter in each case will be a GINT_TO_POINTER of the
* index into the queue arrays to use.
*
* TODO: Possibly there is still some code duplication among these, which we
* need to sort out at some point.
*/
static gboolean idle_calc_showing (gpointer data);
static gboolean idle_move_resize (gpointer data);
static gboolean idle_update_icon (gpointer data);
G_DEFINE_TYPE (MetaWindow, meta_window, G_TYPE_OBJECT);
#define SNAP_DELAY 2000
enum {
PROP_0,
PROP_TITLE,
PROP_ICON,
PROP_MINI_ICON,
PROP_DECORATED,
PROP_FULLSCREEN,
PROP_MAXIMIZED_HORIZONTALLY,
PROP_MAXIMIZED_VERTICALLY,
PROP_TILE_TYPE,
PROP_MINIMIZED,
PROP_WINDOW_TYPE,
PROP_USER_TIME,
PROP_DEMANDS_ATTENTION,
PROP_URGENT,
PROP_MUFFIN_HINTS,
PROP_APPEARS_FOCUSED,
PROP_RESIZEABLE,
PROP_ABOVE,
PROP_WM_CLASS,
PROP_GTK_APPLICATION_ID,
PROP_GTK_UNIQUE_BUS_NAME,
PROP_GTK_APPLICATION_OBJECT_PATH,
PROP_GTK_WINDOW_OBJECT_PATH,
PROP_GTK_APP_MENU_OBJECT_PATH,
PROP_GTK_MENUBAR_OBJECT_PATH
};
enum
{
WORKSPACE_CHANGED,
FOCUS,
RAISED,
UNMANAGED,
LAST_SIGNAL
};
typedef enum
{
ZONE_TOP = 1 << 0,
ZONE_RIGHT = 1 << 1,
ZONE_BOTTOM = 1 << 2,
ZONE_LEFT = 1 << 3,
ZONE_ULC = ZONE_TOP | ZONE_LEFT,
ZONE_LLC = ZONE_BOTTOM | ZONE_LEFT,
ZONE_URC = ZONE_TOP | ZONE_RIGHT,
ZONE_LRC = ZONE_BOTTOM | ZONE_RIGHT
} TileZone;
static guint window_signals[LAST_SIGNAL] = { 0 };
static void
prefs_changed_callback (MetaPreference pref,
gpointer data)
{
MetaWindow *window = data;
if (pref != META_PREF_WORKSPACES_ONLY_ON_PRIMARY)
return;
meta_window_update_on_all_workspaces (window);
meta_window_queue (window, META_QUEUE_CALC_SHOWING);
}
static void
meta_window_finalize (GObject *object)
{
MetaWindow *window = META_WINDOW (object);
if (window->icon)
g_object_unref (G_OBJECT (window->icon));
if (window->mini_icon)
g_object_unref (G_OBJECT (window->mini_icon));
if (window->frame_bounds)
cairo_region_destroy (window->frame_bounds);
meta_icon_cache_free (&window->icon_cache);
g_free (window->sm_client_id);
g_free (window->wm_client_machine);
g_free (window->startup_id);
g_free (window->role);
g_free (window->res_class);
g_free (window->res_name);
g_free (window->title);
g_free (window->icon_name);
g_free (window->desc);
g_free (window->gtk_theme_variant);
g_free (window->gtk_application_id);
g_free (window->gtk_unique_bus_name);
g_free (window->gtk_application_object_path);
g_free (window->gtk_window_object_path);
g_free (window->gtk_app_menu_object_path);
g_free (window->gtk_menubar_object_path);
G_OBJECT_CLASS (meta_window_parent_class)->finalize (object);
}
static void
meta_window_get_property(GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaWindow *win = META_WINDOW (object);
switch (prop_id)
{
case PROP_TITLE:
g_value_set_string (value, win->title);
break;
case PROP_ICON:
g_value_set_object (value, win->icon);
break;
case PROP_MINI_ICON:
g_value_set_object (value, win->mini_icon);
break;
case PROP_DECORATED:
g_value_set_boolean (value, win->decorated);
break;
case PROP_FULLSCREEN:
g_value_set_boolean (value, win->fullscreen);
break;
case PROP_MAXIMIZED_HORIZONTALLY:
g_value_set_boolean (value, win->maximized_horizontally);
break;
case PROP_MAXIMIZED_VERTICALLY:
g_value_set_boolean (value, win->maximized_vertically);
break;
case PROP_TILE_TYPE:
g_value_set_int (value, win->tile_type);
break;
case PROP_MINIMIZED:
g_value_set_boolean (value, win->minimized);
break;
case PROP_WINDOW_TYPE:
g_value_set_enum (value, win->type);
break;
case PROP_USER_TIME:
g_value_set_uint (value, win->net_wm_user_time);
break;
case PROP_DEMANDS_ATTENTION:
g_value_set_boolean (value, win->wm_state_demands_attention);
break;
case PROP_URGENT:
g_value_set_boolean (value, win->wm_hints_urgent);
break;
case PROP_MUFFIN_HINTS:
g_value_set_string (value, win->muffin_hints);
break;
case PROP_APPEARS_FOCUSED:
g_value_set_boolean (value, meta_window_appears_focused (win));
break;
case PROP_WM_CLASS:
g_value_set_string (value, win->res_class);
break;
case PROP_RESIZEABLE:
g_value_set_boolean (value, win->has_resize_func);
break;
case PROP_ABOVE:
g_value_set_boolean (value, win->wm_state_above);
break;
case PROP_GTK_APPLICATION_ID:
g_value_set_string (value, win->gtk_application_id);
break;
case PROP_GTK_UNIQUE_BUS_NAME:
g_value_set_string (value, win->gtk_unique_bus_name);
break;
case PROP_GTK_APPLICATION_OBJECT_PATH:
g_value_set_string (value, win->gtk_application_object_path);
break;
case PROP_GTK_WINDOW_OBJECT_PATH:
g_value_set_string (value, win->gtk_window_object_path);
break;
case PROP_GTK_APP_MENU_OBJECT_PATH:
g_value_set_string (value, win->gtk_app_menu_object_path);
break;
case PROP_GTK_MENUBAR_OBJECT_PATH:
g_value_set_string (value, win->gtk_menubar_object_path);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_window_set_property(GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_window_class_init (MetaWindowClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_window_finalize;
object_class->get_property = meta_window_get_property;
object_class->set_property = meta_window_set_property;
g_object_class_install_property (object_class,
PROP_TITLE,
g_param_spec_string ("title",
"Title",
"The title of the window",
NULL,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_ICON,
g_param_spec_object ("icon",
"Icon",
"32 pixel sized icon",
GDK_TYPE_PIXBUF,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_MINI_ICON,
g_param_spec_object ("mini-icon",
"Mini Icon",
"16 pixel sized icon",
GDK_TYPE_PIXBUF,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_DECORATED,
g_param_spec_boolean ("decorated",
"Decorated",
"Whether window is decorated",
TRUE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_FULLSCREEN,
g_param_spec_boolean ("fullscreen",
"Fullscreen",
"Whether window is fullscreened",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_MAXIMIZED_HORIZONTALLY,
g_param_spec_boolean ("maximized-horizontally",
"Maximized horizontally",
"Whether window is maximized horizontally",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_MAXIMIZED_VERTICALLY,
g_param_spec_boolean ("maximized-vertically",
"Maximizing vertically",
"Whether window is maximized vertically",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_TILE_TYPE,
g_param_spec_int ("tile-type",
"Window is tiled or snapped",
"Whether window is tiled or snapped",
META_WINDOW_TILE_TYPE_NONE,
META_WINDOW_TILE_TYPE_SNAPPED,
META_WINDOW_TILE_TYPE_NONE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_MINIMIZED,
g_param_spec_boolean ("minimized",
"Minimizing",
"Whether window is minimized",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_WINDOW_TYPE,
g_param_spec_enum ("window-type",
"Window Type",
"The type of the window",
META_TYPE_WINDOW_TYPE,
META_WINDOW_NORMAL,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_USER_TIME,
g_param_spec_uint ("user-time",
"User time",
"Timestamp of last user interaction",
0,
G_MAXUINT,
0,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_DEMANDS_ATTENTION,
g_param_spec_boolean ("demands-attention",
"Demands Attention",
"Whether the window has _NET_WM_STATE_DEMANDS_ATTENTION set",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_URGENT,
g_param_spec_boolean ("urgent",
"Urgent",
"Whether the urgent flag of WM_HINTS is set",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_MUFFIN_HINTS,
g_param_spec_string ("muffin-hints",
"_MUFFIN_HINTS",
"Contents of the _MUFFIN_HINTS property of this window",
NULL,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_APPEARS_FOCUSED,
g_param_spec_boolean ("appears-focused",
"Appears focused",
"Whether the window is drawn as being focused",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_RESIZEABLE,
g_param_spec_boolean ("resizeable",
"Resizeable",
"Whether the window can be resized",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_ABOVE,
g_param_spec_boolean ("above",
"Above",
"Whether the window is shown as always-on-top",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_WM_CLASS,
g_param_spec_string ("wm-class",
"WM_CLASS",
"Contents of the WM_CLASS property of this window",
NULL,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_GTK_APPLICATION_ID,
g_param_spec_string ("gtk-application-id",
"_GTK_APPLICATION_ID",
"Contents of the _GTK_APPLICATION_ID property of this window",
NULL,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_GTK_UNIQUE_BUS_NAME,
g_param_spec_string ("gtk-unique-bus-name",
"_GTK_UNIQUE_BUS_NAME",
"Contents of the _GTK_UNIQUE_BUS_NAME property of this window",
NULL,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_GTK_APPLICATION_OBJECT_PATH,
g_param_spec_string ("gtk-application-object-path",
"_GTK_APPLICATION_OBJECT_PATH",
"Contents of the _GTK_APPLICATION_OBJECT_PATH property of this window",
NULL,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_GTK_WINDOW_OBJECT_PATH,
g_param_spec_string ("gtk-window-object-path",
"_GTK_WINDOW_OBJECT_PATH",
"Contents of the _GTK_WINDOW_OBJECT_PATH property of this window",
NULL,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_GTK_APP_MENU_OBJECT_PATH,
g_param_spec_string ("gtk-app-menu-object-path",
"_GTK_APP_MENU_OBJECT_PATH",
"Contents of the _GTK_APP_MENU_OBJECT_PATH property of this window",
NULL,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_GTK_MENUBAR_OBJECT_PATH,
g_param_spec_string ("gtk-menubar-object-path",
"_GTK_MENUBAR_OBJECT_PATH",
"Contents of the _GTK_MENUBAR_OBJECT_PATH property of this window",
NULL,
G_PARAM_READABLE));
window_signals[WORKSPACE_CHANGED] =
g_signal_new ("workspace-changed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MetaWindowClass, workspace_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
G_TYPE_INT);
window_signals[FOCUS] =
g_signal_new ("focus",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MetaWindowClass, focus),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
window_signals[RAISED] =
g_signal_new ("raised",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MetaWindowClass, raised),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
window_signals[UNMANAGED] =
g_signal_new ("unmanaged",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MetaWindowClass, unmanaged),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
}
static void
meta_window_init (MetaWindow *self)
{
meta_prefs_add_listener (prefs_changed_callback, self);
}
#ifdef WITH_VERBOSE_MODE
static const char*
wm_state_to_string (int state)
{
switch (state)
{
case NormalState:
return "NormalState";
case IconicState:
return "IconicState";
case WithdrawnState:
return "WithdrawnState";
}
return "Unknown";
}
#endif
static gboolean
is_desktop_or_dock_foreach (MetaWindow *window,
void *data)
{
gboolean *result = data;
*result =
window->type == META_WINDOW_DESKTOP ||
window->type == META_WINDOW_DOCK;
if (*result)
return FALSE; /* stop as soon as we find one */
else
return TRUE;
}
/* window is the window that's newly mapped provoking
* the possible change
*/
static void
maybe_leave_show_desktop_mode (MetaWindow *window)
{
gboolean is_desktop_or_dock;
if (!window->screen->active_workspace->showing_desktop)
return;
/* If the window is a transient for the dock or desktop, don't
* leave show desktop mode when the window opens. That's
* so you can e.g. hide all windows, manipulate a file on
* the desktop via a dialog, then unshow windows again.
*/
is_desktop_or_dock = FALSE;
is_desktop_or_dock_foreach (window,
&is_desktop_or_dock);
meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach,
&is_desktop_or_dock);
if (!is_desktop_or_dock)
{
meta_screen_minimize_all_on_active_workspace_except (window->screen,
window);
meta_screen_unshow_desktop (window->screen);
}
}
static gboolean
client_window_should_be_mapped (MetaWindow *window)
{
return !window->shaded;
}
static void
sync_client_window_mapped (MetaWindow *window)
{
gboolean should_be_mapped = client_window_should_be_mapped (window);
g_return_if_fail (!window->override_redirect);
if (window->mapped == should_be_mapped)
return;
window->mapped = should_be_mapped;
meta_error_trap_push (window->display);
if (should_be_mapped)
{
XMapWindow (window->display->xdisplay, window->xwindow);
}
else
{
XUnmapWindow (window->display->xdisplay, window->xwindow);
window->unmaps_pending ++;
}
meta_error_trap_pop (window->display);
}
LOCAL_SYMBOL MetaWindow*
meta_window_new (MetaDisplay *display,
Window xwindow,
gboolean must_be_viewable)
{
XWindowAttributes attrs;
MetaWindow *window;
meta_display_grab (display);
meta_error_trap_push (display); /* Push a trap over all of window
* creation, to reduce XSync() calls
*/
meta_error_trap_push_with_return (display);
if (XGetWindowAttributes (display->xdisplay,xwindow, &attrs))
{
if(meta_error_trap_pop_with_return (display) != Success)
{
meta_verbose ("Failed to get attributes for window 0x%lx\n",
xwindow);
meta_error_trap_pop (display);
meta_display_ungrab (display);
return NULL;
}
window = meta_window_new_with_attrs (display, xwindow,
must_be_viewable,
META_COMP_EFFECT_CREATE,
&attrs);
}
else
{
meta_error_trap_pop_with_return (display);
meta_verbose ("Failed to get attributes for window 0x%lx\n",
xwindow);
meta_error_trap_pop (display);
meta_display_ungrab (display);
return NULL;
}
meta_error_trap_pop (display);
meta_display_ungrab (display);
return window;
}
/* The MUFFIN_WM_CLASS_FILTER environment variable is designed for
* performance and regression testing environments where we want to do
* tests with only a limited set of windows and ignore all other windows
*
* When it is set to a comma separated list of WM_CLASS class names, all
* windows not matching the list will be ignored.
*
* Returns TRUE if window has been filtered out and should be ignored.
*/
static gboolean
maybe_filter_window (MetaDisplay *display,
Window xwindow,
gboolean must_be_viewable,
XWindowAttributes *attrs)
{
static char **filter_wm_classes = NULL;
static gboolean initialized = FALSE;
XClassHint class_hint;
gboolean filtered;
Status success;
int i;
if (!initialized)
{
const char *filter_string = g_getenv ("MUFFIN_WM_CLASS_FILTER");
if (filter_string)
filter_wm_classes = g_strsplit (filter_string, ",", -1);
initialized = TRUE;
}
if (!filter_wm_classes || !filter_wm_classes[0])
return FALSE;
filtered = TRUE;
meta_error_trap_push (display);
success = XGetClassHint (display->xdisplay, xwindow, &class_hint);
if (success)
{
for (i = 0; filter_wm_classes[i]; i++)
{
if (strcmp (class_hint.res_class, filter_wm_classes[i]) == 0)
{
filtered = FALSE;
break;
}
}
XFree (class_hint.res_name);
XFree (class_hint.res_class);
}
if (filtered)
{
/* We want to try and get the window managed by the next WM that come along,
* so we need to make sure that windows that are requested to be mapped while
* Muffin is running (!must_be_viewable), or windows already viewable at startup
* get a non-withdrawn WM_STATE property. Previously unmapped windows are left
* with whatever WM_STATE property they had.
*/
if (!must_be_viewable || attrs->map_state == IsViewable)
{
gulong old_state;
if (!meta_prop_get_cardinal_with_atom_type (display, xwindow,
display->atom_WM_STATE,
display->atom_WM_STATE,
&old_state))
old_state = WithdrawnState;
if (old_state == WithdrawnState)
set_wm_state_on_xwindow (display, xwindow, NormalState);
}
/* Make sure filtered windows are hidden from view */
XUnmapWindow (display->xdisplay, xwindow);
}
meta_error_trap_pop (display);
return filtered;
}
LOCAL_SYMBOL gboolean
meta_window_should_attach_to_parent (MetaWindow *window)
{
MetaWindow *parent;
if (!meta_prefs_get_attach_modal_dialogs () ||
window->type != META_WINDOW_MODAL_DIALOG)
return FALSE;
parent = meta_window_get_transient_for (window);
if (!parent)
return FALSE;
switch (parent->type)
{
case META_WINDOW_NORMAL:
case META_WINDOW_DIALOG:
case META_WINDOW_MODAL_DIALOG:
return TRUE;
default:
return FALSE;
}
}
LOCAL_SYMBOL LOCAL_SYMBOL MetaWindow*
meta_window_new_with_attrs (MetaDisplay *display,
Window xwindow,
gboolean must_be_viewable,
MetaCompEffect effect,
XWindowAttributes *attrs)
{
MetaWindow *window;
GSList *tmp;
MetaWorkspace *space;
gulong existing_wm_state;
gulong event_mask;
MetaMoveResizeFlags flags;
gboolean has_shape;
MetaScreen *screen;
g_assert (attrs != NULL);
meta_verbose ("Attempting to manage 0x%lx\n", xwindow);
if (meta_display_xwindow_is_a_no_focus_window (display, xwindow))
{
meta_verbose ("Not managing no_focus_window 0x%lx\n",
xwindow);
return NULL;
}
screen = NULL;
for (tmp = display->screens; tmp != NULL; tmp = tmp->next)
{
MetaScreen *scr = tmp->data;
if (scr->xroot == attrs->root)
{
screen = tmp->data;
break;
}
}
g_assert (screen);
/* A black list of override redirect windows that we don't need to manage: */
if (attrs->override_redirect &&
(xwindow == screen->no_focus_window ||
xwindow == screen->flash_window ||
xwindow == screen->wm_sn_selection_window ||
attrs->class == InputOnly ||
/* any windows created via meta_create_offscreen_window: */
(attrs->x == -100 && attrs->y == -100
&& attrs->width == 1 && attrs->height == 1) ||
xwindow == screen->wm_cm_selection_window ||
xwindow == screen->guard_window ||
xwindow == XCompositeGetOverlayWindow (display->xdisplay,
screen->xroot)
)
) {
meta_verbose ("Not managing our own windows\n");
return NULL;
}
if (maybe_filter_window (display, xwindow, must_be_viewable, attrs))
{
meta_verbose ("Not managing filtered window\n");
return NULL;
}
/* Grab server */
meta_display_grab (display);
meta_error_trap_push (display); /* Push a trap over all of window
* creation, to reduce XSync() calls
*/
meta_verbose ("must_be_viewable = %d attrs->map_state = %d (%s)\n",
must_be_viewable,
attrs->map_state,
(attrs->map_state == IsUnmapped) ?
"IsUnmapped" :
(attrs->map_state == IsViewable) ?
"IsViewable" :
(attrs->map_state == IsUnviewable) ?
"IsUnviewable" :
"(unknown)");
existing_wm_state = WithdrawnState;
if (must_be_viewable && attrs->map_state != IsViewable)
{
/* Only manage if WM_STATE is IconicState or NormalState */
gulong state;
/* WM_STATE isn't a cardinal, it's type WM_STATE, but is an int */
if (!(meta_prop_get_cardinal_with_atom_type (display, xwindow,
display->atom_WM_STATE,
display->atom_WM_STATE,
&state) &&
(state == IconicState || state == NormalState)))
{
meta_verbose ("Deciding not to manage unmapped or unviewable window 0x%lx\n", xwindow);
meta_error_trap_pop (display);
meta_display_ungrab (display);
return NULL;
}
existing_wm_state = state;
meta_verbose ("WM_STATE of %lx = %s\n", xwindow,
wm_state_to_string (existing_wm_state));
}
meta_error_trap_push_with_return (display);
/*
* XAddToSaveSet can only be called on windows created by a different client.
* with Muffin we want to be able to create manageable windows from within
* the process (such as a dummy desktop window), so we do not want this
* call failing to prevent the window from being managed -- wrap it in its
* own error trap (we use the _with_return() version here to ensure that
* XSync() is done on the pop, otherwise the error will not get caught).
*/
meta_error_trap_push_with_return (display);
XAddToSaveSet (display->xdisplay, xwindow);
meta_error_trap_pop_with_return (display);
event_mask =
PropertyChangeMask | EnterWindowMask | LeaveWindowMask |
FocusChangeMask | ColormapChangeMask;
if (attrs->override_redirect)
event_mask |= StructureNotifyMask;
/* If the window is from this client (a menu, say) we need to augment
* the event mask, not replace it. For windows from other clients,
* attrs->your_event_mask will be empty at this point.
*/
XSelectInput (display->xdisplay, xwindow, attrs->your_event_mask | event_mask);
has_shape = FALSE;
#ifdef HAVE_SHAPE
if (META_DISPLAY_HAS_SHAPE (display))
{
int x_bounding, y_bounding, x_clip, y_clip;
unsigned w_bounding, h_bounding, w_clip, h_clip;
int bounding_shaped, clip_shaped;
XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask);
XShapeQueryExtents (display->xdisplay, xwindow,
&bounding_shaped, &x_bounding, &y_bounding,
&w_bounding, &h_bounding,
&clip_shaped, &x_clip, &y_clip,
&w_clip, &h_clip);
has_shape = bounding_shaped != FALSE;
meta_topic (META_DEBUG_SHAPES,
"Window has_shape = %d extents %d,%d %u x %u\n",
has_shape, x_bounding, y_bounding,
w_bounding, h_bounding);
}
#endif
/* Get rid of any borders */
if (attrs->border_width != 0)
XSetWindowBorderWidth (display->xdisplay, xwindow, 0);
/* Get rid of weird gravities */
if (attrs->win_gravity != NorthWestGravity)
{
XSetWindowAttributes set_attrs;
set_attrs.win_gravity = NorthWestGravity;
XChangeWindowAttributes (display->xdisplay,
xwindow,
CWWinGravity,
&set_attrs);
}
if (meta_error_trap_pop_with_return (display) != Success)
{
meta_verbose ("Window 0x%lx disappeared just as we tried to manage it\n",
xwindow);
meta_error_trap_pop (display);
meta_display_ungrab (display);
return NULL;
}
window = g_object_new (META_TYPE_WINDOW, NULL);
window->constructing = TRUE;
window->dialog_pid = -1;
window->xwindow = xwindow;
/* this is in window->screen->display, but that's too annoying to
* type
*/
window->display = display;
window->workspace = NULL;
#ifdef HAVE_XSYNC
window->sync_request_counter = None;
window->sync_request_serial = 0;
window->sync_request_timeout_id = 0;
window->sync_request_alarm = None;
#endif
window->screen = screen;
window->desc = g_strdup_printf ("0x%lx", window->xwindow);
window->override_redirect = attrs->override_redirect;
/* avoid tons of stack updates */
meta_stack_freeze (window->screen->stack);
window->has_shape = has_shape;
window->rect.x = attrs->x;
window->rect.y = attrs->y;
window->rect.width = attrs->width;
window->rect.height = attrs->height;
/* And border width, size_hints are the "request" */
window->border_width = attrs->border_width;
window->size_hints.x = attrs->x;
window->size_hints.y = attrs->y;
window->size_hints.width = attrs->width;
window->size_hints.height = attrs->height;
/* initialize the remaining size_hints as if size_hints.flags were zero */
meta_set_normal_hints (window, NULL);
/* And this is our unmaximized size */
window->saved_rect = window->rect;
window->user_rect = window->rect;
window->snapped_rect = window->rect;
window->depth = attrs->depth;
window->xvisual = attrs->visual;
window->colormap = attrs->colormap;
window->title = NULL;
window->icon_name = NULL;
window->icon = NULL;
window->mini_icon = NULL;
meta_icon_cache_init (&window->icon_cache);
window->wm_hints_pixmap = None;
window->wm_hints_mask = None;
window->wm_hints_urgent = FALSE;
window->frame = NULL;
window->has_focus = FALSE;
window->attached_focus_window = NULL;
window->maximized_horizontally = FALSE;
window->maximized_vertically = FALSE;
window->tile_type = META_WINDOW_TILE_TYPE_NONE;
window->snap_queued = FALSE;
window->current_proximity_zone = 0;
window->mouse_on_edge = FALSE;