/* -*- 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 .
*/
/**
* SECTION:display
* @title: MetaDisplay
* @short_description: Mutter display representation
*
* The display is represented as a #MetaDisplay struct.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "backends/meta-backend-private.h"
#include "backends/meta-cursor-sprite-xcursor.h"
#include "backends/meta-cursor-tracker-private.h"
#include "backends/meta-input-device-private.h"
#include "backends/meta-input-mapper-private.h"
#include "backends/meta-stage-private.h"
#include "backends/x11/meta-backend-x11.h"
#include "backends/x11/meta-event-x11.h"
#include "backends/x11/cm/meta-backend-x11-cm.h"
#include "backends/x11/nested/meta-backend-x11-nested.h"
#include "clutter/x11/clutter-x11.h"
#include "compositor/compositor-private.h"
#include "compositor/meta-compositor-x11.h"
#include "cogl/cogl.h"
#include "core/bell.h"
#include "core/boxes-private.h"
#include "core/display-private.h"
#include "core/events.h"
#include "core/frame.h"
#include "core/keybindings-private.h"
#include "core/meta-clipboard-manager.h"
#include "core/meta-workspace-manager-private.h"
#include "core/util-private.h"
#include "core/window-private.h"
#include "core/workspace-private.h"
#include "meta/compositor-mutter.h"
#include "meta/compositor.h"
#include "meta/main.h"
#include "meta/meta-backend.h"
#include "meta/meta-enum-types.h"
#include "meta/meta-sound-player.h"
#include "meta/meta-x11-errors.h"
#include "meta/prefs.h"
#include "x11/meta-startup-notification-x11.h"
#include "x11/meta-x11-display-private.h"
#include "x11/window-x11.h"
#include "x11/xprops.h"
#ifdef HAVE_WAYLAND
#include "compositor/meta-compositor-native.h"
#include "compositor/meta-compositor-server.h"
#include "wayland/meta-xwayland-private.h"
#include "wayland/meta-wayland-tablet-seat.h"
#include "wayland/meta-wayland-tablet-pad.h"
#endif
#ifdef HAVE_NATIVE_BACKEND
#include "backends/native/meta-backend-native.h"
#endif
/*
* SECTION:pings
*
* Sometimes we want to see whether a window is responding,
* so we send it a "ping" message and see whether it sends us back a "pong"
* message within a reasonable time. Here we have a system which lets us
* nominate one function to be called if we get the pong in time and another
* function if we don't. The system is rather more complicated than it needs
* to be, since we only ever use it to destroy windows which are asked to
* close themselves and don't do so within a reasonable amount of time, and
* therefore we always use the same callbacks. It's possible that we might
* use it for other things in future, or on the other hand we might decide
* that we're never going to do so and simplify it a bit.
*/
/**
* MetaPingData:
*
* Describes a ping on a window. When we send a ping to a window, we build
* one of these structs, and it eventually gets passed to the timeout function
* or to the function which handles the response from the window. If the window
* does or doesn't respond to the ping, we use this information to deal with
* these facts; we have a handler function for each.
*/
typedef struct
{
MetaWindow *window;
guint32 serial;
guint ping_timeout_id;
} MetaPingData;
typedef struct _MetaDisplayPrivate
{
MetaContext *context;
} MetaDisplayPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (MetaDisplay, meta_display, G_TYPE_OBJECT)
/* Signals */
enum
{
CURSOR_UPDATED,
X11_DISPLAY_SETUP,
X11_DISPLAY_OPENED,
X11_DISPLAY_CLOSING,
OVERLAY_KEY,
ACCELERATOR_ACTIVATED,
MODIFIERS_ACCELERATOR_ACTIVATED,
FOCUS_WINDOW,
WINDOW_CREATED,
WINDOW_DEMANDS_ATTENTION,
WINDOW_MARKED_URGENT,
GRAB_OP_BEGIN,
GRAB_OP_END,
SHOW_RESTART_MESSAGE,
RESTART,
SHOW_RESIZE_POPUP,
GL_VIDEO_MEMORY_PURGED,
SHOW_PAD_OSD,
SHOW_OSD,
PAD_MODE_SWITCH,
WINDOW_ENTERED_MONITOR,
WINDOW_LEFT_MONITOR,
WORKSPACE_ADDED,
WORKSPACE_REMOVED,
WORKSPACE_SWITCHED,
ACTIVE_WORKSPACE_CHANGED,
IN_FULLSCREEN_CHANGED,
SHOWING_DESKTOP_CHANGED,
RESTACKED,
WORKAREAS_CHANGED,
CLOSING,
INIT_XSERVER,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_COMPOSITOR_MODIFIERS,
PROP_FOCUS_WINDOW
};
static guint display_signals [LAST_SIGNAL] = { 0 };
#define META_GRAB_OP_GET_BASE_TYPE(op) (op & 0x00FF)
/*
* The display we're managing. This is a singleton object. (Historically,
* this was a list of displays, but there was never any way to add more
* than one element to it.) The goofy name is because we don't want it
* to shadow the parameter in its object methods.
*/
static MetaDisplay *the_display = NULL;
static void on_monitors_changed_internal (MetaMonitorManager *monitor_manager,
MetaDisplay *display);
static void prefs_changed_callback (MetaPreference pref,
void *data);
static int mru_cmp (gconstpointer a,
gconstpointer b);
static void
meta_display_get_property(GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaDisplay *display = META_DISPLAY (object);
switch (prop_id)
{
case PROP_COMPOSITOR_MODIFIERS:
g_value_set_flags (value, meta_display_get_compositor_modifiers (display));
break;
case PROP_FOCUS_WINDOW:
g_value_set_object (value, display->focus_window);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_display_set_property(GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_display_class_init (MetaDisplayClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = meta_display_get_property;
object_class->set_property = meta_display_set_property;
display_signals[CURSOR_UPDATED] =
g_signal_new ("cursor-updated",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
display_signals[X11_DISPLAY_SETUP] =
g_signal_new ("x11-display-setup",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
display_signals[X11_DISPLAY_OPENED] =
g_signal_new ("x11-display-opened",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
display_signals[X11_DISPLAY_CLOSING] =
g_signal_new ("x11-display-closing",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
display_signals[OVERLAY_KEY] =
g_signal_new ("overlay-key",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
display_signals[ACCELERATOR_ACTIVATED] =
g_signal_new ("accelerator-activated",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 3, G_TYPE_UINT, CLUTTER_TYPE_INPUT_DEVICE, G_TYPE_UINT);
/**
* MetaDisplay::modifiers-accelerator-activated:
* @display: the #MetaDisplay instance
*
* The ::modifiers-accelerator-activated signal will be emitted when
* a special modifiers-only keybinding is activated.
*
* Returns: %TRUE means that the keyboard device should remain
* frozen and %FALSE for the default behavior of unfreezing the
* keyboard.
*/
display_signals[MODIFIERS_ACCELERATOR_ACTIVATED] =
g_signal_new ("modifiers-accelerator-activated",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
g_signal_accumulator_first_wins, NULL, NULL,
G_TYPE_BOOLEAN, 0);
display_signals[WINDOW_CREATED] =
g_signal_new ("window-created",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 1, META_TYPE_WINDOW);
display_signals[WINDOW_DEMANDS_ATTENTION] =
g_signal_new ("window-demands-attention",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 1, META_TYPE_WINDOW);
display_signals[WINDOW_MARKED_URGENT] =
g_signal_new ("window-marked-urgent",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 1,
META_TYPE_WINDOW);
display_signals[GRAB_OP_BEGIN] =
g_signal_new ("grab-op-begin",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 2,
META_TYPE_WINDOW,
META_TYPE_GRAB_OP);
display_signals[GRAB_OP_END] =
g_signal_new ("grab-op-end",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 2,
META_TYPE_WINDOW,
META_TYPE_GRAB_OP);
/**
* MetaDisplay::show-restart-message:
* @display: the #MetaDisplay instance
* @message: (allow-none): The message to display, or %NULL
* to clear a previous restart message.
*
* The ::show-restart-message signal will be emitted to indicate
* that the compositor should show a message during restart. This is
* emitted when meta_restart() is called, either by Mutter
* internally or by the embedding compositor. The message should be
* immediately added to the Clutter stage in its final form -
* ::restart will be emitted to exit the application and leave the
* stage contents frozen as soon as the the stage is painted again.
*
* On case of failure to restart, this signal will be emitted again
* with %NULL for @message.
*
* Returns: %TRUE means the message was added to the stage; %FALSE
* indicates that the compositor did not show the message.
*/
display_signals[SHOW_RESTART_MESSAGE] =
g_signal_new ("show-restart-message",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
g_signal_accumulator_true_handled,
NULL, NULL,
G_TYPE_BOOLEAN, 1,
G_TYPE_STRING);
/**
* MetaDisplay::restart:
* @display: the #MetaDisplay instance
*
* The ::restart signal is emitted to indicate that compositor
* should reexec the process. This is
* emitted when meta_restart() is called, either by Mutter
* internally or by the embedding compositor. See also
* ::show-restart-message.
*
* Returns: %FALSE to indicate that the compositor could not
* be restarted. When the compositor is restarted, the signal
* should not return.
*/
display_signals[RESTART] =
g_signal_new ("restart",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
g_signal_accumulator_true_handled,
NULL, NULL,
G_TYPE_BOOLEAN, 0);
display_signals[SHOW_RESIZE_POPUP] =
g_signal_new ("show-resize-popup",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
g_signal_accumulator_true_handled,
NULL, NULL,
G_TYPE_BOOLEAN, 4,
G_TYPE_BOOLEAN, META_TYPE_RECTANGLE, G_TYPE_INT, G_TYPE_INT);
display_signals[GL_VIDEO_MEMORY_PURGED] =
g_signal_new ("gl-video-memory-purged",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
/**
* MetaDisplay::show-pad-osd:
* @display: the #MetaDisplay instance
* @pad: the pad device
* @settings: the pad device settings
* @layout_path: path to the layout image
* @edition_mode: Whether the OSD should be shown in edition mode
* @monitor_idx: Monitor to show the OSD on
*
* Requests the pad button mapping OSD to be shown.
*
* Returns: (transfer none) (nullable): The OSD actor
*/
display_signals[SHOW_PAD_OSD] =
g_signal_new ("show-pad-osd",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
CLUTTER_TYPE_ACTOR, 5, CLUTTER_TYPE_INPUT_DEVICE,
G_TYPE_SETTINGS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INT);
display_signals[SHOW_OSD] =
g_signal_new ("show-osd",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING);
display_signals[PAD_MODE_SWITCH] =
g_signal_new ("pad-mode-switch",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 3, CLUTTER_TYPE_INPUT_DEVICE,
G_TYPE_UINT, G_TYPE_UINT);
display_signals[WINDOW_ENTERED_MONITOR] =
g_signal_new ("window-entered-monitor",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 2,
G_TYPE_INT,
META_TYPE_WINDOW);
display_signals[WINDOW_LEFT_MONITOR] =
g_signal_new ("window-left-monitor",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 2,
G_TYPE_INT,
META_TYPE_WINDOW);
display_signals[IN_FULLSCREEN_CHANGED] =
g_signal_new ("in-fullscreen-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 0);
display_signals[SHOWING_DESKTOP_CHANGED] =
g_signal_new ("showing-desktop-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
display_signals[RESTACKED] =
g_signal_new ("restacked",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 0);
display_signals[WORKAREAS_CHANGED] =
g_signal_new ("workareas-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 0);
display_signals[CLOSING] =
g_signal_new ("closing",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 0);
display_signals[INIT_XSERVER] =
g_signal_new ("init-xserver",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, g_signal_accumulator_first_wins,
NULL, NULL,
G_TYPE_BOOLEAN, 1, G_TYPE_TASK);
g_object_class_install_property (object_class,
PROP_COMPOSITOR_MODIFIERS,
g_param_spec_flags ("compositor-modifiers",
"Compositor modifiers",
"Modifiers reserved for compositor actions",
CLUTTER_TYPE_MODIFIER_TYPE,
0,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_FOCUS_WINDOW,
g_param_spec_object ("focus-window",
"Focus window",
"Currently focused window",
META_TYPE_WINDOW,
G_PARAM_READABLE));
}
/**
* ping_data_free:
*
* Destructor for #MetaPingData structs. Will destroy the
* event source for the struct as well.
*/
static void
ping_data_free (MetaPingData *ping_data)
{
/* Remove the timeout */
g_clear_handle_id (&ping_data->ping_timeout_id, g_source_remove);
g_free (ping_data);
}
void
meta_display_remove_pending_pings_for_window (MetaDisplay *display,
MetaWindow *window)
{
GSList *tmp;
GSList *dead;
/* could obviously be more efficient, don't care */
/* build list to be removed */
dead = NULL;
for (tmp = display->pending_pings; tmp; tmp = tmp->next)
{
MetaPingData *ping_data = tmp->data;
if (ping_data->window == window)
dead = g_slist_prepend (dead, ping_data);
}
/* remove what we found */
for (tmp = dead; tmp; tmp = tmp->next)
{
MetaPingData *ping_data = tmp->data;
display->pending_pings = g_slist_remove (display->pending_pings, ping_data);
ping_data_free (ping_data);
}
g_slist_free (dead);
}
static MetaCompositor *
create_compositor (MetaDisplay *display)
{
MetaBackend *backend = meta_get_backend ();
#ifdef HAVE_WAYLAND
#ifdef HAVE_NATIVE_BACKEND
if (META_IS_BACKEND_NATIVE (backend))
return META_COMPOSITOR (meta_compositor_native_new (display, backend));
#endif
if (META_IS_BACKEND_X11_NESTED (backend))
return META_COMPOSITOR (meta_compositor_server_new (display, backend));
#endif
return META_COMPOSITOR (meta_compositor_x11_new (display, backend));
}
static void
meta_display_init (MetaDisplay *disp)
{
/* Some stuff could go in here that's currently in _open,
* but it doesn't really matter. */
}
void
meta_display_cancel_touch (MetaDisplay *display)
{
#ifdef HAVE_WAYLAND
MetaWaylandCompositor *compositor;
if (!meta_is_wayland_compositor ())
return;
compositor = meta_wayland_compositor_get_default ();
meta_wayland_touch_cancel (compositor->seat->touch);
#endif
}
static void
gesture_tracker_state_changed (MetaGestureTracker *tracker,
ClutterEventSequence *sequence,
MetaSequenceState state,
MetaDisplay *display)
{
switch (state)
{
case META_SEQUENCE_NONE:
case META_SEQUENCE_PENDING_END:
return;
case META_SEQUENCE_ACCEPTED:
meta_display_cancel_touch (display);
G_GNUC_FALLTHROUGH;
case META_SEQUENCE_REJECTED:
{
MetaBackend *backend;
backend = meta_get_backend ();
meta_backend_finish_touch_sequence (backend, sequence, state);
break;
}
}
}
static void
on_ui_scaling_factor_changed (MetaSettings *settings,
MetaDisplay *display)
{
meta_display_reload_cursor (display);
}
static gboolean
meta_display_init_x11_display (MetaDisplay *display,
GError **error)
{
MetaX11Display *x11_display;
x11_display = meta_x11_display_new (display, error);
if (!x11_display)
return FALSE;
display->x11_display = x11_display;
g_signal_emit (display, display_signals[X11_DISPLAY_SETUP], 0);
meta_x11_display_create_guard_window (x11_display);
if (!display->display_opening)
{
g_signal_emit (display, display_signals[X11_DISPLAY_OPENED], 0);
meta_display_manage_all_xwindows (display);
meta_compositor_redirect_x11_windows (display->compositor);
}
return TRUE;
}
#ifdef HAVE_WAYLAND
gboolean
meta_display_init_x11_finish (MetaDisplay *display,
GAsyncResult *result,
GError **error)
{
MetaX11Display *x11_display;
g_assert (g_task_get_source_tag (G_TASK (result)) == meta_display_init_x11);
if (!g_task_propagate_boolean (G_TASK (result), error))
{
if (*error == NULL)
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown error");
return FALSE;
}
if (display->x11_display)
return TRUE;
x11_display = meta_x11_display_new (display, error);
if (!x11_display)
return FALSE;
display->x11_display = x11_display;
g_signal_emit (display, display_signals[X11_DISPLAY_SETUP], 0);
meta_x11_display_create_guard_window (x11_display);
if (!display->display_opening)
{
g_signal_emit (display, display_signals[X11_DISPLAY_OPENED], 0);
meta_x11_display_set_cm_selection (x11_display);
meta_display_manage_all_xwindows (display);
meta_compositor_redirect_x11_windows (display->compositor);
}
return TRUE;
}
static void
on_xserver_started (MetaXWaylandManager *manager,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr (GTask) task = user_data;
MetaDisplay *display = g_task_get_source_object (task);
GError *error = NULL;
gboolean retval = FALSE;
if (!meta_xwayland_start_xserver_finish (manager, result, &error))
{
if (error)
g_task_return_error (task, error);
else
g_task_return_boolean (task, FALSE);
return;
}
g_signal_emit (display, display_signals[INIT_XSERVER], 0, task, &retval);
if (!retval)
{
/* No handlers for this signal, proceed right away */
g_task_return_boolean (task, TRUE);
}
}
void
meta_display_init_x11 (MetaDisplay *display,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
g_autoptr (GTask) task = NULL;
task = g_task_new (display, cancellable, callback, user_data);
g_task_set_source_tag (task, meta_display_init_x11);
meta_xwayland_start_xserver (&compositor->xwayland_manager,
cancellable,
(GAsyncReadyCallback) on_xserver_started,
g_steal_pointer (&task));
}
static void
on_x11_initialized (MetaDisplay *display,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr (GError) error = NULL;
if (!meta_display_init_x11_finish (display, result, &error))
g_critical ("Failed to init X11 display: %s", error->message);
}
#endif
void
meta_display_shutdown_x11 (MetaDisplay *display)
{
if (!display->x11_display)
return;
g_signal_emit (display, display_signals[X11_DISPLAY_CLOSING], 0);
g_object_run_dispose (G_OBJECT (display->x11_display));
g_clear_object (&display->x11_display);
}
MetaDisplay *
meta_display_new (MetaContext *context,
GError **error)
{
MetaDisplay *display;
MetaDisplayPrivate *priv;
int i;
guint32 timestamp;
Window old_active_xwindow = None;
MetaBackend *backend = meta_get_backend ();
MetaMonitorManager *monitor_manager;
MetaSettings *settings;
g_assert (the_display == NULL);
display = the_display = g_object_new (META_TYPE_DISPLAY, NULL);
priv = meta_display_get_instance_private (display);
priv->context = context;
display->closing = 0;
display->display_opening = TRUE;
display->pending_pings = NULL;
display->autoraise_timeout_id = 0;
display->autoraise_window = NULL;
display->focus_window = NULL;
display->workspace_manager = NULL;
display->x11_display = NULL;
display->current_cursor = -1; /* invalid/unset */
display->tile_preview_timeout_id = 0;
display->check_fullscreen_later = 0;
display->work_area_later = 0;
display->mouse_mode = TRUE; /* Only relevant for mouse or sloppy focus */
display->allow_terminal_deactivation = TRUE; /* Only relevant for when a
terminal has the focus */
i = 0;
while (i < N_IGNORED_CROSSING_SERIALS)
{
display->ignored_crossing_serials[i] = 0;
++i;
}
display->current_time = META_CURRENT_TIME;
display->grab_resize_timeout_id = 0;
display->grab_have_keyboard = FALSE;
display->grab_op = META_GRAB_OP_NONE;
display->grab_window = NULL;
display->grab_tile_mode = META_TILE_NONE;
display->grab_tile_monitor_number = -1;
meta_display_cleanup_edges (display);
meta_display_init_keys (display);
meta_prefs_add_listener (prefs_changed_callback, display);
/* Get events */
meta_display_init_events (display);
display->stamps = g_hash_table_new (g_int64_hash,
g_int64_equal);
display->wayland_windows = g_hash_table_new (NULL, NULL);
monitor_manager = meta_backend_get_monitor_manager (backend);
g_signal_connect (monitor_manager, "monitors-changed-internal",
G_CALLBACK (on_monitors_changed_internal), display);
display->pad_action_mapper = meta_pad_action_mapper_new (monitor_manager);
settings = meta_backend_get_settings (backend);
g_signal_connect (settings, "ui-scaling-factor-changed",
G_CALLBACK (on_ui_scaling_factor_changed), display);
display->compositor = create_compositor (display);
meta_display_set_cursor (display, META_CURSOR_DEFAULT);
display->stack = meta_stack_new (display);
display->stack_tracker = meta_stack_tracker_new (display);
display->workspace_manager = meta_workspace_manager_new (display);
display->startup_notification = meta_startup_notification_new (display);
display->bell = meta_bell_new (display);
display->selection = meta_selection_new (display);
meta_clipboard_manager_init (display);
#ifdef HAVE_WAYLAND
if (meta_is_wayland_compositor ())
{
MetaX11DisplayPolicy x11_display_policy;
x11_display_policy = meta_context_get_x11_display_policy (context);
if (x11_display_policy == META_X11_DISPLAY_POLICY_MANDATORY)
{
meta_display_init_x11 (display, NULL,
(GAsyncReadyCallback) on_x11_initialized,
NULL);
}
timestamp = meta_display_get_current_time_roundtrip (display);
}
else
#endif
{
if (!meta_display_init_x11_display (display, error))
{
g_object_unref (display);
return NULL;
}
timestamp = display->x11_display->timestamp;
}
display->last_focus_time = timestamp;
display->last_user_time = timestamp;
if (!meta_is_wayland_compositor ())
meta_prop_get_window (display->x11_display,
display->x11_display->xroot,
display->x11_display->atom__NET_ACTIVE_WINDOW,
&old_active_xwindow);
if (!meta_compositor_do_manage (display->compositor, error))
{
g_object_unref (display);
return NULL;
}
if (display->x11_display)
{
g_signal_emit (display, display_signals[X11_DISPLAY_OPENED], 0);
meta_x11_display_restore_active_workspace (display->x11_display);
meta_x11_display_create_guard_window (display->x11_display);
}
/* Set up touch support */
display->gesture_tracker = meta_gesture_tracker_new ();
g_signal_connect (display->gesture_tracker, "state-changed",
G_CALLBACK (gesture_tracker_state_changed), display);
/* We know that if mutter is running as a Wayland compositor,
* we start out with no windows.
*/
if (!meta_is_wayland_compositor ())
meta_display_manage_all_xwindows (display);
if (old_active_xwindow != None)
{
MetaWindow *old_active_window;
old_active_window = meta_x11_display_lookup_x_window (display->x11_display,
old_active_xwindow);
if (old_active_window)
meta_window_focus (old_active_window, timestamp);
else
meta_display_unset_input_focus (display, timestamp);
}
else
{
meta_display_unset_input_focus (display, timestamp);
}
display->sound_player = g_object_new (META_TYPE_SOUND_PLAYER, NULL);
/* Done opening new display */
display->display_opening = FALSE;
return display;
}
static gint
ptrcmp (gconstpointer a, gconstpointer b)
{
if (a < b)
return -1;
else if (a > b)
return 1;
else
return 0;
}
/**
* meta_display_list_windows:
* @display: a #MetaDisplay
* @flags: options for listing
*
* Lists windows for the display, the @flags parameter for
* now determines whether override-redirect windows will be
* included.
*
* Return value: (transfer container): the list of windows.
*/
GSList*
meta_display_list_windows (MetaDisplay *display,
MetaListWindowsFlags flags)
{
GSList *winlist;
GSList *prev;
GSList *tmp;
GHashTableIter iter;
gpointer key, value;
winlist = NULL;
if (display->x11_display)
{
g_hash_table_iter_init (&iter, display->x11_display->xids);
while (g_hash_table_iter_next (&iter, &key, &value))
{
MetaWindow *window = value;
if (!META_IS_WINDOW (window) || window->unmanaging)
continue;
if (!window->override_redirect ||
(flags & META_LIST_INCLUDE_OVERRIDE_REDIRECT) != 0)
winlist = g_slist_prepend (winlist, window);
}
}
g_hash_table_iter_init (&iter, display->wayland_windows);
while (g_hash_table_iter_next (&iter, &key, &value))
{
MetaWindow *window = value;
if (!META_IS_WINDOW (window) || window->unmanaging)
continue;
if (!window->override_redirect ||
(flags & META_LIST_INCLUDE_OVERRIDE_REDIRECT) != 0)
winlist = g_slist_prepend (winlist, window);
}
/* Uniquify the list, since both frame windows and plain
* windows are in the hash
*/
winlist = g_slist_sort (winlist, ptrcmp);
prev = NULL;
tmp = winlist;
while (tmp != NULL)
{
GSList *next;
next = tmp->next;
if (next &&
next->data == tmp->data)
{
/* Delete tmp from list */
if (prev)
prev->next = next;
if (tmp == winlist)
winlist = next;
g_slist_free_1 (tmp);
/* leave prev unchanged */
}
else
{
prev = tmp;
}
tmp = next;
}
if (flags & META_LIST_SORTED)
winlist = g_slist_sort (winlist, mru_cmp);
return winlist;
}
void
meta_display_close (MetaDisplay *display,
guint32 timestamp)
{
g_assert (display != NULL);
if (display->closing != 0)
{
/* The display's already been closed. */
return;
}
g_assert (display == the_display);
display->closing += 1;
g_signal_emit (display, display_signals[CLOSING], 0);
meta_compositor_unmanage (display->compositor);
meta_display_unmanage_windows (display, timestamp);
meta_prefs_remove_listener (prefs_changed_callback, display);
meta_display_remove_autoraise_callback (display);
g_clear_object (&display->gesture_tracker);
g_clear_handle_id (&display->focus_timeout_id, g_source_remove);
g_clear_handle_id (&display->tile_preview_timeout_id, g_source_remove);
if (display->work_area_later != 0)
meta_later_remove (display->work_area_later);
if (display->check_fullscreen_later != 0)
meta_later_remove (display->check_fullscreen_later);
/* Stop caring about events */
meta_display_free_events (display);
g_clear_pointer (&display->compositor, meta_compositor_destroy);
meta_display_shutdown_x11 (display);
g_clear_object (&display->stack);
g_clear_pointer (&display->stack_tracker,
meta_stack_tracker_free);
/* Must be after all calls to meta_window_unmanage() since they
* unregister windows
*/
g_hash_table_destroy (display->wayland_windows);
g_hash_table_destroy (display->stamps);
meta_display_shutdown_keys (display);
g_clear_object (&display->bell);
g_clear_object (&display->startup_notification);
g_clear_object (&display->workspace_manager);
g_clear_object (&display->sound_player);
meta_clipboard_manager_shutdown (display);
g_clear_object (&display->selection);
g_clear_object (&display->pad_action_mapper);
the_display = NULL;
}
/**
* meta_display_for_x_display:
* @xdisplay: An X display
*
* Returns the singleton MetaDisplay if @xdisplay matches the X display it's
* managing; otherwise gives a warning and returns %NULL. When we were claiming
* to be able to manage multiple displays, this was supposed to find the
* display out of the list which matched that display. Now it's merely an
* extra sanity check.
*
* Returns: The singleton X display, or %NULL if @xdisplay isn't the one
* we're managing.
*/
MetaDisplay*
meta_display_for_x_display (Display *xdisplay)
{
if (the_display->x11_display->xdisplay == xdisplay)
return the_display;
meta_warning ("Could not find display for X display %p, probably going to crash",
xdisplay);
return NULL;
}
/**
* meta_get_display:
*
* Accessor for the singleton MetaDisplay.
*
* Returns: The only #MetaDisplay there is. This can be %NULL, but only
* during startup.
*/
MetaDisplay*
meta_get_display (void)
{
return the_display;
}
static inline gboolean
grab_op_is_window (MetaGrabOp op)
{
return META_GRAB_OP_GET_BASE_TYPE (op) == META_GRAB_OP_WINDOW_BASE;
}
gboolean
meta_grab_op_is_mouse (MetaGrabOp op)
{
if (!grab_op_is_window (op))
return FALSE;
return (op & META_GRAB_OP_WINDOW_FLAG_KEYBOARD) == 0;
}
gboolean
meta_grab_op_is_keyboard (MetaGrabOp op)
{
if (!grab_op_is_window (op))
return FALSE;
return (op & META_GRAB_OP_WINDOW_FLAG_KEYBOARD) != 0;
}
gboolean
meta_grab_op_is_resizing (MetaGrabOp op)
{
if (!grab_op_is_window (op))
return FALSE;
return (op & META_GRAB_OP_WINDOW_DIR_MASK) != 0 || op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN;
}
gboolean
meta_grab_op_is_moving (MetaGrabOp op)
{
if (!grab_op_is_window (op))
return FALSE;
return !meta_grab_op_is_resizing (op);
}
/**
* meta_display_windows_are_interactable:
* @op: A #MetaGrabOp
*
* Whether windows can be interacted with.
*/
gboolean
meta_display_windows_are_interactable (MetaDisplay *display)
{
switch (display->event_route)
{
case META_EVENT_ROUTE_NORMAL:
case META_EVENT_ROUTE_WAYLAND_POPUP:
return TRUE;
default:
return FALSE;
}
}
/**
* meta_display_xserver_time_is_before:
* @display: a #MetaDisplay
* @time1: An event timestamp
* @time2: An event timestamp
*
* Xserver time can wraparound, thus comparing two timestamps needs to take
* this into account. If no wraparound has occurred, this is equivalent to
* time1 < time2
* Otherwise, we need to account for the fact that wraparound can occur
* and the fact that a timestamp of 0 must be special-cased since it
* means "older than anything else".
*
* Note that this is NOT an equivalent for time1 <= time2; if that's what
* you need then you'll need to swap the order of the arguments and negate
* the result.
*/
gboolean
meta_display_xserver_time_is_before (MetaDisplay *display,
guint32 time1,
guint32 time2)
{
return XSERVER_TIME_IS_BEFORE(time1, time2);
}
/**
* meta_display_get_last_user_time:
* @display: a #MetaDisplay
*
* Returns: Timestamp of the last user interaction event with a window
*/
guint32
meta_display_get_last_user_time (MetaDisplay *display)
{
return display->last_user_time;
}
/* Get time of current event, or CurrentTime if none. */
guint32
meta_display_get_current_time (MetaDisplay *display)
{
return display->current_time;
}
guint32
meta_display_get_current_time_roundtrip (MetaDisplay *display)
{
if (meta_is_wayland_compositor ())
/* Xwayland uses monotonic clock, so lets use it here as well */
return (guint32) (g_get_monotonic_time () / 1000);
else
return meta_x11_display_get_current_time_roundtrip (display->x11_display);
}
/**
* meta_display_add_ignored_crossing_serial:
* @display: a #MetaDisplay
* @serial: the serial to ignore
*
* Save the specified serial and ignore crossing events with that
* serial for the purpose of focus-follows-mouse. This can be used
* for certain changes to the window hierarchy that we don't want
* to change the focus window, even if they cause the pointer to
* end up in a new window.
*/
void
meta_display_add_ignored_crossing_serial (MetaDisplay *display,
unsigned long serial)
{
int i;
/* don't add the same serial more than once */
if (display->ignored_crossing_serials[N_IGNORED_CROSSING_SERIALS-1] == serial)
return;
/* shift serials to the left */
i = 0;
while (i < (N_IGNORED_CROSSING_SERIALS - 1))
{
display->ignored_crossing_serials[i] = display->ignored_crossing_serials[i+1];
++i;
}
/* put new one on the end */
display->ignored_crossing_serials[i] = serial;
}
static gboolean
window_raise_with_delay_callback (void *data)
{
MetaWindow *window = data;
window->display->autoraise_timeout_id = 0;
window->display->autoraise_window = NULL;
/* If we aren't already on top, check whether the pointer is inside
* the window and raise the window if so.
*/
if (meta_stack_get_top (window->display->stack) != window)
{
if (meta_window_has_pointer (window))
meta_window_raise (window);
else
meta_topic (META_DEBUG_FOCUS,
"Pointer not inside window, not raising %s",
window->desc);
}
return G_SOURCE_REMOVE;
}
void
meta_display_queue_autoraise_callback (MetaDisplay *display,
MetaWindow *window)
{
meta_topic (META_DEBUG_FOCUS,
"Queuing an autoraise timeout for %s with delay %d",
window->desc,
meta_prefs_get_auto_raise_delay ());
g_clear_handle_id (&display->autoraise_timeout_id, g_source_remove);
display->autoraise_timeout_id =
g_timeout_add_full (G_PRIORITY_DEFAULT,
meta_prefs_get_auto_raise_delay (),
window_raise_with_delay_callback,
window, NULL);
g_source_set_name_by_id (display->autoraise_timeout_id, "[mutter] window_raise_with_delay_callback");
display->autoraise_window = window;
}
void
meta_display_sync_wayland_input_focus (MetaDisplay *display)
{
#ifdef HAVE_WAYLAND
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
MetaWindow *focus_window = NULL;
MetaBackend *backend = meta_get_backend ();
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
ClutterSeat *seat = clutter_backend_get_default_seat (clutter_backend);
MetaStage *stage = META_STAGE (meta_backend_get_stage (backend));
gboolean is_no_focus_xwindow = FALSE;
if (display->x11_display)
is_no_focus_xwindow = meta_x11_display_xwindow_is_a_no_focus_window (display->x11_display,
display->x11_display->focus_xwindow);
if (!meta_display_windows_are_interactable (display))
focus_window = NULL;
else if (is_no_focus_xwindow)
focus_window = NULL;
else if (display->focus_window && display->focus_window->surface)
focus_window = display->focus_window;
else
meta_topic (META_DEBUG_FOCUS, "Focus change has no effect, because there is no matching wayland surface");
meta_stage_set_active (stage, focus_window == NULL);
meta_wayland_compositor_set_input_focus (compositor, focus_window);
clutter_stage_repick_device (CLUTTER_STAGE (stage),
clutter_seat_get_pointer (seat));
#endif
}
void
meta_display_update_focus_window (MetaDisplay *display,
MetaWindow *window)
{
if (display->focus_window == window)
return;
if (display->focus_window)
{
MetaWindow *previous;
meta_topic (META_DEBUG_FOCUS,
"%s is now the previous focus window due to being focused out or unmapped",
display->focus_window->desc);
/* Make sure that signals handlers invoked by
* meta_window_set_focused_internal() don't see
* display->focus_window->has_focus == FALSE
*/
previous = display->focus_window;
display->focus_window = NULL;
meta_window_set_focused_internal (previous, FALSE);
}
display->focus_window = window;
if (display->focus_window)
{
meta_topic (META_DEBUG_FOCUS, "* Focus --> %s",
display->focus_window->desc);
meta_window_set_focused_internal (display->focus_window, TRUE);
}
else
meta_topic (META_DEBUG_FOCUS, "* Focus --> NULL");
if (meta_is_wayland_compositor ())
meta_display_sync_wayland_input_focus (display);
g_object_notify (G_OBJECT (display), "focus-window");
}
gboolean
meta_display_timestamp_too_old (MetaDisplay *display,
guint32 *timestamp)
{
/* FIXME: If Soeren's suggestion in bug 151984 is implemented, it will allow
* us to sanity check the timestamp here and ensure it doesn't correspond to
* a future time (though we would want to rename to
* timestamp_too_old_or_in_future).
*/
if (*timestamp == META_CURRENT_TIME)
{
*timestamp = meta_display_get_current_time_roundtrip (display);
return FALSE;
}
else if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_focus_time))
{
if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_user_time))
return TRUE;
else
{
*timestamp = display->last_focus_time;
return FALSE;
}
}
return FALSE;
}
void
meta_display_set_input_focus (MetaDisplay *display,
MetaWindow *window,
gboolean focus_frame,
guint32 timestamp)
{
if (meta_display_timestamp_too_old (display, ×tamp))
return;
if (display->x11_display)
{
meta_x11_display_set_input_focus (display->x11_display, window,
focus_frame, timestamp);
}
meta_display_update_focus_window (display, window);
display->last_focus_time = timestamp;
if (window == NULL || window != display->autoraise_window)
meta_display_remove_autoraise_callback (display);
}
void
meta_display_unset_input_focus (MetaDisplay *display,
guint32 timestamp)
{
meta_display_set_input_focus (display, NULL, FALSE, timestamp);
}
void
meta_display_register_wayland_window (MetaDisplay *display,
MetaWindow *window)
{
g_hash_table_add (display->wayland_windows, window);
}
void
meta_display_unregister_wayland_window (MetaDisplay *display,
MetaWindow *window)
{
g_hash_table_remove (display->wayland_windows, window);
}
MetaWindow*
meta_display_lookup_stamp (MetaDisplay *display,
guint64 stamp)
{
return g_hash_table_lookup (display->stamps, &stamp);
}
void
meta_display_register_stamp (MetaDisplay *display,
guint64 *stampp,
MetaWindow *window)
{
g_return_if_fail (g_hash_table_lookup (display->stamps, stampp) == NULL);
g_hash_table_insert (display->stamps, stampp, window);
}
void
meta_display_unregister_stamp (MetaDisplay *display,
guint64 stamp)
{
g_return_if_fail (g_hash_table_lookup (display->stamps, &stamp) != NULL);
g_hash_table_remove (display->stamps, &stamp);
}
MetaWindow*
meta_display_lookup_stack_id (MetaDisplay *display,
guint64 stack_id)
{
if (META_STACK_ID_IS_X11 (stack_id))
{
if (!display->x11_display)
return NULL;
return meta_x11_display_lookup_x_window (display->x11_display,
(Window)stack_id);
}
else
{
return meta_display_lookup_stamp (display, stack_id);
}
}
/* We return a pointer into a ring of static buffers. This is to make
* using this function for debug-logging convenient and avoid temporary
* strings that must be freed. */
const char *
meta_display_describe_stack_id (MetaDisplay *display,
guint64 stack_id)
{
/* 0x<64-bit: 16 characters> (<10 characters of title>)\0' */
static char buffer[5][32];
MetaWindow *window;
static int pos = 0;
char *result;
result = buffer[pos];
pos = (pos + 1) % 5;
window = meta_display_lookup_stack_id (display, stack_id);
if (window && window->title)
snprintf (result, sizeof(buffer[0]), "%#" G_GINT64_MODIFIER "x (%.10s)", stack_id, window->title);
else
snprintf (result, sizeof(buffer[0]), "%#" G_GINT64_MODIFIER "x", stack_id);
return result;
}
void
meta_display_notify_window_created (MetaDisplay *display,
MetaWindow *window)
{
COGL_TRACE_BEGIN_SCOPED (MetaDisplayNotifyWindowCreated,
"Display (notify window created)");
g_signal_emit (display, display_signals[WINDOW_CREATED], 0, window);
}
static MetaCursor
meta_cursor_for_grab_op (MetaGrabOp op)
{
switch (op)
{
case META_GRAB_OP_RESIZING_SE:
case META_GRAB_OP_KEYBOARD_RESIZING_SE:
return META_CURSOR_SE_RESIZE;
break;
case META_GRAB_OP_RESIZING_S:
case META_GRAB_OP_KEYBOARD_RESIZING_S:
return META_CURSOR_SOUTH_RESIZE;
break;
case META_GRAB_OP_RESIZING_SW:
case META_GRAB_OP_KEYBOARD_RESIZING_SW:
return META_CURSOR_SW_RESIZE;
break;
case META_GRAB_OP_RESIZING_N:
case META_GRAB_OP_KEYBOARD_RESIZING_N:
return META_CURSOR_NORTH_RESIZE;
break;
case META_GRAB_OP_RESIZING_NE:
case META_GRAB_OP_KEYBOARD_RESIZING_NE:
return META_CURSOR_NE_RESIZE;
break;
case META_GRAB_OP_RESIZING_NW:
case META_GRAB_OP_KEYBOARD_RESIZING_NW:
return META_CURSOR_NW_RESIZE;
break;
case META_GRAB_OP_RESIZING_W:
case META_GRAB_OP_KEYBOARD_RESIZING_W:
return META_CURSOR_WEST_RESIZE;
break;
case META_GRAB_OP_RESIZING_E:
case META_GRAB_OP_KEYBOARD_RESIZING_E:
return META_CURSOR_EAST_RESIZE;
break;
case META_GRAB_OP_MOVING:
case META_GRAB_OP_KEYBOARD_MOVING:
case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
return META_CURSOR_MOVE_OR_RESIZE_WINDOW;
break;
default:
break;
}
return META_CURSOR_DEFAULT;
}
static void
root_cursor_prepare_at (MetaCursorSpriteXcursor *sprite_xcursor,
float best_scale,
int x,
int y,
MetaDisplay *display)
{
MetaCursorSprite *cursor_sprite = META_CURSOR_SPRITE (sprite_xcursor);
MetaBackend *backend = meta_get_backend ();
if (meta_is_stage_views_scaled ())
{
if (best_scale != 0.0f)
{
float ceiled_scale;
ceiled_scale = ceilf (best_scale);
meta_cursor_sprite_xcursor_set_theme_scale (sprite_xcursor,
(int) ceiled_scale);
meta_cursor_sprite_set_texture_scale (cursor_sprite,
1.0 / ceiled_scale);
}
}
else
{
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaLogicalMonitor *logical_monitor;
logical_monitor =
meta_monitor_manager_get_logical_monitor_at (monitor_manager, x, y);
/* Reload the cursor texture if the scale has changed. */
if (logical_monitor)
{
meta_cursor_sprite_xcursor_set_theme_scale (sprite_xcursor,
logical_monitor->scale);
meta_cursor_sprite_set_texture_scale (cursor_sprite, 1.0);
}
}
}
static void
manage_root_cursor_sprite_scale (MetaDisplay *display,
MetaCursorSpriteXcursor *sprite_xcursor)
{
g_signal_connect_object (sprite_xcursor,
"prepare-at",
G_CALLBACK (root_cursor_prepare_at),
display,
0);
}
void
meta_display_reload_cursor (MetaDisplay *display)
{
MetaCursor cursor = display->current_cursor;
MetaCursorSpriteXcursor *sprite_xcursor;
MetaBackend *backend = meta_get_backend ();
MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
sprite_xcursor = meta_cursor_sprite_xcursor_new (cursor);
if (meta_is_wayland_compositor ())
manage_root_cursor_sprite_scale (display, sprite_xcursor);
meta_cursor_tracker_set_root_cursor (cursor_tracker,
META_CURSOR_SPRITE (sprite_xcursor));
g_object_unref (sprite_xcursor);
g_signal_emit (display, display_signals[CURSOR_UPDATED], 0, display);
}
void
meta_display_set_cursor (MetaDisplay *display,
MetaCursor cursor)
{
if (cursor == display->current_cursor)
return;
display->current_cursor = cursor;
meta_display_reload_cursor (display);
}
void
meta_display_update_cursor (MetaDisplay *display)
{
meta_display_set_cursor (display, meta_cursor_for_grab_op (display->grab_op));
}
static MetaWindow *
get_first_freefloating_window (MetaWindow *window)
{
while (meta_window_is_attached_dialog (window))
window = meta_window_get_transient_for (window);
/* Attached dialogs should always have a non-NULL transient-for */
g_assert (window != NULL);
return window;
}
static MetaEventRoute
get_event_route_from_grab_op (MetaGrabOp op)
{
switch (META_GRAB_OP_GET_BASE_TYPE (op))
{
case META_GRAB_OP_NONE:
/* begin_grab_op shouldn't be called with META_GRAB_OP_NONE. */
g_assert_not_reached ();
case META_GRAB_OP_WINDOW_BASE:
return META_EVENT_ROUTE_WINDOW_OP;
case META_GRAB_OP_COMPOSITOR:
/* begin_grab_op shouldn't be called with META_GRAB_OP_COMPOSITOR. */
g_assert_not_reached ();
case META_GRAB_OP_WAYLAND_POPUP:
return META_EVENT_ROUTE_WAYLAND_POPUP;
case META_GRAB_OP_FRAME_BUTTON:
return META_EVENT_ROUTE_FRAME_BUTTON;
default:
g_assert_not_reached ();
return 0;
}
}
gboolean
meta_display_begin_grab_op (MetaDisplay *display,
MetaWindow *window,
MetaGrabOp op,
gboolean pointer_already_grabbed,
gboolean frame_action,
int button,
gulong modmask, /* XXX - ignored */
guint32 timestamp,
int root_x,
int root_y)
{
MetaBackend *backend = meta_get_backend ();
MetaWindow *grab_window = NULL;
MetaEventRoute event_route;
g_assert (window != NULL);
meta_topic (META_DEBUG_WINDOW_OPS,
"Doing grab op %u on window %s button %d pointer already grabbed: %d pointer pos %d,%d",
op, window->desc, button, pointer_already_grabbed,
root_x, root_y);
if (display->grab_op != META_GRAB_OP_NONE)
{
meta_warning ("Attempt to perform window operation %u on window %s when operation %u on %s already in effect",
op, window->desc, display->grab_op,
display->grab_window ? display->grab_window->desc : "none");
return FALSE;
}
event_route = get_event_route_from_grab_op (op);
if (event_route == META_EVENT_ROUTE_WINDOW_OP)
{
if (meta_prefs_get_raise_on_click ())
meta_window_raise (window);
else
{
display->grab_initial_x = root_x;
display->grab_initial_y = root_y;
display->grab_threshold_movement_reached = FALSE;
}
}
grab_window = window;
/* If we're trying to move a window, move the first
* non-attached dialog instead.
*/
if (meta_grab_op_is_moving (op))
grab_window = get_first_freefloating_window (window);
g_assert (grab_window != NULL);
g_assert (op != META_GRAB_OP_NONE);
display->grab_have_pointer = FALSE;
if (pointer_already_grabbed)
display->grab_have_pointer = TRUE;
if (META_IS_BACKEND_X11 (meta_get_backend ()) && display->x11_display)
{
/* Since grab operations often happen as a result of implicit
* pointer operations on the display X11 connection, we need
* to ungrab here to ensure that the backend's X11 can take
* the device grab. */
XIUngrabDevice (display->x11_display->xdisplay,
META_VIRTUAL_CORE_POINTER_ID,
timestamp);
XSync (display->x11_display->xdisplay, False);
}
if (meta_backend_grab_device (backend, META_VIRTUAL_CORE_POINTER_ID, timestamp))
display->grab_have_pointer = TRUE;
if (!display->grab_have_pointer && !meta_grab_op_is_keyboard (op))
{
meta_topic (META_DEBUG_WINDOW_OPS, "XIGrabDevice() failed");
return FALSE;
}
/* Grab keys when beginning window ops; see #126497 */
if (event_route == META_EVENT_ROUTE_WINDOW_OP)
{
display->grab_have_keyboard = meta_window_grab_all_keys (grab_window, timestamp);
if (!display->grab_have_keyboard)
{
meta_topic (META_DEBUG_WINDOW_OPS, "grabbing all keys failed, ungrabbing pointer");
meta_backend_ungrab_device (backend, META_VIRTUAL_CORE_POINTER_ID, timestamp);
display->grab_have_pointer = FALSE;
return FALSE;
}
}
display->event_route = event_route;
display->grab_op = op;
display->grab_window = grab_window;
display->grab_button = button;
display->grab_tile_mode = grab_window->tile_mode;
display->grab_tile_monitor_number = grab_window->tile_monitor_number;
display->grab_anchor_root_x = root_x;
display->grab_anchor_root_y = root_y;
display->grab_latest_motion_x = root_x;
display->grab_latest_motion_y = root_y;
display->grab_last_moveresize_time = 0;
display->grab_last_edge_resistance_flags = META_EDGE_RESISTANCE_DEFAULT;
display->grab_frame_action = frame_action;
meta_display_update_cursor (display);
g_clear_handle_id (&display->grab_resize_timeout_id, g_source_remove);
meta_topic (META_DEBUG_WINDOW_OPS,
"Grab op %u on window %s successful",
display->grab_op, window ? window->desc : "(null)");
meta_window_get_frame_rect (display->grab_window,
&display->grab_initial_window_pos);
display->grab_anchor_window_pos = display->grab_initial_window_pos;
if (meta_is_wayland_compositor ())
{
meta_display_sync_wayland_input_focus (display);
meta_display_cancel_touch (display);
}
g_signal_emit (display, display_signals[GRAB_OP_BEGIN], 0,
display->grab_window, display->grab_op);
if (display->event_route == META_EVENT_ROUTE_WINDOW_OP)
meta_window_grab_op_began (display->grab_window, display->grab_op);
return TRUE;
}
void
meta_display_end_grab_op (MetaDisplay *display,
guint32 timestamp)
{
MetaWindow *grab_window = display->grab_window;
MetaGrabOp grab_op = display->grab_op;
meta_topic (META_DEBUG_WINDOW_OPS,
"Ending grab op %u at time %u", grab_op, timestamp);
if (display->event_route == META_EVENT_ROUTE_NORMAL ||
display->event_route == META_EVENT_ROUTE_COMPOSITOR_GRAB)
return;
g_assert (grab_window != NULL);
/* We need to reset this early, since the
* meta_window_grab_op_ended callback relies on this being
* up to date. */
display->grab_op = META_GRAB_OP_NONE;
if (display->event_route == META_EVENT_ROUTE_WINDOW_OP)
{
/* Clear out the edge cache */
meta_display_cleanup_edges (display);
/* Only raise the window in orthogonal raise
* ('do-not-raise-on-click') mode if the user didn't try to move
* or resize the given window by at least a threshold amount.
* For raise on click mode, the window was raised at the
* beginning of the grab_op.
*/
if (!meta_prefs_get_raise_on_click () &&
!display->grab_threshold_movement_reached)
meta_window_raise (display->grab_window);
meta_window_grab_op_ended (grab_window, grab_op);
}
if (display->grab_have_pointer)
{
MetaBackend *backend = meta_get_backend ();
meta_backend_ungrab_device (backend, META_VIRTUAL_CORE_POINTER_ID, timestamp);
}
if (display->grab_have_keyboard)
{
meta_topic (META_DEBUG_WINDOW_OPS,
"Ungrabbing all keys timestamp %u", timestamp);
meta_window_ungrab_all_keys (grab_window, timestamp);
}
display->event_route = META_EVENT_ROUTE_NORMAL;
display->grab_window = NULL;
display->grab_button = 0;
display->grab_tile_mode = META_TILE_NONE;
display->grab_tile_monitor_number = -1;
display->grab_anchor_root_x = 0;
display->grab_anchor_root_y = 0;
display->grab_latest_motion_x = 0;
display->grab_latest_motion_y = 0;
display->grab_last_moveresize_time = 0;
display->grab_last_edge_resistance_flags = META_EDGE_RESISTANCE_DEFAULT;
display->grab_frame_action = FALSE;
meta_display_update_cursor (display);
g_clear_handle_id (&display->grab_resize_timeout_id, g_source_remove);
if (meta_is_wayland_compositor ())
meta_display_sync_wayland_input_focus (display);
g_signal_emit (display, display_signals[GRAB_OP_END], 0,
grab_window, grab_op);
}
/**
* meta_display_get_grab_op:
* @display: The #MetaDisplay that the window is on
* Gets the current grab operation, if any.
*
* Return value: the current grab operation, or %META_GRAB_OP_NONE if
* Mutter doesn't currently have a grab. %META_GRAB_OP_COMPOSITOR will
* be returned if a compositor-plugin modal operation is in effect
* (See mutter_begin_modal_for_plugin())
*/
MetaGrabOp
meta_display_get_grab_op (MetaDisplay *display)
{
return display->grab_op;
}
void
meta_display_check_threshold_reached (MetaDisplay *display,
int x,
int y)
{
/* Don't bother doing the check again if we've already reached the threshold */
if (meta_prefs_get_raise_on_click () ||
display->grab_threshold_movement_reached)
return;
if (ABS (display->grab_initial_x - x) >= 8 ||
ABS (display->grab_initial_y - y) >= 8)
display->grab_threshold_movement_reached = TRUE;
}
void
meta_display_queue_retheme_all_windows (MetaDisplay *display)
{
GSList* windows;
GSList *tmp;
windows = meta_display_list_windows (display, META_LIST_DEFAULT);
tmp = windows;
while (tmp != NULL)
{
MetaWindow *window = tmp->data;
meta_window_queue (window, META_QUEUE_MOVE_RESIZE);
meta_window_frame_size_changed (window);
if (window->frame)
{
meta_frame_queue_draw (window->frame);
}
tmp = tmp->next;
}
g_slist_free (windows);
}
/*
* Stores whether syncing is currently enabled.
*/
static gboolean is_syncing = FALSE;
/**
* meta_is_syncing:
*
* Returns whether X synchronisation is currently enabled.
*
* FIXME: This is *only* called by meta_display_open(), but by that time
* we have already turned syncing on or off on startup, and we don't
* have any way to do so while Mutter is running, so it's rather
* pointless.
*
* Returns: %TRUE if we must wait for events whenever we send X requests;
* %FALSE otherwise.
*/
gboolean
meta_is_syncing (void)
{
return is_syncing;
}
/**
* meta_set_syncing:
* @setting: whether to turn syncing on or off
*
* A handy way to turn on synchronisation on or off for every display.
*/
void
meta_set_syncing (gboolean setting)
{
if (setting != is_syncing)
{
is_syncing = setting;
if (meta_get_display ())
XSynchronize (meta_get_display ()->x11_display->xdisplay, is_syncing);
}
}
/**
* meta_display_ping_timeout:
* @data: All the information about this ping. It is a #MetaPingData
* cast to a #gpointer in order to be passable to a timeout function.
* This function will also free this parameter.
*
* Does whatever it is we decided to do when a window didn't respond
* to a ping. We also remove the ping from the display's list of
* pending pings. This function is called by the event loop when the timeout
* times out which we created at the start of the ping.
*
* Returns: Always returns %FALSE, because this function is called as a
* timeout and we don't want to run the timer again.
*/
static gboolean
meta_display_ping_timeout (gpointer data)
{
MetaPingData *ping_data = data;
MetaWindow *window = ping_data->window;
MetaDisplay *display = window->display;
meta_window_set_alive (window, FALSE);
ping_data->ping_timeout_id = 0;
meta_topic (META_DEBUG_PING,
"Ping %u on window %s timed out",
ping_data->serial, ping_data->window->desc);
display->pending_pings = g_slist_remove (display->pending_pings, ping_data);
ping_data_free (ping_data);
return FALSE;
}
/**
* meta_display_ping_window:
* @display: The #MetaDisplay that the window is on
* @window: The #MetaWindow to send the ping to
* @timestamp: The timestamp of the ping. Used for uniqueness.
* Cannot be CurrentTime; use a real timestamp!
*
* Sends a ping request to a window. The window must respond to
* the request within a certain amount of time. If it does, we
* will call one callback; if the time passes and we haven't had
* a response, we call a different callback. The window must have
* the hint showing that it can respond to a ping; if it doesn't,
* we call the "got a response" callback immediately and return.
* This function returns straight away after setting things up;
* the callbacks will be called from the event loop.
*/
void
meta_display_ping_window (MetaWindow *window,
guint32 serial)
{
GSList *l;
MetaDisplay *display = window->display;
MetaPingData *ping_data;
unsigned int check_alive_timeout;
check_alive_timeout = meta_prefs_get_check_alive_timeout ();
if (check_alive_timeout == 0)
return;
if (serial == 0)
{
meta_warning ("Tried to ping window %s with a bad serial! Not allowed.",
window->desc);
return;
}
if (!meta_window_can_ping (window))
return;
for (l = display->pending_pings; l; l = l->next)
{
MetaPingData *ping_data = l->data;
if (window == ping_data->window)
{
meta_topic (META_DEBUG_PING,
"Window %s already is being pinged with serial %u",
window->desc, ping_data->serial);
return;
}
if (serial == ping_data->serial)
{
meta_warning ("Ping serial %u was reused for window %s, "
"previous use was for window %s.",
serial, window->desc, ping_data->window->desc);
return;
}
}
ping_data = g_new (MetaPingData, 1);
ping_data->window = window;
ping_data->serial = serial;
ping_data->ping_timeout_id =
g_timeout_add (check_alive_timeout,
meta_display_ping_timeout,
ping_data);
g_source_set_name_by_id (ping_data->ping_timeout_id, "[mutter] meta_display_ping_timeout");
display->pending_pings = g_slist_prepend (display->pending_pings, ping_data);
meta_topic (META_DEBUG_PING,
"Sending ping with serial %u to window %s",
serial, window->desc);
META_WINDOW_GET_CLASS (window)->ping (window, serial);
}
/**
* meta_display_pong_for_serial:
* @display: the display we got the pong from
* @serial: the serial in the pong response
*
* Process the pong (the response message) from the ping we sent
* to the window. This involves removing the timeout, calling the
* reply handler function, and freeing memory.
*/
void
meta_display_pong_for_serial (MetaDisplay *display,
guint32 serial)
{
GSList *tmp;
meta_topic (META_DEBUG_PING, "Received a pong with serial %u", serial);
for (tmp = display->pending_pings; tmp; tmp = tmp->next)
{
MetaPingData *ping_data = tmp->data;
if (serial == ping_data->serial)
{
meta_topic (META_DEBUG_PING,
"Matching ping found for pong %u",
ping_data->serial);
/* Remove the ping data from the list */
display->pending_pings = g_slist_remove (display->pending_pings,
ping_data);
/* Remove the timeout */
g_clear_handle_id (&ping_data->ping_timeout_id, g_source_remove);
meta_window_set_alive (ping_data->window, TRUE);
ping_data_free (ping_data);
break;
}
}
}
static MetaGroup *
get_focused_group (MetaDisplay *display)
{
if (display->focus_window)
return display->focus_window->group;
else
return NULL;
}
#define IN_TAB_CHAIN(w,t) (((t) == META_TAB_LIST_NORMAL && META_WINDOW_IN_NORMAL_TAB_CHAIN (w)) \
|| ((t) == META_TAB_LIST_DOCKS && META_WINDOW_IN_DOCK_TAB_CHAIN (w)) \
|| ((t) == META_TAB_LIST_GROUP && META_WINDOW_IN_GROUP_TAB_CHAIN (w, get_focused_group (w->display))) \
|| ((t) == META_TAB_LIST_NORMAL_ALL && META_WINDOW_IN_NORMAL_TAB_CHAIN_TYPE (w)))
static MetaWindow*
find_tab_forward (MetaDisplay *display,
MetaTabList type,
MetaWorkspace *workspace,
GList *start,
gboolean skip_first)
{
GList *tmp;
g_return_val_if_fail (start != NULL, NULL);
g_return_val_if_fail (workspace != NULL, NULL);
tmp = start;
if (skip_first)
tmp = tmp->next;
while (tmp != NULL)
{
MetaWindow *window = tmp->data;
if (IN_TAB_CHAIN (window, type))
return window;
tmp = tmp->next;
}
tmp = workspace->mru_list;
while (tmp != start)
{
MetaWindow *window = tmp->data;
if (IN_TAB_CHAIN (window, type))
return window;
tmp = tmp->next;
}
return NULL;
}
static MetaWindow*
find_tab_backward (MetaDisplay *display,
MetaTabList type,
MetaWorkspace *workspace,
GList *start,
gboolean skip_last)
{
GList *tmp;
g_return_val_if_fail (start != NULL, NULL);
g_return_val_if_fail (workspace != NULL, NULL);
tmp = start;
if (skip_last)
tmp = tmp->prev;
while (tmp != NULL)
{
MetaWindow *window = tmp->data;
if (IN_TAB_CHAIN (window, type))
return window;
tmp = tmp->prev;
}
tmp = g_list_last (workspace->mru_list);
while (tmp != start)
{
MetaWindow *window = tmp->data;
if (IN_TAB_CHAIN (window, type))
return window;
tmp = tmp->prev;
}
return NULL;
}
static int
mru_cmp (gconstpointer a,
gconstpointer b)
{
guint32 time_a, time_b;
time_a = meta_window_get_user_time ((MetaWindow *)a);
time_b = meta_window_get_user_time ((MetaWindow *)b);
if (time_a > time_b)
return -1;
else if (time_a < time_b)
return 1;
else
return 0;
}
/**
* meta_display_get_tab_list:
* @display: a #MetaDisplay
* @type: type of tab list
* @workspace: (nullable): origin workspace
*
* Determine the list of windows that should be displayed for Alt-TAB
* functionality. The windows are returned in most recently used order.
* If @workspace is not %NULL, the list only contains windows that are on
* @workspace or have the demands-attention hint set; otherwise it contains
* all windows.
*
* Returns: (transfer container) (element-type Meta.Window): List of windows
*/
GList*
meta_display_get_tab_list (MetaDisplay *display,
MetaTabList type,
MetaWorkspace *workspace)
{
GList *tab_list = NULL;
GList *global_mru_list = NULL;
GList *mru_list, *tmp;
GSList *windows = meta_display_list_windows (display, META_LIST_DEFAULT);
GSList *w;
if (workspace == NULL)
{
/* Yay for mixing GList and GSList in the API */
for (w = windows; w; w = w->next)
global_mru_list = g_list_prepend (global_mru_list, w->data);
global_mru_list = g_list_sort (global_mru_list, mru_cmp);
}
mru_list = workspace ? workspace->mru_list : global_mru_list;
/* Windows sellout mode - MRU order. Collect unminimized windows
* then minimized so minimized windows aren't in the way so much.
*/
for (tmp = mru_list; tmp; tmp = tmp->next)
{
MetaWindow *window = tmp->data;
if (!window->minimized && IN_TAB_CHAIN (window, type))
tab_list = g_list_prepend (tab_list, window);
}
for (tmp = mru_list; tmp; tmp = tmp->next)
{
MetaWindow *window = tmp->data;
if (window->minimized && IN_TAB_CHAIN (window, type))
tab_list = g_list_prepend (tab_list, window);
}
tab_list = g_list_reverse (tab_list);
/* If filtering by workspace, include windows from
* other workspaces that demand attention
*/
if (workspace)
for (w = windows; w; w = w->next)
{
MetaWindow *l_window = w->data;
if (l_window->wm_state_demands_attention &&
!meta_window_located_on_workspace (l_window, workspace) &&
IN_TAB_CHAIN (l_window, type))
tab_list = g_list_prepend (tab_list, l_window);
}
g_list_free (global_mru_list);
g_slist_free (windows);
return tab_list;
}
/**
* meta_display_get_tab_next:
* @display: a #MetaDisplay
* @type: type of tab list
* @workspace: origin workspace
* @window: (nullable): starting window
* @backward: If %TRUE, look for the previous window.
*
* Determine the next window that should be displayed for Alt-TAB
* functionality.
*
* Returns: (transfer none): Next window
*
*/
MetaWindow*
meta_display_get_tab_next (MetaDisplay *display,
MetaTabList type,
MetaWorkspace *workspace,
MetaWindow *window,
gboolean backward)
{
gboolean skip;
GList *tab_list;
MetaWindow *ret;
tab_list = meta_display_get_tab_list (display, type, workspace);
if (tab_list == NULL)
return NULL;
if (window != NULL)
{
g_assert (window->display == display);
if (backward)
ret = find_tab_backward (display, type, workspace, g_list_find (tab_list, window), TRUE);
else
ret = find_tab_forward (display, type, workspace, g_list_find (tab_list, window), TRUE);
}
else
{
skip = display->focus_window != NULL &&
tab_list->data == display->focus_window;
if (backward)
ret = find_tab_backward (display, type, workspace, tab_list, skip);
else
ret = find_tab_forward (display, type, workspace, tab_list, skip);
}
g_list_free (tab_list);
return ret;
}
/**
* meta_display_get_tab_current:
* @display: a #MetaDisplay
* @type: type of tab list
* @workspace: origin workspace
*
* Determine the active window that should be displayed for Alt-TAB.
*
* Returns: (transfer none): Current window
*
*/
MetaWindow*
meta_display_get_tab_current (MetaDisplay *display,
MetaTabList type,
MetaWorkspace *workspace)
{
MetaWindow *window;
window = display->focus_window;
if (window != NULL &&
IN_TAB_CHAIN (window, type) &&
(workspace == NULL ||
meta_window_located_on_workspace (window, workspace)))
return window;
else
return NULL;
}
MetaGravity
meta_resize_gravity_from_grab_op (MetaGrabOp op)
{
MetaGravity gravity;
gravity = -1;
switch (op)
{
case META_GRAB_OP_RESIZING_SE:
case META_GRAB_OP_KEYBOARD_RESIZING_SE:
gravity = META_GRAVITY_NORTH_WEST;
break;
case META_GRAB_OP_KEYBOARD_RESIZING_S:
case META_GRAB_OP_RESIZING_S:
gravity = META_GRAVITY_NORTH;
break;
case META_GRAB_OP_KEYBOARD_RESIZING_SW:
case META_GRAB_OP_RESIZING_SW:
gravity = META_GRAVITY_NORTH_EAST;
break;
case META_GRAB_OP_KEYBOARD_RESIZING_N:
case META_GRAB_OP_RESIZING_N:
gravity = META_GRAVITY_SOUTH;
break;
case META_GRAB_OP_KEYBOARD_RESIZING_NE:
case META_GRAB_OP_RESIZING_NE:
gravity = META_GRAVITY_SOUTH_WEST;
break;
case META_GRAB_OP_KEYBOARD_RESIZING_NW:
case META_GRAB_OP_RESIZING_NW:
gravity = META_GRAVITY_SOUTH_EAST;
break;
case META_GRAB_OP_KEYBOARD_RESIZING_E:
case META_GRAB_OP_RESIZING_E:
gravity = META_GRAVITY_WEST;
break;
case META_GRAB_OP_KEYBOARD_RESIZING_W:
case META_GRAB_OP_RESIZING_W:
gravity = META_GRAVITY_EAST;
break;
case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
gravity = META_GRAVITY_CENTER;
break;
default:
break;
}
return gravity;
}
void
meta_display_manage_all_xwindows (MetaDisplay *display)
{
guint64 *_children;
guint64 *children;
int n_children, i;
meta_stack_freeze (display->stack);
meta_stack_tracker_get_stack (display->stack_tracker, &_children, &n_children);
/* Copy the stack as it will be modified as part of the loop */
children = g_memdup2 (_children, sizeof (uint64_t) * n_children);
for (i = 0; i < n_children; ++i)
{
if (!META_STACK_ID_IS_X11 (children[i]))
continue;
meta_window_x11_new (display, children[i], TRUE,
META_COMP_EFFECT_NONE);
}
g_free (children);
meta_stack_thaw (display->stack);
}
void
meta_display_unmanage_windows (MetaDisplay *display,
guint32 timestamp)
{
GSList *tmp;
GSList *winlist;
winlist = meta_display_list_windows (display,
META_LIST_INCLUDE_OVERRIDE_REDIRECT);
winlist = g_slist_sort (winlist, meta_display_stack_cmp);
g_slist_foreach (winlist, (GFunc)g_object_ref, NULL);
/* Unmanage all windows */
tmp = winlist;
while (tmp != NULL)
{
MetaWindow *window = tmp->data;
/* Check if already unmanaged for safety - in particular, catch
* the case where unmanaging a parent window can cause attached
* dialogs to be (temporarily) unmanaged.
*/
if (!window->unmanaging)
meta_window_unmanage (window, timestamp);
g_object_unref (window);
tmp = tmp->next;
}
g_slist_free (winlist);
}
int
meta_display_stack_cmp (const void *a,
const void *b)
{
MetaWindow *aw = (void*) a;
MetaWindow *bw = (void*) b;
return meta_stack_windows_cmp (aw->display->stack, aw, bw);
}
/**
* meta_display_sort_windows_by_stacking:
* @display: a #MetaDisplay
* @windows: (element-type MetaWindow): Set of windows
*
* Sorts a set of windows according to their current stacking order. If windows
* from multiple screens are present in the set of input windows, then all the
* windows on screen 0 are sorted below all the windows on screen 1, and so forth.
* Since the stacking order of override-redirect windows isn't controlled by
* Metacity, if override-redirect windows are in the input, the result may not
* correspond to the actual stacking order in the X server.
*
* An example of using this would be to sort the list of transient dialogs for a
* window into their current stacking order.
*
* Returns: (transfer container) (element-type MetaWindow): Input windows sorted by stacking order, from lowest to highest
*/
GSList *
meta_display_sort_windows_by_stacking (MetaDisplay *display,
GSList *windows)
{
GSList *copy = g_slist_copy (windows);
copy = g_slist_sort (copy, meta_display_stack_cmp);
return copy;
}
static void
prefs_changed_callback (MetaPreference pref,
void *data)
{
MetaDisplay *display = data;
switch (pref)
{
case META_PREF_DRAGGABLE_BORDER_WIDTH:
meta_display_queue_retheme_all_windows (display);
break;
case META_PREF_CURSOR_THEME:
case META_PREF_CURSOR_SIZE:
meta_display_reload_cursor (display);
break;
default:
break;
}
}
void
meta_display_sanity_check_timestamps (MetaDisplay *display,
guint32 timestamp)
{
if (XSERVER_TIME_IS_BEFORE (timestamp, display->last_focus_time))
{
meta_warning ("last_focus_time (%u) is greater than comparison "
"timestamp (%u). This most likely represents a buggy "
"client sending inaccurate timestamps in messages such as "
"_NET_ACTIVE_WINDOW. Trying to work around...",
display->last_focus_time, timestamp);
display->last_focus_time = timestamp;
}
if (XSERVER_TIME_IS_BEFORE (timestamp, display->last_user_time))
{
GSList *windows;
GSList *tmp;
meta_warning ("last_user_time (%u) is greater than comparison "
"timestamp (%u). This most likely represents a buggy "
"client sending inaccurate timestamps in messages such as "
"_NET_ACTIVE_WINDOW. Trying to work around...",
display->last_user_time, timestamp);
display->last_user_time = timestamp;
windows = meta_display_list_windows (display, META_LIST_DEFAULT);
tmp = windows;
while (tmp != NULL)
{
MetaWindow *window = tmp->data;
if (XSERVER_TIME_IS_BEFORE (timestamp, window->net_wm_user_time))
{
meta_warning ("%s appears to be one of the offending windows "
"with a timestamp of %u. Working around...",
window->desc, window->net_wm_user_time);
meta_window_set_user_time (window, timestamp);
}
tmp = tmp->next;
}
g_slist_free (windows);
}
}
void
meta_display_remove_autoraise_callback (MetaDisplay *display)
{
g_clear_handle_id (&display->autoraise_timeout_id, g_source_remove);
display->autoraise_window = NULL;
}
void
meta_display_overlay_key_activate (MetaDisplay *display)
{
g_signal_emit (display, display_signals[OVERLAY_KEY], 0);
}
void
meta_display_accelerator_activate (MetaDisplay *display,
guint action,
ClutterKeyEvent *event)
{
g_signal_emit (display, display_signals[ACCELERATOR_ACTIVATED], 0,
action,
clutter_event_get_source_device ((ClutterEvent *) event),
event->time);
}
gboolean
meta_display_modifiers_accelerator_activate (MetaDisplay *display)
{
gboolean freeze;
g_signal_emit (display, display_signals[MODIFIERS_ACCELERATOR_ACTIVATED], 0, &freeze);
return freeze;
}
/**
* meta_display_supports_extended_barriers:
* @display: a #MetaDisplay
*
* Returns: whether pointer barriers can be supported.
*
* When running as an X compositor the X server needs XInput 2
* version 2.3. When running as a display server it is supported
* when running on the native backend.
*
* Clients should use this method to determine whether their
* interfaces should depend on new barrier features.
*/
gboolean
meta_display_supports_extended_barriers (MetaDisplay *display)
{
#ifdef HAVE_NATIVE_BACKEND
if (META_IS_BACKEND_NATIVE (meta_get_backend ()))
return TRUE;
#endif
if (META_IS_BACKEND_X11_CM (meta_get_backend ()))
{
if (meta_is_wayland_compositor())
return FALSE;
return META_X11_DISPLAY_HAS_XINPUT_23 (display->x11_display);
}
return FALSE;
}
/**
* meta_display_get_context:
* @display: a #MetaDisplay
*
* Returns: (transfer none): the #MetaContext
*/
MetaContext *
meta_display_get_context (MetaDisplay *display)
{
MetaDisplayPrivate *priv = meta_display_get_instance_private (display);
return priv->context;
}
/**
* meta_display_get_compositor: (skip)
* @display: a #MetaDisplay
*
*/
MetaCompositor *
meta_display_get_compositor (MetaDisplay *display)
{
return display->compositor;
}
/**
* meta_display_get_x11_display: (skip)
* @display: a #MetaDisplay
*
*/
MetaX11Display *
meta_display_get_x11_display (MetaDisplay *display)
{
return display->x11_display;
}
/**
* meta_display_get_size:
* @display: A #MetaDisplay
* @width: (out): The width of the screen
* @height: (out): The height of the screen
*
* Retrieve the size of the display.
*/
void
meta_display_get_size (MetaDisplay *display,
int *width,
int *height)
{
MetaBackend *backend = meta_get_backend ();
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
int display_width, display_height;
meta_monitor_manager_get_screen_size (monitor_manager,
&display_width,
&display_height);
if (width != NULL)
*width = display_width;
if (height != NULL)
*height = display_height;
}
/**
* meta_display_get_focus_window:
* @display: a #MetaDisplay
*
* Get our best guess as to the "currently" focused window (that is,
* the window that we expect will be focused at the point when the X
* server processes our next request).
*
* Return Value: (transfer none): The current focus window
*/
MetaWindow *
meta_display_get_focus_window (MetaDisplay *display)
{
return display->focus_window;
}
/**
* meta_display_clear_mouse_mode:
* @display: a #MetaDisplay
*
* Sets the mouse-mode flag to %FALSE, which means that motion events are
* no longer ignored in mouse or sloppy focus.
* This is an internal function. It should be used only for reimplementing
* keybindings, and only in a manner compatible with core code.
*/
void
meta_display_clear_mouse_mode (MetaDisplay *display)
{
display->mouse_mode = FALSE;
}
MetaGestureTracker *
meta_display_get_gesture_tracker (MetaDisplay *display)
{
return display->gesture_tracker;
}
gboolean
meta_display_show_restart_message (MetaDisplay *display,
const char *message)
{
gboolean result = FALSE;
g_signal_emit (display,
display_signals[SHOW_RESTART_MESSAGE], 0,
message, &result);
return result;
}
gboolean
meta_display_request_restart (MetaDisplay *display)
{
gboolean result = FALSE;
g_signal_emit (display,
display_signals[RESTART], 0,
&result);
return result;
}
gboolean
meta_display_show_resize_popup (MetaDisplay *display,
gboolean show,
MetaRectangle *rect,
int display_w,
int display_h)
{
gboolean result = FALSE;
g_signal_emit (display,
display_signals[SHOW_RESIZE_POPUP], 0,
show, rect, display_w, display_h, &result);
return result;
}
/**
* meta_display_is_pointer_emulating_sequence:
* @display: the display
* @sequence: (nullable): a #ClutterEventSequence
*
* Tells whether the event sequence is the used for pointer emulation
* and single-touch interaction.
*
* Returns: #TRUE if the sequence emulates pointer behavior
**/
gboolean
meta_display_is_pointer_emulating_sequence (MetaDisplay *display,
ClutterEventSequence *sequence)
{
if (!sequence)
return FALSE;
return display->pointer_emulating_sequence == sequence;
}
void
meta_display_request_pad_osd (MetaDisplay *display,
ClutterInputDevice *pad,
gboolean edition_mode)
{
MetaBackend *backend = meta_get_backend ();
MetaInputMapper *input_mapper;
const gchar *layout_path = NULL;
ClutterActor *osd;
MetaLogicalMonitor *logical_monitor;
GSettings *settings;
#ifdef HAVE_LIBWACOM
WacomDevice *wacom_device;
#endif
/* Avoid emitting the signal while there is an OSD being currently
* displayed, the first OSD will have to be dismissed before showing
* any other one.
*/
if (display->current_pad_osd)
return;
input_mapper = meta_backend_get_input_mapper (meta_get_backend ());
if (input_mapper)
{
settings = meta_input_mapper_get_tablet_settings (input_mapper, pad);
logical_monitor =
meta_input_mapper_get_device_logical_monitor (input_mapper, pad);
#ifdef HAVE_LIBWACOM
wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (pad));
layout_path = libwacom_get_layout_filename (wacom_device);
#endif
}
if (!layout_path || !settings)
return;
if (!logical_monitor)
logical_monitor = meta_backend_get_current_logical_monitor (backend);
g_signal_emit (display, display_signals[SHOW_PAD_OSD], 0,
pad, settings, layout_path,
edition_mode, logical_monitor->number, &osd);
if (osd)
{
display->current_pad_osd = osd;
g_object_add_weak_pointer (G_OBJECT (display->current_pad_osd),
(gpointer *) &display->current_pad_osd);
}
}
gchar *
meta_display_get_pad_action_label (MetaDisplay *display,
ClutterInputDevice *pad,
MetaPadActionType action_type,
guint action_number)
{
gchar *label;
/* First, lookup the action, as imposed by settings */
label = meta_pad_action_mapper_get_action_label (display->pad_action_mapper,
pad, action_type,
action_number);
if (label)
return label;
#ifdef HAVE_WAYLAND
/* Second, if this wayland, lookup the actions set by the clients */
if (meta_is_wayland_compositor ())
{
MetaWaylandCompositor *compositor;
MetaWaylandTabletSeat *tablet_seat;
MetaWaylandTabletPad *tablet_pad = NULL;
compositor = meta_wayland_compositor_get_default ();
tablet_seat = meta_wayland_tablet_manager_ensure_seat (compositor->tablet_manager,
compositor->seat);
if (tablet_seat)
tablet_pad = meta_wayland_tablet_seat_lookup_pad (tablet_seat, pad);
if (tablet_pad)
{
label = meta_wayland_tablet_pad_get_label (tablet_pad, action_type,
action_number);
}
if (label)
return label;
}
#endif
return NULL;
}
static void
meta_display_show_osd (MetaDisplay *display,
gint monitor_idx,
const gchar *icon_name,
const gchar *message)
{
g_signal_emit (display, display_signals[SHOW_OSD], 0,
monitor_idx, icon_name, message);
}
static gint
lookup_tablet_monitor (MetaDisplay *display,
ClutterInputDevice *device)
{
MetaInputMapper *input_mapper;
MetaLogicalMonitor *monitor;
gint monitor_idx = -1;
input_mapper = meta_backend_get_input_mapper (meta_get_backend ());
if (!input_mapper)
return -1;
monitor = meta_input_mapper_get_device_logical_monitor (input_mapper, device);
if (monitor)
{
monitor_idx = meta_display_get_monitor_index_for_rect (display,
&monitor->rect);
}
return monitor_idx;
}
void
meta_display_show_tablet_mapping_notification (MetaDisplay *display,
ClutterInputDevice *pad,
const gchar *pretty_name)
{
if (!pretty_name)
pretty_name = clutter_input_device_get_device_name (pad);
meta_display_show_osd (display, lookup_tablet_monitor (display, pad),
"input-tablet-symbolic", pretty_name);
}
void
meta_display_notify_pad_group_switch (MetaDisplay *display,
ClutterInputDevice *pad,
const gchar *pretty_name,
guint n_group,
guint n_mode,
guint n_modes)
{
GString *message;
guint i;
if (!pretty_name)
pretty_name = clutter_input_device_get_device_name (pad);
message = g_string_new (pretty_name);
g_string_append_c (message, '\n');
for (i = 0; i < n_modes; i++)
g_string_append (message, (i == n_mode) ? "⚫" : "⚪");
meta_display_show_osd (display, lookup_tablet_monitor (display, pad),
"input-tablet-symbolic", message->str);
g_signal_emit (display, display_signals[PAD_MODE_SWITCH], 0, pad,
n_group, n_mode);
g_string_free (message, TRUE);
}
void
meta_display_foreach_window (MetaDisplay *display,
MetaListWindowsFlags flags,
MetaDisplayWindowFunc func,
gpointer data)
{
GSList *windows;
/* If we end up doing this often, just keeping a list
* of windows might be sensible.
*/
windows = meta_display_list_windows (display, flags);
g_slist_foreach (windows, (GFunc) func, data);
g_slist_free (windows);
}
static void
meta_display_resize_func (MetaWindow *window,
gpointer user_data)
{
if (window->struts)
{
meta_window_update_struts (window);
}
meta_window_queue (window, META_QUEUE_MOVE_RESIZE);
meta_window_recalc_features (window);
}
static void
on_monitors_changed_internal (MetaMonitorManager *monitor_manager,
MetaDisplay *display)
{
meta_workspace_manager_reload_work_areas (display->workspace_manager);
/* Fix up monitor for all windows on this display */
meta_display_foreach_window (display, META_LIST_INCLUDE_OVERRIDE_REDIRECT,
(MetaDisplayWindowFunc)
meta_window_update_for_monitors_changed, 0);
/* Queue a resize on all the windows */
meta_display_foreach_window (display, META_LIST_DEFAULT,
meta_display_resize_func, 0);
meta_display_queue_check_fullscreen (display);
}
void
meta_display_restacked (MetaDisplay *display)
{
g_signal_emit (display, display_signals[RESTACKED], 0);
}
static gboolean
meta_display_update_tile_preview_timeout (gpointer data)
{
MetaDisplay *display = data;
MetaWindow *window = display->grab_window;
gboolean needs_preview = FALSE;
display->tile_preview_timeout_id = 0;
if (window)
{
switch (display->preview_tile_mode)
{
case META_TILE_LEFT:
case META_TILE_RIGHT:
if (!META_WINDOW_TILED_SIDE_BY_SIDE (window))
needs_preview = TRUE;
break;
case META_TILE_MAXIMIZED:
if (!META_WINDOW_MAXIMIZED (window))
needs_preview = TRUE;
break;
default:
needs_preview = FALSE;
break;
}
}
if (needs_preview)
{
MetaRectangle tile_rect;
int monitor;
monitor = meta_window_get_current_tile_monitor_number (window);
meta_window_get_tile_area (window, display->preview_tile_mode,
&tile_rect);
meta_compositor_show_tile_preview (display->compositor,
window, &tile_rect, monitor);
}
else
meta_compositor_hide_tile_preview (display->compositor);
return FALSE;
}
#define TILE_PREVIEW_TIMEOUT_MS 200
void
meta_display_update_tile_preview (MetaDisplay *display,
gboolean delay)
{
if (delay)
{
if (display->tile_preview_timeout_id > 0)
return;
display->tile_preview_timeout_id =
g_timeout_add (TILE_PREVIEW_TIMEOUT_MS,
meta_display_update_tile_preview_timeout,
display);
g_source_set_name_by_id (display->tile_preview_timeout_id,
"[mutter] meta_display_update_tile_preview_timeout");
}
else
{
g_clear_handle_id (&display->tile_preview_timeout_id, g_source_remove);
meta_display_update_tile_preview_timeout ((gpointer)display);
}
}
void
meta_display_hide_tile_preview (MetaDisplay *display)
{
g_clear_handle_id (&display->tile_preview_timeout_id, g_source_remove);
display->preview_tile_mode = META_TILE_NONE;
meta_compositor_hide_tile_preview (display->compositor);
}
static MetaStartupSequence *
find_startup_sequence_by_wmclass (MetaDisplay *display,
MetaWindow *window)
{
GSList *startup_sequences, *l;
startup_sequences =
meta_startup_notification_get_sequences (display->startup_notification);
for (l = startup_sequences; l; l = l->next)
{
MetaStartupSequence *sequence = l->data;
const char *wmclass;
wmclass = meta_startup_sequence_get_wmclass (sequence);
if (wmclass != NULL &&
((window->res_class &&
strcmp (wmclass, window->res_class) == 0) ||
(window->res_name &&
strcmp (wmclass, window->res_name) == 0)))
return sequence;
}
return NULL;
}
/* Sets the initial_timestamp and initial_workspace properties
* of a window according to information given us by the
* startup-notification library.
*
* Returns TRUE if startup properties have been applied, and
* FALSE if they have not (for example, if they had already
* been applied.)
*/
gboolean
meta_display_apply_startup_properties (MetaDisplay *display,
MetaWindow *window)
{
const char *startup_id;
MetaStartupSequence *sequence = NULL;
/* Does the window have a startup ID stored? */
startup_id = meta_window_get_startup_id (window);
meta_topic (META_DEBUG_STARTUP,
"Applying startup props to %s id \"%s\"",
window->desc,
startup_id ? startup_id : "(none)");
if (!startup_id)
{
/* No startup ID stored for the window. Let's ask the
* startup-notification library whether there's anything
* stored for the resource name or resource class hints.
*/
sequence = find_startup_sequence_by_wmclass (display, window);
if (sequence)
{
g_assert (window->startup_id == NULL);
window->startup_id = g_strdup (meta_startup_sequence_get_id (sequence));
startup_id = window->startup_id;
meta_topic (META_DEBUG_STARTUP,
"Ending legacy sequence %s due to window %s",
meta_startup_sequence_get_id (sequence),
window->desc);
meta_startup_sequence_complete (sequence);
}
}
/* Still no startup ID? Bail. */
if (!startup_id)
return FALSE;
/* We might get this far and not know the sequence ID (if the window
* already had a startup ID stored), so let's look for one if we don't
* already know it.
*/
if (sequence == NULL)
{
sequence =
meta_startup_notification_lookup_sequence (display->startup_notification,
startup_id);
}
if (sequence != NULL)
{
gboolean changed_something = FALSE;
meta_topic (META_DEBUG_STARTUP,
"Found startup sequence for window %s ID \"%s\"",
window->desc, startup_id);
if (!window->initial_workspace_set)
{
int space = meta_startup_sequence_get_workspace (sequence);
if (space >= 0)
{
meta_topic (META_DEBUG_STARTUP,
"Setting initial window workspace to %d based on startup info",
space);
window->initial_workspace_set = TRUE;
window->initial_workspace = space;
changed_something = TRUE;
}
}
if (!window->initial_timestamp_set)
{
guint32 timestamp = meta_startup_sequence_get_timestamp (sequence);
meta_topic (META_DEBUG_STARTUP,
"Setting initial window timestamp to %u based on startup info",
timestamp);
window->initial_timestamp_set = TRUE;
window->initial_timestamp = timestamp;
changed_something = TRUE;
}
return changed_something;
}
else
{
meta_topic (META_DEBUG_STARTUP,
"Did not find startup sequence for window %s ID \"%s\"",
window->desc, startup_id);
}
return FALSE;
}
static gboolean
set_work_area_later_func (MetaDisplay *display)
{
meta_topic (META_DEBUG_WORKAREA,
"Running work area hint computation function");
display->work_area_later = 0;
g_signal_emit (display, display_signals[WORKAREAS_CHANGED], 0);
return FALSE;
}
void
meta_display_queue_workarea_recalc (MetaDisplay *display)
{
/* Recompute work area later before redrawing */
if (display->work_area_later == 0)
{
meta_topic (META_DEBUG_WORKAREA,
"Adding work area hint computation function");
display->work_area_later =
meta_later_add (META_LATER_BEFORE_REDRAW,
(GSourceFunc) set_work_area_later_func,
display,
NULL);
}
}
static gboolean
check_fullscreen_func (gpointer data)
{
MetaDisplay *display = data;
MetaBackend *backend = meta_get_backend ();
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
GList *logical_monitors, *l;
MetaWindow *window;
GSList *fullscreen_monitors = NULL;
GSList *obscured_monitors = NULL;
gboolean in_fullscreen_changed = FALSE;
display->check_fullscreen_later = 0;
logical_monitors =
meta_monitor_manager_get_logical_monitors (monitor_manager);
/* We consider a monitor in fullscreen if it contains a fullscreen window;
* however we make an exception for maximized windows above the fullscreen
* one, as in that case window+chrome fully obscure the fullscreen window.
*/
for (window = meta_stack_get_top (display->stack);
window;
window = meta_stack_get_below (display->stack, window, FALSE))
{
gboolean covers_monitors = FALSE;
if (window->hidden)
continue;
if (window->fullscreen)
{
covers_monitors = TRUE;
}
else if (window->override_redirect)
{
/* We want to handle the case where an application is creating an
* override-redirect window the size of the screen (monitor) and treat
* it similarly to a fullscreen window, though it doesn't have fullscreen
* window management behavior. (Being O-R, it's not managed at all.)
*/
if (meta_window_is_monitor_sized (window))
covers_monitors = TRUE;
}
else if (window->maximized_horizontally &&
window->maximized_vertically)
{
MetaLogicalMonitor *logical_monitor;
logical_monitor = meta_window_get_main_logical_monitor (window);
if (!g_slist_find (obscured_monitors, logical_monitor))
obscured_monitors = g_slist_prepend (obscured_monitors,
logical_monitor);
}
if (covers_monitors)
{
MetaRectangle window_rect;
meta_window_get_frame_rect (window, &window_rect);
for (l = logical_monitors; l; l = l->next)
{
MetaLogicalMonitor *logical_monitor = l->data;
if (meta_rectangle_overlap (&window_rect,
&logical_monitor->rect) &&
!g_slist_find (fullscreen_monitors, logical_monitor) &&
!g_slist_find (obscured_monitors, logical_monitor))
fullscreen_monitors = g_slist_prepend (fullscreen_monitors,
logical_monitor);
}
}
}
g_slist_free (obscured_monitors);
for (l = logical_monitors; l; l = l->next)
{
MetaLogicalMonitor *logical_monitor = l->data;
gboolean in_fullscreen;
in_fullscreen = g_slist_find (fullscreen_monitors,
logical_monitor) != NULL;
if (in_fullscreen != logical_monitor->in_fullscreen)
{
logical_monitor->in_fullscreen = in_fullscreen;
in_fullscreen_changed = TRUE;
}
}
g_slist_free (fullscreen_monitors);
if (in_fullscreen_changed)
{
/* DOCK window stacking depends on the monitor's fullscreen
status so we need to trigger a re-layering. */
MetaWindow *window = meta_stack_get_top (display->stack);
if (window)
meta_stack_update_layer (display->stack, window);
g_signal_emit (display, display_signals[IN_FULLSCREEN_CHANGED], 0, NULL);
}
return FALSE;
}
void
meta_display_queue_check_fullscreen (MetaDisplay *display)
{
if (!display->check_fullscreen_later)
display->check_fullscreen_later = meta_later_add (META_LATER_CHECK_FULLSCREEN,
check_fullscreen_func,
display, NULL);
}
int
meta_display_get_monitor_index_for_rect (MetaDisplay *display,
MetaRectangle *rect)
{
MetaBackend *backend = meta_get_backend ();
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaLogicalMonitor *logical_monitor;
logical_monitor =
meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager, rect);
if (!logical_monitor)
return -1;
return logical_monitor->number;
}
int
meta_display_get_monitor_neighbor_index (MetaDisplay *display,
int which_monitor,
MetaDisplayDirection direction)
{
MetaBackend *backend = meta_get_backend ();
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaLogicalMonitor *logical_monitor;
MetaLogicalMonitor *neighbor;
logical_monitor =
meta_monitor_manager_get_logical_monitor_from_number (monitor_manager,
which_monitor);
neighbor = meta_monitor_manager_get_logical_monitor_neighbor (monitor_manager,
logical_monitor,
direction);
return neighbor ? neighbor->number : -1;
}
/**
* meta_display_get_current_monitor:
* @display: a #MetaDisplay
*
* Gets the index of the monitor that currently has the mouse pointer.
*
* Return value: a monitor index
*/
int
meta_display_get_current_monitor (MetaDisplay *display)
{
MetaBackend *backend = meta_get_backend ();
MetaLogicalMonitor *logical_monitor;
logical_monitor = meta_backend_get_current_logical_monitor (backend);
/* Pretend its the first when there is no actual current monitor. */
if (!logical_monitor)
return 0;
return logical_monitor->number;
}
/**
* meta_display_get_n_monitors:
* @display: a #MetaDisplay
*
* Gets the number of monitors that are joined together to form @display.
*
* Return value: the number of monitors
*/
int
meta_display_get_n_monitors (MetaDisplay *display)
{
MetaBackend *backend = meta_get_backend ();
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
g_return_val_if_fail (META_IS_DISPLAY (display), 0);
return meta_monitor_manager_get_num_logical_monitors (monitor_manager);
}
/**
* meta_display_get_primary_monitor:
* @display: a #MetaDisplay
*
* Gets the index of the primary monitor on this @display.
*
* Return value: a monitor index
*/
int
meta_display_get_primary_monitor (MetaDisplay *display)
{
MetaBackend *backend = meta_get_backend ();
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaLogicalMonitor *logical_monitor;
g_return_val_if_fail (META_IS_DISPLAY (display), 0);
logical_monitor =
meta_monitor_manager_get_primary_logical_monitor (monitor_manager);
if (logical_monitor)
return logical_monitor->number;
else
return 0;
}
/**
* meta_display_get_monitor_geometry:
* @display: a #MetaDisplay
* @monitor: the monitor number
* @geometry: (out): location to store the monitor geometry
*
* Stores the location and size of the indicated @monitor in @geometry.
*/
void
meta_display_get_monitor_geometry (MetaDisplay *display,
int monitor,
MetaRectangle *geometry)
{
MetaBackend *backend = meta_get_backend ();
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaLogicalMonitor *logical_monitor;
#ifndef G_DISABLE_CHECKS
int n_logical_monitors =
meta_monitor_manager_get_num_logical_monitors (monitor_manager);
#endif
g_return_if_fail (META_IS_DISPLAY (display));
g_return_if_fail (monitor >= 0 && monitor < n_logical_monitors);
g_return_if_fail (geometry != NULL);
logical_monitor =
meta_monitor_manager_get_logical_monitor_from_number (monitor_manager,
monitor);
*geometry = logical_monitor->rect;
}
/**
* meta_display_get_monitor_scale:
* @display: a #MetaDisplay
* @monitor: the monitor number
*
* Gets the monitor scaling value for the given @monitor.
*
* Return value: the monitor scaling value
*/
float
meta_display_get_monitor_scale (MetaDisplay *display,
int monitor)
{
MetaBackend *backend = meta_get_backend ();
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaLogicalMonitor *logical_monitor;
#ifndef G_DISABLE_CHECKS
int n_logical_monitors =
meta_monitor_manager_get_num_logical_monitors (monitor_manager);
#endif
g_return_val_if_fail (META_IS_DISPLAY (display), 1.0f);
g_return_val_if_fail (monitor >= 0 && monitor < n_logical_monitors, 1.0f);
logical_monitor =
meta_monitor_manager_get_logical_monitor_from_number (monitor_manager,
monitor);
return logical_monitor->scale;
}
/**
* meta_display_get_monitor_in_fullscreen:
* @display: a #MetaDisplay
* @monitor: the monitor number
*
* Determines whether there is a fullscreen window obscuring the specified
* monitor. If there is a fullscreen window, the desktop environment will
* typically hide any controls that might obscure the fullscreen window.
*
* You can get notification when this changes by connecting to
* MetaDisplay::in-fullscreen-changed.
*
* Returns: %TRUE if there is a fullscreen window covering the specified monitor.
*/
gboolean
meta_display_get_monitor_in_fullscreen (MetaDisplay *display,
int monitor)
{
MetaBackend *backend = meta_get_backend ();
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaLogicalMonitor *logical_monitor;
#ifndef G_DISABLE_CHECKS
int n_logical_monitors =
meta_monitor_manager_get_num_logical_monitors (monitor_manager);
#endif
g_return_val_if_fail (META_IS_DISPLAY (display), FALSE);
g_return_val_if_fail (monitor >= 0 &&
monitor < n_logical_monitors, FALSE);
logical_monitor =
meta_monitor_manager_get_logical_monitor_from_number (monitor_manager,
monitor);
/* We use -1 as a flag to mean "not known yet" for notification
purposes */ return logical_monitor->in_fullscreen == TRUE;
}
MetaWindow *
meta_display_get_pointer_window (MetaDisplay *display,
MetaWindow *not_this_one)
{
MetaWorkspaceManager *workspace_manager = display->workspace_manager;
MetaBackend *backend = meta_get_backend ();
MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
MetaWindow *window;
graphene_point_t point;
if (not_this_one)
meta_topic (META_DEBUG_FOCUS,
"Focusing mouse window excluding %s", not_this_one->desc);
meta_cursor_tracker_get_pointer (cursor_tracker, &point, NULL);
window = meta_stack_get_default_focus_window_at_point (display->stack,
workspace_manager->active_workspace,
not_this_one,
point.x, point.y);
return window;
}
void
meta_display_focus_default_window (MetaDisplay *display,
guint32 timestamp)
{
MetaWorkspaceManager *workspace_manager = display->workspace_manager;
meta_workspace_focus_default_window (workspace_manager->active_workspace,
NULL,
timestamp);
}
/**
* meta_display_get_workspace_manager:
* @display: a #MetaDisplay
*
* Returns: (transfer none): The workspace manager of the display
*/
MetaWorkspaceManager *
meta_display_get_workspace_manager (MetaDisplay *display)
{
return display->workspace_manager;
}
MetaStartupNotification *
meta_display_get_startup_notification (MetaDisplay *display)
{
return display->startup_notification;
}
MetaWindow *
meta_display_get_window_from_id (MetaDisplay *display,
uint64_t window_id)
{
g_autoptr (GSList) windows = NULL;
GSList *l;
windows = meta_display_list_windows (display, META_LIST_DEFAULT);
for (l = windows; l; l = l->next)
{
MetaWindow *window = l->data;
if (window->id == window_id)
return window;
}
return NULL;
}
uint64_t
meta_display_generate_window_id (MetaDisplay *display)
{
static uint64_t base_window_id;
static uint64_t last_window_id;
if (!base_window_id)
base_window_id = g_random_int () + 1;
/* We can overflow here, that's fine */
return (base_window_id + last_window_id++);
}
/**
* meta_display_get_sound_player:
* @display: a #MetaDisplay
*
* Returns: (transfer none): The sound player of the display
*/
MetaSoundPlayer *
meta_display_get_sound_player (MetaDisplay *display)
{
return display->sound_player;
}
/**
* meta_display_get_selection:
* @display: a #MetaDisplay
*
* Returns: (transfer none): The selection manager of the display
*/
MetaSelection *
meta_display_get_selection (MetaDisplay *display)
{
return display->selection;
}