/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2001 Havoc Pennington
* Copyright (C) 2002, 2003, 2004 Red Hat, Inc.
* Copyright (C) 2003, 2004 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 .
*/
/**
* MetaX11Display:
*
* Mutter X display handler
*
* The X11 display is represented as a #MetaX11Display struct.
*/
#include "config.h"
#include "core/display-private.h"
#include "x11/meta-x11-display-private.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "backends/meta-backend-private.h"
#include "backends/meta-dnd-private.h"
#include "backends/meta-cursor-sprite-xcursor.h"
#include "backends/meta-logical-monitor.h"
#include "backends/meta-settings-private.h"
#include "backends/x11/meta-backend-x11.h"
#include "backends/x11/meta-stage-x11.h"
#include "core/frame.h"
#include "core/meta-workspace-manager-private.h"
#include "core/util-private.h"
#include "core/workspace-private.h"
#include "meta/main.h"
#include "meta/meta-x11-errors.h"
#include "x11/events.h"
#include "x11/group-props.h"
#include "x11/meta-x11-selection-private.h"
#include "x11/window-props.h"
#include "x11/xprops.h"
#ifdef HAVE_XWAYLAND
#include "wayland/meta-xwayland-private.h"
#endif
G_DEFINE_TYPE (MetaX11Display, meta_x11_display, G_TYPE_OBJECT)
static GQuark quark_x11_display_logical_monitor_data = 0;
typedef struct _MetaX11EventFilter MetaX11EventFilter;
struct _MetaX11EventFilter
{
unsigned int id;
MetaX11DisplayEventFunc func;
gpointer user_data;
GDestroyNotify destroy_notify;
};
typedef struct _MetaX11DisplayLogicalMonitorData
{
int xinerama_index;
} MetaX11DisplayLogicalMonitorData;
static char *get_screen_name (Display *xdisplay,
int number);
static void on_monitors_changed_internal (MetaMonitorManager *monitor_manager,
MetaX11Display *x11_display);
static void update_cursor_theme (MetaX11Display *x11_display);
static void unset_wm_check_hint (MetaX11Display *x11_display);
static void prefs_changed_callback (MetaPreference pref,
void *data);
static void meta_x11_display_init_frames_client (MetaX11Display *x11_display);
static void meta_x11_display_remove_cursor_later (MetaX11Display *x11_display);
static MetaBackend *
backend_from_x11_display (MetaX11Display *x11_display)
{
MetaDisplay *display = meta_x11_display_get_display (x11_display);
MetaContext *context = meta_display_get_context (display);
return meta_context_get_backend (context);
}
static void
meta_x11_display_unmanage_windows (MetaX11Display *x11_display)
{
GList *windows, *l;
if (!x11_display->xids)
return;
windows = g_hash_table_get_values (x11_display->xids);
g_list_foreach (windows, (GFunc) g_object_ref, NULL);
for (l = windows; l; l = l->next)
{
MetaWindow *window = META_WINDOW (l->data);
if (!window->unmanaging)
meta_window_unmanage (window, META_CURRENT_TIME);
}
g_list_free_full (windows, g_object_unref);
}
static void
meta_x11_event_filter_free (MetaX11EventFilter *filter)
{
if (filter->destroy_notify && filter->user_data)
filter->destroy_notify (filter->user_data);
g_free (filter);
}
static void
meta_x11_display_dispose (GObject *object)
{
MetaX11Display *x11_display = META_X11_DISPLAY (object);
x11_display->closing = TRUE;
g_clear_pointer (&x11_display->alarm_filters, g_ptr_array_unref);
g_clear_list (&x11_display->event_funcs,
(GDestroyNotify) meta_x11_event_filter_free);
if (x11_display->frames_client_cancellable)
{
g_cancellable_cancel (x11_display->frames_client_cancellable);
g_clear_object (&x11_display->frames_client_cancellable);
}
if (x11_display->frames_client)
{
g_subprocess_send_signal (x11_display->frames_client, SIGTERM);
if (x11_display->display->closing)
g_subprocess_wait (x11_display->frames_client, NULL, NULL);
g_clear_object (&x11_display->frames_client);
}
if (x11_display->empty_region != None)
{
XFixesDestroyRegion (x11_display->xdisplay,
x11_display->empty_region);
x11_display->empty_region = None;
}
meta_x11_startup_notification_release (x11_display);
meta_prefs_remove_listener (prefs_changed_callback, x11_display);
meta_x11_display_ungrab_keys (x11_display);
g_clear_object (&x11_display->x11_stack);
meta_x11_selection_shutdown (x11_display);
meta_x11_display_unmanage_windows (x11_display);
if (x11_display->no_focus_window != None)
{
XUnmapWindow (x11_display->xdisplay, x11_display->no_focus_window);
XDestroyWindow (x11_display->xdisplay, x11_display->no_focus_window);
x11_display->no_focus_window = None;
}
if (x11_display->composite_overlay_window != None)
{
XCompositeReleaseOverlayWindow (x11_display->xdisplay,
x11_display->composite_overlay_window);
x11_display->composite_overlay_window = None;
}
if (x11_display->wm_sn_selection_window != None)
{
XDestroyWindow (x11_display->xdisplay, x11_display->wm_sn_selection_window);
x11_display->wm_sn_selection_window = None;
}
if (x11_display->timestamp_pinging_window != None)
{
XDestroyWindow (x11_display->xdisplay, x11_display->timestamp_pinging_window);
x11_display->timestamp_pinging_window = None;
}
if (x11_display->leader_window != None)
{
XDestroyWindow (x11_display->xdisplay, x11_display->leader_window);
x11_display->leader_window = None;
}
if (x11_display->guard_window != None)
{
XUnmapWindow (x11_display->xdisplay, x11_display->guard_window);
XDestroyWindow (x11_display->xdisplay, x11_display->guard_window);
x11_display->guard_window = None;
}
if (x11_display->prop_hooks)
{
meta_x11_display_free_window_prop_hooks (x11_display);
x11_display->prop_hooks = NULL;
}
if (x11_display->group_prop_hooks)
{
meta_x11_display_free_group_prop_hooks (x11_display);
x11_display->group_prop_hooks = NULL;
}
if (x11_display->xids)
{
/* Must be after all calls to meta_window_unmanage() since they
* unregister windows
*/
g_hash_table_destroy (x11_display->xids);
x11_display->xids = NULL;
}
g_clear_pointer (&x11_display->alarms, g_hash_table_unref);
if (x11_display->xroot != None)
{
unset_wm_check_hint (x11_display);
meta_x11_error_trap_push (x11_display);
XSelectInput (x11_display->xdisplay, x11_display->xroot, 0);
if (meta_x11_error_trap_pop_with_return (x11_display) != Success)
meta_warning ("Could not release screen %d on display \"%s\"",
DefaultScreen (x11_display->xdisplay),
x11_display->name);
x11_display->xroot = None;
}
meta_x11_display_destroy_error_traps (x11_display);
if (x11_display->xdisplay)
{
meta_x11_display_free_events (x11_display);
XCloseDisplay (x11_display->xdisplay);
x11_display->xdisplay = NULL;
}
g_clear_handle_id (&x11_display->display_close_idle, g_source_remove);
meta_x11_display_remove_cursor_later (x11_display);
g_free (x11_display->name);
x11_display->name = NULL;
g_free (x11_display->screen_name);
x11_display->screen_name = NULL;
G_OBJECT_CLASS (meta_x11_display_parent_class)->dispose (object);
}
static void
on_x11_display_opened (MetaX11Display *x11_display,
MetaDisplay *display)
{
meta_display_manage_all_xwindows (display);
meta_x11_display_redirect_windows (x11_display, display);
}
static void
meta_x11_display_class_init (MetaX11DisplayClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = meta_x11_display_dispose;
}
static void
meta_x11_display_init (MetaX11Display *x11_display)
{
quark_x11_display_logical_monitor_data =
g_quark_from_static_string ("-meta-x11-display-logical-monitor-data");
}
static void
query_xsync_extension (MetaX11Display *x11_display)
{
int major, minor;
x11_display->have_xsync = FALSE;
x11_display->xsync_error_base = 0;
x11_display->xsync_event_base = 0;
/* I don't think we really have to fill these in */
major = SYNC_MAJOR_VERSION;
minor = SYNC_MINOR_VERSION;
if (!XSyncQueryExtension (x11_display->xdisplay,
&x11_display->xsync_event_base,
&x11_display->xsync_error_base) ||
!XSyncInitialize (x11_display->xdisplay,
&major, &minor))
{
x11_display->xsync_error_base = 0;
x11_display->xsync_event_base = 0;
}
else
{
x11_display->have_xsync = TRUE;
XSyncSetPriority (x11_display->xdisplay, None, 10);
}
meta_verbose ("Attempted to init Xsync, found version %d.%d error base %d event base %d",
major, minor,
x11_display->xsync_error_base,
x11_display->xsync_event_base);
}
static void
query_xshape_extension (MetaX11Display *x11_display)
{
x11_display->have_shape = FALSE;
x11_display->shape_error_base = 0;
x11_display->shape_event_base = 0;
if (!XShapeQueryExtension (x11_display->xdisplay,
&x11_display->shape_event_base,
&x11_display->shape_error_base))
{
x11_display->shape_error_base = 0;
x11_display->shape_event_base = 0;
}
else
x11_display->have_shape = TRUE;
meta_verbose ("Attempted to init Shape, found error base %d event base %d",
x11_display->shape_error_base,
x11_display->shape_event_base);
}
static void
query_xcomposite_extension (MetaX11Display *x11_display)
{
x11_display->have_composite = FALSE;
x11_display->composite_error_base = 0;
x11_display->composite_event_base = 0;
if (!XCompositeQueryExtension (x11_display->xdisplay,
&x11_display->composite_event_base,
&x11_display->composite_error_base))
{
x11_display->composite_error_base = 0;
x11_display->composite_event_base = 0;
}
else
{
x11_display->composite_major_version = 0;
x11_display->composite_minor_version = 0;
if (XCompositeQueryVersion (x11_display->xdisplay,
&x11_display->composite_major_version,
&x11_display->composite_minor_version))
{
x11_display->have_composite = TRUE;
}
else
{
x11_display->composite_major_version = 0;
x11_display->composite_minor_version = 0;
}
}
meta_verbose ("Attempted to init Composite, found error base %d event base %d "
"extn ver %d %d",
x11_display->composite_error_base,
x11_display->composite_event_base,
x11_display->composite_major_version,
x11_display->composite_minor_version);
}
static void
query_xdamage_extension (MetaX11Display *x11_display)
{
x11_display->have_damage = FALSE;
x11_display->damage_error_base = 0;
x11_display->damage_event_base = 0;
if (!XDamageQueryExtension (x11_display->xdisplay,
&x11_display->damage_event_base,
&x11_display->damage_error_base))
{
x11_display->damage_error_base = 0;
x11_display->damage_event_base = 0;
}
else
x11_display->have_damage = TRUE;
meta_verbose ("Attempted to init Damage, found error base %d event base %d",
x11_display->damage_error_base,
x11_display->damage_event_base);
}
static void
query_xfixes_extension (MetaX11Display *x11_display)
{
x11_display->xfixes_error_base = 0;
x11_display->xfixes_event_base = 0;
if (XFixesQueryExtension (x11_display->xdisplay,
&x11_display->xfixes_event_base,
&x11_display->xfixes_error_base))
{
int xfixes_major, xfixes_minor;
XFixesQueryVersion (x11_display->xdisplay, &xfixes_major, &xfixes_minor);
if (xfixes_major * 100 + xfixes_minor < 500)
meta_fatal ("Mutter requires XFixes 5.0");
}
else
{
meta_fatal ("Mutter requires XFixes 5.0");
}
meta_verbose ("Attempted to init XFixes, found error base %d event base %d",
x11_display->xfixes_error_base,
x11_display->xfixes_event_base);
}
static void
query_xi_extension (MetaX11Display *x11_display)
{
int major = 2, minor = 3;
gboolean has_xi = FALSE;
if (XQueryExtension (x11_display->xdisplay,
"XInputExtension",
&x11_display->xinput_opcode,
&x11_display->xinput_error_base,
&x11_display->xinput_event_base))
{
if (XIQueryVersion (x11_display->xdisplay, &major, &minor) == Success)
has_xi = TRUE;
}
if (!has_xi)
meta_fatal ("X server doesn't have the XInput extension, version 2.2 or newer");
}
/*
* Initialises the bell subsystem. This involves initialising
* XKB (which, despite being a keyboard extension, is the
* place to look for bell notifications), then asking it
* to send us bell notifications, and then also switching
* off the audible bell if we're using a visual one ourselves.
*
* \bug There is a line of code that's never run that tells
* XKB to reset the bell status after we quit. Bill H said
* ()
* that XFree86's implementation is broken so we shouldn't
* call it, but that was in 2002. Is it working now?
*/
static void
init_x11_bell (MetaX11Display *x11_display)
{
int xkb_base_error_type, xkb_opcode;
if (!XkbQueryExtension (x11_display->xdisplay, &xkb_opcode,
&x11_display->xkb_base_event_type,
&xkb_base_error_type,
NULL, NULL))
{
x11_display->xkb_base_event_type = -1;
meta_warning ("could not find XKB extension.");
}
else
{
unsigned int mask = XkbBellNotifyMask;
gboolean visual_bell_auto_reset = FALSE;
/* TRUE if and when non-broken version is available */
XkbSelectEvents (x11_display->xdisplay,
XkbUseCoreKbd,
XkbBellNotifyMask,
XkbBellNotifyMask);
if (visual_bell_auto_reset)
{
XkbSetAutoResetControls (x11_display->xdisplay,
XkbAudibleBellMask,
&mask,
&mask);
}
}
/* We are playing sounds using libcanberra support, we handle the
* bell whether its an audible bell or a visible bell */
XkbChangeEnabledControls (x11_display->xdisplay,
XkbUseCoreKbd,
XkbAudibleBellMask,
0);
}
/*
* \bug This is never called! If we had XkbSetAutoResetControls
* enabled in meta_x11_bell_init(), this wouldn't be a problem,
* but we don't.
*/
G_GNUC_UNUSED static void
shutdown_x11_bell (MetaX11Display *x11_display)
{
/* TODO: persist initial bell state in display, reset here */
XkbChangeEnabledControls (x11_display->xdisplay,
XkbUseCoreKbd,
XkbAudibleBellMask,
XkbAudibleBellMask);
}
static void
set_desktop_geometry_hint (MetaX11Display *x11_display)
{
unsigned long data[2];
int monitor_width, monitor_height;
if (x11_display->display->closing > 0)
return;
meta_display_get_size (x11_display->display, &monitor_width, &monitor_height);
data[0] = monitor_width;
data[1] = monitor_height;
meta_verbose ("Setting _NET_DESKTOP_GEOMETRY to %lu, %lu", data[0], data[1]);
meta_x11_error_trap_push (x11_display);
XChangeProperty (x11_display->xdisplay,
x11_display->xroot,
x11_display->atom__NET_DESKTOP_GEOMETRY,
XA_CARDINAL,
32, PropModeReplace, (guchar*) data, 2);
meta_x11_error_trap_pop (x11_display);
}
static void
set_desktop_viewport_hint (MetaX11Display *x11_display)
{
unsigned long data[2];
if (x11_display->display->closing > 0)
return;
/*
* Mutter does not implement viewports, so this is a fixed 0,0
*/
data[0] = 0;
data[1] = 0;
meta_verbose ("Setting _NET_DESKTOP_VIEWPORT to 0, 0");
meta_x11_error_trap_push (x11_display);
XChangeProperty (x11_display->xdisplay,
x11_display->xroot,
x11_display->atom__NET_DESKTOP_VIEWPORT,
XA_CARDINAL,
32, PropModeReplace, (guchar*) data, 2);
meta_x11_error_trap_pop (x11_display);
}
static int
set_wm_check_hint (MetaX11Display *x11_display)
{
unsigned long data[1];
g_return_val_if_fail (x11_display->leader_window != None, 0);
data[0] = x11_display->leader_window;
XChangeProperty (x11_display->xdisplay,
x11_display->xroot,
x11_display->atom__NET_SUPPORTING_WM_CHECK,
XA_WINDOW,
32, PropModeReplace, (guchar*) data, 1);
return Success;
}
static void
unset_wm_check_hint (MetaX11Display *x11_display)
{
XDeleteProperty (x11_display->xdisplay,
x11_display->xroot,
x11_display->atom__NET_SUPPORTING_WM_CHECK);
}
static int
set_supported_hint (MetaX11Display *x11_display)
{
Atom atoms[] = {
#define EWMH_ATOMS_ONLY
#define item(x) x11_display->atom_##x,
#include "x11/atomnames.h"
#undef item
#undef EWMH_ATOMS_ONLY
x11_display->atom__GTK_FRAME_EXTENTS,
x11_display->atom__GTK_SHOW_WINDOW_MENU,
x11_display->atom__GTK_EDGE_CONSTRAINTS,
x11_display->atom__GTK_WORKAREAS,
};
XChangeProperty (x11_display->xdisplay,
x11_display->xroot,
x11_display->atom__NET_SUPPORTED,
XA_ATOM,
32, PropModeReplace,
(guchar*) atoms, G_N_ELEMENTS(atoms));
return Success;
}
static int
set_wm_icon_size_hint (MetaX11Display *x11_display)
{
#define N_VALS 6
gulong vals[N_VALS];
/* We've bumped the real icon size up to 96x96, but
* we really should not add these sorts of constraints
* on clients still using the legacy WM_HINTS interface.
*/
#define LEGACY_ICON_SIZE 32
/* min width, min height, max w, max h, width inc, height inc */
vals[0] = LEGACY_ICON_SIZE;
vals[1] = LEGACY_ICON_SIZE;
vals[2] = LEGACY_ICON_SIZE;
vals[3] = LEGACY_ICON_SIZE;
vals[4] = 0;
vals[5] = 0;
#undef LEGACY_ICON_SIZE
XChangeProperty (x11_display->xdisplay,
x11_display->xroot,
x11_display->atom_WM_ICON_SIZE,
XA_CARDINAL,
32, PropModeReplace, (guchar*) vals, N_VALS);
return Success;
#undef N_VALS
}
static Window
take_manager_selection (MetaX11Display *x11_display,
Window xroot,
Atom manager_atom,
int timestamp,
gboolean should_replace)
{
Window current_owner, new_owner;
current_owner = XGetSelectionOwner (x11_display->xdisplay, manager_atom);
if (current_owner != None)
{
XSetWindowAttributes attrs;
if (should_replace)
{
/* We want to find out when the current selection owner dies */
meta_x11_error_trap_push (x11_display);
attrs.event_mask = StructureNotifyMask;
XChangeWindowAttributes (x11_display->xdisplay, current_owner, CWEventMask, &attrs);
if (meta_x11_error_trap_pop_with_return (x11_display) != Success)
current_owner = None; /* don't wait for it to die later on */
}
else
{
meta_warning (_("Display “%s” already has a window manager; try using the --replace option to replace the current window manager."),
x11_display->name);
return None;
}
}
/* We need SelectionClear and SelectionRequest events on the new owner,
* but those cannot be masked, so we only need NoEventMask.
*/
new_owner = meta_x11_display_create_offscreen_window (x11_display, xroot, NoEventMask);
XSetSelectionOwner (x11_display->xdisplay, manager_atom, new_owner, timestamp);
if (XGetSelectionOwner (x11_display->xdisplay, manager_atom) != new_owner)
{
meta_warning ("Could not acquire selection: %s", XGetAtomName (x11_display->xdisplay, manager_atom));
return None;
}
{
/* Send client message indicating that we are now the selection owner */
XClientMessageEvent ev = { 0, };
ev.type = ClientMessage;
ev.window = xroot;
ev.message_type = x11_display->atom_MANAGER;
ev.format = 32;
ev.data.l[0] = timestamp;
ev.data.l[1] = manager_atom;
XSendEvent (x11_display->xdisplay, xroot, False, StructureNotifyMask, (XEvent *) &ev);
}
/* Wait for old window manager to go away */
if (current_owner != None)
{
XEvent event;
#ifdef HAVE_XWAYLAND
g_return_val_if_fail (!meta_is_wayland_compositor (), new_owner);
#endif
/* We sort of block infinitely here which is probably lame. */
meta_verbose ("Waiting for old window manager to exit");
do
XWindowEvent (x11_display->xdisplay, current_owner, StructureNotifyMask, &event);
while (event.type != DestroyNotify);
}
return new_owner;
}
/* Create the leader window here. Set its properties and
* use the timestamp from one of the PropertyNotify events
* that will follow.
*/
static void
init_leader_window (MetaX11Display *x11_display,
guint32 *timestamp)
{
MetaContext *context = meta_display_get_context (x11_display->display);
const char *gnome_wm_keybindings;
gulong data[1];
XEvent event;
x11_display->leader_window =
meta_x11_display_create_offscreen_window (x11_display,
x11_display->xroot,
PropertyChangeMask);
meta_prop_set_utf8_string_hint (x11_display,
x11_display->leader_window,
x11_display->atom__NET_WM_NAME,
meta_context_get_name (context));
gnome_wm_keybindings = meta_context_get_gnome_wm_keybindings (context);
meta_prop_set_utf8_string_hint (x11_display,
x11_display->leader_window,
x11_display->atom__GNOME_WM_KEYBINDINGS,
gnome_wm_keybindings);
meta_prop_set_utf8_string_hint (x11_display,
x11_display->leader_window,
x11_display->atom__MUTTER_VERSION,
VERSION);
data[0] = x11_display->leader_window;
XChangeProperty (x11_display->xdisplay,
x11_display->leader_window,
x11_display->atom__NET_SUPPORTING_WM_CHECK,
XA_WINDOW,
32, PropModeReplace, (guchar*) data, 1);
XWindowEvent (x11_display->xdisplay,
x11_display->leader_window,
PropertyChangeMask,
&event);
if (timestamp)
*timestamp = event.xproperty.time;
/* Make it painfully clear that we can't rely on PropertyNotify events on
* this window, as per bug 354213.
*/
XSelectInput (x11_display->xdisplay,
x11_display->leader_window,
NoEventMask);
}
static void
init_event_masks (MetaX11Display *x11_display)
{
long 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 (x11_display->xdisplay, x11_display->xroot, &mask, 1);
event_mask = (SubstructureRedirectMask | SubstructureNotifyMask |
StructureNotifyMask | ColormapChangeMask | PropertyChangeMask);
XSelectInput (x11_display->xdisplay, x11_display->xroot, event_mask);
}
static void
set_active_workspace_hint (MetaWorkspaceManager *workspace_manager,
MetaX11Display *x11_display)
{
unsigned long data[1];
/* this is because we destroy the spaces in order,
* so we always end up setting a current desktop of
* 0 when closing a screen, so lose the current desktop
* on restart. By doing this we keep the current
* desktop on restart.
*/
if (x11_display->display->closing > 0)
return;
data[0] = meta_workspace_index (workspace_manager->active_workspace);
meta_verbose ("Setting _NET_CURRENT_DESKTOP to %lu", data[0]);
meta_x11_error_trap_push (x11_display);
XChangeProperty (x11_display->xdisplay,
x11_display->xroot,
x11_display->atom__NET_CURRENT_DESKTOP,
XA_CARDINAL,
32, PropModeReplace, (guchar*) data, 1);
meta_x11_error_trap_pop (x11_display);
}
static void
set_number_of_spaces_hint (MetaWorkspaceManager *workspace_manager,
GParamSpec *pspec,
gpointer user_data)
{
MetaX11Display *x11_display = user_data;
unsigned long data[1];
if (x11_display->display->closing > 0)
return;
data[0] = meta_workspace_manager_get_n_workspaces (workspace_manager);
meta_verbose ("Setting _NET_NUMBER_OF_DESKTOPS to %lu", data[0]);
meta_x11_error_trap_push (x11_display);
XChangeProperty (x11_display->xdisplay,
x11_display->xroot,
x11_display->atom__NET_NUMBER_OF_DESKTOPS,
XA_CARDINAL,
32, PropModeReplace, (guchar*) data, 1);
meta_x11_error_trap_pop (x11_display);
}
static void
set_showing_desktop_hint (MetaWorkspaceManager *workspace_manager,
MetaX11Display *x11_display)
{
unsigned long data[1];
data[0] = workspace_manager->active_workspace->showing_desktop ? 1 : 0;
meta_x11_error_trap_push (x11_display);
XChangeProperty (x11_display->xdisplay,
x11_display->xroot,
x11_display->atom__NET_SHOWING_DESKTOP,
XA_CARDINAL,
32, PropModeReplace, (guchar*) data, 1);
meta_x11_error_trap_pop (x11_display);
}
static void
set_workspace_names (MetaX11Display *x11_display)
{
MetaWorkspaceManager *workspace_manager;
GString *flattened;
int i;
int n_spaces;
workspace_manager = x11_display->display->workspace_manager;
/* flatten to nul-separated list */
n_spaces = meta_workspace_manager_get_n_workspaces (workspace_manager);
flattened = g_string_new ("");
i = 0;
while (i < n_spaces)
{
const char *name;
name = meta_prefs_get_workspace_name (i);
if (name)
g_string_append_len (flattened, name,
strlen (name) + 1);
else
g_string_append_len (flattened, "", 1);
++i;
}
meta_x11_error_trap_push (x11_display);
XChangeProperty (x11_display->xdisplay,
x11_display->xroot,
x11_display->atom__NET_DESKTOP_NAMES,
x11_display->atom_UTF8_STRING,
8, PropModeReplace,
(unsigned char *)flattened->str, flattened->len);
meta_x11_error_trap_pop (x11_display);
g_string_free (flattened, TRUE);
}
static void
set_workspace_work_area_hint (MetaWorkspace *workspace,
MetaX11Display *x11_display)
{
MetaMonitorManager *monitor_manager;
GList *logical_monitors;
GList *l;
int num_monitors;
unsigned long *data;
unsigned long *tmp;
g_autofree char *workarea_name = NULL;
Atom workarea_atom;
monitor_manager =
meta_backend_get_monitor_manager (backend_from_x11_display (x11_display));
logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager);
num_monitors = meta_monitor_manager_get_num_logical_monitors (monitor_manager);
data = g_new (unsigned long, num_monitors * 4);
tmp = data;
for (l = logical_monitors; l; l = l->next)
{
MetaRectangle area;
meta_workspace_get_work_area_for_logical_monitor (workspace, l->data, &area);
tmp[0] = area.x;
tmp[1] = area.y;
tmp[2] = area.width;
tmp[3] = area.height;
tmp += 4;
}
workarea_name = g_strdup_printf ("_GTK_WORKAREAS_D%d",
meta_workspace_index (workspace));
workarea_atom = XInternAtom (x11_display->xdisplay, workarea_name, False);
meta_x11_error_trap_push (x11_display);
XChangeProperty (x11_display->xdisplay,
x11_display->xroot,
workarea_atom,
XA_CARDINAL, 32, PropModeReplace,
(guchar*) data, num_monitors * 4);
meta_x11_error_trap_pop (x11_display);
g_free (data);
}
static void
set_work_area_hint (MetaDisplay *display,
MetaX11Display *x11_display)
{
MetaWorkspaceManager *workspace_manager = display->workspace_manager;
int num_workspaces;
GList *l;
unsigned long *data, *tmp;
MetaRectangle area;
num_workspaces = meta_workspace_manager_get_n_workspaces (workspace_manager);
data = g_new (unsigned long, num_workspaces * 4);
tmp = data;
for (l = workspace_manager->workspaces; l; l = l->next)
{
MetaWorkspace *workspace = l->data;
meta_workspace_get_work_area_all_monitors (workspace, &area);
set_workspace_work_area_hint (workspace, x11_display);
tmp[0] = area.x;
tmp[1] = area.y;
tmp[2] = area.width;
tmp[3] = area.height;
tmp += 4;
}
meta_x11_error_trap_push (x11_display);
XChangeProperty (x11_display->xdisplay,
x11_display->xroot,
x11_display->atom__NET_WORKAREA,
XA_CARDINAL, 32, PropModeReplace,
(guchar*) data, num_workspaces*4);
meta_x11_error_trap_pop (x11_display);
g_free (data);
}
static const char *
get_display_name (MetaDisplay *display)
{
#ifdef HAVE_XWAYLAND
MetaContext *context = meta_display_get_context (display);
MetaWaylandCompositor *compositor =
meta_context_get_wayland_compositor (context);
if (compositor)
return meta_wayland_get_private_xwayland_display_name (compositor);
else
#endif
return g_getenv ("DISPLAY");
}
static Display *
open_x_display (MetaDisplay *display,
GError **error)
{
const char *xdisplay_name;
Display *xdisplay;
xdisplay_name = get_display_name (display);
if (!xdisplay_name)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Unable to open display, DISPLAY not set");
return NULL;
}
meta_verbose ("Opening display '%s'", xdisplay_name);
xdisplay = XOpenDisplay (xdisplay_name);
if (xdisplay == NULL)
{
meta_warning (_("Failed to open X Window System display “%s”"),
xdisplay_name);
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to open X11 display");
return NULL;
}
return xdisplay;
}
static void
on_window_visibility_updated (MetaDisplay *display,
GList *placed_windows,
GList *shown_windows,
GList *hidden_windows,
MetaX11Display *x11_display)
{
GList *l;
if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK)
return;
if (display->mouse_mode)
return;
for (l = shown_windows; l; l = l->next)
meta_x11_display_increment_focus_sentinel (x11_display);
}
static void
on_frames_client_died (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
MetaX11Display *x11_display = user_data;
GSubprocess *proc = G_SUBPROCESS (source);
g_autoptr (GError) error = NULL;
if (!g_subprocess_wait_finish (proc, result, &error))
{
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
g_warning ("Error obtaining frames client exit status: %s\n", error->message);
}
g_clear_object (&x11_display->frames_client_cancellable);
g_clear_object (&x11_display->frames_client);
if (g_subprocess_get_if_signaled (proc))
{
int signum;
signum = g_subprocess_get_term_sig (proc);
/* Bring it up again, unless it was forcibly closed */
if (signum != SIGTERM && signum != SIGKILL)
meta_x11_display_init_frames_client (x11_display);
}
}
static void
meta_x11_display_init_frames_client (MetaX11Display *x11_display)
{
const char *display_name;
display_name = get_display_name (x11_display->display);
x11_display->frames_client_cancellable = g_cancellable_new ();
x11_display->frames_client = meta_frame_launch_client (x11_display,
display_name);
g_subprocess_wait_async (x11_display->frames_client,
x11_display->frames_client_cancellable,
on_frames_client_died, x11_display);
}
/**
* meta_x11_display_new:
*
* Opens a new X11 display, sets it up, initialises all the X extensions
* we will need.
*
* Returns: #MetaX11Display if the display was opened successfully,
* and %NULL otherwise-- that is, if the display doesn't exist or
* it already has a window manager, and sets the error appropriately.
*/
MetaX11Display *
meta_x11_display_new (MetaDisplay *display,
GError **error)
{
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
g_autoptr (MetaX11Display) x11_display = NULL;
Display *xdisplay;
Screen *xscreen;
Window xroot;
int i, number;
Window new_wm_sn_owner;
gboolean replace_current_wm;
Atom wm_sn_atom;
Atom wm_cm_atom;
char buf[128];
guint32 timestamp;
Atom atom_restart_helper;
Window restart_helper_window = None;
gboolean is_restart = FALSE;
/* A list of all atom names, so that we can intern them in one go. */
const char *atom_names[] = {
#define item(x) #x,
#include "x11/atomnames.h"
#undef item
};
Atom atoms[G_N_ELEMENTS(atom_names)];
xdisplay = open_x_display (display, error);
if (!xdisplay)
return NULL;
XSynchronize (xdisplay, meta_context_is_x11_sync (context));
#ifdef HAVE_XWAYLAND
if (meta_is_wayland_compositor ())
{
MetaWaylandCompositor *compositor =
meta_context_get_wayland_compositor (context);
meta_xwayland_setup_xdisplay (&compositor->xwayland_manager, xdisplay);
}
#endif
replace_current_wm =
meta_context_is_replacing (meta_backend_get_context (backend));
number = DefaultScreen (xdisplay);
xroot = RootWindow (xdisplay, number);
/* FVWM checks for None here, I don't know if this
* ever actually happens
*/
if (xroot == None)
{
meta_warning (_("Screen %d on display “%s” is invalid"),
number, XDisplayName (NULL));
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to open default X11 screen");
XFlush (xdisplay);
XCloseDisplay (xdisplay);
return NULL;
}
xscreen = ScreenOfDisplay (xdisplay, number);
atom_restart_helper = XInternAtom (xdisplay, "_MUTTER_RESTART_HELPER", False);
restart_helper_window = XGetSelectionOwner (xdisplay, atom_restart_helper);
if (restart_helper_window)
{
is_restart = TRUE;
meta_set_is_restart (TRUE);
}
x11_display = g_object_new (META_TYPE_X11_DISPLAY, NULL);
x11_display->display = display;
/* here we use XDisplayName which is what the user
* probably put in, vs. DisplayString(display) which is
* canonicalized by XOpenDisplay()
*/
x11_display->xdisplay = xdisplay;
x11_display->xroot = xroot;
x11_display->name = g_strdup (XDisplayName (NULL));
x11_display->screen_name = get_screen_name (xdisplay, number);
x11_display->default_xvisual = DefaultVisualOfScreen (xscreen);
x11_display->default_depth = DefaultDepthOfScreen (xscreen);
meta_verbose ("Creating %d atoms", (int) G_N_ELEMENTS (atom_names));
XInternAtoms (xdisplay, (char **)atom_names, G_N_ELEMENTS (atom_names),
False, atoms);
i = 0;
#define item(x) x11_display->atom_##x = atoms[i++];
#include "x11/atomnames.h"
#undef item
meta_x11_display_init_error_traps (x11_display);
query_xsync_extension (x11_display);
query_xshape_extension (x11_display);
query_xcomposite_extension (x11_display);
query_xdamage_extension (x11_display);
query_xfixes_extension (x11_display);
query_xi_extension (x11_display);
g_signal_connect_object (display,
"cursor-updated",
G_CALLBACK (update_cursor_theme),
x11_display,
G_CONNECT_SWAPPED);
g_signal_connect_object (display,
"window-visibility-updated",
G_CALLBACK (on_window_visibility_updated),
x11_display, 0);
g_signal_connect_object (display,
"x11-display-opened",
G_CALLBACK (on_x11_display_opened),
x11_display,
G_CONNECT_SWAPPED);
update_cursor_theme (x11_display);
x11_display->xids = g_hash_table_new (meta_unsigned_long_hash,
meta_unsigned_long_equal);
x11_display->alarms = g_hash_table_new (meta_unsigned_long_hash,
meta_unsigned_long_equal);
x11_display->groups_by_leader = NULL;
x11_display->composite_overlay_window = None;
x11_display->guard_window = None;
x11_display->leader_window = None;
x11_display->timestamp_pinging_window = None;
x11_display->wm_sn_selection_window = None;
x11_display->display_close_idle = 0;
x11_display->xselectionclear_timestamp = 0;
x11_display->last_bell_time = 0;
x11_display->focus_serial = 0;
x11_display->server_focus_window = None;
x11_display->server_focus_serial = 0;
i = 0;
while (i < N_IGNORED_CROSSING_SERIALS)
{
x11_display->ignored_crossing_serials[i] = 0;
++i;
}
x11_display->prop_hooks = NULL;
meta_x11_display_init_window_prop_hooks (x11_display);
x11_display->group_prop_hooks = NULL;
meta_x11_display_init_group_prop_hooks (x11_display);
g_signal_connect_object (monitor_manager,
"monitors-changed-internal",
G_CALLBACK (on_monitors_changed_internal),
x11_display,
0);
init_leader_window (x11_display, ×tamp);
x11_display->timestamp = timestamp;
/* Make a little window used only for pinging the server for timestamps; note
* that meta_create_offscreen_window already selects for PropertyChangeMask.
*/
x11_display->timestamp_pinging_window =
meta_x11_display_create_offscreen_window (x11_display,
xroot,
PropertyChangeMask);
/* Select for cursor changes so the cursor tracker is up to date. */
XFixesSelectCursorInput (xdisplay, xroot, XFixesDisplayCursorNotifyMask);
/* If we're a Wayland compositor, then we don't grab the COW, since it
* will map it. */
if (!meta_is_wayland_compositor ())
x11_display->composite_overlay_window = XCompositeGetOverlayWindow (xdisplay, xroot);
/* Now that we've gotten taken a reference count on the COW, we
* can close the helper that is holding on to it */
if (is_restart)
XSetSelectionOwner (xdisplay, atom_restart_helper, None, META_CURRENT_TIME);
/* Handle creating a no_focus_window for this screen */
x11_display->no_focus_window =
meta_x11_display_create_offscreen_window (x11_display,
xroot,
FocusChangeMask |
KeyPressMask | KeyReleaseMask);
XMapWindow (xdisplay, x11_display->no_focus_window);
/* Done with no_focus_window stuff */
meta_x11_display_init_events (x11_display);
set_wm_icon_size_hint (x11_display);
set_supported_hint (x11_display);
set_wm_check_hint (x11_display);
set_desktop_viewport_hint (x11_display);
set_desktop_geometry_hint (x11_display);
x11_display->x11_stack = meta_x11_stack_new (x11_display);
x11_display->keys_grabbed = FALSE;
meta_x11_display_grab_keys (x11_display);
meta_x11_display_update_workspace_layout (x11_display);
if (meta_prefs_get_dynamic_workspaces ())
{
int num = 0;
int n_items = 0;
uint32_t *list = NULL;
if (meta_prop_get_cardinal_list (x11_display,
x11_display->xroot,
x11_display->atom__NET_NUMBER_OF_DESKTOPS,
&list, &n_items))
{
num = list[0];
g_free (list);
}
if (num >
meta_workspace_manager_get_n_workspaces (display->workspace_manager))
{
meta_workspace_manager_update_num_workspaces (
display->workspace_manager, timestamp, num);
}
}
g_signal_connect_object (display->workspace_manager, "active-workspace-changed",
G_CALLBACK (set_active_workspace_hint),
x11_display, 0);
set_number_of_spaces_hint (display->workspace_manager, NULL, x11_display);
g_signal_connect_object (display->workspace_manager, "notify::n-workspaces",
G_CALLBACK (set_number_of_spaces_hint),
x11_display, 0);
set_showing_desktop_hint (display->workspace_manager, x11_display);
g_signal_connect_object (display->workspace_manager, "showing-desktop-changed",
G_CALLBACK (set_showing_desktop_hint),
x11_display, 0);
set_workspace_names (x11_display);
meta_prefs_add_listener (prefs_changed_callback, x11_display);
set_work_area_hint (display, x11_display);
g_signal_connect_object (display, "workareas-changed",
G_CALLBACK (set_work_area_hint),
x11_display, 0);
init_x11_bell (x11_display);
meta_x11_startup_notification_init (x11_display);
meta_x11_selection_init (x11_display);
#ifdef HAVE_X11
if (!meta_is_wayland_compositor ())
meta_dnd_init_xdnd (x11_display);
#endif
sprintf (buf, "WM_S%d", number);
wm_sn_atom = XInternAtom (xdisplay, buf, False);
new_wm_sn_owner = take_manager_selection (x11_display,
xroot,
wm_sn_atom,
timestamp,
replace_current_wm);
if (new_wm_sn_owner == None)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to acquire window manager ownership");
g_object_run_dispose (G_OBJECT (x11_display));
return NULL;
}
x11_display->wm_sn_selection_window = new_wm_sn_owner;
x11_display->wm_sn_atom = wm_sn_atom;
x11_display->wm_sn_timestamp = timestamp;
g_snprintf (buf, sizeof (buf), "_NET_WM_CM_S%d", number);
wm_cm_atom = XInternAtom (x11_display->xdisplay, buf, False);
x11_display->wm_cm_selection_window =
take_manager_selection (x11_display, xroot, wm_cm_atom, timestamp,
replace_current_wm);
if (x11_display->wm_cm_selection_window == None)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to acquire compositor ownership");
g_object_run_dispose (G_OBJECT (x11_display));
return NULL;
}
init_event_masks (x11_display);
meta_x11_display_init_frames_client (x11_display);
return g_steal_pointer (&x11_display);
}
void
meta_x11_display_restore_active_workspace (MetaX11Display *x11_display)
{
MetaDisplay *display;
MetaWorkspace *current_workspace;
uint32_t current_workspace_index = 0;
guint32 timestamp;
g_return_if_fail (META_IS_X11_DISPLAY (x11_display));
display = x11_display->display;
timestamp = x11_display->timestamp;
/* Get current workspace */
if (meta_prop_get_cardinal (x11_display,
x11_display->xroot,
x11_display->atom__NET_CURRENT_DESKTOP,
¤t_workspace_index))
{
meta_verbose ("Read existing _NET_CURRENT_DESKTOP = %d",
(int) current_workspace_index);
/* Switch to the _NET_CURRENT_DESKTOP workspace */
current_workspace = meta_workspace_manager_get_workspace_by_index (display->workspace_manager,
current_workspace_index);
if (current_workspace != NULL)
meta_workspace_activate (current_workspace, timestamp);
}
else
{
meta_verbose ("No _NET_CURRENT_DESKTOP present");
}
set_active_workspace_hint (display->workspace_manager, x11_display);
}
int
meta_x11_display_get_screen_number (MetaX11Display *x11_display)
{
return DefaultScreen (x11_display->xdisplay);
}
MetaDisplay *
meta_x11_display_get_display (MetaX11Display *x11_display)
{
return x11_display->display;
}
/**
* meta_x11_display_get_xdisplay: (skip)
* @x11_display: a #MetaX11Display
*
*/
Display *
meta_x11_display_get_xdisplay (MetaX11Display *x11_display)
{
return x11_display->xdisplay;
}
/**
* meta_x11_display_get_xroot: (skip)
* @x11_display: A #MetaX11Display
*
*/
Window
meta_x11_display_get_xroot (MetaX11Display *x11_display)
{
return x11_display->xroot;
}
int
meta_x11_display_get_damage_event_base (MetaX11Display *x11_display)
{
return x11_display->damage_event_base;
}
Window
meta_x11_display_create_offscreen_window (MetaX11Display *x11_display,
Window parent,
long valuemask)
{
XSetWindowAttributes attrs;
/* we want to be override redirect because sometimes we
* create a window on a screen we aren't managing.
* (but on a display we are managing at least one screen for)
*/
attrs.override_redirect = True;
attrs.event_mask = valuemask;
return XCreateWindow (x11_display->xdisplay,
parent,
-100, -100, 1, 1,
0,
CopyFromParent,
CopyFromParent,
(Visual *)CopyFromParent,
CWOverrideRedirect | CWEventMask,
&attrs);
}
Cursor
meta_x11_display_create_x_cursor (MetaX11Display *x11_display,
MetaCursor cursor)
{
return meta_create_x_cursor (x11_display->xdisplay, cursor);
}
static char *
get_screen_name (Display *xdisplay,
int number)
{
char *p;
char *dname;
char *scr;
/* DisplayString gives us a sort of canonical display,
* vs. the user-entered name from XDisplayName()
*/
dname = g_strdup (DisplayString (xdisplay));
/* Change display name to specify this screen.
*/
p = strrchr (dname, ':');
if (p)
{
p = strchr (p, '.');
if (p)
*p = '\0';
}
scr = g_strdup_printf ("%s.%d", dname, number);
g_free (dname);
return scr;
}
void
meta_x11_display_reload_cursor (MetaX11Display *x11_display)
{
Cursor xcursor;
MetaCursor cursor = x11_display->display->current_cursor;
/* Set a cursor for X11 applications that don't specify their own */
xcursor = meta_x11_display_create_x_cursor (x11_display, cursor);
XDefineCursor (x11_display->xdisplay, x11_display->xroot, xcursor);
XFlush (x11_display->xdisplay);
if (xcursor)
XFreeCursor (x11_display->xdisplay, xcursor);
}
static void
set_cursor_theme (Display *xdisplay,
MetaBackend *backend)
{
MetaSettings *settings = meta_backend_get_settings (backend);
int scale;
scale = meta_settings_get_ui_scaling_factor (settings);
XcursorSetTheme (xdisplay, meta_prefs_get_cursor_theme ());
XcursorSetDefaultSize (xdisplay,
meta_prefs_get_cursor_size () * scale);
}
static void
meta_x11_display_remove_cursor_later (MetaX11Display *x11_display)
{
if (x11_display->reload_x11_cursor_later)
{
MetaDisplay *display = x11_display->display;
MetaLaters *laters = meta_compositor_get_laters (display->compositor);
meta_laters_remove (laters, x11_display->reload_x11_cursor_later);
x11_display->reload_x11_cursor_later = 0;
}
}
static gboolean
reload_x11_cursor_later (gpointer user_data)
{
MetaX11Display *x11_display = user_data;
x11_display->reload_x11_cursor_later = 0;
meta_x11_display_reload_cursor (x11_display);
return G_SOURCE_REMOVE;
}
static void
schedule_reload_x11_cursor (MetaX11Display *x11_display)
{
MetaDisplay *display = x11_display->display;
MetaLaters *laters = meta_compositor_get_laters (display->compositor);
if (x11_display->reload_x11_cursor_later)
return;
x11_display->reload_x11_cursor_later =
meta_laters_add (laters, META_LATER_BEFORE_REDRAW,
reload_x11_cursor_later,
x11_display,
NULL);
}
static void
update_cursor_theme (MetaX11Display *x11_display)
{
MetaBackend *backend = backend_from_x11_display (x11_display);
set_cursor_theme (x11_display->xdisplay, backend);
schedule_reload_x11_cursor (x11_display);
if (META_IS_BACKEND_X11 (backend))
{
MetaBackendX11 *backend_x11 = META_BACKEND_X11 (backend);
Display *xdisplay = meta_backend_x11_get_xdisplay (backend_x11);
set_cursor_theme (xdisplay, backend);
meta_backend_x11_reload_cursor (backend_x11);
}
}
MetaWindow *
meta_x11_display_lookup_x_window (MetaX11Display *x11_display,
Window xwindow)
{
return g_hash_table_lookup (x11_display->xids, &xwindow);
}
void
meta_x11_display_register_x_window (MetaX11Display *x11_display,
Window *xwindowp,
MetaWindow *window)
{
g_return_if_fail (g_hash_table_lookup (x11_display->xids, xwindowp) == NULL);
g_hash_table_insert (x11_display->xids, xwindowp, window);
}
void
meta_x11_display_unregister_x_window (MetaX11Display *x11_display,
Window xwindow)
{
g_return_if_fail (g_hash_table_lookup (x11_display->xids, &xwindow) != NULL);
g_hash_table_remove (x11_display->xids, &xwindow);
}
MetaSyncCounter *
meta_x11_display_lookup_sync_alarm (MetaX11Display *x11_display,
XSyncAlarm alarm)
{
return g_hash_table_lookup (x11_display->alarms, &alarm);
}
void
meta_x11_display_register_sync_alarm (MetaX11Display *x11_display,
XSyncAlarm *alarmp,
MetaSyncCounter *sync_counter)
{
g_return_if_fail (g_hash_table_lookup (x11_display->alarms, alarmp) == NULL);
g_hash_table_insert (x11_display->alarms, alarmp, sync_counter);
}
void
meta_x11_display_unregister_sync_alarm (MetaX11Display *x11_display,
XSyncAlarm alarm)
{
g_return_if_fail (g_hash_table_lookup (x11_display->alarms, &alarm) != NULL);
g_hash_table_remove (x11_display->alarms, &alarm);
}
MetaX11AlarmFilter *
meta_x11_display_add_alarm_filter (MetaX11Display *x11_display,
MetaAlarmFilter filter,
gpointer user_data)
{
MetaX11AlarmFilter *alarm_filter;
if (!x11_display->alarm_filters)
x11_display->alarm_filters = g_ptr_array_new_with_free_func (g_free);
alarm_filter = g_new0 (MetaX11AlarmFilter, 1);
alarm_filter->filter = filter;
alarm_filter->user_data = user_data;
g_ptr_array_add (x11_display->alarm_filters, alarm_filter);
return alarm_filter;
}
void
meta_x11_display_remove_alarm_filter (MetaX11Display *x11_display,
MetaX11AlarmFilter *alarm_filter)
{
g_ptr_array_remove (x11_display->alarm_filters, alarm_filter);
}
/* The guard window allows us to leave minimized windows mapped so
* that compositor code may provide live previews of them.
* Instead of being unmapped/withdrawn, they get pushed underneath
* the guard window. We also select events on the guard window, which
* should effectively be forwarded to events on the background actor,
* providing that the scene graph is set up correctly.
*/
static Window
create_guard_window (MetaX11Display *x11_display)
{
XSetWindowAttributes attributes;
Window guard_window;
gulong create_serial;
int display_width, display_height;
meta_display_get_size (x11_display->display,
&display_width,
&display_height);
attributes.event_mask = NoEventMask;
attributes.override_redirect = True;
/* We have to call record_add() after we have the new window ID,
* so save the serial for the CreateWindow request until then */
create_serial = XNextRequest (x11_display->xdisplay);
guard_window =
XCreateWindow (x11_display->xdisplay,
x11_display->xroot,
0, /* x */
0, /* y */
display_width,
display_height,
0, /* border width */
0, /* depth */
InputOnly, /* class */
CopyFromParent, /* visual */
CWEventMask | CWOverrideRedirect,
&attributes);
/* https://bugzilla.gnome.org/show_bug.cgi?id=710346 */
XStoreName (x11_display->xdisplay, guard_window, "mutter guard window");
{
if (!meta_is_wayland_compositor ())
{
MetaBackendX11 *backend =
META_BACKEND_X11 (backend_from_x11_display (x11_display));
Display *backend_xdisplay = meta_backend_x11_get_xdisplay (backend);
unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
XISetMask (mask.mask, XI_ButtonPress);
XISetMask (mask.mask, XI_ButtonRelease);
XISetMask (mask.mask, XI_Motion);
/* Sync on the connection we created the window on to
* make sure it's created before we select on it on the
* backend connection. */
XSync (x11_display->xdisplay, False);
XISelectEvents (backend_xdisplay, guard_window, &mask, 1);
}
}
meta_stack_tracker_record_add (x11_display->display->stack_tracker,
guard_window,
create_serial);
meta_stack_tracker_lower (x11_display->display->stack_tracker,
guard_window);
XMapWindow (x11_display->xdisplay, guard_window);
return guard_window;
}
void
meta_x11_display_create_guard_window (MetaX11Display *x11_display)
{
if (x11_display->guard_window == None)
x11_display->guard_window = create_guard_window (x11_display);
}
static void
on_monitors_changed_internal (MetaMonitorManager *monitor_manager,
MetaX11Display *x11_display)
{
int display_width, display_height;
meta_monitor_manager_get_screen_size (monitor_manager,
&display_width,
&display_height);
set_desktop_geometry_hint (x11_display);
/* Resize the guard window to fill the screen again. */
if (x11_display->guard_window != None)
{
XWindowChanges changes;
changes.x = 0;
changes.y = 0;
changes.width = display_width;
changes.height = display_height;
XConfigureWindow (x11_display->xdisplay,
x11_display->guard_window,
CWX | CWY | CWWidth | CWHeight,
&changes);
}
x11_display->has_xinerama_indices = FALSE;
}
static Bool
find_timestamp_predicate (Display *xdisplay,
XEvent *ev,
XPointer arg)
{
MetaX11Display *x11_display = (MetaX11Display *) arg;
return (ev->type == PropertyNotify &&
ev->xproperty.atom == x11_display->atom__MUTTER_TIMESTAMP_PING);
}
/* Get a timestamp, even if it means a roundtrip */
guint32
meta_x11_display_get_current_time_roundtrip (MetaX11Display *x11_display)
{
guint32 timestamp;
timestamp = meta_display_get_current_time (x11_display->display);
if (timestamp == META_CURRENT_TIME)
{
XEvent property_event;
XChangeProperty (x11_display->xdisplay,
x11_display->timestamp_pinging_window,
x11_display->atom__MUTTER_TIMESTAMP_PING,
XA_STRING, 8, PropModeAppend, NULL, 0);
XIfEvent (x11_display->xdisplay,
&property_event,
find_timestamp_predicate,
(XPointer) x11_display);
timestamp = property_event.xproperty.time;
}
meta_display_sanity_check_timestamps (x11_display->display, timestamp);
return timestamp;
}
/**
* meta_x11_display_xwindow_is_a_no_focus_window:
* @x11_display: A #MetaX11Display
* @xwindow: An X11 window
*
* Returns: %TRUE iff window is one of mutter's internal "no focus" windows
* which will have the focus when there is no actual client window focused.
*/
gboolean
meta_x11_display_xwindow_is_a_no_focus_window (MetaX11Display *x11_display,
Window xwindow)
{
return xwindow == x11_display->no_focus_window;
}
void
meta_x11_display_increment_event_serial (MetaX11Display *x11_display)
{
/* We just make some random X request */
XDeleteProperty (x11_display->xdisplay,
x11_display->leader_window,
x11_display->atom__MOTIF_WM_HINTS);
}
static void
meta_x11_display_update_active_window_hint (MetaX11Display *x11_display)
{
MetaWindow *focus_window;
gulong data[1];
if (x11_display->display->closing)
return; /* Leave old value for a replacement */
focus_window = meta_x11_display_lookup_x_window (x11_display,
x11_display->focus_xwindow);
if (focus_window)
data[0] = focus_window->xwindow;
else
data[0] = None;
meta_x11_error_trap_push (x11_display);
XChangeProperty (x11_display->xdisplay,
x11_display->xroot,
x11_display->atom__NET_ACTIVE_WINDOW,
XA_WINDOW,
32, PropModeReplace, (guchar*) data, 1);
meta_x11_error_trap_pop (x11_display);
}
void
meta_x11_display_update_focus_window (MetaX11Display *x11_display,
Window xwindow,
gulong serial,
gboolean focused_by_us)
{
x11_display->focus_serial = serial;
x11_display->focused_by_us = !!focused_by_us;
if (x11_display->focus_xwindow == xwindow)
return;
meta_topic (META_DEBUG_FOCUS, "Updating X11 focus window from 0x%lx to 0x%lx",
x11_display->focus_xwindow, xwindow);
x11_display->focus_xwindow = xwindow;
meta_x11_display_update_active_window_hint (x11_display);
}
static void
meta_x11_display_set_input_focus_internal (MetaX11Display *x11_display,
Window xwindow,
uint32_t timestamp)
{
meta_x11_error_trap_push (x11_display);
/* In order for mutter to know that the focus request succeeded, we track
* the serial of the "focus request" we made, but if we take the serial
* of the XSetInputFocus request, then there's no way to determine the
* difference between focus events as a result of the SetInputFocus and
* focus events that other clients send around the same time. Ensure that
* we know which is which by making two requests that the server will
* process at the same time.
*/
XGrabServer (x11_display->xdisplay);
XSetInputFocus (x11_display->xdisplay,
xwindow,
RevertToPointerRoot,
timestamp);
XChangeProperty (x11_display->xdisplay,
x11_display->timestamp_pinging_window,
x11_display->atom__MUTTER_FOCUS_SET,
XA_STRING, 8, PropModeAppend, NULL, 0);
XUngrabServer (x11_display->xdisplay);
XFlush (x11_display->xdisplay);
meta_x11_error_trap_pop (x11_display);
}
void
meta_x11_display_set_input_focus (MetaX11Display *x11_display,
MetaWindow *window,
gboolean focus_frame,
uint32_t timestamp)
{
Window xwindow;
gulong serial;
if (window)
xwindow = focus_frame ? window->frame->xwindow : window->xwindow;
else
xwindow = x11_display->no_focus_window;
meta_topic (META_DEBUG_FOCUS, "Setting X11 input focus for window %s to 0x%lx",
window ? window->desc : "none", xwindow);
meta_x11_error_trap_push (x11_display);
meta_x11_display_set_input_focus_internal (x11_display, xwindow, timestamp);
serial = XNextRequest (x11_display->xdisplay);
meta_x11_display_update_focus_window (x11_display, xwindow, serial, TRUE);
meta_x11_error_trap_pop (x11_display);
}
void
meta_x11_display_set_input_focus_xwindow (MetaX11Display *x11_display,
Window window,
guint32 timestamp)
{
gulong serial;
if (meta_display_timestamp_too_old (x11_display->display, ×tamp))
return;
meta_topic (META_DEBUG_FOCUS, "Setting X11 input focus to 0x%lx", window);
meta_x11_display_set_input_focus_internal (x11_display, window, timestamp);
serial = XNextRequest (x11_display->xdisplay);
meta_x11_display_update_focus_window (x11_display, window, serial, TRUE);
meta_display_update_focus_window (x11_display->display, NULL);
meta_display_remove_autoraise_callback (x11_display->display);
x11_display->display->last_focus_time = timestamp;
}
static MetaX11DisplayLogicalMonitorData *
get_x11_display_logical_monitor_data (MetaLogicalMonitor *logical_monitor)
{
return g_object_get_qdata (G_OBJECT (logical_monitor),
quark_x11_display_logical_monitor_data);
}
static MetaX11DisplayLogicalMonitorData *
ensure_x11_display_logical_monitor_data (MetaLogicalMonitor *logical_monitor)
{
MetaX11DisplayLogicalMonitorData *data;
data = get_x11_display_logical_monitor_data (logical_monitor);
if (data)
return data;
data = g_new0 (MetaX11DisplayLogicalMonitorData, 1);
g_object_set_qdata_full (G_OBJECT (logical_monitor),
quark_x11_display_logical_monitor_data,
data,
g_free);
return data;
}
static void
meta_x11_display_ensure_xinerama_indices (MetaX11Display *x11_display)
{
MetaBackend *backend = backend_from_x11_display (x11_display);
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
GList *logical_monitors, *l;
XineramaScreenInfo *infos;
int n_infos, j;
if (x11_display->has_xinerama_indices)
return;
x11_display->has_xinerama_indices = TRUE;
if (!XineramaIsActive (x11_display->xdisplay))
return;
infos = XineramaQueryScreens (x11_display->xdisplay,
&n_infos);
if (n_infos <= 0 || infos == NULL)
{
meta_XFree (infos);
return;
}
logical_monitors =
meta_monitor_manager_get_logical_monitors (monitor_manager);
for (l = logical_monitors; l; l = l->next)
{
MetaLogicalMonitor *logical_monitor = l->data;
for (j = 0; j < n_infos; ++j)
{
if (logical_monitor->rect.x == infos[j].x_org &&
logical_monitor->rect.y == infos[j].y_org &&
logical_monitor->rect.width == infos[j].width &&
logical_monitor->rect.height == infos[j].height)
{
MetaX11DisplayLogicalMonitorData *logical_monitor_data;
logical_monitor_data =
ensure_x11_display_logical_monitor_data (logical_monitor);
logical_monitor_data->xinerama_index = j;
}
}
}
meta_XFree (infos);
}
int
meta_x11_display_logical_monitor_to_xinerama_index (MetaX11Display *x11_display,
MetaLogicalMonitor *logical_monitor)
{
MetaX11DisplayLogicalMonitorData *logical_monitor_data;
g_return_val_if_fail (logical_monitor, -1);
meta_x11_display_ensure_xinerama_indices (x11_display);
logical_monitor_data = get_x11_display_logical_monitor_data (logical_monitor);
return logical_monitor_data->xinerama_index;
}
MetaLogicalMonitor *
meta_x11_display_xinerama_index_to_logical_monitor (MetaX11Display *x11_display,
int xinerama_index)
{
MetaBackend *backend = backend_from_x11_display (x11_display);
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
GList *logical_monitors, *l;
meta_x11_display_ensure_xinerama_indices (x11_display);
logical_monitors =
meta_monitor_manager_get_logical_monitors (monitor_manager);
for (l = logical_monitors; l; l = l->next)
{
MetaLogicalMonitor *logical_monitor = l->data;
MetaX11DisplayLogicalMonitorData *logical_monitor_data;
logical_monitor_data =
ensure_x11_display_logical_monitor_data (logical_monitor);
if (logical_monitor_data->xinerama_index == xinerama_index)
return logical_monitor;
}
return NULL;
}
void
meta_x11_display_update_workspace_names (MetaX11Display *x11_display)
{
char **names;
int n_names;
int i;
/* this updates names in prefs when the root window property changes,
* iff the new property contents don't match what's already in prefs
*/
names = NULL;
n_names = 0;
if (!meta_prop_get_utf8_list (x11_display,
x11_display->xroot,
x11_display->atom__NET_DESKTOP_NAMES,
&names, &n_names))
{
meta_verbose ("Failed to get workspace names from root window");
return;
}
i = 0;
while (i < n_names)
{
meta_topic (META_DEBUG_PREFS,
"Setting workspace %d name to \"%s\" due to _NET_DESKTOP_NAMES change",
i, names[i] ? names[i] : "null");
meta_prefs_change_workspace_name (i, names[i]);
++i;
}
g_strfreev (names);
}
#define _NET_WM_ORIENTATION_HORZ 0
#define _NET_WM_ORIENTATION_VERT 1
#define _NET_WM_TOPLEFT 0
#define _NET_WM_TOPRIGHT 1
#define _NET_WM_BOTTOMRIGHT 2
#define _NET_WM_BOTTOMLEFT 3
void
meta_x11_display_update_workspace_layout (MetaX11Display *x11_display)
{
MetaWorkspaceManager *workspace_manager = x11_display->display->workspace_manager;
gboolean vertical_layout = FALSE;
int n_rows = 1;
int n_columns = -1;
MetaDisplayCorner starting_corner = META_DISPLAY_TOPLEFT;
uint32_t *list;
int n_items;
if (workspace_manager->workspace_layout_overridden)
return;
list = NULL;
n_items = 0;
if (meta_prop_get_cardinal_list (x11_display,
x11_display->xroot,
x11_display->atom__NET_DESKTOP_LAYOUT,
&list, &n_items))
{
if (n_items == 3 || n_items == 4)
{
int cols, rows;
switch (list[0])
{
case _NET_WM_ORIENTATION_HORZ:
vertical_layout = FALSE;
break;
case _NET_WM_ORIENTATION_VERT:
vertical_layout = TRUE;
break;
default:
meta_warning ("Someone set a weird orientation in _NET_DESKTOP_LAYOUT");
break;
}
cols = list[1];
rows = list[2];
if (rows <= 0 && cols <= 0)
{
meta_warning ("Columns = %d rows = %d in _NET_DESKTOP_LAYOUT makes no sense", rows, cols);
}
else
{
if (rows > 0)
n_rows = rows;
else
n_rows = -1;
if (cols > 0)
n_columns = cols;
else
n_columns = -1;
}
if (n_items == 4)
{
switch (list[3])
{
case _NET_WM_TOPLEFT:
starting_corner = META_DISPLAY_TOPLEFT;
break;
case _NET_WM_TOPRIGHT:
starting_corner = META_DISPLAY_TOPRIGHT;
break;
case _NET_WM_BOTTOMRIGHT:
starting_corner = META_DISPLAY_BOTTOMRIGHT;
break;
case _NET_WM_BOTTOMLEFT:
starting_corner = META_DISPLAY_BOTTOMLEFT;
break;
default:
meta_warning ("Someone set a weird starting corner in _NET_DESKTOP_LAYOUT");
break;
}
}
}
else
{
meta_warning ("Someone set _NET_DESKTOP_LAYOUT to %d integers instead of 4 "
"(3 is accepted for backwards compat)", n_items);
}
g_free (list);
meta_workspace_manager_update_workspace_layout (workspace_manager,
starting_corner,
vertical_layout,
n_rows,
n_columns);
}
}
static void
prefs_changed_callback (MetaPreference pref,
void *data)
{
MetaX11Display *x11_display = data;
if (pref == META_PREF_WORKSPACE_NAMES)
{
set_workspace_names (x11_display);
}
}
void
meta_x11_display_increment_focus_sentinel (MetaX11Display *x11_display)
{
unsigned long data[1];
data[0] = meta_display_get_current_time (x11_display->display);
XChangeProperty (x11_display->xdisplay,
x11_display->xroot,
x11_display->atom__MUTTER_SENTINEL,
XA_CARDINAL,
32, PropModeReplace, (guchar*) data, 1);
x11_display->sentinel_counter += 1;
}
void
meta_x11_display_decrement_focus_sentinel (MetaX11Display *x11_display)
{
x11_display->sentinel_counter -= 1;
if (x11_display->sentinel_counter < 0)
x11_display->sentinel_counter = 0;
}
gboolean
meta_x11_display_focus_sentinel_clear (MetaX11Display *x11_display)
{
return (x11_display->sentinel_counter == 0);
}
static void
meta_x11_display_add_ignored_crossing_serial (MetaX11Display *x11_display,
unsigned long serial)
{
int i;
/* don't add the same serial more than once */
if (serial ==
x11_display->ignored_crossing_serials[N_IGNORED_CROSSING_SERIALS - 1])
return;
/* shift serials to the left */
i = 0;
while (i < (N_IGNORED_CROSSING_SERIALS - 1))
{
x11_display->ignored_crossing_serials[i] =
x11_display->ignored_crossing_serials[i + 1];
++i;
}
/* put new one on the end */
x11_display->ignored_crossing_serials[i] = serial;
}
void
meta_x11_display_set_stage_input_region (MetaX11Display *x11_display,
XserverRegion region)
{
Display *xdisplay = x11_display->xdisplay;
MetaBackend *backend = backend_from_x11_display (x11_display);
ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
Window stage_xwindow;
g_return_if_fail (!meta_is_wayland_compositor ());
stage_xwindow = meta_x11_get_stage_window (stage);
XFixesSetWindowShapeRegion (xdisplay, stage_xwindow,
ShapeInput, 0, 0, region);
/*
* It's generally a good heuristic that when a crossing event is generated
* because we reshape the overlay, we don't want it to affect
* focus-follows-mouse focus - it's not the user doing something, it's the
* environment changing under the user.
*/
meta_x11_display_add_ignored_crossing_serial (x11_display,
XNextRequest (xdisplay));
XFixesSetWindowShapeRegion (xdisplay,
x11_display->composite_overlay_window,
ShapeInput, 0, 0, region);
}
void
meta_x11_display_clear_stage_input_region (MetaX11Display *x11_display)
{
if (x11_display->empty_region == None)
{
x11_display->empty_region = XFixesCreateRegion (x11_display->xdisplay,
NULL, 0);
}
meta_x11_display_set_stage_input_region (x11_display,
x11_display->empty_region);
}
/**
* meta_x11_display_add_event_func: (skip):
**/
unsigned int
meta_x11_display_add_event_func (MetaX11Display *x11_display,
MetaX11DisplayEventFunc event_func,
gpointer user_data,
GDestroyNotify destroy_notify)
{
MetaX11EventFilter *filter;
static unsigned int id = 0;
filter = g_new0 (MetaX11EventFilter, 1);
filter->func = event_func;
filter->user_data = user_data;
filter->destroy_notify = destroy_notify;
filter->id = ++id;
x11_display->event_funcs = g_list_prepend (x11_display->event_funcs, filter);
return filter->id;
}
/**
* meta_x11_display_remove_event_func: (skip):
**/
void
meta_x11_display_remove_event_func (MetaX11Display *x11_display,
unsigned int id)
{
MetaX11EventFilter *filter;
GList *l;
for (l = x11_display->event_funcs; l; l = l->next)
{
filter = l->data;
if (filter->id != id)
continue;
x11_display->event_funcs =
g_list_delete_link (x11_display->event_funcs, l);
meta_x11_event_filter_free (filter);
break;
}
}
void
meta_x11_display_run_event_funcs (MetaX11Display *x11_display,
XEvent *xevent)
{
MetaX11EventFilter *filter;
GList *next, *l = x11_display->event_funcs;
while (l)
{
filter = l->data;
next = l->next;
filter->func (x11_display, xevent, filter->user_data);
l = next;
}
}
void
meta_x11_display_redirect_windows (MetaX11Display *x11_display,
MetaDisplay *display)
{
MetaContext *context = meta_display_get_context (display);
Display *xdisplay = meta_x11_display_get_xdisplay (x11_display);
Window xroot = meta_x11_display_get_xroot (x11_display);
int screen_number = meta_x11_display_get_screen_number (x11_display);
guint n_retries;
guint max_retries;
if (meta_context_is_replacing (context))
max_retries = 5;
else
max_retries = 1;
n_retries = 0;
/* Some compositors (like old versions of Mutter) might not properly unredirect
* subwindows before destroying the WM selection window; so we wait a while
* for such a compositor to exit before giving up.
*/
while (TRUE)
{
meta_x11_error_trap_push (x11_display);
XCompositeRedirectSubwindows (xdisplay, xroot, CompositeRedirectManual);
XSync (xdisplay, FALSE);
if (!meta_x11_error_trap_pop_with_return (x11_display))
break;
if (n_retries == max_retries)
{
/* This probably means that a non-WM compositor like xcompmgr is running;
* we have no way to get it to exit */
meta_fatal (_("Another compositing manager is already running on screen %i on display “%s”."),
screen_number, x11_display->name);
}
n_retries++;
g_usleep (G_USEC_PER_SEC);
}
}