97f142d1cc
When a window's workspace is not NULL, on_all_workspace should be FALSE. Similarly, when on_all_workspace is TRUE, the window workspace should be NULL. This is an assumption in multiple places in the code, including when setting the workspace state, the window is either added or removed from all workspaces only if the window's workspace is NULL. This rule is initially enforced at creation in _meta_window_shared_new() when a initial workspace is set. However, when the initial workspace is set from the session info, the initial workspace is not marked as “set” which leads to an assertion failure when unmanaging windows, because the window is not removed from all the workspaces. When applying the session info to a window, mark the workspace as “set”. https://gitlab.gnome.org/GNOME/mutter/issues/4 Closes: #4
3538 lines
113 KiB
C
3538 lines
113 KiB
C
/* -*- 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "window-x11.h"
|
|
#include "window-x11-private.h"
|
|
|
|
#include <string.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/Xlibint.h> /* For display->resource_mask */
|
|
#include <X11/Xlib-xcb.h>
|
|
|
|
#include <xcb/res.h>
|
|
|
|
#include <X11/extensions/shape.h>
|
|
|
|
#include <X11/extensions/Xcomposite.h>
|
|
#include "core.h"
|
|
|
|
#include <meta/common.h>
|
|
#include <meta/errors.h>
|
|
#include <meta/prefs.h>
|
|
#include <meta/meta-cursor-tracker.h>
|
|
|
|
#include "frame.h"
|
|
#include "boxes-private.h"
|
|
#include "window-private.h"
|
|
#include "window-props.h"
|
|
#include "xprops.h"
|
|
#include "session.h"
|
|
#include "workspace-private.h"
|
|
|
|
#include "backends/meta-logical-monitor.h"
|
|
#include "backends/x11/meta-backend-x11.h"
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (MetaWindowX11, meta_window_x11, META_TYPE_WINDOW)
|
|
|
|
static void
|
|
meta_window_x11_init (MetaWindowX11 *window_x11)
|
|
{
|
|
window_x11->priv = meta_window_x11_get_instance_private (window_x11);
|
|
}
|
|
|
|
static void
|
|
send_icccm_message (MetaWindow *window,
|
|
Atom atom,
|
|
guint32 timestamp)
|
|
{
|
|
/* This comment and code are from twm, copyright
|
|
* Open Group, Evans & Sutherland, etc.
|
|
*/
|
|
|
|
/*
|
|
* ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
|
|
* client messages will have the following form:
|
|
*
|
|
* event type ClientMessage
|
|
* message type _XA_WM_PROTOCOLS
|
|
* window tmp->w
|
|
* format 32
|
|
* data[0] message atom
|
|
* data[1] time stamp
|
|
*/
|
|
|
|
XClientMessageEvent ev;
|
|
|
|
ev.type = ClientMessage;
|
|
ev.window = window->xwindow;
|
|
ev.message_type = window->display->atom_WM_PROTOCOLS;
|
|
ev.format = 32;
|
|
ev.data.l[0] = atom;
|
|
ev.data.l[1] = timestamp;
|
|
|
|
meta_error_trap_push (window->display);
|
|
XSendEvent (window->display->xdisplay,
|
|
window->xwindow, False, 0, (XEvent*) &ev);
|
|
meta_error_trap_pop (window->display);
|
|
}
|
|
|
|
static Window
|
|
read_client_leader (MetaDisplay *display,
|
|
Window xwindow)
|
|
{
|
|
Window retval = None;
|
|
|
|
meta_prop_get_window (display, xwindow,
|
|
display->atom_WM_CLIENT_LEADER,
|
|
&retval);
|
|
|
|
return retval;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
Window leader;
|
|
} ClientLeaderData;
|
|
|
|
static gboolean
|
|
find_client_leader_func (MetaWindow *ancestor,
|
|
void *data)
|
|
{
|
|
ClientLeaderData *d;
|
|
|
|
d = data;
|
|
|
|
d->leader = read_client_leader (ancestor->display,
|
|
ancestor->xwindow);
|
|
|
|
/* keep going if no client leader found */
|
|
return d->leader == None;
|
|
}
|
|
|
|
static void
|
|
update_sm_hints (MetaWindow *window)
|
|
{
|
|
Window leader;
|
|
|
|
window->xclient_leader = None;
|
|
window->sm_client_id = NULL;
|
|
|
|
/* If not on the current window, we can get the client
|
|
* leader from transient parents. If we find a client
|
|
* leader, we read the SM_CLIENT_ID from it.
|
|
*/
|
|
leader = read_client_leader (window->display, window->xwindow);
|
|
if (leader == None)
|
|
{
|
|
ClientLeaderData d;
|
|
d.leader = None;
|
|
meta_window_foreach_ancestor (window, find_client_leader_func,
|
|
&d);
|
|
leader = d.leader;
|
|
}
|
|
|
|
if (leader != None)
|
|
{
|
|
char *str;
|
|
|
|
window->xclient_leader = leader;
|
|
|
|
if (meta_prop_get_latin1_string (window->display, leader,
|
|
window->display->atom_SM_CLIENT_ID,
|
|
&str))
|
|
{
|
|
window->sm_client_id = g_strdup (str);
|
|
meta_XFree (str);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
meta_verbose ("Didn't find a client leader for %s\n", window->desc);
|
|
|
|
if (!meta_prefs_get_disable_workarounds ())
|
|
{
|
|
/* Some broken apps (kdelibs fault?) set SM_CLIENT_ID on the app
|
|
* instead of the client leader
|
|
*/
|
|
char *str;
|
|
|
|
str = NULL;
|
|
if (meta_prop_get_latin1_string (window->display, window->xwindow,
|
|
window->display->atom_SM_CLIENT_ID,
|
|
&str))
|
|
{
|
|
if (window->sm_client_id == NULL) /* first time through */
|
|
meta_warning ("Window %s sets SM_CLIENT_ID on itself, instead of on the WM_CLIENT_LEADER window as specified in the ICCCM.\n",
|
|
window->desc);
|
|
|
|
window->sm_client_id = g_strdup (str);
|
|
meta_XFree (str);
|
|
}
|
|
}
|
|
}
|
|
|
|
meta_verbose ("Window %s client leader: 0x%lx SM_CLIENT_ID: '%s'\n",
|
|
window->desc, window->xclient_leader,
|
|
window->sm_client_id ? window->sm_client_id : "none");
|
|
}
|
|
|
|
static void
|
|
send_configure_notify (MetaWindow *window)
|
|
{
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
XEvent event;
|
|
|
|
g_assert (!window->override_redirect);
|
|
|
|
/* from twm */
|
|
|
|
event.type = ConfigureNotify;
|
|
event.xconfigure.display = window->display->xdisplay;
|
|
event.xconfigure.event = window->xwindow;
|
|
event.xconfigure.window = window->xwindow;
|
|
event.xconfigure.x = priv->client_rect.x - priv->border_width;
|
|
event.xconfigure.y = priv->client_rect.y - priv->border_width;
|
|
if (window->frame)
|
|
{
|
|
if (window->withdrawn)
|
|
{
|
|
MetaFrameBorders borders;
|
|
/* We reparent the client window and put it to the position
|
|
* where the visible top-left of the frame window currently is.
|
|
*/
|
|
|
|
meta_frame_calc_borders (window->frame, &borders);
|
|
|
|
event.xconfigure.x = window->frame->rect.x + borders.invisible.left;
|
|
event.xconfigure.y = window->frame->rect.y + borders.invisible.top;
|
|
}
|
|
else
|
|
{
|
|
/* Need to be in root window coordinates */
|
|
event.xconfigure.x += window->frame->rect.x;
|
|
event.xconfigure.y += window->frame->rect.y;
|
|
}
|
|
}
|
|
event.xconfigure.width = priv->client_rect.width;
|
|
event.xconfigure.height = priv->client_rect.height;
|
|
event.xconfigure.border_width = priv->border_width; /* requested not actual */
|
|
event.xconfigure.above = None; /* FIXME */
|
|
event.xconfigure.override_redirect = False;
|
|
|
|
meta_topic (META_DEBUG_GEOMETRY,
|
|
"Sending synthetic configure notify to %s with x: %d y: %d w: %d h: %d\n",
|
|
window->desc,
|
|
event.xconfigure.x, event.xconfigure.y,
|
|
event.xconfigure.width, event.xconfigure.height);
|
|
|
|
meta_error_trap_push (window->display);
|
|
XSendEvent (window->display->xdisplay,
|
|
window->xwindow,
|
|
False, StructureNotifyMask, &event);
|
|
meta_error_trap_pop (window->display);
|
|
}
|
|
|
|
static void
|
|
adjust_for_gravity (MetaWindow *window,
|
|
gboolean coords_assume_border,
|
|
int gravity,
|
|
MetaRectangle *rect)
|
|
{
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
int ref_x, ref_y;
|
|
int bw;
|
|
int child_x, child_y;
|
|
int frame_width, frame_height;
|
|
MetaFrameBorders borders;
|
|
|
|
/* We're computing position to pass to window_move, which is
|
|
* the position of the client window (StaticGravity basically)
|
|
*
|
|
* (see WM spec description of gravity computation, but note that
|
|
* their formulas assume we're honoring the border width, rather
|
|
* than compensating for having turned it off)
|
|
*/
|
|
|
|
if (gravity == StaticGravity)
|
|
return;
|
|
|
|
if (coords_assume_border)
|
|
bw = priv->border_width;
|
|
else
|
|
bw = 0;
|
|
|
|
meta_frame_calc_borders (window->frame, &borders);
|
|
|
|
child_x = borders.visible.left;
|
|
child_y = borders.visible.top;
|
|
frame_width = child_x + rect->width + borders.visible.right;
|
|
frame_height = child_y + rect->height + borders.visible.bottom;
|
|
|
|
/* Calculate the the reference point, which is the corner of the
|
|
* outer window specified by the gravity. So, NorthEastGravity
|
|
* would have the reference point as the top-right corner of the
|
|
* outer window. */
|
|
ref_x = rect->x;
|
|
ref_y = rect->y;
|
|
|
|
switch (gravity)
|
|
{
|
|
case NorthGravity:
|
|
case CenterGravity:
|
|
case SouthGravity:
|
|
ref_x += rect->width / 2 + bw;
|
|
break;
|
|
case NorthEastGravity:
|
|
case EastGravity:
|
|
case SouthEastGravity:
|
|
ref_x += rect->width + bw * 2;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (gravity)
|
|
{
|
|
case WestGravity:
|
|
case CenterGravity:
|
|
case EastGravity:
|
|
ref_y += rect->height / 2 + bw;
|
|
break;
|
|
case SouthWestGravity:
|
|
case SouthGravity:
|
|
case SouthEastGravity:
|
|
ref_y += rect->height + bw * 2;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Find the top-left corner of the outer window from
|
|
* the reference point. */
|
|
|
|
rect->x = ref_x;
|
|
rect->y = ref_y;
|
|
|
|
switch (gravity)
|
|
{
|
|
case NorthGravity:
|
|
case CenterGravity:
|
|
case SouthGravity:
|
|
rect->x -= frame_width / 2;
|
|
break;
|
|
case NorthEastGravity:
|
|
case EastGravity:
|
|
case SouthEastGravity:
|
|
rect->x -= frame_width;
|
|
break;
|
|
}
|
|
|
|
switch (gravity)
|
|
{
|
|
case WestGravity:
|
|
case CenterGravity:
|
|
case EastGravity:
|
|
rect->y -= frame_height / 2;
|
|
break;
|
|
case SouthWestGravity:
|
|
case SouthGravity:
|
|
case SouthEastGravity:
|
|
rect->y -= frame_height;
|
|
break;
|
|
}
|
|
|
|
/* Adjust to get the top-left corner of the inner window. */
|
|
rect->x += child_x;
|
|
rect->y += child_y;
|
|
}
|
|
|
|
static void
|
|
meta_window_apply_session_info (MetaWindow *window,
|
|
const MetaWindowSessionInfo *info)
|
|
{
|
|
if (info->stack_position_set)
|
|
{
|
|
meta_topic (META_DEBUG_SM,
|
|
"Restoring stack position %d for window %s\n",
|
|
info->stack_position, window->desc);
|
|
|
|
/* FIXME well, I'm not sure how to do this. */
|
|
}
|
|
|
|
if (info->minimized_set)
|
|
{
|
|
meta_topic (META_DEBUG_SM,
|
|
"Restoring minimized state %d for window %s\n",
|
|
info->minimized, window->desc);
|
|
|
|
if (info->minimized)
|
|
meta_window_minimize (window);
|
|
}
|
|
|
|
if (info->maximized_set)
|
|
{
|
|
meta_topic (META_DEBUG_SM,
|
|
"Restoring maximized state %d for window %s\n",
|
|
info->maximized, window->desc);
|
|
|
|
if (window->has_maximize_func && info->maximized)
|
|
{
|
|
meta_window_maximize (window, META_MAXIMIZE_BOTH);
|
|
|
|
if (info->saved_rect_set)
|
|
{
|
|
meta_topic (META_DEBUG_SM,
|
|
"Restoring saved rect %d,%d %dx%d for window %s\n",
|
|
info->saved_rect.x,
|
|
info->saved_rect.y,
|
|
info->saved_rect.width,
|
|
info->saved_rect.height,
|
|
window->desc);
|
|
|
|
window->saved_rect.x = info->saved_rect.x;
|
|
window->saved_rect.y = info->saved_rect.y;
|
|
window->saved_rect.width = info->saved_rect.width;
|
|
window->saved_rect.height = info->saved_rect.height;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (info->on_all_workspaces_set)
|
|
{
|
|
window->on_all_workspaces_requested = info->on_all_workspaces;
|
|
meta_window_on_all_workspaces_changed (window);
|
|
meta_topic (META_DEBUG_SM,
|
|
"Restoring sticky state %d for window %s\n",
|
|
window->on_all_workspaces_requested, window->desc);
|
|
}
|
|
|
|
if (info->workspace_indices)
|
|
{
|
|
GSList *tmp;
|
|
GSList *spaces;
|
|
|
|
spaces = NULL;
|
|
|
|
tmp = info->workspace_indices;
|
|
while (tmp != NULL)
|
|
{
|
|
MetaWorkspace *space;
|
|
|
|
space =
|
|
meta_screen_get_workspace_by_index (window->screen,
|
|
GPOINTER_TO_INT (tmp->data));
|
|
|
|
if (space)
|
|
spaces = g_slist_prepend (spaces, space);
|
|
|
|
tmp = tmp->next;
|
|
}
|
|
|
|
if (spaces)
|
|
{
|
|
/* XXX: What should we do if there's more than one workspace
|
|
* listed? We only support one workspace for each window.
|
|
*
|
|
* For now, just choose the first one.
|
|
*/
|
|
MetaWorkspace *workspace = spaces->data;
|
|
|
|
meta_window_change_workspace (window, workspace);
|
|
window->initial_workspace_set = TRUE;
|
|
|
|
meta_topic (META_DEBUG_SM,
|
|
"Restoring saved window %s to workspace %d\n",
|
|
window->desc,
|
|
meta_workspace_index (workspace));
|
|
|
|
g_slist_free (spaces);
|
|
}
|
|
}
|
|
|
|
if (info->geometry_set)
|
|
{
|
|
MetaRectangle rect;
|
|
MetaMoveResizeFlags flags;
|
|
int gravity;
|
|
|
|
window->placed = TRUE; /* don't do placement algorithms later */
|
|
|
|
rect.x = info->rect.x;
|
|
rect.y = info->rect.y;
|
|
|
|
rect.width = window->size_hints.base_width + info->rect.width * window->size_hints.width_inc;
|
|
rect.height = window->size_hints.base_height + info->rect.height * window->size_hints.height_inc;
|
|
|
|
/* Force old gravity, ignoring anything now set */
|
|
window->size_hints.win_gravity = info->gravity;
|
|
gravity = window->size_hints.win_gravity;
|
|
|
|
flags = META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION;
|
|
|
|
adjust_for_gravity (window, FALSE, gravity, &rect);
|
|
meta_window_client_rect_to_frame_rect (window, &rect, &rect);
|
|
meta_window_move_resize_internal (window, flags, gravity, rect);
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_window_x11_manage (MetaWindow *window)
|
|
{
|
|
MetaDisplay *display = window->display;
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
|
|
meta_icon_cache_init (&priv->icon_cache);
|
|
|
|
meta_display_register_x_window (display, &window->xwindow, window);
|
|
|
|
/* assign the window to its group, or create a new group if needed */
|
|
window->group = NULL;
|
|
window->xgroup_leader = None;
|
|
meta_window_compute_group (window);
|
|
|
|
meta_window_load_initial_properties (window);
|
|
|
|
if (!window->override_redirect)
|
|
update_sm_hints (window); /* must come after transient_for */
|
|
|
|
if (window->decorated)
|
|
meta_window_ensure_frame (window);
|
|
|
|
/* Now try applying saved stuff from the session */
|
|
{
|
|
const MetaWindowSessionInfo *info;
|
|
|
|
info = meta_window_lookup_saved_state (window);
|
|
|
|
if (info)
|
|
{
|
|
meta_window_apply_session_info (window, info);
|
|
meta_window_release_saved_state (info);
|
|
}
|
|
}
|
|
|
|
/* For override-redirect windows, save the client rect
|
|
* directly. window->rect was assigned from the XWindowAttributes
|
|
* in the main meta_window_shared_new.
|
|
*
|
|
* For normal windows, do a full ConfigureRequest based on the
|
|
* window hints, as that's what the ICCCM says to do.
|
|
*/
|
|
priv->client_rect = window->rect;
|
|
window->buffer_rect = window->rect;
|
|
|
|
if (!window->override_redirect)
|
|
{
|
|
MetaRectangle rect;
|
|
MetaMoveResizeFlags flags;
|
|
int gravity = window->size_hints.win_gravity;
|
|
|
|
rect.x = window->size_hints.x;
|
|
rect.y = window->size_hints.y;
|
|
rect.width = window->size_hints.width;
|
|
rect.height = window->size_hints.height;
|
|
|
|
flags = META_MOVE_RESIZE_CONFIGURE_REQUEST | META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION;
|
|
|
|
adjust_for_gravity (window, TRUE, gravity, &rect);
|
|
meta_window_client_rect_to_frame_rect (window, &rect, &rect);
|
|
meta_window_move_resize_internal (window, flags, gravity, rect);
|
|
}
|
|
|
|
meta_window_x11_update_shape_region (window);
|
|
meta_window_x11_update_input_region (window);
|
|
}
|
|
|
|
static void
|
|
meta_window_x11_unmanage (MetaWindow *window)
|
|
{
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
|
|
meta_error_trap_push (window->display);
|
|
|
|
meta_window_x11_destroy_sync_request_alarm (window);
|
|
|
|
if (window->withdrawn)
|
|
{
|
|
/* We need to clean off the window's state so it
|
|
* won't be restored if the app maps it again.
|
|
*/
|
|
meta_verbose ("Cleaning state from window %s\n", window->desc);
|
|
XDeleteProperty (window->display->xdisplay,
|
|
window->xwindow,
|
|
window->display->atom__NET_WM_DESKTOP);
|
|
XDeleteProperty (window->display->xdisplay,
|
|
window->xwindow,
|
|
window->display->atom__NET_WM_STATE);
|
|
XDeleteProperty (window->display->xdisplay,
|
|
window->xwindow,
|
|
window->display->atom__NET_WM_FULLSCREEN_MONITORS);
|
|
meta_window_x11_set_wm_state (window);
|
|
}
|
|
else
|
|
{
|
|
/* We need to put WM_STATE so that others will understand it on
|
|
* restart.
|
|
*/
|
|
if (!window->minimized)
|
|
meta_window_x11_set_wm_state (window);
|
|
|
|
/* If we're unmanaging a window that is not withdrawn, then
|
|
* either (a) mutter is exiting, in which case we need to map
|
|
* the window so the next WM will know that it's not Withdrawn,
|
|
* or (b) we want to create a new MetaWindow to replace the
|
|
* current one, which will happen automatically if we re-map
|
|
* the X Window.
|
|
*/
|
|
XMapWindow (window->display->xdisplay,
|
|
window->xwindow);
|
|
}
|
|
|
|
meta_display_unregister_x_window (window->display, window->xwindow);
|
|
|
|
/* Put back anything we messed up */
|
|
if (priv->border_width != 0)
|
|
XSetWindowBorderWidth (window->display->xdisplay,
|
|
window->xwindow,
|
|
priv->border_width);
|
|
|
|
/* No save set */
|
|
XRemoveFromSaveSet (window->display->xdisplay,
|
|
window->xwindow);
|
|
|
|
/* Even though the window is now unmanaged, we can't unselect events. This
|
|
* window might be a window from this process, like a GdkMenu, in
|
|
* which case it will have pointer events and so forth selected
|
|
* for it by GDK. There's no way to disentangle those events from the events
|
|
* we've selected. Even for a window from a different X client,
|
|
* GDK could also have selected events for it for IPC purposes, so we
|
|
* can't unselect in that case either.
|
|
*
|
|
* Similarly, we can't unselected for events on window->user_time_window.
|
|
* It might be our own GDK focus window, or it might be a window that a
|
|
* different client is using for multiple different things:
|
|
* _NET_WM_USER_TIME_WINDOW and IPC, perhaps.
|
|
*/
|
|
|
|
if (window->user_time_window != None)
|
|
{
|
|
meta_display_unregister_x_window (window->display,
|
|
window->user_time_window);
|
|
window->user_time_window = None;
|
|
}
|
|
|
|
if (META_DISPLAY_HAS_SHAPE (window->display))
|
|
XShapeSelectInput (window->display->xdisplay, window->xwindow, NoEventMask);
|
|
|
|
meta_window_ungrab_keys (window);
|
|
meta_display_ungrab_window_buttons (window->display, window->xwindow);
|
|
meta_display_ungrab_focus_window_button (window->display, window);
|
|
|
|
meta_error_trap_pop (window->display);
|
|
|
|
if (window->frame)
|
|
{
|
|
/* The XReparentWindow call in meta_window_destroy_frame() moves the
|
|
* window so we need to send a configure notify; see bug 399552. (We
|
|
* also do this just in case a window got unmaximized.)
|
|
*/
|
|
send_configure_notify (window);
|
|
|
|
meta_window_destroy_frame (window);
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_window_x11_ping (MetaWindow *window,
|
|
guint32 serial)
|
|
{
|
|
MetaDisplay *display = window->display;
|
|
|
|
send_icccm_message (window, display->atom__NET_WM_PING, serial);
|
|
}
|
|
|
|
static void
|
|
meta_window_x11_delete (MetaWindow *window,
|
|
guint32 timestamp)
|
|
{
|
|
meta_error_trap_push (window->display);
|
|
if (window->delete_window)
|
|
{
|
|
meta_topic (META_DEBUG_WINDOW_OPS,
|
|
"Deleting %s with delete_window request\n",
|
|
window->desc);
|
|
send_icccm_message (window, window->display->atom_WM_DELETE_WINDOW, timestamp);
|
|
}
|
|
else
|
|
{
|
|
meta_topic (META_DEBUG_WINDOW_OPS,
|
|
"Deleting %s with explicit kill\n",
|
|
window->desc);
|
|
XKillClient (window->display->xdisplay, window->xwindow);
|
|
}
|
|
meta_error_trap_pop (window->display);
|
|
}
|
|
|
|
static void
|
|
meta_window_x11_kill (MetaWindow *window)
|
|
{
|
|
meta_topic (META_DEBUG_WINDOW_OPS,
|
|
"Disconnecting %s with XKillClient()\n",
|
|
window->desc);
|
|
|
|
meta_error_trap_push (window->display);
|
|
XKillClient (window->display->xdisplay, window->xwindow);
|
|
meta_error_trap_pop (window->display);
|
|
}
|
|
|
|
static void
|
|
request_take_focus (MetaWindow *window,
|
|
guint32 timestamp)
|
|
{
|
|
MetaDisplay *display = window->display;
|
|
|
|
meta_topic (META_DEBUG_FOCUS, "WM_TAKE_FOCUS(%s, %u)\n",
|
|
window->desc, timestamp);
|
|
|
|
send_icccm_message (window, display->atom_WM_TAKE_FOCUS, timestamp);
|
|
}
|
|
|
|
static void
|
|
meta_window_x11_focus (MetaWindow *window,
|
|
guint32 timestamp)
|
|
{
|
|
/* For output-only or shaded windows, focus the frame.
|
|
* This seems to result in the client window getting key events
|
|
* though, so I don't know if it's icccm-compliant.
|
|
*
|
|
* Still, we have to do this or keynav breaks for these windows.
|
|
*/
|
|
if (window->frame &&
|
|
(window->shaded ||
|
|
!(window->input || window->take_focus)))
|
|
{
|
|
meta_topic (META_DEBUG_FOCUS,
|
|
"Focusing frame of %s\n", window->desc);
|
|
meta_display_set_input_focus_window (window->display,
|
|
window,
|
|
TRUE,
|
|
timestamp);
|
|
}
|
|
else
|
|
{
|
|
if (window->input)
|
|
{
|
|
meta_topic (META_DEBUG_FOCUS,
|
|
"Setting input focus on %s since input = true\n",
|
|
window->desc);
|
|
meta_display_set_input_focus_window (window->display,
|
|
window,
|
|
FALSE,
|
|
timestamp);
|
|
}
|
|
|
|
if (window->take_focus)
|
|
{
|
|
meta_topic (META_DEBUG_FOCUS,
|
|
"Sending WM_TAKE_FOCUS to %s since take_focus = true\n",
|
|
window->desc);
|
|
|
|
if (!window->input)
|
|
{
|
|
/* The "Globally Active Input" window case, where the window
|
|
* doesn't want us to call XSetInputFocus on it, but does
|
|
* want us to send a WM_TAKE_FOCUS.
|
|
*
|
|
* Normally, we want to just leave the focus undisturbed until
|
|
* the window responds to WM_TAKE_FOCUS, but if we're unmanaging
|
|
* the current focus window we *need* to move the focus away, so
|
|
* we focus the no_focus_window now (and set
|
|
* display->focus_window to that) before sending WM_TAKE_FOCUS.
|
|
*/
|
|
if (window->display->focus_window != NULL &&
|
|
window->display->focus_window->unmanaging)
|
|
meta_display_focus_the_no_focus_window (window->display,
|
|
window->screen,
|
|
timestamp);
|
|
}
|
|
|
|
request_take_focus (window, timestamp);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_window_get_client_root_coords (MetaWindow *window,
|
|
MetaRectangle *rect)
|
|
{
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
|
|
*rect = priv->client_rect;
|
|
|
|
if (window->frame)
|
|
{
|
|
rect->x += window->frame->rect.x;
|
|
rect->y += window->frame->rect.y;
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_window_refresh_resize_popup (MetaWindow *window)
|
|
{
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
|
|
if (priv->showing_resize_popup)
|
|
{
|
|
MetaRectangle rect;
|
|
int display_w, display_h;
|
|
|
|
meta_window_get_client_root_coords (window, &rect);
|
|
|
|
display_w = (rect.width - window->size_hints.base_width);
|
|
if (window->size_hints.width_inc > 0)
|
|
display_w /= window->size_hints.width_inc;
|
|
|
|
display_h = (rect.height - window->size_hints.base_height);
|
|
if (window->size_hints.height_inc > 0)
|
|
display_h /= window->size_hints.height_inc;
|
|
|
|
meta_display_show_resize_popup (window->display, TRUE, &rect, display_w, display_h);
|
|
}
|
|
else
|
|
{
|
|
meta_display_show_resize_popup (window->display, FALSE, NULL, 0, 0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_window_x11_grab_op_began (MetaWindow *window,
|
|
MetaGrabOp op)
|
|
{
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
|
|
if (meta_grab_op_is_resizing (op))
|
|
{
|
|
if (window->sync_request_counter != None)
|
|
meta_window_x11_create_sync_request_alarm (window);
|
|
|
|
if (window->size_hints.width_inc > 2 || window->size_hints.height_inc > 2)
|
|
{
|
|
priv->showing_resize_popup = TRUE;
|
|
meta_window_refresh_resize_popup (window);
|
|
}
|
|
}
|
|
|
|
META_WINDOW_CLASS (meta_window_x11_parent_class)->grab_op_began (window, op);
|
|
}
|
|
|
|
static void
|
|
meta_window_x11_grab_op_ended (MetaWindow *window,
|
|
MetaGrabOp op)
|
|
{
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
|
|
if (priv->showing_resize_popup)
|
|
{
|
|
priv->showing_resize_popup = FALSE;
|
|
meta_window_refresh_resize_popup (window);
|
|
}
|
|
|
|
META_WINDOW_CLASS (meta_window_x11_parent_class)->grab_op_ended (window, op);
|
|
}
|
|
|
|
static void
|
|
update_net_frame_extents (MetaWindow *window)
|
|
{
|
|
unsigned long data[4];
|
|
MetaFrameBorders borders;
|
|
|
|
meta_frame_calc_borders (window->frame, &borders);
|
|
/* Left */
|
|
data[0] = borders.visible.left;
|
|
/* Right */
|
|
data[1] = borders.visible.right;
|
|
/* Top */
|
|
data[2] = borders.visible.top;
|
|
/* Bottom */
|
|
data[3] = borders.visible.bottom;
|
|
|
|
meta_topic (META_DEBUG_GEOMETRY,
|
|
"Setting _NET_FRAME_EXTENTS on managed window 0x%lx "
|
|
"to left = %lu, right = %lu, top = %lu, bottom = %lu\n",
|
|
window->xwindow, data[0], data[1], data[2], data[3]);
|
|
|
|
meta_error_trap_push (window->display);
|
|
XChangeProperty (window->display->xdisplay, window->xwindow,
|
|
window->display->atom__NET_FRAME_EXTENTS,
|
|
XA_CARDINAL,
|
|
32, PropModeReplace, (guchar*) data, 4);
|
|
meta_error_trap_pop (window->display);
|
|
}
|
|
|
|
static void
|
|
update_gtk_edge_constraints (MetaWindow *window)
|
|
{
|
|
MetaEdgeConstraint *constraints = window->edge_constraints;
|
|
unsigned long data[1];
|
|
|
|
/* Edge constraints */
|
|
data[0] = (constraints[0] != META_EDGE_CONSTRAINT_NONE ? 1 : 0) << 0 |
|
|
(constraints[0] != META_EDGE_CONSTRAINT_MONITOR ? 1 : 0) << 1 |
|
|
(constraints[1] != META_EDGE_CONSTRAINT_NONE ? 1 : 0) << 2 |
|
|
(constraints[1] != META_EDGE_CONSTRAINT_MONITOR ? 1 : 0) << 3 |
|
|
(constraints[2] != META_EDGE_CONSTRAINT_NONE ? 1 : 0) << 4 |
|
|
(constraints[2] != META_EDGE_CONSTRAINT_MONITOR ? 1 : 0) << 5 |
|
|
(constraints[3] != META_EDGE_CONSTRAINT_NONE ? 1 : 0) << 6 |
|
|
(constraints[3] != META_EDGE_CONSTRAINT_MONITOR ? 1 : 0) << 7;
|
|
|
|
meta_verbose ("Setting _GTK_EDGE_CONSTRAINTS to %lu\n", data[0]);
|
|
|
|
meta_error_trap_push (window->display);
|
|
XChangeProperty (window->display->xdisplay,
|
|
window->xwindow,
|
|
window->display->atom__GTK_EDGE_CONSTRAINTS,
|
|
XA_CARDINAL, 32, PropModeReplace,
|
|
(guchar*) data, 1);
|
|
meta_error_trap_pop (window->display);
|
|
}
|
|
|
|
static gboolean
|
|
sync_request_timeout (gpointer data)
|
|
{
|
|
MetaWindow *window = data;
|
|
|
|
window->sync_request_timeout_id = 0;
|
|
|
|
/* We have now waited for more than a second for the
|
|
* application to respond to the sync request
|
|
*/
|
|
window->disable_sync = TRUE;
|
|
|
|
/* Reset the wait serial, so we don't continue freezing
|
|
* window updates
|
|
*/
|
|
window->sync_request_wait_serial = 0;
|
|
meta_compositor_sync_updates_frozen (window->display->compositor, window);
|
|
|
|
if (window == window->display->grab_window &&
|
|
meta_grab_op_is_resizing (window->display->grab_op))
|
|
{
|
|
meta_window_update_resize (window,
|
|
window->display->grab_last_user_action_was_snap,
|
|
window->display->grab_latest_motion_x,
|
|
window->display->grab_latest_motion_y,
|
|
TRUE);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
send_sync_request (MetaWindow *window)
|
|
{
|
|
XClientMessageEvent ev;
|
|
gint64 wait_serial;
|
|
|
|
/* For the old style of _NET_WM_SYNC_REQUEST_COUNTER, we just have to
|
|
* increase the value, but for the new "extended" style we need to
|
|
* pick an even (unfrozen) value sufficiently ahead of the last serial
|
|
* that we received from the client; the same code still works
|
|
* for the old style. The increment of 240 is specified by the EWMH
|
|
* and is (1 second) * (60fps) * (an increment of 4 per frame).
|
|
*/
|
|
wait_serial = window->sync_request_serial + 240;
|
|
|
|
window->sync_request_wait_serial = wait_serial;
|
|
|
|
ev.type = ClientMessage;
|
|
ev.window = window->xwindow;
|
|
ev.message_type = window->display->atom_WM_PROTOCOLS;
|
|
ev.format = 32;
|
|
ev.data.l[0] = window->display->atom__NET_WM_SYNC_REQUEST;
|
|
/* FIXME: meta_display_get_current_time() is bad, but since calls
|
|
* come from meta_window_move_resize_internal (which in turn come
|
|
* from all over), I'm not sure what we can do to fix it. Do we
|
|
* want to use _roundtrip, though?
|
|
*/
|
|
ev.data.l[1] = meta_display_get_current_time (window->display);
|
|
ev.data.l[2] = wait_serial & G_GUINT64_CONSTANT(0xffffffff);
|
|
ev.data.l[3] = wait_serial >> 32;
|
|
ev.data.l[4] = window->extended_sync_request_counter ? 1 : 0;
|
|
|
|
/* We don't need to trap errors here as we are already
|
|
* inside an error_trap_push()/pop() pair.
|
|
*/
|
|
XSendEvent (window->display->xdisplay,
|
|
window->xwindow, False, 0, (XEvent*) &ev);
|
|
|
|
/* We give the window 1 sec to respond to _NET_WM_SYNC_REQUEST;
|
|
* if this time expires, we consider the window unresponsive
|
|
* and resize it unsynchonized.
|
|
*/
|
|
window->sync_request_timeout_id = g_timeout_add (1000,
|
|
sync_request_timeout,
|
|
window);
|
|
g_source_set_name_by_id (window->sync_request_timeout_id,
|
|
"[mutter] sync_request_timeout");
|
|
|
|
meta_compositor_sync_updates_frozen (window->display->compositor, window);
|
|
}
|
|
|
|
static unsigned long
|
|
meta_window_get_net_wm_desktop (MetaWindow *window)
|
|
{
|
|
if (window->on_all_workspaces)
|
|
return 0xFFFFFFFF;
|
|
else
|
|
return meta_workspace_index (window->workspace);
|
|
}
|
|
|
|
static void
|
|
meta_window_x11_current_workspace_changed (MetaWindow *window)
|
|
{
|
|
/* FIXME if on more than one workspace, we claim to be "sticky",
|
|
* the WM spec doesn't say what to do here.
|
|
*/
|
|
unsigned long data[1];
|
|
|
|
if (window->workspace == NULL)
|
|
{
|
|
/* this happens when unmanaging windows */
|
|
return;
|
|
}
|
|
|
|
data[0] = meta_window_get_net_wm_desktop (window);
|
|
|
|
meta_verbose ("Setting _NET_WM_DESKTOP of %s to %lu\n",
|
|
window->desc, data[0]);
|
|
|
|
meta_error_trap_push (window->display);
|
|
XChangeProperty (window->display->xdisplay, window->xwindow,
|
|
window->display->atom__NET_WM_DESKTOP,
|
|
XA_CARDINAL,
|
|
32, PropModeReplace, (guchar*) data, 1);
|
|
meta_error_trap_pop (window->display);
|
|
}
|
|
|
|
static void
|
|
meta_window_x11_move_resize_internal (MetaWindow *window,
|
|
int gravity,
|
|
MetaRectangle unconstrained_rect,
|
|
MetaRectangle constrained_rect,
|
|
MetaMoveResizeFlags flags,
|
|
MetaMoveResizeResultFlags *result)
|
|
{
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
MetaFrameBorders borders;
|
|
MetaRectangle client_rect;
|
|
int size_dx, size_dy;
|
|
XWindowChanges values;
|
|
unsigned int mask;
|
|
gboolean need_configure_notify;
|
|
gboolean need_move_client = FALSE;
|
|
gboolean need_move_frame = FALSE;
|
|
gboolean need_resize_client = FALSE;
|
|
gboolean need_resize_frame = FALSE;
|
|
gboolean frame_shape_changed = FALSE;
|
|
gboolean configure_frame_first;
|
|
|
|
gboolean is_configure_request;
|
|
|
|
is_configure_request = (flags & META_MOVE_RESIZE_CONFIGURE_REQUEST) != 0;
|
|
|
|
meta_frame_calc_borders (window->frame, &borders);
|
|
|
|
size_dx = constrained_rect.x - window->rect.width;
|
|
size_dy = constrained_rect.y - window->rect.height;
|
|
|
|
window->rect = constrained_rect;
|
|
|
|
if (window->frame)
|
|
{
|
|
int new_w, new_h;
|
|
int new_x, new_y;
|
|
|
|
/* Compute new frame size */
|
|
new_w = window->rect.width + borders.invisible.left + borders.invisible.right;
|
|
|
|
if (window->shaded)
|
|
new_h = borders.total.top + borders.total.bottom;
|
|
else
|
|
new_h = window->rect.height + borders.invisible.top + borders.invisible.bottom;
|
|
|
|
if (new_w != window->frame->rect.width ||
|
|
new_h != window->frame->rect.height)
|
|
{
|
|
need_resize_frame = TRUE;
|
|
window->frame->rect.width = new_w;
|
|
window->frame->rect.height = new_h;
|
|
}
|
|
|
|
/* Compute new frame coords */
|
|
new_x = window->rect.x - borders.invisible.left;
|
|
new_y = window->rect.y - borders.invisible.top;
|
|
|
|
if (new_x != window->frame->rect.x ||
|
|
new_y != window->frame->rect.y)
|
|
{
|
|
need_move_frame = TRUE;
|
|
window->frame->rect.x = new_x;
|
|
window->frame->rect.y = new_y;
|
|
}
|
|
}
|
|
|
|
/* Calculate the new client rect */
|
|
meta_window_frame_rect_to_client_rect (window, &constrained_rect, &client_rect);
|
|
|
|
/* The above client_rect is in root window coordinates. The
|
|
* values we need to pass to XConfigureWindow are in parent
|
|
* coordinates, so if the window is in a frame, we need to
|
|
* correct the x/y positions here. */
|
|
if (window->frame)
|
|
{
|
|
client_rect.x = borders.total.left;
|
|
client_rect.y = borders.total.top;
|
|
}
|
|
|
|
if (client_rect.x != priv->client_rect.x ||
|
|
client_rect.y != priv->client_rect.y)
|
|
{
|
|
need_move_client = TRUE;
|
|
priv->client_rect.x = client_rect.x;
|
|
priv->client_rect.y = client_rect.y;
|
|
}
|
|
|
|
if (client_rect.width != priv->client_rect.width ||
|
|
client_rect.height != priv->client_rect.height)
|
|
{
|
|
need_resize_client = TRUE;
|
|
priv->client_rect.width = client_rect.width;
|
|
priv->client_rect.height = client_rect.height;
|
|
}
|
|
|
|
/* If frame extents have changed, fill in other frame fields and
|
|
change frame's extents property. */
|
|
if (window->frame &&
|
|
(window->frame->child_x != borders.total.left ||
|
|
window->frame->child_y != borders.total.top ||
|
|
window->frame->right_width != borders.total.right ||
|
|
window->frame->bottom_height != borders.total.bottom))
|
|
{
|
|
window->frame->child_x = borders.total.left;
|
|
window->frame->child_y = borders.total.top;
|
|
window->frame->right_width = borders.total.right;
|
|
window->frame->bottom_height = borders.total.bottom;
|
|
|
|
update_net_frame_extents (window);
|
|
}
|
|
|
|
/* See ICCCM 4.1.5 for when to send ConfigureNotify */
|
|
|
|
need_configure_notify = FALSE;
|
|
|
|
/* If this is a configure request and we change nothing, then we
|
|
* must send configure notify.
|
|
*/
|
|
if (is_configure_request &&
|
|
!(need_move_client || need_move_frame ||
|
|
need_resize_client || need_resize_frame ||
|
|
priv->border_width != 0))
|
|
need_configure_notify = TRUE;
|
|
|
|
/* We must send configure notify if we move but don't resize, since
|
|
* the client window may not get a real event
|
|
*/
|
|
if ((need_move_client || need_move_frame) &&
|
|
!(need_resize_client || need_resize_frame))
|
|
need_configure_notify = TRUE;
|
|
|
|
/* MapRequest events with a PPosition or UPosition hint with a frame
|
|
* are moved by mutter without resizing; send a configure notify
|
|
* in such cases. See #322840. (Note that window->constructing is
|
|
* only true iff this call is due to a MapRequest, and when
|
|
* PPosition/UPosition hints aren't set, mutter seems to send a
|
|
* ConfigureNotify anyway due to the above code.)
|
|
*/
|
|
if (window->constructing && window->frame &&
|
|
((window->size_hints.flags & PPosition) ||
|
|
(window->size_hints.flags & USPosition)))
|
|
need_configure_notify = TRUE;
|
|
|
|
/* The rest of this function syncs our new size/pos with X as
|
|
* efficiently as possible
|
|
*/
|
|
|
|
/* For nice effect, when growing the window we want to move/resize
|
|
* the frame first, when shrinking the window we want to move/resize
|
|
* the client first. If we grow one way and shrink the other,
|
|
* see which way we're moving "more"
|
|
*
|
|
* Mail from Owen subject "Suggestion: Gravity and resizing from the left"
|
|
* http://mail.gnome.org/archives/wm-spec-list/1999-November/msg00088.html
|
|
*
|
|
* An annoying fact you need to know in this code is that StaticGravity
|
|
* does nothing if you _only_ resize or _only_ move the frame;
|
|
* it must move _and_ resize, otherwise you get NorthWestGravity
|
|
* behavior. The move and resize must actually occur, it is not
|
|
* enough to set CWX | CWWidth but pass in the current size/pos.
|
|
*/
|
|
|
|
/* Normally, we configure the frame first depending on whether
|
|
* we grow the frame more than we shrink. The idea is to avoid
|
|
* messing up the window contents by having a temporary situation
|
|
* where the frame is smaller than the window. However, if we're
|
|
* cooperating with the client to create an atomic frame upate,
|
|
* and the window is redirected, then we should always update
|
|
* the frame first, since updating the frame will force a new
|
|
* backing pixmap to be allocated, and the old backing pixmap
|
|
* will be left undisturbed for us to paint to the screen until
|
|
* the client finishes redrawing.
|
|
*/
|
|
if (window->extended_sync_request_counter)
|
|
configure_frame_first = TRUE;
|
|
else
|
|
configure_frame_first = size_dx + size_dy >= 0;
|
|
|
|
if (configure_frame_first && window->frame)
|
|
frame_shape_changed = meta_frame_sync_to_window (window->frame, need_resize_frame);
|
|
|
|
values.border_width = 0;
|
|
values.x = client_rect.x;
|
|
values.y = client_rect.y;
|
|
values.width = client_rect.width;
|
|
values.height = client_rect.height;
|
|
|
|
mask = 0;
|
|
if (is_configure_request && priv->border_width != 0)
|
|
mask |= CWBorderWidth; /* must force to 0 */
|
|
if (need_move_client)
|
|
mask |= (CWX | CWY);
|
|
if (need_resize_client)
|
|
mask |= (CWWidth | CWHeight);
|
|
|
|
if (mask != 0)
|
|
{
|
|
meta_error_trap_push (window->display);
|
|
|
|
if (window == window->display->grab_window &&
|
|
meta_grab_op_is_resizing (window->display->grab_op) &&
|
|
!window->disable_sync &&
|
|
window->sync_request_counter != None &&
|
|
window->sync_request_alarm != None &&
|
|
window->sync_request_timeout_id == 0)
|
|
{
|
|
send_sync_request (window);
|
|
}
|
|
|
|
XConfigureWindow (window->display->xdisplay,
|
|
window->xwindow,
|
|
mask,
|
|
&values);
|
|
|
|
meta_error_trap_pop (window->display);
|
|
}
|
|
|
|
if (!configure_frame_first && window->frame)
|
|
frame_shape_changed = meta_frame_sync_to_window (window->frame, need_resize_frame);
|
|
|
|
if (window->frame)
|
|
window->buffer_rect = window->frame->rect;
|
|
else
|
|
window->buffer_rect = client_rect;
|
|
|
|
if (need_configure_notify)
|
|
send_configure_notify (window);
|
|
|
|
if (priv->showing_resize_popup)
|
|
meta_window_refresh_resize_popup (window);
|
|
|
|
if (frame_shape_changed)
|
|
*result |= META_MOVE_RESIZE_RESULT_FRAME_SHAPE_CHANGED;
|
|
if (need_move_client || need_move_frame)
|
|
*result |= META_MOVE_RESIZE_RESULT_MOVED;
|
|
if (need_resize_client || need_resize_frame)
|
|
*result |= META_MOVE_RESIZE_RESULT_RESIZED;
|
|
|
|
update_gtk_edge_constraints (window);
|
|
}
|
|
|
|
static gboolean
|
|
meta_window_x11_update_struts (MetaWindow *window)
|
|
{
|
|
GSList *old_struts;
|
|
GSList *new_struts;
|
|
GSList *old_iter, *new_iter;
|
|
uint32_t *struts = NULL;
|
|
int nitems;
|
|
gboolean changed;
|
|
|
|
g_return_val_if_fail (!window->override_redirect, FALSE);
|
|
|
|
meta_verbose ("Updating struts for %s\n", window->desc);
|
|
|
|
old_struts = window->struts;
|
|
new_struts = NULL;
|
|
|
|
if (meta_prop_get_cardinal_list (window->display,
|
|
window->xwindow,
|
|
window->display->atom__NET_WM_STRUT_PARTIAL,
|
|
&struts, &nitems))
|
|
{
|
|
if (nitems != 12)
|
|
meta_verbose ("_NET_WM_STRUT_PARTIAL on %s has %d values instead "
|
|
"of 12\n",
|
|
window->desc, nitems);
|
|
else
|
|
{
|
|
/* Pull out the strut info for each side in the hint */
|
|
int i;
|
|
for (i=0; i<4; i++)
|
|
{
|
|
MetaStrut *temp;
|
|
int thickness, strut_begin, strut_end;
|
|
|
|
thickness = struts[i];
|
|
if (thickness == 0)
|
|
continue;
|
|
strut_begin = struts[4+(i*2)];
|
|
strut_end = struts[4+(i*2)+1];
|
|
|
|
temp = g_new (MetaStrut, 1);
|
|
temp->side = 1 << i; /* See MetaSide def. Matches nicely, eh? */
|
|
temp->rect = window->screen->rect;
|
|
switch (temp->side)
|
|
{
|
|
case META_SIDE_RIGHT:
|
|
temp->rect.x = BOX_RIGHT(temp->rect) - thickness;
|
|
/* Intentionally fall through without breaking */
|
|
case META_SIDE_LEFT:
|
|
temp->rect.width = thickness;
|
|
temp->rect.y = strut_begin;
|
|
temp->rect.height = strut_end - strut_begin + 1;
|
|
break;
|
|
case META_SIDE_BOTTOM:
|
|
temp->rect.y = BOX_BOTTOM(temp->rect) - thickness;
|
|
/* Intentionally fall through without breaking */
|
|
case META_SIDE_TOP:
|
|
temp->rect.height = thickness;
|
|
temp->rect.x = strut_begin;
|
|
temp->rect.width = strut_end - strut_begin + 1;
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
new_struts = g_slist_prepend (new_struts, temp);
|
|
}
|
|
|
|
meta_verbose ("_NET_WM_STRUT_PARTIAL struts %u %u %u %u for "
|
|
"window %s\n",
|
|
struts[0], struts[1], struts[2], struts[3],
|
|
window->desc);
|
|
}
|
|
meta_XFree (struts);
|
|
}
|
|
else
|
|
{
|
|
meta_verbose ("No _NET_WM_STRUT property for %s\n",
|
|
window->desc);
|
|
}
|
|
|
|
if (!new_struts &&
|
|
meta_prop_get_cardinal_list (window->display,
|
|
window->xwindow,
|
|
window->display->atom__NET_WM_STRUT,
|
|
&struts, &nitems))
|
|
{
|
|
if (nitems != 4)
|
|
meta_verbose ("_NET_WM_STRUT on %s has %d values instead of 4\n",
|
|
window->desc, nitems);
|
|
else
|
|
{
|
|
/* Pull out the strut info for each side in the hint */
|
|
int i;
|
|
for (i=0; i<4; i++)
|
|
{
|
|
MetaStrut *temp;
|
|
int thickness;
|
|
|
|
thickness = struts[i];
|
|
if (thickness == 0)
|
|
continue;
|
|
|
|
temp = g_new (MetaStrut, 1);
|
|
temp->side = 1 << i;
|
|
temp->rect = window->screen->rect;
|
|
switch (temp->side)
|
|
{
|
|
case META_SIDE_RIGHT:
|
|
temp->rect.x = BOX_RIGHT(temp->rect) - thickness;
|
|
/* Intentionally fall through without breaking */
|
|
case META_SIDE_LEFT:
|
|
temp->rect.width = thickness;
|
|
break;
|
|
case META_SIDE_BOTTOM:
|
|
temp->rect.y = BOX_BOTTOM(temp->rect) - thickness;
|
|
/* Intentionally fall through without breaking */
|
|
case META_SIDE_TOP:
|
|
temp->rect.height = thickness;
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
new_struts = g_slist_prepend (new_struts, temp);
|
|
}
|
|
|
|
meta_verbose ("_NET_WM_STRUT struts %u %u %u %u for window %s\n",
|
|
struts[0], struts[1], struts[2], struts[3],
|
|
window->desc);
|
|
}
|
|
meta_XFree (struts);
|
|
}
|
|
else if (!new_struts)
|
|
{
|
|
meta_verbose ("No _NET_WM_STRUT property for %s\n",
|
|
window->desc);
|
|
}
|
|
|
|
/* Determine whether old_struts and new_struts are the same */
|
|
old_iter = old_struts;
|
|
new_iter = new_struts;
|
|
while (old_iter && new_iter)
|
|
{
|
|
MetaStrut *old_strut = (MetaStrut*) old_iter->data;
|
|
MetaStrut *new_strut = (MetaStrut*) new_iter->data;
|
|
|
|
if (old_strut->side != new_strut->side ||
|
|
!meta_rectangle_equal (&old_strut->rect, &new_strut->rect))
|
|
break;
|
|
|
|
old_iter = old_iter->next;
|
|
new_iter = new_iter->next;
|
|
}
|
|
changed = (old_iter != NULL || new_iter != NULL);
|
|
|
|
/* Update appropriately */
|
|
meta_free_gslist_and_elements (old_struts);
|
|
window->struts = new_struts;
|
|
return changed;
|
|
}
|
|
|
|
static void
|
|
meta_window_x11_get_default_skip_hints (MetaWindow *window,
|
|
gboolean *skip_taskbar_out,
|
|
gboolean *skip_pager_out)
|
|
{
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
|
|
*skip_taskbar_out = priv->wm_state_skip_taskbar;
|
|
*skip_pager_out = priv->wm_state_skip_pager;
|
|
}
|
|
|
|
static gboolean
|
|
meta_window_x11_update_icon (MetaWindow *window,
|
|
cairo_surface_t **icon,
|
|
cairo_surface_t **mini_icon)
|
|
{
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
|
|
return meta_read_icons (window->screen,
|
|
window->xwindow,
|
|
&priv->icon_cache,
|
|
priv->wm_hints_pixmap,
|
|
priv->wm_hints_mask,
|
|
icon,
|
|
META_ICON_WIDTH, META_ICON_HEIGHT,
|
|
mini_icon,
|
|
META_MINI_ICON_WIDTH, META_MINI_ICON_HEIGHT);
|
|
}
|
|
|
|
static void
|
|
meta_window_x11_update_main_monitor (MetaWindow *window,
|
|
gboolean user_op)
|
|
{
|
|
window->monitor = meta_window_calculate_main_logical_monitor (window);
|
|
}
|
|
|
|
static void
|
|
meta_window_x11_main_monitor_changed (MetaWindow *window,
|
|
const MetaLogicalMonitor *old)
|
|
{
|
|
}
|
|
|
|
static uint32_t
|
|
meta_window_x11_get_client_pid (MetaWindow *window)
|
|
{
|
|
xcb_connection_t *xcb = XGetXCBConnection (window->display->xdisplay);
|
|
xcb_res_client_id_spec_t spec = { 0 };
|
|
xcb_res_query_client_ids_cookie_t cookie;
|
|
xcb_res_query_client_ids_reply_t *reply = NULL;
|
|
|
|
spec.client = window->xwindow;
|
|
spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID;
|
|
|
|
cookie = xcb_res_query_client_ids (xcb, 1, &spec);
|
|
reply = xcb_res_query_client_ids_reply (xcb, cookie, NULL);
|
|
|
|
if (reply == NULL)
|
|
return 0;
|
|
|
|
uint32_t pid = 0, *value;
|
|
xcb_res_client_id_value_iterator_t it;
|
|
for (it = xcb_res_query_client_ids_ids_iterator (reply);
|
|
it.rem;
|
|
xcb_res_client_id_value_next (&it))
|
|
{
|
|
spec = it.data->spec;
|
|
if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID)
|
|
{
|
|
value = xcb_res_client_id_value_value (it.data);
|
|
pid = *value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
free (reply);
|
|
return pid;
|
|
}
|
|
|
|
static void
|
|
meta_window_x11_force_restore_shortcuts (MetaWindow *window,
|
|
ClutterInputDevice *source)
|
|
{
|
|
/*
|
|
* Not needed on X11 because clients can use a keyboard grab
|
|
* to bypass the compositor shortcuts.
|
|
*/
|
|
}
|
|
|
|
static gboolean
|
|
meta_window_x11_shortcuts_inhibited (MetaWindow *window,
|
|
ClutterInputDevice *source)
|
|
{
|
|
/*
|
|
* On X11, we don't use a shortcuts inhibitor, clients just grab
|
|
* the keyboard.
|
|
*/
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
meta_window_x11_is_stackable (MetaWindow *window)
|
|
{
|
|
return !window->override_redirect;
|
|
}
|
|
|
|
static void
|
|
meta_window_x11_class_init (MetaWindowX11Class *klass)
|
|
{
|
|
MetaWindowClass *window_class = META_WINDOW_CLASS (klass);
|
|
|
|
window_class->manage = meta_window_x11_manage;
|
|
window_class->unmanage = meta_window_x11_unmanage;
|
|
window_class->ping = meta_window_x11_ping;
|
|
window_class->delete = meta_window_x11_delete;
|
|
window_class->kill = meta_window_x11_kill;
|
|
window_class->focus = meta_window_x11_focus;
|
|
window_class->grab_op_began = meta_window_x11_grab_op_began;
|
|
window_class->grab_op_ended = meta_window_x11_grab_op_ended;
|
|
window_class->current_workspace_changed = meta_window_x11_current_workspace_changed;
|
|
window_class->move_resize_internal = meta_window_x11_move_resize_internal;
|
|
window_class->update_struts = meta_window_x11_update_struts;
|
|
window_class->get_default_skip_hints = meta_window_x11_get_default_skip_hints;
|
|
window_class->update_icon = meta_window_x11_update_icon;
|
|
window_class->update_main_monitor = meta_window_x11_update_main_monitor;
|
|
window_class->main_monitor_changed = meta_window_x11_main_monitor_changed;
|
|
window_class->get_client_pid = meta_window_x11_get_client_pid;
|
|
window_class->force_restore_shortcuts = meta_window_x11_force_restore_shortcuts;
|
|
window_class->shortcuts_inhibited = meta_window_x11_shortcuts_inhibited;
|
|
window_class->is_stackable = meta_window_x11_is_stackable;
|
|
}
|
|
|
|
void
|
|
meta_window_x11_set_net_wm_state (MetaWindow *window)
|
|
{
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
int i;
|
|
unsigned long data[13];
|
|
|
|
i = 0;
|
|
if (window->shaded)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_STATE_SHADED;
|
|
++i;
|
|
}
|
|
if (priv->wm_state_modal)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_STATE_MODAL;
|
|
++i;
|
|
}
|
|
if (window->skip_pager)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_STATE_SKIP_PAGER;
|
|
++i;
|
|
}
|
|
if (window->skip_taskbar)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_STATE_SKIP_TASKBAR;
|
|
++i;
|
|
}
|
|
if (window->maximized_horizontally)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_STATE_MAXIMIZED_HORZ;
|
|
++i;
|
|
}
|
|
if (window->maximized_vertically)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_STATE_MAXIMIZED_VERT;
|
|
++i;
|
|
}
|
|
if (window->fullscreen)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_STATE_FULLSCREEN;
|
|
++i;
|
|
}
|
|
if (!meta_window_showing_on_its_workspace (window) || window->shaded)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_STATE_HIDDEN;
|
|
++i;
|
|
}
|
|
if (window->wm_state_above)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_STATE_ABOVE;
|
|
++i;
|
|
}
|
|
if (window->wm_state_below)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_STATE_BELOW;
|
|
++i;
|
|
}
|
|
if (window->wm_state_demands_attention)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_STATE_DEMANDS_ATTENTION;
|
|
++i;
|
|
}
|
|
if (window->on_all_workspaces_requested)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_STATE_STICKY;
|
|
++i;
|
|
}
|
|
if (meta_window_appears_focused (window))
|
|
{
|
|
data[i] = window->display->atom__NET_WM_STATE_FOCUSED;
|
|
++i;
|
|
}
|
|
|
|
meta_verbose ("Setting _NET_WM_STATE with %d atoms\n", i);
|
|
|
|
meta_error_trap_push (window->display);
|
|
XChangeProperty (window->display->xdisplay, window->xwindow,
|
|
window->display->atom__NET_WM_STATE,
|
|
XA_ATOM,
|
|
32, PropModeReplace, (guchar*) data, i);
|
|
meta_error_trap_pop (window->display);
|
|
|
|
if (window->fullscreen)
|
|
{
|
|
if (meta_window_has_fullscreen_monitors (window))
|
|
{
|
|
data[0] =
|
|
meta_screen_logical_monitor_to_xinerama_index (window->screen,
|
|
window->fullscreen_monitors.top);
|
|
data[1] =
|
|
meta_screen_logical_monitor_to_xinerama_index (window->screen,
|
|
window->fullscreen_monitors.bottom);
|
|
data[2] =
|
|
meta_screen_logical_monitor_to_xinerama_index (window->screen,
|
|
window->fullscreen_monitors.left);
|
|
data[3] =
|
|
meta_screen_logical_monitor_to_xinerama_index (window->screen,
|
|
window->fullscreen_monitors.right);
|
|
|
|
meta_verbose ("Setting _NET_WM_FULLSCREEN_MONITORS\n");
|
|
meta_error_trap_push (window->display);
|
|
XChangeProperty (window->display->xdisplay,
|
|
window->xwindow,
|
|
window->display->atom__NET_WM_FULLSCREEN_MONITORS,
|
|
XA_CARDINAL, 32, PropModeReplace,
|
|
(guchar*) data, 4);
|
|
meta_error_trap_pop (window->display);
|
|
}
|
|
else
|
|
{
|
|
meta_verbose ("Clearing _NET_WM_FULLSCREEN_MONITORS\n");
|
|
meta_error_trap_push (window->display);
|
|
XDeleteProperty (window->display->xdisplay,
|
|
window->xwindow,
|
|
window->display->atom__NET_WM_FULLSCREEN_MONITORS);
|
|
meta_error_trap_pop (window->display);
|
|
}
|
|
}
|
|
|
|
/* Edge constraints */
|
|
update_gtk_edge_constraints (window);
|
|
}
|
|
|
|
static cairo_region_t *
|
|
region_create_from_x_rectangles (const XRectangle *rects,
|
|
int n_rects)
|
|
{
|
|
int i;
|
|
cairo_rectangle_int_t *cairo_rects = g_newa (cairo_rectangle_int_t, n_rects);
|
|
|
|
for (i = 0; i < n_rects; i ++)
|
|
{
|
|
cairo_rects[i].x = rects[i].x;
|
|
cairo_rects[i].y = rects[i].y;
|
|
cairo_rects[i].width = rects[i].width;
|
|
cairo_rects[i].height = rects[i].height;
|
|
}
|
|
|
|
return cairo_region_create_rectangles (cairo_rects, n_rects);
|
|
}
|
|
|
|
static void
|
|
meta_window_set_input_region (MetaWindow *window,
|
|
cairo_region_t *region)
|
|
{
|
|
if (cairo_region_equal (window->input_region, region))
|
|
return;
|
|
|
|
g_clear_pointer (&window->input_region, cairo_region_destroy);
|
|
|
|
if (region != NULL)
|
|
window->input_region = cairo_region_reference (region);
|
|
|
|
meta_compositor_window_shape_changed (window->display->compositor, window);
|
|
}
|
|
|
|
#if 0
|
|
/* Print out a region; useful for debugging */
|
|
static void
|
|
print_region (cairo_region_t *region)
|
|
{
|
|
int n_rects;
|
|
int i;
|
|
|
|
n_rects = cairo_region_num_rectangles (region);
|
|
g_print ("[");
|
|
for (i = 0; i < n_rects; i++)
|
|
{
|
|
cairo_rectangle_int_t rect;
|
|
cairo_region_get_rectangle (region, i, &rect);
|
|
g_print ("+%d+%dx%dx%d ",
|
|
rect.x, rect.y, rect.width, rect.height);
|
|
}
|
|
g_print ("]\n");
|
|
}
|
|
#endif
|
|
|
|
void
|
|
meta_window_x11_update_input_region (MetaWindow *window)
|
|
{
|
|
cairo_region_t *region = NULL;
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
|
|
/* Decorated windows don't have an input region, because
|
|
we don't shape the frame to match the client windows
|
|
(so the events are blocked by the frame anyway)
|
|
*/
|
|
if (window->decorated)
|
|
{
|
|
if (window->input_region)
|
|
meta_window_set_input_region (window, NULL);
|
|
return;
|
|
}
|
|
|
|
if (META_DISPLAY_HAS_SHAPE (window->display))
|
|
{
|
|
/* Translate the set of XShape rectangles that we
|
|
* get from the X server to a cairo_region. */
|
|
XRectangle *rects = NULL;
|
|
int n_rects = -1, ordering;
|
|
|
|
meta_error_trap_push (window->display);
|
|
rects = XShapeGetRectangles (window->display->xdisplay,
|
|
window->xwindow,
|
|
ShapeInput,
|
|
&n_rects,
|
|
&ordering);
|
|
meta_error_trap_pop (window->display);
|
|
|
|
/* XXX: The X Shape specification is quite unfortunately specified.
|
|
*
|
|
* By default, the window has a shape the same as its bounding region,
|
|
* which we consider "NULL".
|
|
*
|
|
* If the window sets an empty region, then we'll get n_rects as 0
|
|
* and rects as NULL, which we need to transform back into an empty
|
|
* region.
|
|
*
|
|
* It would be great to have a less-broken extension for this, but
|
|
* hey, it's X11!
|
|
*/
|
|
|
|
if (n_rects == -1)
|
|
{
|
|
/* We had an error. */
|
|
region = NULL;
|
|
}
|
|
else if (n_rects == 0)
|
|
{
|
|
/* Client set an empty region. */
|
|
region = cairo_region_create ();
|
|
}
|
|
else if (n_rects == 1 &&
|
|
(rects[0].x == 0 &&
|
|
rects[0].y == 0 &&
|
|
rects[0].width == priv->client_rect.width &&
|
|
rects[0].height == priv->client_rect.height))
|
|
{
|
|
/* This is the bounding region case. Keep the
|
|
* region as NULL. */
|
|
region = NULL;
|
|
}
|
|
else
|
|
{
|
|
/* Window has a custom shape. */
|
|
region = region_create_from_x_rectangles (rects, n_rects);
|
|
}
|
|
|
|
meta_XFree (rects);
|
|
}
|
|
|
|
if (region != NULL)
|
|
{
|
|
cairo_rectangle_int_t client_area;
|
|
|
|
client_area.x = 0;
|
|
client_area.y = 0;
|
|
client_area.width = priv->client_rect.width;
|
|
client_area.height = priv->client_rect.height;
|
|
|
|
/* The shape we get back from the client may have coordinates
|
|
* outside of the frame. The X SHAPE Extension requires that
|
|
* the overall shape the client provides never exceeds the
|
|
* "bounding rectangle" of the window -- the shape that the
|
|
* window would have gotten if it was unshaped. In our case,
|
|
* this is simply the client area.
|
|
*/
|
|
cairo_region_intersect_rectangle (region, &client_area);
|
|
}
|
|
|
|
meta_window_set_input_region (window, region);
|
|
cairo_region_destroy (region);
|
|
}
|
|
|
|
static void
|
|
meta_window_set_shape_region (MetaWindow *window,
|
|
cairo_region_t *region)
|
|
{
|
|
if (cairo_region_equal (window->shape_region, region))
|
|
return;
|
|
|
|
g_clear_pointer (&window->shape_region, cairo_region_destroy);
|
|
|
|
if (region != NULL)
|
|
window->shape_region = cairo_region_reference (region);
|
|
|
|
meta_compositor_window_shape_changed (window->display->compositor, window);
|
|
}
|
|
|
|
void
|
|
meta_window_x11_update_shape_region (MetaWindow *window)
|
|
{
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
cairo_region_t *region = NULL;
|
|
|
|
if (META_DISPLAY_HAS_SHAPE (window->display))
|
|
{
|
|
/* Translate the set of XShape rectangles that we
|
|
* get from the X server to a cairo_region. */
|
|
XRectangle *rects = NULL;
|
|
int n_rects, ordering;
|
|
|
|
int x_bounding, y_bounding, x_clip, y_clip;
|
|
unsigned w_bounding, h_bounding, w_clip, h_clip;
|
|
int bounding_shaped, clip_shaped;
|
|
|
|
meta_error_trap_push (window->display);
|
|
XShapeQueryExtents (window->display->xdisplay, window->xwindow,
|
|
&bounding_shaped, &x_bounding, &y_bounding,
|
|
&w_bounding, &h_bounding,
|
|
&clip_shaped, &x_clip, &y_clip,
|
|
&w_clip, &h_clip);
|
|
|
|
if (bounding_shaped)
|
|
{
|
|
rects = XShapeGetRectangles (window->display->xdisplay,
|
|
window->xwindow,
|
|
ShapeBounding,
|
|
&n_rects,
|
|
&ordering);
|
|
}
|
|
meta_error_trap_pop (window->display);
|
|
|
|
if (rects)
|
|
{
|
|
region = region_create_from_x_rectangles (rects, n_rects);
|
|
XFree (rects);
|
|
}
|
|
}
|
|
|
|
if (region != NULL)
|
|
{
|
|
cairo_rectangle_int_t client_area;
|
|
|
|
client_area.x = 0;
|
|
client_area.y = 0;
|
|
client_area.width = priv->client_rect.width;
|
|
client_area.height = priv->client_rect.height;
|
|
|
|
/* The shape we get back from the client may have coordinates
|
|
* outside of the frame. The X SHAPE Extension requires that
|
|
* the overall shape the client provides never exceeds the
|
|
* "bounding rectangle" of the window -- the shape that the
|
|
* window would have gotten if it was unshaped. In our case,
|
|
* this is simply the client area.
|
|
*/
|
|
cairo_region_intersect_rectangle (region, &client_area);
|
|
|
|
/* Some applications might explicitly set their bounding region
|
|
* to the client area. Detect these cases, and throw out the
|
|
* bounding region in this case. */
|
|
if (cairo_region_contains_rectangle (region, &client_area) == CAIRO_REGION_OVERLAP_IN)
|
|
g_clear_pointer (®ion, cairo_region_destroy);
|
|
}
|
|
|
|
meta_window_set_shape_region (window, region);
|
|
cairo_region_destroy (region);
|
|
}
|
|
|
|
/* Generally meta_window_same_application() is a better idea
|
|
* of "sameness", since it handles the case where multiple apps
|
|
* want to look like the same app or the same app wants to look
|
|
* like multiple apps, but in the case of workarounds for legacy
|
|
* applications (which likely aren't setting the group properly
|
|
* anyways), it may be desirable to check this as well.
|
|
*/
|
|
static gboolean
|
|
meta_window_same_client (MetaWindow *window,
|
|
MetaWindow *other_window)
|
|
{
|
|
int resource_mask = window->display->xdisplay->resource_mask;
|
|
|
|
return ((window->xwindow & ~resource_mask) ==
|
|
(other_window->xwindow & ~resource_mask));
|
|
}
|
|
|
|
static void
|
|
meta_window_move_resize_request (MetaWindow *window,
|
|
guint value_mask,
|
|
int gravity,
|
|
int new_x,
|
|
int new_y,
|
|
int new_width,
|
|
int new_height)
|
|
{
|
|
int x, y, width, height;
|
|
gboolean allow_position_change;
|
|
gboolean in_grab_op;
|
|
MetaMoveResizeFlags flags;
|
|
|
|
/* We ignore configure requests while the user is moving/resizing
|
|
* the window, since these represent the app sucking and fighting
|
|
* the user, most likely due to a bug in the app (e.g. pfaedit
|
|
* seemed to do this)
|
|
*
|
|
* Still have to do the ConfigureNotify and all, but pretend the
|
|
* app asked for the current size/position instead of the new one.
|
|
*/
|
|
in_grab_op = (window->display->grab_window == window &&
|
|
meta_grab_op_is_mouse (window->display->grab_op));
|
|
|
|
/* it's essential to use only the explicitly-set fields,
|
|
* and otherwise use our current up-to-date position.
|
|
*
|
|
* Otherwise you get spurious position changes when the app changes
|
|
* size, for example, if window->rect is not in sync with the
|
|
* server-side position in effect when the configure request was
|
|
* generated.
|
|
*/
|
|
meta_window_get_gravity_position (window,
|
|
gravity,
|
|
&x, &y);
|
|
|
|
allow_position_change = FALSE;
|
|
|
|
if (meta_prefs_get_disable_workarounds ())
|
|
{
|
|
if (window->type == META_WINDOW_DIALOG ||
|
|
window->type == META_WINDOW_MODAL_DIALOG ||
|
|
window->type == META_WINDOW_SPLASHSCREEN)
|
|
; /* No position change for these */
|
|
else if ((window->size_hints.flags & PPosition) ||
|
|
/* USPosition is just stale if window is placed;
|
|
* no --geometry involved here.
|
|
*/
|
|
((window->size_hints.flags & USPosition) &&
|
|
!window->placed))
|
|
allow_position_change = TRUE;
|
|
}
|
|
else
|
|
{
|
|
allow_position_change = TRUE;
|
|
}
|
|
|
|
if (in_grab_op)
|
|
allow_position_change = FALSE;
|
|
|
|
if (allow_position_change)
|
|
{
|
|
if (value_mask & CWX)
|
|
x = new_x;
|
|
if (value_mask & CWY)
|
|
y = new_y;
|
|
if (value_mask & (CWX | CWY))
|
|
{
|
|
/* Once manually positioned, windows shouldn't be placed
|
|
* by the window manager.
|
|
*/
|
|
window->placed = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
meta_topic (META_DEBUG_GEOMETRY,
|
|
"Not allowing position change for window %s PPosition 0x%lx USPosition 0x%lx type %u\n",
|
|
window->desc, window->size_hints.flags & PPosition,
|
|
window->size_hints.flags & USPosition,
|
|
window->type);
|
|
}
|
|
|
|
width = window->rect.width;
|
|
height = window->rect.height;
|
|
if (!in_grab_op)
|
|
{
|
|
if (value_mask & CWWidth)
|
|
width = new_width;
|
|
|
|
if (value_mask & CWHeight)
|
|
height = new_height;
|
|
}
|
|
|
|
/* ICCCM 4.1.5 */
|
|
|
|
/* We're ignoring the value_mask here, since sizes
|
|
* not in the mask will be the current window geometry.
|
|
*/
|
|
window->size_hints.x = x;
|
|
window->size_hints.y = y;
|
|
window->size_hints.width = width;
|
|
window->size_hints.height = height;
|
|
|
|
/* NOTE: We consider ConfigureRequests to be "user" actions in one
|
|
* way, but not in another. Explanation of the two cases are in the
|
|
* next two big comments.
|
|
*/
|
|
|
|
/* The constraints code allows user actions to move windows
|
|
* offscreen, etc., and configure request actions would often send
|
|
* windows offscreen when users don't want it if not constrained
|
|
* (e.g. hitting a dropdown triangle in a fileselector to show more
|
|
* options, which makes the window bigger). Thus we do not set
|
|
* META_MOVE_RESIZE_USER_ACTION in flags to the
|
|
* meta_window_move_resize_internal() call.
|
|
*/
|
|
flags = META_MOVE_RESIZE_CONFIGURE_REQUEST;
|
|
if (value_mask & (CWX | CWY))
|
|
flags |= META_MOVE_RESIZE_MOVE_ACTION;
|
|
if (value_mask & (CWWidth | CWHeight))
|
|
flags |= META_MOVE_RESIZE_RESIZE_ACTION;
|
|
|
|
if (flags & (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION))
|
|
{
|
|
MetaRectangle rect, monitor_rect;
|
|
|
|
rect.x = x;
|
|
rect.y = y;
|
|
rect.width = width;
|
|
rect.height = height;
|
|
|
|
if (window->monitor)
|
|
{
|
|
meta_screen_get_monitor_geometry (window->screen, window->monitor->number, &monitor_rect);
|
|
|
|
/* Workaround braindead legacy apps that don't know how to
|
|
* fullscreen themselves properly - don't get fooled by
|
|
* windows which hide their titlebar when maximized or which are
|
|
* client decorated; that's not the same as fullscreen, even
|
|
* if there are no struts making the workarea smaller than
|
|
* the monitor.
|
|
*/
|
|
if (meta_prefs_get_force_fullscreen() &&
|
|
!window->hide_titlebar_when_maximized &&
|
|
(window->decorated || !meta_window_is_client_decorated (window)) &&
|
|
meta_rectangle_equal (&rect, &monitor_rect) &&
|
|
window->has_fullscreen_func &&
|
|
!window->fullscreen)
|
|
{
|
|
/*
|
|
meta_topic (META_DEBUG_GEOMETRY,
|
|
*/
|
|
meta_warning (
|
|
"Treating resize request of legacy application %s as a "
|
|
"fullscreen request\n",
|
|
window->desc);
|
|
meta_window_make_fullscreen_internal (window);
|
|
}
|
|
}
|
|
|
|
adjust_for_gravity (window, TRUE, gravity, &rect);
|
|
meta_window_client_rect_to_frame_rect (window, &rect, &rect);
|
|
meta_window_move_resize_internal (window, flags, gravity, rect);
|
|
}
|
|
}
|
|
|
|
static void
|
|
restack_window (MetaWindow *window,
|
|
MetaWindow *sibling,
|
|
int direction)
|
|
{
|
|
switch (direction)
|
|
{
|
|
case Above:
|
|
if (sibling)
|
|
meta_window_stack_just_above (window, sibling);
|
|
else
|
|
meta_window_raise (window);
|
|
break;
|
|
case Below:
|
|
if (sibling)
|
|
meta_window_stack_just_below (window, sibling);
|
|
else
|
|
meta_window_lower (window);
|
|
break;
|
|
case TopIf:
|
|
case BottomIf:
|
|
case Opposite:
|
|
break;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
meta_window_x11_configure_request (MetaWindow *window,
|
|
XEvent *event)
|
|
{
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
|
|
/* Note that x, y is the corner of the window border,
|
|
* and width, height is the size of the window inside
|
|
* its border, but that we always deny border requests
|
|
* and give windows a border of 0. But we save the
|
|
* requested border here.
|
|
*/
|
|
if (event->xconfigurerequest.value_mask & CWBorderWidth)
|
|
priv->border_width = event->xconfigurerequest.border_width;
|
|
|
|
meta_window_move_resize_request(window,
|
|
event->xconfigurerequest.value_mask,
|
|
window->size_hints.win_gravity,
|
|
event->xconfigurerequest.x,
|
|
event->xconfigurerequest.y,
|
|
event->xconfigurerequest.width,
|
|
event->xconfigurerequest.height);
|
|
|
|
/* Handle stacking. We only handle raises/lowers, mostly because
|
|
* stack.c really can't deal with anything else. I guess we'll fix
|
|
* that if a client turns up that really requires it. Only a very
|
|
* few clients even require the raise/lower (and in fact all client
|
|
* attempts to deal with stacking order are essentially broken,
|
|
* since they have no idea what other clients are involved or how
|
|
* the stack looks).
|
|
*
|
|
* I'm pretty sure no interesting client uses TopIf, BottomIf, or
|
|
* Opposite anyway.
|
|
*/
|
|
if (event->xconfigurerequest.value_mask & CWStackMode)
|
|
{
|
|
MetaWindow *active_window;
|
|
active_window = window->display->focus_window;
|
|
if (meta_prefs_get_disable_workarounds ())
|
|
{
|
|
meta_topic (META_DEBUG_STACK,
|
|
"%s sent an xconfigure stacking request; this is "
|
|
"broken behavior and the request is being ignored.\n",
|
|
window->desc);
|
|
}
|
|
else if (active_window &&
|
|
!meta_window_same_application (window, active_window) &&
|
|
!meta_window_same_client (window, active_window) &&
|
|
XSERVER_TIME_IS_BEFORE (window->net_wm_user_time,
|
|
active_window->net_wm_user_time))
|
|
{
|
|
meta_topic (META_DEBUG_STACK,
|
|
"Ignoring xconfigure stacking request from %s (with "
|
|
"user_time %u); currently active application is %s (with "
|
|
"user_time %u).\n",
|
|
window->desc,
|
|
window->net_wm_user_time,
|
|
active_window->desc,
|
|
active_window->net_wm_user_time);
|
|
if (event->xconfigurerequest.detail == Above)
|
|
meta_window_set_demands_attention(window);
|
|
}
|
|
else
|
|
{
|
|
MetaWindow *sibling = NULL;
|
|
/* Handle Above/Below with a sibling set */
|
|
if (event->xconfigurerequest.above != None)
|
|
{
|
|
MetaDisplay *display;
|
|
|
|
display = meta_window_get_display (window);
|
|
sibling = meta_display_lookup_x_window (display,
|
|
event->xconfigurerequest.above);
|
|
if (sibling == NULL)
|
|
return TRUE;
|
|
|
|
meta_topic (META_DEBUG_STACK,
|
|
"xconfigure stacking request from window %s sibling %s stackmode %d\n",
|
|
window->desc, sibling->desc, event->xconfigurerequest.detail);
|
|
}
|
|
restack_window (window, sibling, event->xconfigurerequest.detail);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
process_property_notify (MetaWindow *window,
|
|
XPropertyEvent *event)
|
|
{
|
|
Window xid = window->xwindow;
|
|
|
|
if (meta_is_verbose ()) /* avoid looking up the name if we don't have to */
|
|
{
|
|
char *property_name = XGetAtomName (window->display->xdisplay,
|
|
event->atom);
|
|
|
|
meta_verbose ("Property notify on %s for %s\n",
|
|
window->desc, property_name);
|
|
XFree (property_name);
|
|
}
|
|
|
|
if (event->atom == window->display->atom__NET_WM_USER_TIME &&
|
|
window->user_time_window)
|
|
{
|
|
xid = window->user_time_window;
|
|
}
|
|
|
|
meta_window_reload_property_from_xwindow (window, xid, event->atom, FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_window_x11_property_notify (MetaWindow *window,
|
|
XEvent *event)
|
|
{
|
|
return process_property_notify (window, &event->xproperty);
|
|
}
|
|
|
|
#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
|
|
#define _NET_WM_MOVERESIZE_SIZE_TOP 1
|
|
#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
|
|
#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
|
|
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
|
|
#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
|
|
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
|
|
#define _NET_WM_MOVERESIZE_SIZE_LEFT 7
|
|
#define _NET_WM_MOVERESIZE_MOVE 8
|
|
#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9
|
|
#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10
|
|
#define _NET_WM_MOVERESIZE_CANCEL 11
|
|
|
|
static int
|
|
query_pressed_buttons (MetaWindow *window)
|
|
{
|
|
MetaCursorTracker *tracker = meta_cursor_tracker_get_for_screen (window->screen);
|
|
ClutterModifierType mods;
|
|
int button = 0;
|
|
|
|
meta_cursor_tracker_get_pointer (tracker, NULL, NULL, &mods);
|
|
|
|
if (mods & CLUTTER_BUTTON1_MASK)
|
|
button |= 1 << 1;
|
|
if (mods & CLUTTER_BUTTON2_MASK)
|
|
button |= 1 << 2;
|
|
if (mods & CLUTTER_BUTTON3_MASK)
|
|
button |= 1 << 3;
|
|
|
|
return button;
|
|
}
|
|
|
|
static void
|
|
handle_net_restack_window (MetaDisplay *display,
|
|
XEvent *event)
|
|
{
|
|
MetaWindow *window, *sibling = NULL;
|
|
|
|
/* Ignore if this does not come from a pager, see the WM spec
|
|
*/
|
|
if (event->xclient.data.l[0] != 2)
|
|
return;
|
|
|
|
window = meta_display_lookup_x_window (display,
|
|
event->xclient.window);
|
|
|
|
if (window)
|
|
{
|
|
if (event->xclient.data.l[1])
|
|
sibling = meta_display_lookup_x_window (display,
|
|
event->xclient.data.l[1]);
|
|
|
|
restack_window (window, sibling, event->xclient.data.l[2]);
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
meta_window_x11_client_message (MetaWindow *window,
|
|
XEvent *event)
|
|
{
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
MetaDisplay *display;
|
|
|
|
display = window->display;
|
|
|
|
if (window->override_redirect)
|
|
{
|
|
/* Don't warn here: we could warn on any of the messages below,
|
|
* but we might also receive other client messages that are
|
|
* part of protocols we don't know anything about. So, silently
|
|
* ignoring is simplest.
|
|
*/
|
|
return FALSE;
|
|
}
|
|
|
|
if (event->xclient.message_type ==
|
|
display->atom__NET_CLOSE_WINDOW)
|
|
{
|
|
guint32 timestamp;
|
|
|
|
if (event->xclient.data.l[0] != 0)
|
|
timestamp = event->xclient.data.l[0];
|
|
else
|
|
{
|
|
meta_warning ("Receiving a NET_CLOSE_WINDOW message for %s without "
|
|
"a timestamp! This means some buggy (outdated) "
|
|
"application is on the loose!\n",
|
|
window->desc);
|
|
timestamp = meta_display_get_current_time (window->display);
|
|
}
|
|
|
|
meta_window_delete (window, timestamp);
|
|
|
|
return TRUE;
|
|
}
|
|
else if (event->xclient.message_type ==
|
|
display->atom__NET_WM_DESKTOP)
|
|
{
|
|
int space;
|
|
MetaWorkspace *workspace;
|
|
|
|
space = event->xclient.data.l[0];
|
|
|
|
meta_verbose ("Request to move %s to workspace %d\n",
|
|
window->desc, space);
|
|
|
|
workspace =
|
|
meta_screen_get_workspace_by_index (window->screen,
|
|
space);
|
|
|
|
if (workspace)
|
|
meta_window_change_workspace (window, workspace);
|
|
else if (space == (int) 0xFFFFFFFF)
|
|
meta_window_stick (window);
|
|
else
|
|
meta_verbose ("No such workspace %d for screen\n", space);
|
|
|
|
meta_verbose ("Window %s now on_all_workspaces = %d\n",
|
|
window->desc, window->on_all_workspaces);
|
|
|
|
return TRUE;
|
|
}
|
|
else if (event->xclient.message_type ==
|
|
display->atom__NET_WM_STATE)
|
|
{
|
|
gulong action;
|
|
Atom first;
|
|
Atom second;
|
|
|
|
action = event->xclient.data.l[0];
|
|
first = event->xclient.data.l[1];
|
|
second = event->xclient.data.l[2];
|
|
|
|
if (meta_is_verbose ())
|
|
{
|
|
char *str1;
|
|
char *str2;
|
|
|
|
meta_error_trap_push (display);
|
|
str1 = XGetAtomName (display->xdisplay, first);
|
|
if (meta_error_trap_pop_with_return (display) != Success)
|
|
str1 = NULL;
|
|
|
|
meta_error_trap_push (display);
|
|
str2 = XGetAtomName (display->xdisplay, second);
|
|
if (meta_error_trap_pop_with_return (display) != Success)
|
|
str2 = NULL;
|
|
|
|
meta_verbose ("Request to change _NET_WM_STATE action %lu atom1: %s atom2: %s\n",
|
|
action,
|
|
str1 ? str1 : "(unknown)",
|
|
str2 ? str2 : "(unknown)");
|
|
|
|
meta_XFree (str1);
|
|
meta_XFree (str2);
|
|
}
|
|
|
|
if (first == display->atom__NET_WM_STATE_SHADED ||
|
|
second == display->atom__NET_WM_STATE_SHADED)
|
|
{
|
|
gboolean shade;
|
|
guint32 timestamp;
|
|
|
|
/* Stupid protocol has no timestamp; of course, shading
|
|
* sucks anyway so who really cares that we're forced to do
|
|
* a roundtrip here?
|
|
*/
|
|
timestamp = meta_display_get_current_time_roundtrip (window->display);
|
|
|
|
shade = (action == _NET_WM_STATE_ADD ||
|
|
(action == _NET_WM_STATE_TOGGLE && !window->shaded));
|
|
if (shade && window->has_shade_func)
|
|
meta_window_shade (window, timestamp);
|
|
else
|
|
meta_window_unshade (window, timestamp);
|
|
}
|
|
|
|
if (first == display->atom__NET_WM_STATE_FULLSCREEN ||
|
|
second == display->atom__NET_WM_STATE_FULLSCREEN)
|
|
{
|
|
gboolean make_fullscreen;
|
|
|
|
make_fullscreen = (action == _NET_WM_STATE_ADD ||
|
|
(action == _NET_WM_STATE_TOGGLE && !window->fullscreen));
|
|
if (make_fullscreen && window->has_fullscreen_func)
|
|
meta_window_make_fullscreen (window);
|
|
else
|
|
meta_window_unmake_fullscreen (window);
|
|
}
|
|
|
|
if (first == display->atom__NET_WM_STATE_MAXIMIZED_HORZ ||
|
|
second == display->atom__NET_WM_STATE_MAXIMIZED_HORZ ||
|
|
first == display->atom__NET_WM_STATE_MAXIMIZED_VERT ||
|
|
second == display->atom__NET_WM_STATE_MAXIMIZED_VERT)
|
|
{
|
|
gboolean max;
|
|
MetaMaximizeFlags directions = 0;
|
|
|
|
max = (action == _NET_WM_STATE_ADD ||
|
|
(action == _NET_WM_STATE_TOGGLE &&
|
|
!window->maximized_horizontally));
|
|
|
|
if (first == display->atom__NET_WM_STATE_MAXIMIZED_HORZ ||
|
|
second == display->atom__NET_WM_STATE_MAXIMIZED_HORZ)
|
|
directions |= META_MAXIMIZE_HORIZONTAL;
|
|
|
|
if (first == display->atom__NET_WM_STATE_MAXIMIZED_VERT ||
|
|
second == display->atom__NET_WM_STATE_MAXIMIZED_VERT)
|
|
directions |= META_MAXIMIZE_VERTICAL;
|
|
|
|
if (max && window->has_maximize_func)
|
|
{
|
|
if (meta_prefs_get_raise_on_click ())
|
|
meta_window_raise (window);
|
|
meta_window_maximize (window, directions);
|
|
}
|
|
else
|
|
{
|
|
if (meta_prefs_get_raise_on_click ())
|
|
meta_window_raise (window);
|
|
meta_window_unmaximize (window, directions);
|
|
}
|
|
}
|
|
|
|
if (first == display->atom__NET_WM_STATE_MODAL ||
|
|
second == display->atom__NET_WM_STATE_MODAL)
|
|
{
|
|
priv->wm_state_modal =
|
|
(action == _NET_WM_STATE_ADD) ||
|
|
(action == _NET_WM_STATE_TOGGLE && !priv->wm_state_modal);
|
|
|
|
meta_window_x11_recalc_window_type (window);
|
|
meta_window_queue(window, META_QUEUE_MOVE_RESIZE);
|
|
}
|
|
|
|
if (first == display->atom__NET_WM_STATE_SKIP_PAGER ||
|
|
second == display->atom__NET_WM_STATE_SKIP_PAGER)
|
|
{
|
|
priv->wm_state_skip_pager =
|
|
(action == _NET_WM_STATE_ADD) ||
|
|
(action == _NET_WM_STATE_TOGGLE && !window->skip_pager);
|
|
|
|
meta_window_recalc_features (window);
|
|
meta_window_x11_set_net_wm_state (window);
|
|
}
|
|
|
|
if (first == display->atom__NET_WM_STATE_SKIP_TASKBAR ||
|
|
second == display->atom__NET_WM_STATE_SKIP_TASKBAR)
|
|
{
|
|
priv->wm_state_skip_taskbar =
|
|
(action == _NET_WM_STATE_ADD) ||
|
|
(action == _NET_WM_STATE_TOGGLE && !window->skip_taskbar);
|
|
|
|
meta_window_recalc_features (window);
|
|
meta_window_x11_set_net_wm_state (window);
|
|
}
|
|
|
|
if (first == display->atom__NET_WM_STATE_ABOVE ||
|
|
second == display->atom__NET_WM_STATE_ABOVE)
|
|
{
|
|
if ((action == _NET_WM_STATE_ADD) ||
|
|
(action == _NET_WM_STATE_TOGGLE && !window->wm_state_demands_attention))
|
|
meta_window_make_above (window);
|
|
else
|
|
meta_window_unmake_above (window);
|
|
}
|
|
|
|
if (first == display->atom__NET_WM_STATE_BELOW ||
|
|
second == display->atom__NET_WM_STATE_BELOW)
|
|
{
|
|
window->wm_state_below =
|
|
(action == _NET_WM_STATE_ADD) ||
|
|
(action == _NET_WM_STATE_TOGGLE && !window->wm_state_below);
|
|
|
|
meta_window_update_layer (window);
|
|
meta_window_x11_set_net_wm_state (window);
|
|
}
|
|
|
|
if (first == display->atom__NET_WM_STATE_DEMANDS_ATTENTION ||
|
|
second == display->atom__NET_WM_STATE_DEMANDS_ATTENTION)
|
|
{
|
|
if ((action == _NET_WM_STATE_ADD) ||
|
|
(action == _NET_WM_STATE_TOGGLE && !window->wm_state_demands_attention))
|
|
meta_window_set_demands_attention (window);
|
|
else
|
|
meta_window_unset_demands_attention (window);
|
|
}
|
|
|
|
if (first == display->atom__NET_WM_STATE_STICKY ||
|
|
second == display->atom__NET_WM_STATE_STICKY)
|
|
{
|
|
if ((action == _NET_WM_STATE_ADD) ||
|
|
(action == _NET_WM_STATE_TOGGLE && !window->on_all_workspaces_requested))
|
|
meta_window_stick (window);
|
|
else
|
|
meta_window_unstick (window);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
else if (event->xclient.message_type ==
|
|
display->atom_WM_CHANGE_STATE)
|
|
{
|
|
meta_verbose ("WM_CHANGE_STATE client message, state: %ld\n",
|
|
event->xclient.data.l[0]);
|
|
if (event->xclient.data.l[0] == IconicState)
|
|
meta_window_minimize (window);
|
|
|
|
return TRUE;
|
|
}
|
|
else if (event->xclient.message_type ==
|
|
display->atom__NET_WM_MOVERESIZE)
|
|
{
|
|
int x_root;
|
|
int y_root;
|
|
int action;
|
|
MetaGrabOp op;
|
|
int button;
|
|
guint32 timestamp;
|
|
|
|
/* _NET_WM_MOVERESIZE messages are almost certainly going to come from
|
|
* clients when users click on the fake "frame" that the client has,
|
|
* thus we should also treat such messages as though it were a
|
|
* "frame action".
|
|
*/
|
|
gboolean const frame_action = TRUE;
|
|
|
|
x_root = event->xclient.data.l[0];
|
|
y_root = event->xclient.data.l[1];
|
|
action = event->xclient.data.l[2];
|
|
button = event->xclient.data.l[3];
|
|
|
|
/* FIXME: What a braindead protocol; no timestamp?!? */
|
|
timestamp = meta_display_get_current_time_roundtrip (display);
|
|
meta_topic (META_DEBUG_WINDOW_OPS,
|
|
"Received _NET_WM_MOVERESIZE message on %s, %d,%d action = %d, button %d\n",
|
|
window->desc,
|
|
x_root, y_root, action, button);
|
|
|
|
op = META_GRAB_OP_NONE;
|
|
switch (action)
|
|
{
|
|
case _NET_WM_MOVERESIZE_SIZE_TOPLEFT:
|
|
op = META_GRAB_OP_RESIZING_NW;
|
|
break;
|
|
case _NET_WM_MOVERESIZE_SIZE_TOP:
|
|
op = META_GRAB_OP_RESIZING_N;
|
|
break;
|
|
case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT:
|
|
op = META_GRAB_OP_RESIZING_NE;
|
|
break;
|
|
case _NET_WM_MOVERESIZE_SIZE_RIGHT:
|
|
op = META_GRAB_OP_RESIZING_E;
|
|
break;
|
|
case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT:
|
|
op = META_GRAB_OP_RESIZING_SE;
|
|
break;
|
|
case _NET_WM_MOVERESIZE_SIZE_BOTTOM:
|
|
op = META_GRAB_OP_RESIZING_S;
|
|
break;
|
|
case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT:
|
|
op = META_GRAB_OP_RESIZING_SW;
|
|
break;
|
|
case _NET_WM_MOVERESIZE_SIZE_LEFT:
|
|
op = META_GRAB_OP_RESIZING_W;
|
|
break;
|
|
case _NET_WM_MOVERESIZE_MOVE:
|
|
op = META_GRAB_OP_MOVING;
|
|
break;
|
|
case _NET_WM_MOVERESIZE_SIZE_KEYBOARD:
|
|
op = META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN;
|
|
break;
|
|
case _NET_WM_MOVERESIZE_MOVE_KEYBOARD:
|
|
op = META_GRAB_OP_KEYBOARD_MOVING;
|
|
break;
|
|
case _NET_WM_MOVERESIZE_CANCEL:
|
|
/* handled below */
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (action == _NET_WM_MOVERESIZE_CANCEL)
|
|
{
|
|
meta_display_end_grab_op (window->display, timestamp);
|
|
}
|
|
else if (op != META_GRAB_OP_NONE &&
|
|
((window->has_move_func && op == META_GRAB_OP_KEYBOARD_MOVING) ||
|
|
(window->has_resize_func && op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN)))
|
|
{
|
|
meta_window_begin_grab_op (window, op, frame_action, timestamp);
|
|
}
|
|
else if (op != META_GRAB_OP_NONE &&
|
|
((window->has_move_func && op == META_GRAB_OP_MOVING) ||
|
|
(window->has_resize_func &&
|
|
(op != META_GRAB_OP_MOVING &&
|
|
op != META_GRAB_OP_KEYBOARD_MOVING))))
|
|
{
|
|
int button_mask;
|
|
|
|
meta_topic (META_DEBUG_WINDOW_OPS,
|
|
"Beginning move/resize with button = %d\n", button);
|
|
meta_display_begin_grab_op (window->display,
|
|
window->screen,
|
|
window,
|
|
op,
|
|
FALSE,
|
|
frame_action,
|
|
button, 0,
|
|
timestamp,
|
|
x_root,
|
|
y_root);
|
|
|
|
button_mask = query_pressed_buttons (window);
|
|
|
|
if (button == 0)
|
|
{
|
|
/*
|
|
* the button SHOULD already be included in the message
|
|
*/
|
|
if ((button_mask & (1 << 1)) != 0)
|
|
button = 1;
|
|
else if ((button_mask & (1 << 2)) != 0)
|
|
button = 2;
|
|
else if ((button_mask & (1 << 3)) != 0)
|
|
button = 3;
|
|
|
|
if (button != 0)
|
|
window->display->grab_button = button;
|
|
else
|
|
meta_display_end_grab_op (window->display,
|
|
timestamp);
|
|
}
|
|
else
|
|
{
|
|
/* There is a potential race here. If the user presses and
|
|
* releases their mouse button very fast, it's possible for
|
|
* both the ButtonPress and ButtonRelease to be sent to the
|
|
* client before it can get a chance to send _NET_WM_MOVERESIZE
|
|
* to us. When that happens, we'll become stuck in a grab
|
|
* state, as we haven't received a ButtonRelease to cancel the
|
|
* grab.
|
|
*
|
|
* We can solve this by querying after we take the explicit
|
|
* pointer grab -- if the button isn't pressed, we cancel the
|
|
* drag immediately.
|
|
*/
|
|
|
|
if ((button_mask & (1 << button)) == 0)
|
|
meta_display_end_grab_op (window->display, timestamp);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
else if (event->xclient.message_type ==
|
|
display->atom__NET_MOVERESIZE_WINDOW)
|
|
{
|
|
int gravity;
|
|
guint value_mask;
|
|
|
|
gravity = (event->xclient.data.l[0] & 0xff);
|
|
value_mask = (event->xclient.data.l[0] & 0xf00) >> 8;
|
|
/* source = (event->xclient.data.l[0] & 0xf000) >> 12; */
|
|
|
|
if (gravity == 0)
|
|
gravity = window->size_hints.win_gravity;
|
|
|
|
meta_window_move_resize_request(window,
|
|
value_mask,
|
|
gravity,
|
|
event->xclient.data.l[1], /* x */
|
|
event->xclient.data.l[2], /* y */
|
|
event->xclient.data.l[3], /* width */
|
|
event->xclient.data.l[4]); /* height */
|
|
}
|
|
else if (event->xclient.message_type ==
|
|
display->atom__NET_ACTIVE_WINDOW)
|
|
{
|
|
MetaClientType source_indication;
|
|
guint32 timestamp;
|
|
|
|
meta_verbose ("_NET_ACTIVE_WINDOW request for window '%s', activating\n",
|
|
window->desc);
|
|
|
|
source_indication = event->xclient.data.l[0];
|
|
timestamp = event->xclient.data.l[1];
|
|
|
|
if (source_indication > META_CLIENT_TYPE_MAX_RECOGNIZED)
|
|
source_indication = META_CLIENT_TYPE_UNKNOWN;
|
|
|
|
if (timestamp == 0)
|
|
{
|
|
/* Client using older EWMH _NET_ACTIVE_WINDOW without a timestamp */
|
|
meta_warning ("Buggy client sent a _NET_ACTIVE_WINDOW message with a "
|
|
"timestamp of 0 for %s\n",
|
|
window->desc);
|
|
timestamp = meta_display_get_current_time (display);
|
|
}
|
|
|
|
meta_window_activate_full (window, timestamp, source_indication, NULL);
|
|
return TRUE;
|
|
}
|
|
else if (event->xclient.message_type ==
|
|
display->atom__NET_WM_FULLSCREEN_MONITORS)
|
|
{
|
|
MetaLogicalMonitor *top, *bottom, *left, *right;
|
|
|
|
meta_verbose ("_NET_WM_FULLSCREEN_MONITORS request for window '%s'\n",
|
|
window->desc);
|
|
|
|
top =
|
|
meta_screen_xinerama_index_to_logical_monitor (window->screen,
|
|
event->xclient.data.l[0]);
|
|
bottom =
|
|
meta_screen_xinerama_index_to_logical_monitor (window->screen,
|
|
event->xclient.data.l[1]);
|
|
left =
|
|
meta_screen_xinerama_index_to_logical_monitor (window->screen,
|
|
event->xclient.data.l[2]);
|
|
right =
|
|
meta_screen_xinerama_index_to_logical_monitor (window->screen,
|
|
event->xclient.data.l[3]);
|
|
/* source_indication = event->xclient.data.l[4]; */
|
|
|
|
meta_window_update_fullscreen_monitors (window, top, bottom, left, right);
|
|
}
|
|
else if (event->xclient.message_type ==
|
|
display->atom__GTK_SHOW_WINDOW_MENU)
|
|
{
|
|
gulong x, y;
|
|
|
|
/* l[0] is device_id, which we don't use */
|
|
x = event->xclient.data.l[1];
|
|
y = event->xclient.data.l[2];
|
|
|
|
meta_window_show_menu (window, META_WINDOW_MENU_WM, x, y);
|
|
}
|
|
else if (event->xclient.message_type ==
|
|
display->atom__NET_RESTACK_WINDOW)
|
|
{
|
|
handle_net_restack_window (display, event);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
set_wm_state_on_xwindow (MetaDisplay *display,
|
|
Window xwindow,
|
|
int state)
|
|
{
|
|
unsigned long data[2];
|
|
|
|
/* Mutter doesn't use icon windows, so data[1] should be None
|
|
* according to the ICCCM 2.0 Section 4.1.3.1.
|
|
*/
|
|
data[0] = state;
|
|
data[1] = None;
|
|
|
|
meta_error_trap_push (display);
|
|
XChangeProperty (display->xdisplay, xwindow,
|
|
display->atom_WM_STATE,
|
|
display->atom_WM_STATE,
|
|
32, PropModeReplace, (guchar*) data, 2);
|
|
meta_error_trap_pop (display);
|
|
}
|
|
|
|
void
|
|
meta_window_x11_set_wm_state (MetaWindow *window)
|
|
{
|
|
int state;
|
|
|
|
if (window->withdrawn)
|
|
state = WithdrawnState;
|
|
else if (window->iconic)
|
|
state = IconicState;
|
|
else
|
|
state = NormalState;
|
|
|
|
set_wm_state_on_xwindow (window->display, window->xwindow, state);
|
|
}
|
|
|
|
/* The MUTTER_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_xwindow (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 ("MUTTER_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
|
|
* Mutter 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)
|
|
{
|
|
uint32_t 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;
|
|
}
|
|
|
|
static gboolean
|
|
is_our_xwindow (MetaDisplay *display,
|
|
MetaScreen *screen,
|
|
Window xwindow,
|
|
XWindowAttributes *attrs)
|
|
{
|
|
if (xwindow == screen->no_focus_window)
|
|
return TRUE;
|
|
|
|
if (xwindow == screen->wm_sn_selection_window)
|
|
return TRUE;
|
|
|
|
if (xwindow == screen->wm_cm_selection_window)
|
|
return TRUE;
|
|
|
|
if (xwindow == screen->guard_window)
|
|
return TRUE;
|
|
|
|
if (xwindow == screen->composite_overlay_window)
|
|
return TRUE;
|
|
|
|
{
|
|
MetaBackend *backend = meta_get_backend ();
|
|
|
|
if (META_IS_BACKEND_X11 (backend))
|
|
{
|
|
if (xwindow == meta_backend_x11_get_xwindow (META_BACKEND_X11 (backend)))
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/* Any windows created via meta_create_offscreen_window */
|
|
if (attrs->override_redirect && attrs->x == -100 && attrs->y == -100 && attrs->width == 1 && attrs->height == 1)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#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
|
|
|
|
MetaWindow *
|
|
meta_window_x11_new (MetaDisplay *display,
|
|
Window xwindow,
|
|
gboolean must_be_viewable,
|
|
MetaCompEffect effect)
|
|
{
|
|
MetaScreen *screen = display->screen;
|
|
XWindowAttributes attrs;
|
|
gulong existing_wm_state;
|
|
MetaWindow *window = NULL;
|
|
gulong event_mask;
|
|
|
|
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;
|
|
}
|
|
|
|
meta_error_trap_push (display); /* Push a trap over all of window
|
|
* creation, to reduce XSync() calls
|
|
*/
|
|
/*
|
|
* This function executes without any server grabs held. This means that
|
|
* the window could have already gone away, or could go away at any point,
|
|
* so we must be careful with X error handling.
|
|
*/
|
|
|
|
if (!XGetWindowAttributes (display->xdisplay, xwindow, &attrs))
|
|
{
|
|
meta_verbose ("Failed to get attributes for window 0x%lx\n",
|
|
xwindow);
|
|
goto error;
|
|
}
|
|
|
|
if (attrs.root != screen->xroot)
|
|
{
|
|
meta_verbose ("Not on our screen\n");
|
|
goto error;
|
|
}
|
|
|
|
if (attrs.class == InputOnly)
|
|
{
|
|
meta_verbose ("Not managing InputOnly windows\n");
|
|
goto error;
|
|
}
|
|
|
|
if (is_our_xwindow (display, screen, xwindow, &attrs))
|
|
{
|
|
meta_verbose ("Not managing our own windows\n");
|
|
goto error;
|
|
}
|
|
|
|
if (maybe_filter_xwindow (display, xwindow, must_be_viewable, &attrs))
|
|
{
|
|
meta_verbose ("Not managing filtered window\n");
|
|
goto error;
|
|
}
|
|
|
|
existing_wm_state = WithdrawnState;
|
|
if (must_be_viewable && attrs.map_state != IsViewable)
|
|
{
|
|
/* Only manage if WM_STATE is IconicState or NormalState */
|
|
uint32_t 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);
|
|
goto error;
|
|
}
|
|
|
|
existing_wm_state = state;
|
|
meta_verbose ("WM_STATE of %lx = %s\n", xwindow,
|
|
wm_state_to_string (existing_wm_state));
|
|
}
|
|
|
|
/*
|
|
* XAddToSaveSet can only be called on windows created by a different
|
|
* client. with Mutter we want to be able to create manageable windows
|
|
* from within the process (such as a dummy desktop window). As we do not
|
|
* want this call failing to prevent the window from being managed, we
|
|
* call this before creating the return-checked error trap.
|
|
*/
|
|
XAddToSaveSet (display->xdisplay, xwindow);
|
|
|
|
meta_error_trap_push (display);
|
|
|
|
event_mask = PropertyChangeMask;
|
|
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);
|
|
|
|
{
|
|
unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
|
|
XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
|
|
|
|
XISetMask (mask.mask, XI_Enter);
|
|
XISetMask (mask.mask, XI_Leave);
|
|
XISetMask (mask.mask, XI_FocusIn);
|
|
XISetMask (mask.mask, XI_FocusOut);
|
|
|
|
XISelectEvents (display->xdisplay, xwindow, &mask, 1);
|
|
}
|
|
|
|
if (META_DISPLAY_HAS_SHAPE (display))
|
|
XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask);
|
|
|
|
/* 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);
|
|
goto error;
|
|
}
|
|
|
|
window = _meta_window_shared_new (display,
|
|
screen,
|
|
META_WINDOW_CLIENT_TYPE_X11,
|
|
NULL,
|
|
xwindow,
|
|
existing_wm_state,
|
|
effect,
|
|
&attrs);
|
|
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
|
|
priv->border_width = attrs.border_width;
|
|
|
|
meta_window_grab_keys (window);
|
|
if (window->type != META_WINDOW_DOCK && !window->override_redirect)
|
|
{
|
|
meta_display_grab_window_buttons (window->display, window->xwindow);
|
|
meta_display_grab_focus_window_button (window->display, window);
|
|
}
|
|
|
|
meta_error_trap_pop (display); /* pop the XSync()-reducing trap */
|
|
return window;
|
|
|
|
error:
|
|
meta_error_trap_pop (display);
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
meta_window_x11_recalc_window_type (MetaWindow *window)
|
|
{
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
MetaWindowType type;
|
|
|
|
if (priv->type_atom != None)
|
|
{
|
|
if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_DESKTOP)
|
|
type = META_WINDOW_DESKTOP;
|
|
else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_DOCK)
|
|
type = META_WINDOW_DOCK;
|
|
else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_TOOLBAR)
|
|
type = META_WINDOW_TOOLBAR;
|
|
else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_MENU)
|
|
type = META_WINDOW_MENU;
|
|
else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_UTILITY)
|
|
type = META_WINDOW_UTILITY;
|
|
else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_SPLASH)
|
|
type = META_WINDOW_SPLASHSCREEN;
|
|
else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_DIALOG)
|
|
type = META_WINDOW_DIALOG;
|
|
else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_NORMAL)
|
|
type = META_WINDOW_NORMAL;
|
|
/* The below are *typically* override-redirect windows, but the spec does
|
|
* not disallow using them for managed windows.
|
|
*/
|
|
else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_DROPDOWN_MENU)
|
|
type = META_WINDOW_DROPDOWN_MENU;
|
|
else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_POPUP_MENU)
|
|
type = META_WINDOW_POPUP_MENU;
|
|
else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_TOOLTIP)
|
|
type = META_WINDOW_TOOLTIP;
|
|
else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_NOTIFICATION)
|
|
type = META_WINDOW_NOTIFICATION;
|
|
else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_COMBO)
|
|
type = META_WINDOW_COMBO;
|
|
else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_DND)
|
|
type = META_WINDOW_DND;
|
|
else
|
|
{
|
|
char *atom_name;
|
|
|
|
/*
|
|
* Fallback on a normal type, and print warning. Don't abort.
|
|
*/
|
|
type = META_WINDOW_NORMAL;
|
|
|
|
meta_error_trap_push (window->display);
|
|
atom_name = XGetAtomName (window->display->xdisplay,
|
|
priv->type_atom);
|
|
meta_error_trap_pop (window->display);
|
|
|
|
meta_warning ("Unrecognized type atom [%s] set for %s \n",
|
|
atom_name ? atom_name : "unknown",
|
|
window->desc);
|
|
|
|
if (atom_name)
|
|
XFree (atom_name);
|
|
}
|
|
}
|
|
else if (window->transient_for != NULL)
|
|
{
|
|
type = META_WINDOW_DIALOG;
|
|
}
|
|
else
|
|
{
|
|
type = META_WINDOW_NORMAL;
|
|
}
|
|
|
|
if (type == META_WINDOW_DIALOG && priv->wm_state_modal)
|
|
type = META_WINDOW_MODAL_DIALOG;
|
|
|
|
/* We don't want to allow override-redirect windows to have decorated-window
|
|
* types since that's just confusing.
|
|
*/
|
|
if (window->override_redirect)
|
|
{
|
|
switch (type)
|
|
{
|
|
/* Decorated types */
|
|
case META_WINDOW_NORMAL:
|
|
case META_WINDOW_DIALOG:
|
|
case META_WINDOW_MODAL_DIALOG:
|
|
case META_WINDOW_MENU:
|
|
case META_WINDOW_UTILITY:
|
|
type = META_WINDOW_OVERRIDE_OTHER;
|
|
break;
|
|
/* Undecorated types, normally not override-redirect */
|
|
case META_WINDOW_DESKTOP:
|
|
case META_WINDOW_DOCK:
|
|
case META_WINDOW_TOOLBAR:
|
|
case META_WINDOW_SPLASHSCREEN:
|
|
/* Undecorated types, normally override-redirect types */
|
|
case META_WINDOW_DROPDOWN_MENU:
|
|
case META_WINDOW_POPUP_MENU:
|
|
case META_WINDOW_TOOLTIP:
|
|
case META_WINDOW_NOTIFICATION:
|
|
case META_WINDOW_COMBO:
|
|
case META_WINDOW_DND:
|
|
/* To complete enum */
|
|
case META_WINDOW_OVERRIDE_OTHER:
|
|
break;
|
|
}
|
|
}
|
|
|
|
meta_verbose ("Calculated type %u for %s, old type %u\n",
|
|
type, window->desc, type);
|
|
meta_window_set_type (window, type);
|
|
}
|
|
|
|
/**
|
|
* meta_window_x11_configure_notify: (skip)
|
|
* @window: a #MetaWindow
|
|
* @event: a #XConfigureEvent
|
|
*
|
|
* This is used to notify us of an unrequested configuration
|
|
* (only applicable to override redirect windows)
|
|
*/
|
|
void
|
|
meta_window_x11_configure_notify (MetaWindow *window,
|
|
XConfigureEvent *event)
|
|
{
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
|
MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
|
|
|
|
g_assert (window->override_redirect);
|
|
g_assert (window->frame == NULL);
|
|
|
|
window->rect.x = event->x;
|
|
window->rect.y = event->y;
|
|
window->rect.width = event->width;
|
|
window->rect.height = event->height;
|
|
|
|
priv->client_rect = window->rect;
|
|
window->buffer_rect = window->rect;
|
|
|
|
meta_window_update_monitor (window, FALSE);
|
|
|
|
/* Whether an override-redirect window is considered fullscreen depends
|
|
* on its geometry.
|
|
*/
|
|
if (window->override_redirect)
|
|
meta_screen_queue_check_fullscreen (window->screen);
|
|
|
|
if (!event->override_redirect && !event->send_event)
|
|
meta_warning ("Unhandled change of windows override redirect status\n");
|
|
|
|
meta_compositor_sync_window_geometry (window->display->compositor, window, FALSE);
|
|
}
|
|
|
|
void
|
|
meta_window_x11_set_allowed_actions_hint (MetaWindow *window)
|
|
{
|
|
#define MAX_N_ACTIONS 12
|
|
unsigned long data[MAX_N_ACTIONS];
|
|
int i;
|
|
|
|
i = 0;
|
|
if (window->has_move_func)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_ACTION_MOVE;
|
|
++i;
|
|
}
|
|
if (window->has_resize_func)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_ACTION_RESIZE;
|
|
++i;
|
|
}
|
|
if (window->has_fullscreen_func)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_ACTION_FULLSCREEN;
|
|
++i;
|
|
}
|
|
if (window->has_minimize_func)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_ACTION_MINIMIZE;
|
|
++i;
|
|
}
|
|
if (window->has_shade_func)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_ACTION_SHADE;
|
|
++i;
|
|
}
|
|
/* sticky according to EWMH is different from mutter's sticky;
|
|
* mutter doesn't support EWMH sticky
|
|
*/
|
|
if (window->has_maximize_func)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_ACTION_MAXIMIZE_HORZ;
|
|
++i;
|
|
data[i] = window->display->atom__NET_WM_ACTION_MAXIMIZE_VERT;
|
|
++i;
|
|
}
|
|
/* We always allow this */
|
|
data[i] = window->display->atom__NET_WM_ACTION_CHANGE_DESKTOP;
|
|
++i;
|
|
if (window->has_close_func)
|
|
{
|
|
data[i] = window->display->atom__NET_WM_ACTION_CLOSE;
|
|
++i;
|
|
}
|
|
|
|
/* I guess we always allow above/below operations */
|
|
data[i] = window->display->atom__NET_WM_ACTION_ABOVE;
|
|
++i;
|
|
data[i] = window->display->atom__NET_WM_ACTION_BELOW;
|
|
++i;
|
|
|
|
g_assert (i <= MAX_N_ACTIONS);
|
|
|
|
meta_verbose ("Setting _NET_WM_ALLOWED_ACTIONS with %d atoms\n", i);
|
|
|
|
meta_error_trap_push (window->display);
|
|
XChangeProperty (window->display->xdisplay, window->xwindow,
|
|
window->display->atom__NET_WM_ALLOWED_ACTIONS,
|
|
XA_ATOM,
|
|
32, PropModeReplace, (guchar*) data, i);
|
|
meta_error_trap_pop (window->display);
|
|
#undef MAX_N_ACTIONS
|
|
}
|
|
|
|
void
|
|
meta_window_x11_create_sync_request_alarm (MetaWindow *window)
|
|
{
|
|
XSyncAlarmAttributes values;
|
|
XSyncValue init;
|
|
|
|
if (window->sync_request_counter == None ||
|
|
window->sync_request_alarm != None)
|
|
return;
|
|
|
|
meta_error_trap_push (window->display);
|
|
|
|
/* In the new (extended style), the counter value is initialized by
|
|
* the client before mapping the window. In the old style, we're
|
|
* responsible for setting the initial value of the counter.
|
|
*/
|
|
if (window->extended_sync_request_counter)
|
|
{
|
|
if (!XSyncQueryCounter(window->display->xdisplay,
|
|
window->sync_request_counter,
|
|
&init))
|
|
{
|
|
meta_error_trap_pop_with_return (window->display);
|
|
window->sync_request_counter = None;
|
|
return;
|
|
}
|
|
|
|
window->sync_request_serial =
|
|
XSyncValueLow32 (init) + ((gint64)XSyncValueHigh32 (init) << 32);
|
|
}
|
|
else
|
|
{
|
|
XSyncIntToValue (&init, 0);
|
|
XSyncSetCounter (window->display->xdisplay,
|
|
window->sync_request_counter, init);
|
|
window->sync_request_serial = 0;
|
|
}
|
|
|
|
values.trigger.counter = window->sync_request_counter;
|
|
values.trigger.test_type = XSyncPositiveComparison;
|
|
|
|
/* Initialize to one greater than the current value */
|
|
values.trigger.value_type = XSyncRelative;
|
|
XSyncIntToValue (&values.trigger.wait_value, 1);
|
|
|
|
/* After triggering, increment test_value by this until
|
|
* until the test condition is false */
|
|
XSyncIntToValue (&values.delta, 1);
|
|
|
|
/* we want events (on by default anyway) */
|
|
values.events = True;
|
|
|
|
window->sync_request_alarm = XSyncCreateAlarm (window->display->xdisplay,
|
|
XSyncCACounter |
|
|
XSyncCAValueType |
|
|
XSyncCAValue |
|
|
XSyncCATestType |
|
|
XSyncCADelta |
|
|
XSyncCAEvents,
|
|
&values);
|
|
|
|
if (meta_error_trap_pop_with_return (window->display) == Success)
|
|
meta_display_register_sync_alarm (window->display, &window->sync_request_alarm, window);
|
|
else
|
|
{
|
|
window->sync_request_alarm = None;
|
|
window->sync_request_counter = None;
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_window_x11_destroy_sync_request_alarm (MetaWindow *window)
|
|
{
|
|
if (window->sync_request_alarm != None)
|
|
{
|
|
/* Has to be unregistered _before_ clearing the structure field */
|
|
meta_display_unregister_sync_alarm (window->display, window->sync_request_alarm);
|
|
XSyncDestroyAlarm (window->display->xdisplay,
|
|
window->sync_request_alarm);
|
|
window->sync_request_alarm = None;
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_window_x11_update_sync_request_counter (MetaWindow *window,
|
|
gint64 new_counter_value)
|
|
{
|
|
gboolean needs_frame_drawn = FALSE;
|
|
gboolean no_delay_frame = FALSE;
|
|
|
|
if (window->extended_sync_request_counter && new_counter_value % 2 == 0)
|
|
{
|
|
needs_frame_drawn = TRUE;
|
|
no_delay_frame = new_counter_value == window->sync_request_serial + 1;
|
|
}
|
|
|
|
window->sync_request_serial = new_counter_value;
|
|
meta_compositor_sync_updates_frozen (window->display->compositor, window);
|
|
|
|
if (window == window->display->grab_window &&
|
|
meta_grab_op_is_resizing (window->display->grab_op) &&
|
|
new_counter_value >= window->sync_request_wait_serial &&
|
|
(!window->extended_sync_request_counter || new_counter_value % 2 == 0) &&
|
|
window->sync_request_timeout_id)
|
|
{
|
|
meta_topic (META_DEBUG_RESIZING,
|
|
"Alarm event received last motion x = %d y = %d\n",
|
|
window->display->grab_latest_motion_x,
|
|
window->display->grab_latest_motion_y);
|
|
|
|
g_source_remove (window->sync_request_timeout_id);
|
|
window->sync_request_timeout_id = 0;
|
|
|
|
/* This means we are ready for another configure;
|
|
* no pointer round trip here, to keep in sync */
|
|
meta_window_update_resize (window,
|
|
window->display->grab_last_user_action_was_snap,
|
|
window->display->grab_latest_motion_x,
|
|
window->display->grab_latest_motion_y,
|
|
TRUE);
|
|
}
|
|
|
|
/* If sync was previously disabled, turn it back on and hope
|
|
* the application has come to its senses (maybe it was just
|
|
* busy with a pagefault or a long computation).
|
|
*/
|
|
window->disable_sync = FALSE;
|
|
|
|
if (needs_frame_drawn)
|
|
meta_compositor_queue_frame_drawn (window->display->compositor, window,
|
|
no_delay_frame);
|
|
}
|
|
|
|
Window
|
|
meta_window_x11_get_toplevel_xwindow (MetaWindow *window)
|
|
{
|
|
return window->frame ? window->frame->xwindow : window->xwindow;
|
|
}
|