/* -*- 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 "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-clutter-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 "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-wayland.h" #include "wayland/meta-wayland-input-device.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-tablet-seat.h" #include "wayland/meta-wayland-tablet-pad.h" #include "wayland/meta-wayland-tablet-manager.h" #include "wayland/meta-wayland-touch.h" #endif #ifdef HAVE_XWAYLAND #include "wayland/meta-xwayland-private.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 { MetaDisplay *display; int queue_idx; } MetaQueueRunData; typedef struct _MetaDisplayPrivate { MetaContext *context; guint queue_later_ids[META_N_QUEUE_TYPES]; GList *queue_windows[META_N_QUEUE_TYPES]; } 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, WINDOW_VISIBILITY_UPDATED, LAST_SIGNAL }; enum { PROP_0, PROP_COMPOSITOR_MODIFIERS, PROP_FOCUS_WINDOW }; static guint display_signals [LAST_SIGNAL] = { 0 }; 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_show_osd (MetaDisplay *display, gint monitor_idx, const gchar *icon_name, const gchar *message); static MetaBackend * backend_from_display (MetaDisplay *display) { MetaContext *context = meta_display_get_context (display); return meta_context_get_backend (context); } #ifdef HAVE_WAYLAND static MetaWaylandCompositor * wayland_compositor_from_display (MetaDisplay *display) { MetaContext *context = meta_display_get_context (display); return meta_context_get_wayland_compositor (context); } #endif 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); display_signals[WINDOW_VISIBILITY_UPDATED] = g_signal_new ("window-visibility-updated", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 3, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER); 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 = backend_from_display (display); #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 = wayland_compositor_from_display (display); 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 = backend_from_display (display); 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 void on_monitor_privacy_screen_changed (MetaDisplay *display, MetaLogicalMonitor *logical_monitor, gboolean enabled) { meta_display_show_osd (display, logical_monitor->number, enabled ? "screen-privacy-symbolic" : "screen-privacy-disabled-symbolic", enabled ? _("Privacy Screen Enabled") : _("Privacy Screen Disabled")); } 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; } #ifdef HAVE_XWAYLAND 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); } } #endif /* HAVE_XWAYLAND */ void meta_display_init_x11 (MetaDisplay *display, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr (GTask) task = NULL; #ifdef HAVE_XWAYLAND MetaWaylandCompositor *compositor; #endif task = g_task_new (display, cancellable, callback, user_data); g_task_set_source_tag (task, meta_display_init_x11); #ifdef HAVE_XWAYLAND compositor = wayland_compositor_from_display (display); meta_xwayland_start_xserver (&compositor->xwayland_manager, cancellable, (GAsyncReadyCallback) on_xserver_started, g_steal_pointer (&task)); #endif } 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; meta_stack_freeze (display->stack); 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); meta_stack_thaw (display->stack); } MetaDisplay * meta_display_new (MetaContext *context, GError **error) { MetaBackend *backend = meta_context_get_backend (context); MetaDisplay *display; MetaDisplayPrivate *priv; guint32 timestamp; Window old_active_xwindow = None; MetaMonitorManager *monitor_manager; MetaSettings *settings; 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->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 */ display->current_time = META_CURRENT_TIME; 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); g_signal_connect_object (monitor_manager, "monitor-privacy-screen-changed", G_CALLBACK (on_monitor_privacy_screen_changed), display, G_CONNECT_SWAPPED); 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 ()) { MetaWaylandCompositor *wayland_compositor = wayland_compositor_from_display (display); MetaX11DisplayPolicy x11_display_policy; meta_wayland_compositor_init_display (wayland_compositor, display); 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) { MetaCompositor *compositor; MetaLaters *laters; if (display->closing != 0) { /* The display's already been closed. */ return; } display->closing += 1; g_signal_emit (display, display_signals[CLOSING], 0); meta_display_unmanage_windows (display, timestamp); meta_compositor_unmanage (display->compositor); 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); compositor = meta_display_get_compositor (display); laters = meta_compositor_get_laters (compositor); if (display->work_area_later != 0) meta_laters_remove (laters, display->work_area_later); if (display->check_fullscreen_later != 0) meta_laters_remove (laters, display->check_fullscreen_later); /* Stop caring about events */ meta_display_free_events (display); meta_display_shutdown_x11 (display); g_clear_object (&display->stack); g_clear_pointer (&display->stack_tracker, meta_stack_tracker_free); g_clear_pointer (&display->compositor, meta_compositor_destroy); /* 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); } gboolean meta_grab_op_is_mouse (MetaGrabOp op) { return (op & META_GRAB_OP_WINDOW_FLAG_KEYBOARD) == 0; } gboolean meta_grab_op_is_keyboard (MetaGrabOp op) { return (op & META_GRAB_OP_WINDOW_FLAG_KEYBOARD) != 0; } gboolean meta_grab_op_is_resizing (MetaGrabOp op) { return (op & META_GRAB_OP_WINDOW_DIR_MASK) != 0 || op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN; } gboolean meta_grab_op_is_moving (MetaGrabOp op) { 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) { MetaBackend *backend = backend_from_display (display); MetaStage *stage = META_STAGE (meta_backend_get_stage (backend)); if (clutter_stage_get_grab_actor (CLUTTER_STAGE (stage))) return FALSE; return TRUE; } /** * 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); } 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 = wayland_compositor_from_display (display); MetaWindow *focus_window = NULL; MetaBackend *backend = backend_from_display (display); 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 && meta_window_get_wayland_surface (display->focus_window)) focus_window = display->focus_window; else meta_topic (META_DEBUG_FOCUS, "Focus change has no effect, because there is no matching wayland surface"); meta_wayland_compositor_set_input_focus (compositor, focus_window); clutter_stage_repick_device (CLUTTER_STAGE (stage), clutter_seat_get_pointer (seat)); #endif } static void meta_window_set_inactive_since (MetaWindow *window, int64_t inactive_since_us) { GFile *file = NULL; g_autoptr (GFileInfo) file_info = NULL; g_autofree char *timestamp = NULL; timestamp = g_strdup_printf ("%" G_GINT64_FORMAT, inactive_since_us); file = meta_window_get_unit_cgroup (window); if (!file) return; file_info = g_file_info_new (); g_file_info_set_attribute_string (file_info, "xattr::xdg.inactive-since", timestamp); if (!g_file_set_attributes_from_info (file, file_info, G_FILE_QUERY_INFO_NONE, NULL, NULL)) return; } void meta_display_update_focus_window (MetaDisplay *display, MetaWindow *window) { MetaWindow *previous = NULL; if (display->focus_window == window) return; if (display->focus_window) { 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 (!previous || !display->focus_window || !meta_window_unit_cgroup_equal (previous, display->focus_window)) { if (previous) meta_window_set_inactive_since (previous, g_get_monotonic_time ()); if (display->focus_window) meta_window_set_inactive_since (display->focus_window, -1); } 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 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 = backend_from_display (display); if (meta_backend_is_stage_views_scaled (backend)) { 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) { meta_cursor_sprite_set_prepare_func (META_CURSOR_SPRITE (sprite_xcursor), (MetaCursorPrepareFunc) root_cursor_prepare_at, display); } void meta_display_reload_cursor (MetaDisplay *display) { MetaCursor cursor = display->current_cursor; MetaCursorSpriteXcursor *sprite_xcursor; MetaBackend *backend = backend_from_display (display); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); sprite_xcursor = meta_cursor_sprite_xcursor_new (cursor, cursor_tracker); 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); } /** * meta_display_is_grabbed: * @display: The #MetaDisplay that the window is on * Returns %TRUE if there is an ongoing grab operation. * * Return value: Whether there is an active display grab operation. */ gboolean meta_display_is_grabbed (MetaDisplay *display) { return meta_compositor_get_current_window_drag (display->compositor) != NULL; } 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); tmp = tmp->next; } g_slist_free (windows); } /** * 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); meta_window_show_close_dialog (window); 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); window->events_during_ping = 0; } /** * 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_list_all_windows: * @display: a #MetaDisplay * * List all windows, including override-redirect ones. The windows are * in no particular order. * * Returns: (transfer container) (element-type Meta.Window): List of windows */ GList * meta_display_list_all_windows (MetaDisplay *display) { GList *all_windows = NULL; g_autoptr (GSList) windows = NULL; GSList *l; windows = meta_display_list_windows (display, META_LIST_INCLUDE_OVERRIDE_REDIRECT); /* Yay for mixing GList and GSList in the API */ for (l = windows; l; l = l->next) all_windows = g_list_prepend (all_windows, l->data); return all_windows; } /** * 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; op &= ~(META_GRAB_OP_WINDOW_FLAG_UNCONSTRAINED); 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) { MetaContext *context = meta_display_get_context (display); MetaBackend *backend = meta_context_get_backend (context); return !!(meta_backend_get_capabilities (backend) & META_BACKEND_CAPABILITY_BARRIERS); } /** * 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: * @display: a #MetaDisplay * * Returns: (transfer none): the #MetaCompositor */ 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 = backend_from_display (display); 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 = backend_from_display (display); 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 (backend_from_display (display)); 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 = wayland_compositor_from_display (display); 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 (backend_from_display (display)); 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 (message, "\n\n"); for (i = 0; i < n_modes; i++) { if (i > 0) g_string_append_c (message, ' '); 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 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) { MetaLaters *laters = meta_compositor_get_laters (display->compositor); meta_topic (META_DEBUG_WORKAREA, "Adding work area hint computation function"); display->work_area_later = meta_laters_add (laters, META_LATER_BEFORE_REDRAW, (GSourceFunc) set_work_area_later_func, display, NULL); } } static gboolean check_fullscreen_func (gpointer data) { MetaDisplay *display = data; MetaBackend *backend = backend_from_display (display); 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) { MetaLaters *laters; if (display->check_fullscreen_later) return; laters = meta_compositor_get_laters (display->compositor); display->check_fullscreen_later = meta_laters_add (laters, META_LATER_CHECK_FULLSCREEN, check_fullscreen_func, display, NULL); } int meta_display_get_monitor_index_for_rect (MetaDisplay *display, MetaRectangle *rect) { MetaBackend *backend = backend_from_display (display); 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 = backend_from_display (display); 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 = backend_from_display (display); 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 = backend_from_display (display); 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 = backend_from_display (display); 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 = backend_from_display (display); 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 = backend_from_display (display); 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 = backend_from_display (display); 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; } 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; } #ifdef WITH_VERBOSE_MODE static const char* meta_window_queue_names[META_N_QUEUE_TYPES] = { "calc-showing", "move-resize", }; #endif static int window_stack_cmp (gconstpointer a, gconstpointer b) { MetaWindow *aw = (gpointer) a; MetaWindow *bw = (gpointer) b; return meta_stack_windows_cmp (aw->display->stack, aw, bw); } static void warn_on_incorrectly_unmanaged_window (MetaWindow *window) { g_warn_if_fail (!window->unmanaging); } static void update_window_visibilities (MetaDisplay *display, GList *windows) { g_autoptr (GList) unplaced = NULL; g_autoptr (GList) should_show = NULL; g_autoptr (GList) should_hide = NULL; GList *l; COGL_TRACE_BEGIN_SCOPED (MetaDisplayUpdateVisibility, "Display: Update visibility"); for (l = windows; l; l = l->next) { MetaWindow *window = l->data; if (!window->placed) unplaced = g_list_prepend (unplaced, window); else if (meta_window_should_be_showing (window)) should_show = g_list_prepend (should_show, window); else should_hide = g_list_prepend (should_hide, window); } /* Sort bottom to top */ unplaced = g_list_sort (unplaced, window_stack_cmp); should_hide = g_list_sort (should_hide, window_stack_cmp); /* Sort top to bottom */ should_show = g_list_sort (should_show, window_stack_cmp); should_show = g_list_reverse (should_show); COGL_TRACE_BEGIN (MetaDisplayShowUnplacedWindows, "Display: Show unplaced windows"); g_list_foreach (unplaced, (GFunc) meta_window_update_visibility, NULL); COGL_TRACE_END (MetaDisplayShowUnplacedWindows); meta_stack_freeze (display->stack); COGL_TRACE_BEGIN (MetaDisplayShowWindows, "Display: Show windows"); g_list_foreach (should_show, (GFunc) meta_window_update_visibility, NULL); COGL_TRACE_END (MetaDisplayShowWindows); COGL_TRACE_BEGIN (MetaDisplayHideWindows, "Display: Hide windows"); g_list_foreach (should_hide, (GFunc) meta_window_update_visibility, NULL); COGL_TRACE_END (MetaDisplayHideWindows); meta_stack_thaw (display->stack); g_list_foreach (windows, (GFunc) meta_window_clear_queued, NULL); g_signal_emit (display, display_signals[WINDOW_VISIBILITY_UPDATED], 0, unplaced, should_show, should_hide); g_list_foreach (windows, (GFunc) warn_on_incorrectly_unmanaged_window, NULL); } static void move_resize (MetaDisplay *display, GList *windows) { g_list_foreach (windows, (GFunc) meta_window_update_layout, NULL); g_list_foreach (windows, (GFunc) warn_on_incorrectly_unmanaged_window, NULL); } typedef void (* WindowQueueFunc) (MetaDisplay *display, GList *windows); static const WindowQueueFunc window_queue_func[META_N_QUEUE_TYPES] = { update_window_visibilities, move_resize, }; static gboolean window_queue_run_later_func (gpointer user_data) { MetaQueueRunData *run_data = user_data; MetaDisplay *display = run_data->display; MetaDisplayPrivate *priv = meta_display_get_instance_private (display); g_autoptr (GList) windows = NULL; int queue_idx = run_data->queue_idx; windows = g_steal_pointer (&priv->queue_windows[queue_idx]); priv->queue_later_ids[queue_idx] = 0; window_queue_func[queue_idx] (display, windows); return G_SOURCE_REMOVE; } void meta_display_queue_window (MetaDisplay *display, MetaWindow *window, MetaQueueType queue_types) { MetaDisplayPrivate *priv = meta_display_get_instance_private (display); MetaCompositor *compositor = display->compositor; MetaLaters *laters = meta_compositor_get_laters (compositor); int queue_idx; for (queue_idx = 0; queue_idx < META_N_QUEUE_TYPES; queue_idx++) { const MetaLaterType window_queue_later_when[META_N_QUEUE_TYPES] = { META_LATER_CALC_SHOWING, META_LATER_RESIZE, }; if (!(queue_types & 1 << queue_idx)) continue; meta_topic (META_DEBUG_WINDOW_STATE, "Queueing %s for window '%s'", meta_window_queue_names[queue_idx], meta_window_get_description (window)); priv->queue_windows[queue_idx] = g_list_prepend (priv->queue_windows[queue_idx], window); if (!priv->queue_later_ids[queue_idx]) { MetaQueueRunData *run_data; run_data = g_new0 (MetaQueueRunData, 1); run_data->display = display; run_data->queue_idx = queue_idx; priv->queue_later_ids[queue_idx] = meta_laters_add (laters, window_queue_later_when[queue_idx], window_queue_run_later_func, run_data, g_free); } } } void meta_display_unqueue_window (MetaDisplay *display, MetaWindow *window, MetaQueueType queue_types) { MetaDisplayPrivate *priv = meta_display_get_instance_private (display); MetaCompositor *compositor = display->compositor; MetaLaters *laters = meta_compositor_get_laters (compositor); int queue_idx; for (queue_idx = 0; queue_idx < META_N_QUEUE_TYPES; queue_idx++) { if (!(queue_types & 1 << queue_idx)) continue; meta_topic (META_DEBUG_WINDOW_STATE, "Unqueuing %s for window '%s'", meta_window_queue_names[queue_idx], window->desc); priv->queue_windows[queue_idx] = g_list_remove (priv->queue_windows[queue_idx], window); if (!priv->queue_windows[queue_idx] && priv->queue_later_ids[queue_idx]) { meta_laters_remove (laters, priv->queue_later_ids[queue_idx]); priv->queue_later_ids[queue_idx] = 0; } } } void meta_display_flush_queued_window (MetaDisplay *display, MetaWindow *window, MetaQueueType queue_types) { g_autoptr (GList) windows = NULL; int queue_idx; meta_display_unqueue_window (display, window, queue_types); windows = g_list_prepend (windows, window); for (queue_idx = 0; queue_idx < META_N_QUEUE_TYPES; queue_idx++) { if (!(queue_types & 1 << queue_idx)) continue; meta_topic (META_DEBUG_WINDOW_STATE, "Running %s for window '%s'", meta_window_queue_names[queue_idx], window->desc); window_queue_func[queue_idx] (display, windows); } }