diff options
Diffstat (limited to 'src/core')
71 files changed, 0 insertions, 43439 deletions
diff --git a/src/core/bell.c b/src/core/bell.c deleted file mode 100644 index f0c6f9463..000000000 --- a/src/core/bell.c +++ /dev/null @@ -1,209 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Mutter visual bell */ - -/* - * Copyright (C) 2002 Sun Microsystems Inc. - * Copyright (C) 2005, 2006 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -/* - * SECTION:bell - * @short_description: Ring the bell or flash the screen - * - * Sometimes, X programs "ring the bell", whatever that means. Mutter lets - * the user configure the bell to be audible or visible (aka visual), and - * if it's visual it can be configured to be frame-flash or fullscreen-flash. - * We never get told about audible bells; X handles them just fine by itself. - * - * Visual bells come in at meta_bell_notify(), which checks we are actually - * in visual mode and calls through to bell_visual_notify(). That - * function then checks what kind of visual flash you like, and calls either - * bell_flash_fullscreen()-- which calls bell_flash_screen() to do - * its work-- or bell_flash_frame(), which flashes the focused window - * using bell_flash_window(), unless there is no such window, in - * which case it flashes the screen instead. - * - * The visual bell was the result of a discussion in Bugzilla here: - * <http://bugzilla.gnome.org/show_bug.cgi?id=99886>. - * - * Several of the functions in this file are ifdeffed out entirely if we are - * found not to have the XKB extension, which is required to do these clever - * things with bells; some others are entirely no-ops in that case. - */ - -#include "config.h" - -#include "core/bell.h" - -#include "compositor/compositor-private.h" -#include "core/util-private.h" -#include "core/window-private.h" -#include "meta/compositor.h" - -G_DEFINE_TYPE (MetaBell, meta_bell, G_TYPE_OBJECT) - -enum -{ - IS_AUDIBLE_CHANGED, - LAST_SIGNAL -}; - -static guint bell_signals [LAST_SIGNAL] = { 0 }; - -static void -prefs_changed_callback (MetaPreference pref, - gpointer data) -{ - MetaBell *bell = data; - - if (pref == META_PREF_AUDIBLE_BELL) - { - g_signal_emit (bell, bell_signals[IS_AUDIBLE_CHANGED], 0, - meta_prefs_bell_is_audible ()); - } -} - -static void -meta_bell_finalize (GObject *object) -{ - MetaBell *bell = META_BELL (object); - - meta_prefs_remove_listener (prefs_changed_callback, bell); - - G_OBJECT_CLASS (meta_bell_parent_class)->finalize (object); -} - -static void -meta_bell_class_init (MetaBellClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = meta_bell_finalize; - - bell_signals[IS_AUDIBLE_CHANGED] = - g_signal_new ("is-audible-changed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 1, - G_TYPE_BOOLEAN); -} - -static void -meta_bell_init (MetaBell *bell) -{ - meta_prefs_add_listener (prefs_changed_callback, bell); -} - -MetaBell * -meta_bell_new (MetaDisplay *display) -{ - return g_object_new (META_TYPE_BELL, NULL); -} - -/** - * bell_flash_fullscreen: - * @display: The display the event came in on - * @xkb_ev: The bell event - * - * Flashes one screen, or all screens, in response to a bell event. - * If the event is on a particular window, flash the screen that - * window is on. Otherwise, flash every screen on this display. - * - * If the configure script found we had no XKB, this does not exist. - */ -static void -bell_flash_fullscreen (MetaDisplay *display) -{ - meta_compositor_flash_display (display->compositor, display); -} - -static void -bell_flash_window (MetaWindow *window) -{ - meta_compositor_flash_window (window->display->compositor, window); -} - -/** - * bell_flash_frame: - * @display: The display the bell event came in on - * @xkb_ev: The bell event we just received - * - * Flashes the frame of the focused window. If there is no focused window, - * flashes the screen. - */ -static void -bell_flash_frame (MetaDisplay *display, - MetaWindow *window) -{ - if (window) - bell_flash_window (window); - else - bell_flash_fullscreen (display); -} - -/** - * bell_visual_notify: - * @display: The display the bell event came in on - * @xkb_ev: The bell event we just received - * - * Gives the user some kind of visual bell substitute, in response to a - * bell event. What this is depends on the "visual bell type" pref. - */ -static void -bell_visual_notify (MetaDisplay *display, - MetaWindow *window) -{ - switch (meta_prefs_get_visual_bell_type ()) - { - case G_DESKTOP_VISUAL_BELL_FULLSCREEN_FLASH: - bell_flash_fullscreen (display); - break; - case G_DESKTOP_VISUAL_BELL_FRAME_FLASH: - bell_flash_frame (display, window); - break; - } -} - -static gboolean -bell_audible_notify (MetaDisplay *display, - MetaWindow *window) -{ - MetaSoundPlayer *player; - - player = meta_display_get_sound_player (display); - meta_sound_player_play_from_theme (player, - "bell-window-system", - _("Bell event"), - NULL); - return TRUE; -} - -gboolean -meta_bell_notify (MetaDisplay *display, - MetaWindow *window) -{ - /* flash something */ - if (meta_prefs_get_visual_bell ()) - bell_visual_notify (display, window); - - if (meta_prefs_bell_is_audible ()) - return bell_audible_notify (display, window); - - return TRUE; -} diff --git a/src/core/bell.h b/src/core/bell.h deleted file mode 100644 index f86cbb7c2..000000000 --- a/src/core/bell.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2002 Sun Microsystems Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "core/display-private.h" -#include "core/frame.h" - -struct _MetaBell -{ - GObject parent; -}; - -#define META_TYPE_BELL (meta_bell_get_type ()) -G_DECLARE_FINAL_TYPE (MetaBell, meta_bell, META, BELL, GObject) - -MetaBell * meta_bell_new (MetaDisplay *display); - -/** - * meta_bell_notify: - * @display: The display the bell event came in on - * @window: The window the bell event was received on - * - * Gives the user some kind of aural or visual feedback, such as a bell sound - * or flash. What type of feedback is invoked depends on the configuration. - * If the aural feedback could not be invoked, FALSE is returned. - */ -gboolean meta_bell_notify (MetaDisplay *display, - MetaWindow *window); diff --git a/src/core/boxes-private.h b/src/core/boxes-private.h deleted file mode 100644 index d39816495..000000000 --- a/src/core/boxes-private.h +++ /dev/null @@ -1,288 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Simple box operations */ - -/* - * Copyright (C) 2005, 2006 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef META_BOXES_PRIVATE_H -#define META_BOXES_PRIVATE_H - -#include <glib-object.h> - -#include "backends/meta-backend-types.h" -#include "core/util-private.h" -#include "meta/boxes.h" -#include "meta/common.h" - -#define BOX_LEFT(box) ((box).x) /* Leftmost pixel of rect */ -#define BOX_RIGHT(box) ((box).x + (box).width) /* One pixel past right */ -#define BOX_TOP(box) ((box).y) /* Topmost pixel of rect */ -#define BOX_BOTTOM(box) ((box).y + (box).height) /* One pixel past bottom */ - -typedef enum -{ - FIXED_DIRECTION_NONE = 0, - FIXED_DIRECTION_X = 1 << 0, - FIXED_DIRECTION_Y = 1 << 1, -} FixedDirections; - -typedef enum _MetaRoundingStrategy -{ - META_ROUNDING_STRATEGY_SHRINK, - META_ROUNDING_STRATEGY_GROW, - META_ROUNDING_STRATEGY_ROUND, -} MetaRoundingStrategy; - -/* Output functions -- note that the output buffer had better be big enough: - * rect_to_string: RECT_LENGTH - * region_to_string: (RECT_LENGTH+strlen(separator_string)) * - * g_list_length (region) - * edge_to_string: EDGE_LENGTH - * edge_list_to_...: (EDGE_LENGTH+strlen(separator_string)) * - * g_list_length (edge_list) - */ -#define RECT_LENGTH 27 -#define EDGE_LENGTH 37 -char* meta_rectangle_to_string (const MetaRectangle *rect, - char *output); -char* meta_rectangle_region_to_string (GList *region, - const char *separator_string, - char *output); -char* meta_rectangle_edge_to_string (const MetaEdge *edge, - char *output); -char* meta_rectangle_edge_list_to_string ( - GList *edge_list, - const char *separator_string, - char *output); - -/* Resize old_rect to the given new_width and new_height, but store the - * result in rect. NOTE THAT THIS IS RESIZE ONLY SO IT CANNOT BE USED FOR - * A MOVERESIZE OPERATION (that simplifies the routine a little bit as it - * means there's no difference between META_GRAVITY_NORTH_WEST and - * META_GRAVITY_STATIC. Also, I lied a little bit--technically, you could use - * it in a MoveResize operation if you muck with old_rect just right). - */ -META_EXPORT_TEST -void meta_rectangle_resize_with_gravity (const MetaRectangle *old_rect, - MetaRectangle *rect, - MetaGravity gravity, - int new_width, - int new_height); - -/* find a list of rectangles with the property that a window is contained - * in the given region if and only if it is contained in one of the - * rectangles in the list. - * - * In this case, the region is given by taking basic_rect, removing from - * it the intersections with all the rectangles in the all_struts list, - * then expanding all the rectangles in the resulting list by the given - * amounts on each side. - * - * See boxes.c for more details. - */ -META_EXPORT_TEST -GList* meta_rectangle_get_minimal_spanning_set_for_region ( - const MetaRectangle *basic_rect, - const GSList *all_struts); - -/* Expand all rectangles in region by the given amount on each side */ -GList* meta_rectangle_expand_region (GList *region, - const int left_expand, - const int right_expand, - const int top_expand, - const int bottom_expand); -/* Same as for meta_rectangle_expand_region except that rectangles not at - * least min_x or min_y in size are not expanded in that direction - */ -GList* meta_rectangle_expand_region_conditionally ( - GList *region, - const int left_expand, - const int right_expand, - const int top_expand, - const int bottom_expand, - const int min_x, - const int min_y); - -/* Expand rect in direction to the size of expand_to, and then clip out any - * overlapping struts oriented orthogonal to the expansion direction. (Think - * horizontal or vertical maximization) - */ -META_EXPORT_TEST -void meta_rectangle_expand_to_avoiding_struts ( - MetaRectangle *rect, - const MetaRectangle *expand_to, - const MetaDirection direction, - const GSList *all_struts); - -/* Free the list created by - * meta_rectangle_get_minimal_spanning_set_for_region() - * or - * meta_rectangle_find_onscreen_edges () - * or - * meta_rectangle_find_nonintersected_monitor_edges() - */ -META_EXPORT_TEST -void meta_rectangle_free_list_and_elements (GList *filled_list); - -/* could_fit_in_region determines whether one of the spanning_rects is - * big enough to contain rect. contained_in_region checks whether one - * actually contains it. - */ -META_EXPORT_TEST -gboolean meta_rectangle_could_fit_in_region ( - const GList *spanning_rects, - const MetaRectangle *rect); - -META_EXPORT_TEST -gboolean meta_rectangle_contained_in_region ( - const GList *spanning_rects, - const MetaRectangle *rect); - -META_EXPORT_TEST -gboolean meta_rectangle_overlaps_with_region ( - const GList *spanning_rects, - const MetaRectangle *rect); - -/* Make the rectangle small enough to fit into one of the spanning_rects, - * but make it no smaller than min_size. - */ -META_EXPORT_TEST -void meta_rectangle_clamp_to_fit_into_region ( - const GList *spanning_rects, - FixedDirections fixed_directions, - MetaRectangle *rect, - const MetaRectangle *min_size); - -/* Clip the rectangle so that it fits into one of the spanning_rects, assuming - * it overlaps with at least one of them - */ -META_EXPORT_TEST -void meta_rectangle_clip_to_region (const GList *spanning_rects, - FixedDirections fixed_directions, - MetaRectangle *rect); - -/* Shove the rectangle into one of the spanning_rects, assuming it fits in - * one of them. - */ -META_EXPORT_TEST -void meta_rectangle_shove_into_region( - const GList *spanning_rects, - FixedDirections fixed_directions, - MetaRectangle *rect); - -/* Finds the point on the line connecting (x1,y1) to (x2,y2) which is closest - * to (px, py). Useful for finding an optimal rectangle size when given a - * range between two sizes that are all candidates. - */ -META_EXPORT_TEST -void meta_rectangle_find_linepoint_closest_to_point (double x1, double y1, - double x2, double y2, - double px, double py, - double *valx, double *valy); - -/***************************************************************************/ -/* */ -/* Switching gears to code for edges instead of just rectangles */ -/* */ -/***************************************************************************/ - -/* Return whether an edge overlaps or is adjacent to the rectangle in the - * nonzero-width dimension of the edge. - */ -META_EXPORT_TEST -gboolean meta_rectangle_edge_aligns (const MetaRectangle *rect, - const MetaEdge *edge); - -/* Compare two edges, so that sorting functions can put a list of edges in - * canonical order. - */ -META_EXPORT_TEST -gint meta_rectangle_edge_cmp (gconstpointer a, gconstpointer b); - -/* Compare two edges, so that sorting functions can put a list of edges in - * order. This function doesn't separate left edges first, then right edges, - * etc., but rather compares only upon location. - */ -META_EXPORT_TEST -gint meta_rectangle_edge_cmp_ignore_type (gconstpointer a, gconstpointer b); - -/* Removes an parts of edges in the given list that intersect any box in the - * given rectangle list. Returns the result. - */ -GList* meta_rectangle_remove_intersections_with_boxes_from_edges ( - GList *edges, - const GSList *rectangles); - -/* Finds all the edges of an onscreen region, returning a GList* of - * MetaEdgeRect's. - */ -META_EXPORT_TEST -GList* meta_rectangle_find_onscreen_edges (const MetaRectangle *basic_rect, - const GSList *all_struts); - -/* Finds edges between adjacent monitors which are not covered by the given - * struts. - */ -META_EXPORT_TEST -GList* meta_rectangle_find_nonintersected_monitor_edges ( - const GList *monitor_rects, - const GSList *all_struts); - -META_EXPORT_TEST -gboolean meta_rectangle_is_adjacent_to (MetaRectangle *rect, - MetaRectangle *other); - -META_EXPORT_TEST -void meta_rectangle_scale_double (const MetaRectangle *rect, - double scale, - MetaRoundingStrategy rounding_strategy, - MetaRectangle *dest); - -static inline graphene_rect_t -meta_rectangle_to_graphene_rect (MetaRectangle *rect) -{ - return (graphene_rect_t) { - .origin = { - .x = rect->x, - .y = rect->y - }, - .size = { - .width = rect->width, - .height = rect->height - } - }; -} - -META_EXPORT_TEST -void meta_rectangle_transform (const MetaRectangle *rect, - MetaMonitorTransform transform, - int width, - int height, - MetaRectangle *dest); - -void meta_rectangle_from_graphene_rect (const graphene_rect_t *rect, - MetaRoundingStrategy rounding_strategy, - MetaRectangle *dest); - -void meta_rectangle_crop_and_scale (const MetaRectangle *rect, - graphene_rect_t *src_rect, - int dst_width, - int dst_height, - MetaRectangle *dest); - -#endif /* META_BOXES_PRIVATE_H */ diff --git a/src/core/boxes.c b/src/core/boxes.c deleted file mode 100644 index 9a9633e05..000000000 --- a/src/core/boxes.c +++ /dev/null @@ -1,2183 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/** - * SECTION:boxes - * @Title: MetaRectangle - * @Short_Description: Simple box operations - */ - -/* - * Copyright (C) 2005, 2006 Elijah Newren - * [meta_rectangle_intersect() is copyright the GTK+ Team according to Havoc, - * see gdkrectangle.c. As far as Havoc knows, he probably wrote - * meta_rectangle_equal(), and I'm guessing it's (C) Red Hat. So...] - * Copyright (C) 1995-2000 GTK+ Team - * Copyright (C) 2002 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "config.h" - -#include "backends/meta-monitor-transform.h" -#include "core/boxes-private.h" - -#include <math.h> -#include <X11/Xutil.h> - -#include "meta/util.h" - -/* It would make sense to use GSlice here, but until we clean up the - * rest of this file and the internal API to use these functions, we - * leave it using g_malloc()/g_free() for consistency. - */ - -MetaRectangle * -meta_rectangle_copy (const MetaRectangle *rect) -{ - return g_memdup2 (rect, sizeof (MetaRectangle)); -} - -void -meta_rectangle_free (MetaRectangle *rect) -{ - g_free (rect); -} - -G_DEFINE_BOXED_TYPE (MetaRectangle, meta_rectangle, - meta_rectangle_copy, meta_rectangle_free); - -char* -meta_rectangle_to_string (const MetaRectangle *rect, - char *output) -{ - /* 25 chars: 2 commas, space, plus, trailing \0 + 5 for each digit. - * Should be more than enough space. Note that of this space, the - * trailing \0 will be overwritten for all but the last rectangle. - */ - g_snprintf (output, RECT_LENGTH, "%d,%d +%d,%d", - rect->x, rect->y, rect->width, rect->height); - - return output; -} - -char* -meta_rectangle_region_to_string (GList *region, - const char *separator_string, - char *output) -{ - /* 27 chars: 2 commas, 2 square brackets, space, plus, trailing \0 + 5 - * for each digit. Should be more than enough space. Note that of this - * space, the trailing \0 will be overwritten for all but the last - * rectangle. - */ - char rect_string[RECT_LENGTH]; - - GList *tmp = region; - char *cur = output; - - if (region == NULL) - g_snprintf (output, 10, "(EMPTY)"); - - while (tmp) - { - MetaRectangle *rect = tmp->data; - g_snprintf (rect_string, RECT_LENGTH, "[%d,%d +%d,%d]", - rect->x, rect->y, rect->width, rect->height); - cur = g_stpcpy (cur, rect_string); - tmp = tmp->next; - if (tmp) - cur = g_stpcpy (cur, separator_string); - } - - return output; -} - -char* -meta_rectangle_edge_to_string (const MetaEdge *edge, - char *output) -{ - /* 25 chars: 2 commas, space, plus, trailing \0 + 5 for each digit. - * Should be more than enough space. Note that of this space, the - * trailing \0 will be overwritten for all but the last rectangle. - * - * Plus 2 for parenthesis, 4 for 2 more numbers, 2 more commas, and - * 2 more spaces, for a total of 10 more. - */ - g_snprintf (output, EDGE_LENGTH, "[%d,%d +%d,%d], %2d, %2d", - edge->rect.x, edge->rect.y, edge->rect.width, edge->rect.height, - edge->side_type, edge->edge_type); - - return output; -} - -char* -meta_rectangle_edge_list_to_string (GList *edge_list, - const char *separator_string, - char *output) -{ - /* 27 chars: 2 commas, 2 square brackets, space, plus, trailing \0 + 5 for - * each digit. Should be more than enough space. Note that of this - * space, the trailing \0 will be overwritten for all but the last - * rectangle. - * - * Plus 2 for parenthesis, 4 for 2 more numbers, 2 more commas, and - * 2 more spaces, for a total of 10 more. - */ - char rect_string[EDGE_LENGTH]; - - char *cur = output; - GList *tmp = edge_list; - - if (edge_list == NULL) - g_snprintf (output, 10, "(EMPTY)"); - - while (tmp) - { - MetaEdge *edge = tmp->data; - MetaRectangle *rect = &edge->rect; - g_snprintf (rect_string, EDGE_LENGTH, "([%d,%d +%d,%d], %2d, %2d)", - rect->x, rect->y, rect->width, rect->height, - edge->side_type, edge->edge_type); - cur = g_stpcpy (cur, rect_string); - tmp = tmp->next; - if (tmp) - cur = g_stpcpy (cur, separator_string); - } - - return output; -} - -MetaRectangle -meta_rect (int x, int y, int width, int height) -{ - MetaRectangle temporary; - temporary.x = x; - temporary.y = y; - temporary.width = width; - temporary.height = height; - - return temporary; -} - -int -meta_rectangle_area (const MetaRectangle *rect) -{ - g_return_val_if_fail (rect != NULL, 0); - return rect->width * rect->height; -} - -/** - * meta_rectangle_intersect: - * @src1: a #MetaRectangle - * @src2: another #MetaRectangle - * @dest: (out caller-allocates): an empty #MetaRectangle, to be filled - * with the coordinates of the intersection. - * - * Returns: TRUE is some intersection exists and is not degenerate, FALSE - * otherwise. - */ -gboolean -meta_rectangle_intersect (const MetaRectangle *src1, - const MetaRectangle *src2, - MetaRectangle *dest) -{ - int dest_x, dest_y; - int dest_w, dest_h; - int return_val; - - g_return_val_if_fail (src1 != NULL, FALSE); - g_return_val_if_fail (src2 != NULL, FALSE); - g_return_val_if_fail (dest != NULL, FALSE); - - return_val = FALSE; - - dest_x = MAX (src1->x, src2->x); - dest_y = MAX (src1->y, src2->y); - dest_w = MIN (src1->x + src1->width, src2->x + src2->width) - dest_x; - dest_h = MIN (src1->y + src1->height, src2->y + src2->height) - dest_y; - - if (dest_w > 0 && dest_h > 0) - { - dest->x = dest_x; - dest->y = dest_y; - dest->width = dest_w; - dest->height = dest_h; - return_val = TRUE; - } - else - { - dest->width = 0; - dest->height = 0; - } - - return return_val; -} - -gboolean -meta_rectangle_equal (const MetaRectangle *src1, - const MetaRectangle *src2) -{ - return ((src1->x == src2->x) && - (src1->y == src2->y) && - (src1->width == src2->width) && - (src1->height == src2->height)); -} - -/** - * meta_rectangle_union: - * @rect1: a #MetaRectangle - * @rect2: another #MetaRectangle - * @dest: (out caller-allocates): an empty #MetaRectangle, to be filled - * with the coordinates of the bounding box. - */ -void -meta_rectangle_union (const MetaRectangle *rect1, - const MetaRectangle *rect2, - MetaRectangle *dest) -{ - int dest_x, dest_y; - int dest_w, dest_h; - - dest_x = rect1->x; - dest_y = rect1->y; - dest_w = rect1->width; - dest_h = rect1->height; - - if (rect2->x < dest_x) - { - dest_w += dest_x - rect2->x; - dest_x = rect2->x; - } - if (rect2->y < dest_y) - { - dest_h += dest_y - rect2->y; - dest_y = rect2->y; - } - if (rect2->x + rect2->width > dest_x + dest_w) - dest_w = rect2->x + rect2->width - dest_x; - if (rect2->y + rect2->height > dest_y + dest_h) - dest_h = rect2->y + rect2->height - dest_y; - - dest->x = dest_x; - dest->y = dest_y; - dest->width = dest_w; - dest->height = dest_h; -} - -gboolean -meta_rectangle_overlap (const MetaRectangle *rect1, - const MetaRectangle *rect2) -{ - g_return_val_if_fail (rect1 != NULL, FALSE); - g_return_val_if_fail (rect2 != NULL, FALSE); - - return !((rect1->x + rect1->width <= rect2->x) || - (rect2->x + rect2->width <= rect1->x) || - (rect1->y + rect1->height <= rect2->y) || - (rect2->y + rect2->height <= rect1->y)); -} - -gboolean -meta_rectangle_vert_overlap (const MetaRectangle *rect1, - const MetaRectangle *rect2) -{ - return (rect1->y < rect2->y + rect2->height && - rect2->y < rect1->y + rect1->height); -} - -gboolean -meta_rectangle_horiz_overlap (const MetaRectangle *rect1, - const MetaRectangle *rect2) -{ - return (rect1->x < rect2->x + rect2->width && - rect2->x < rect1->x + rect1->width); -} - -gboolean -meta_rectangle_could_fit_rect (const MetaRectangle *outer_rect, - const MetaRectangle *inner_rect) -{ - return (outer_rect->width >= inner_rect->width && - outer_rect->height >= inner_rect->height); -} - -gboolean -meta_rectangle_contains_rect (const MetaRectangle *outer_rect, - const MetaRectangle *inner_rect) -{ - return - inner_rect->x >= outer_rect->x && - inner_rect->y >= outer_rect->y && - inner_rect->x + inner_rect->width <= outer_rect->x + outer_rect->width && - inner_rect->y + inner_rect->height <= outer_rect->y + outer_rect->height; -} - -void -meta_rectangle_resize_with_gravity (const MetaRectangle *old_rect, - MetaRectangle *rect, - MetaGravity gravity, - int new_width, - int new_height) -{ - /* FIXME: I'm too deep into this to know whether the below comment is - * still clear or not now that I've moved it out of constraints.c. - * boxes.h has a good comment, but I'm not sure if the below info is also - * helpful on top of that (or whether it has superfluous info). - */ - - /* These formulas may look overly simplistic at first but you can work - * everything out with a left_frame_with, right_frame_width, - * border_width, and old and new client area widths (instead of old total - * width and new total width) and you come up with the same formulas. - * - * Also, note that the reason we can treat META_GRAVITY_NORTH_WEST and - * META_GRAVITY_STATIC the same is because we're not given a location at - * which to place the window--the window was already placed - * appropriately before. So, META_GRAVITY_NORTH_WEST for this function - * means to just leave the upper left corner of the outer window - * where it already is, and META_GRAVITY_STATIC for this function means to - * just leave the upper left corner of the inner window where it - * already is. But leaving either of those two corners where they - * already are will ensure that the other corner is fixed as well - * (since frame size doesn't change)--thus making the two - * equivalent. - */ - - /* First, the x direction */ - switch (gravity) - { - case META_GRAVITY_NORTH_WEST: - case META_GRAVITY_WEST: - case META_GRAVITY_SOUTH_WEST: - rect->x = old_rect->x; - break; - - case META_GRAVITY_NORTH: - case META_GRAVITY_CENTER: - case META_GRAVITY_SOUTH: - /* FIXME: Needing to adjust new_width kind of sucks, but not doing so - * would cause drift. - */ - new_width -= (old_rect->width - new_width) % 2; - rect->x = old_rect->x + (old_rect->width - new_width)/2; - break; - - case META_GRAVITY_NORTH_EAST: - case META_GRAVITY_EAST: - case META_GRAVITY_SOUTH_EAST: - rect->x = old_rect->x + (old_rect->width - new_width); - break; - - case META_GRAVITY_STATIC: - default: - rect->x = old_rect->x; - break; - } - rect->width = new_width; - - /* Next, the y direction */ - switch (gravity) - { - case META_GRAVITY_NORTH_WEST: - case META_GRAVITY_NORTH: - case META_GRAVITY_NORTH_EAST: - rect->y = old_rect->y; - break; - - case META_GRAVITY_WEST: - case META_GRAVITY_CENTER: - case META_GRAVITY_EAST: - /* FIXME: Needing to adjust new_height kind of sucks, but not doing so - * would cause drift. - */ - new_height -= (old_rect->height - new_height) % 2; - rect->y = old_rect->y + (old_rect->height - new_height)/2; - break; - - case META_GRAVITY_SOUTH_WEST: - case META_GRAVITY_SOUTH: - case META_GRAVITY_SOUTH_EAST: - rect->y = old_rect->y + (old_rect->height - new_height); - break; - - case META_GRAVITY_STATIC: - default: - rect->y = old_rect->y; - break; - } - rect->height = new_height; -} - -/* Not so simple helper function for get_minimal_spanning_set_for_region() */ -static GList* -merge_spanning_rects_in_region (GList *region) -{ - /* NOTE FOR ANY OPTIMIZATION PEOPLE OUT THERE: Please see the - * documentation of get_minimal_spanning_set_for_region() for performance - * considerations that also apply to this function. - */ - - GList* compare; - compare = region; - - if (region == NULL) - { - g_warning ("Region to merge was empty! Either you have a some " - "pathological STRUT list or there's a bug somewhere!"); - return NULL; - } - - while (compare && compare->next) - { - MetaRectangle *a = compare->data; - GList *other = compare->next; - - g_assert (a->width > 0 && a->height > 0); - - while (other) - { - MetaRectangle *b = other->data; - GList *delete_me = NULL; - - g_assert (b->width > 0 && b->height > 0); - - /* If a contains b, just remove b */ - if (meta_rectangle_contains_rect (a, b)) - { - delete_me = other; - } - /* If b contains a, just remove a */ - else if (meta_rectangle_contains_rect (b, a)) - { - delete_me = compare; - } - /* If a and b might be mergeable horizontally */ - else if (a->y == b->y && a->height == b->height) - { - /* If a and b overlap */ - if (meta_rectangle_overlap (a, b)) - { - int new_x = MIN (a->x, b->x); - a->width = MAX (a->x + a->width, b->x + b->width) - new_x; - a->x = new_x; - delete_me = other; - } - /* If a and b are adjacent */ - else if (a->x + a->width == b->x || a->x == b->x + b->width) - { - int new_x = MIN (a->x, b->x); - a->width = MAX (a->x + a->width, b->x + b->width) - new_x; - a->x = new_x; - delete_me = other; - } - } - /* If a and b might be mergeable vertically */ - else if (a->x == b->x && a->width == b->width) - { - /* If a and b overlap */ - if (meta_rectangle_overlap (a, b)) - { - int new_y = MIN (a->y, b->y); - a->height = MAX (a->y + a->height, b->y + b->height) - new_y; - a->y = new_y; - delete_me = other; - } - /* If a and b are adjacent */ - else if (a->y + a->height == b->y || a->y == b->y + b->height) - { - int new_y = MIN (a->y, b->y); - a->height = MAX (a->y + a->height, b->y + b->height) - new_y; - a->y = new_y; - delete_me = other; - } - } - - other = other->next; - - /* Delete any rectangle in the list that is no longer wanted */ - if (delete_me != NULL) - { - /* Deleting the rect we compare others to is a little tricker */ - if (compare == delete_me) - { - compare = compare->next; - other = compare->next; - a = compare->data; - } - - /* Okay, we can free it now */ - g_free (delete_me->data); - region = g_list_delete_link (region, delete_me); - } - - } - - compare = compare->next; - } - - return region; -} - -/* Simple helper function for get_minimal_spanning_set_for_region()... */ -static gint -compare_rect_areas (gconstpointer a, gconstpointer b) -{ - const MetaRectangle *a_rect = (gconstpointer) a; - const MetaRectangle *b_rect = (gconstpointer) b; - - int a_area = meta_rectangle_area (a_rect); - int b_area = meta_rectangle_area (b_rect); - - return b_area - a_area; /* positive ret value denotes b > a, ... */ -} - -/* ... and another helper for get_minimal_spanning_set_for_region()... */ -static gboolean -check_strut_align (MetaStrut *strut, const MetaRectangle *rect) -{ - /* Check whether @strut actually aligns to the side of @rect it claims */ - switch (strut->side) - { - case META_SIDE_TOP: - return BOX_TOP (strut->rect) <= BOX_TOP (*rect); - case META_SIDE_BOTTOM: - return BOX_BOTTOM (strut->rect) >= BOX_BOTTOM (*rect); - case META_SIDE_LEFT: - return BOX_LEFT (strut->rect) <= BOX_LEFT (*rect); - case META_SIDE_RIGHT: - return BOX_RIGHT (strut->rect) >= BOX_RIGHT (*rect); - default: - return FALSE; - } -} - -/** - * meta_rectangle_get_minimal_spanning_set_for_region: - * @basic_rect: Input rectangle - * @all_struts: (element-type Meta.Rectangle): List of struts - * - * This function is trying to find a "minimal spanning set (of rectangles)" - * for a given region. - * - * The region is given by taking basic_rect, then removing the areas - * covered by all the rectangles in the all_struts list, and then expanding - * the resulting region by the given number of pixels in each direction. - * - * A "minimal spanning set (of rectangles)" is the best name I could come - * up with for the concept I had in mind. Basically, for a given region, I - * want a set of rectangles with the property that a window is contained in - * the region if and only if it is contained within at least one of the - * rectangles. - * - * Returns: (transfer full) (element-type Meta.Rectangle): Minimal spanning set - */ -GList* -meta_rectangle_get_minimal_spanning_set_for_region ( - const MetaRectangle *basic_rect, - const GSList *all_struts) -{ - /* NOTE FOR OPTIMIZERS: This function *might* be somewhat slow, - * especially due to the call to merge_spanning_rects_in_region() (which - * is O(n^2) where n is the size of the list generated in this function). - * This is made more onerous due to the fact that it involves a fair - * number of memory allocation and deallocation calls. However, n is 1 - * for default installations of Gnome (because partial struts aren't used - * by default and only partial struts increase the size of the spanning - * set generated). With one partial strut, n will be 2 or 3. With 2 - * partial struts, n will probably be 4 or 5. So, n probably isn't large - * enough to make this worth bothering. Further, it is only called from - * workspace.c:ensure_work_areas_validated (at least as of the time of - * writing this comment), which in turn should only be called if the - * strut list changes or the screen or monitor size changes. If it ever - * does show up on profiles (most likely because people start using - * ridiculously huge numbers of partial struts), possible optimizations - * include: - * - * (1) rewrite merge_spanning_rects_in_region() to be O(n) or O(nlogn). - * I'm not totally sure it's possible, but with a couple copies of - * the list and sorting them appropriately, I believe it might be. - * (2) only call merge_spanning_rects_in_region() with a subset of the - * full list of rectangles. I believe from some of my preliminary - * debugging and thinking about it that it is possible to figure out - * apriori groups of rectangles which are only merge candidates with - * each other. (See testboxes.c:get_screen_region() when which==2 - * and track the steps of this function carefully to see what gave - * me the hint that this might work) - * (3) figure out how to avoid merge_spanning_rects_in_region(). I think - * it might be possible to modify this function to make that - * possible, and I spent just a little while thinking about it, but n - * wasn't large enough to convince me to care yet. - * (4) Some of the stuff Rob mentioned at http://mail.gnome.org/archives\ - * /metacity-devel-list/2005-November/msg00028.html. (Sorry for the - * URL splitting.) - */ - - GList *ret; - GList *tmp_list; - const GSList *strut_iter; - MetaRectangle *temp_rect; - - /* The algorithm is basically as follows: - * Initialize rectangle_set to basic_rect - * Foreach strut: - * Foreach rectangle in rectangle_set: - * - Split the rectangle into new rectangles that don't overlap the - * strut (but which are as big as possible otherwise) - * - Remove the old (pre-split) rectangle from the rectangle_set, - * and replace it with the new rectangles generated from the - * splitting - */ - - temp_rect = g_new (MetaRectangle, 1); - *temp_rect = *basic_rect; - ret = g_list_prepend (NULL, temp_rect); - - for (strut_iter = all_struts; strut_iter; strut_iter = strut_iter->next) - { - GList *rect_iter; - MetaStrut *strut = (MetaStrut*)strut_iter->data; - MetaRectangle *strut_rect = &strut->rect; - - tmp_list = ret; - ret = NULL; - rect_iter = tmp_list; - while (rect_iter) - { - MetaRectangle *rect = (MetaRectangle*) rect_iter->data; - - if (!meta_rectangle_overlap (strut_rect, rect) || - !check_strut_align (strut, basic_rect)) - ret = g_list_prepend (ret, rect); - else - { - /* If there is area in rect left of strut */ - if (BOX_LEFT (*rect) < BOX_LEFT (*strut_rect)) - { - temp_rect = g_new (MetaRectangle, 1); - *temp_rect = *rect; - temp_rect->width = BOX_LEFT (*strut_rect) - BOX_LEFT (*rect); - ret = g_list_prepend (ret, temp_rect); - } - /* If there is area in rect right of strut */ - if (BOX_RIGHT (*rect) > BOX_RIGHT (*strut_rect)) - { - int new_x; - temp_rect = g_new (MetaRectangle, 1); - *temp_rect = *rect; - new_x = BOX_RIGHT (*strut_rect); - temp_rect->width = BOX_RIGHT(*rect) - new_x; - temp_rect->x = new_x; - ret = g_list_prepend (ret, temp_rect); - } - /* If there is area in rect above strut */ - if (BOX_TOP (*rect) < BOX_TOP (*strut_rect)) - { - temp_rect = g_new (MetaRectangle, 1); - *temp_rect = *rect; - temp_rect->height = BOX_TOP (*strut_rect) - BOX_TOP (*rect); - ret = g_list_prepend (ret, temp_rect); - } - /* If there is area in rect below strut */ - if (BOX_BOTTOM (*rect) > BOX_BOTTOM (*strut_rect)) - { - int new_y; - temp_rect = g_new (MetaRectangle, 1); - *temp_rect = *rect; - new_y = BOX_BOTTOM (*strut_rect); - temp_rect->height = BOX_BOTTOM (*rect) - new_y; - temp_rect->y = new_y; - ret = g_list_prepend (ret, temp_rect); - } - g_free (rect); - } - rect_iter = rect_iter->next; - } - g_list_free (tmp_list); - } - - /* Sort by maximal area, just because I feel like it... */ - ret = g_list_sort (ret, compare_rect_areas); - - /* Merge rectangles if possible so that the list really is minimal */ - ret = merge_spanning_rects_in_region (ret); - - return ret; -} - -/** - * meta_rectangle_expand_region: (skip) - * - */ -GList* -meta_rectangle_expand_region (GList *region, - const int left_expand, - const int right_expand, - const int top_expand, - const int bottom_expand) -{ - return meta_rectangle_expand_region_conditionally (region, - left_expand, - right_expand, - top_expand, - bottom_expand, - 0, - 0); -} - -/** - * meta_rectangle_expand_region_conditionally: (skip) - * - */ -GList* -meta_rectangle_expand_region_conditionally (GList *region, - const int left_expand, - const int right_expand, - const int top_expand, - const int bottom_expand, - const int min_x, - const int min_y) -{ - GList *tmp_list = region; - while (tmp_list) - { - MetaRectangle *rect = (MetaRectangle*) tmp_list->data; - if (rect->width >= min_x) - { - rect->x -= left_expand; - rect->width += (left_expand + right_expand); - } - if (rect->height >= min_y) - { - rect->y -= top_expand; - rect->height += (top_expand + bottom_expand); - } - tmp_list = tmp_list->next; - } - - return region; -} - -void -meta_rectangle_expand_to_avoiding_struts (MetaRectangle *rect, - const MetaRectangle *expand_to, - const MetaDirection direction, - const GSList *all_struts) -{ - const GSList *strut_iter; - - /* If someone wants this function to handle more fine-grained - * direction expanding in the future (e.g. only left, or fully - * horizontal plus upward), feel free. But I'm hard-coding for both - * horizontal directions (exclusive-)or both vertical directions. - */ - g_assert ((direction == META_DIRECTION_HORIZONTAL) ^ - (direction == META_DIRECTION_VERTICAL )); - - if (direction == META_DIRECTION_HORIZONTAL) - { - rect->x = expand_to->x; - rect->width = expand_to->width; - } - else - { - rect->y = expand_to->y; - rect->height = expand_to->height; - } - - - /* Run over all struts */ - for (strut_iter = all_struts; strut_iter; strut_iter = strut_iter->next) - { - MetaStrut *strut = (MetaStrut*) strut_iter->data; - - /* Skip struts that don't overlap */ - if (!meta_rectangle_overlap (&strut->rect, rect)) - continue; - - if (direction == META_DIRECTION_HORIZONTAL) - { - if (strut->side == META_SIDE_LEFT) - { - int offset = BOX_RIGHT(strut->rect) - BOX_LEFT(*rect); - rect->x += offset; - rect->width -= offset; - } - else if (strut->side == META_SIDE_RIGHT) - { - int offset = BOX_RIGHT (*rect) - BOX_LEFT(strut->rect); - rect->width -= offset; - } - /* else ignore the strut */ - } - else /* direction == META_DIRECTION_VERTICAL */ - { - if (strut->side == META_SIDE_TOP) - { - int offset = BOX_BOTTOM(strut->rect) - BOX_TOP(*rect); - rect->y += offset; - rect->height -= offset; - } - else if (strut->side == META_SIDE_BOTTOM) - { - int offset = BOX_BOTTOM(*rect) - BOX_TOP(strut->rect); - rect->height -= offset; - } - /* else ignore the strut */ - } - } /* end loop over struts */ -} /* end meta_rectangle_expand_to_avoiding_struts */ - -void -meta_rectangle_free_list_and_elements (GList *filled_list) -{ - g_list_free_full (filled_list, g_free); -} - -gboolean -meta_rectangle_could_fit_in_region (const GList *spanning_rects, - const MetaRectangle *rect) -{ - const GList *temp; - gboolean could_fit; - - temp = spanning_rects; - could_fit = FALSE; - while (!could_fit && temp != NULL) - { - could_fit = could_fit || meta_rectangle_could_fit_rect (temp->data, rect); - temp = temp->next; - } - - return could_fit; -} - -gboolean -meta_rectangle_contained_in_region (const GList *spanning_rects, - const MetaRectangle *rect) -{ - const GList *temp; - gboolean contained; - - temp = spanning_rects; - contained = FALSE; - while (!contained && temp != NULL) - { - contained = contained || meta_rectangle_contains_rect (temp->data, rect); - temp = temp->next; - } - - return contained; -} - -gboolean -meta_rectangle_overlaps_with_region (const GList *spanning_rects, - const MetaRectangle *rect) -{ - const GList *temp; - gboolean overlaps; - - temp = spanning_rects; - overlaps = FALSE; - while (!overlaps && temp != NULL) - { - overlaps = overlaps || meta_rectangle_overlap (temp->data, rect); - temp = temp->next; - } - - return overlaps; -} - - -void -meta_rectangle_clamp_to_fit_into_region (const GList *spanning_rects, - FixedDirections fixed_directions, - MetaRectangle *rect, - const MetaRectangle *min_size) -{ - const GList *temp; - const MetaRectangle *best_rect = NULL; - int best_overlap = 0; - - /* First, find best rectangle from spanning_rects to which we can clamp - * rect to fit into. - */ - for (temp = spanning_rects; temp; temp = temp->next) - { - MetaRectangle *compare_rect = temp->data; - int maximal_overlap_amount_for_compare; - - /* If x is fixed and the entire width of rect doesn't fit in compare, - * skip this rectangle. - */ - if ((fixed_directions & FIXED_DIRECTION_X) && - (compare_rect->x > rect->x || - compare_rect->x + compare_rect->width < rect->x + rect->width)) - continue; - - /* If y is fixed and the entire height of rect doesn't fit in compare, - * skip this rectangle. - */ - if ((fixed_directions & FIXED_DIRECTION_Y) && - (compare_rect->y > rect->y || - compare_rect->y + compare_rect->height < rect->y + rect->height)) - continue; - - /* If compare can't hold the min_size window, skip this rectangle. */ - if (compare_rect->width < min_size->width || - compare_rect->height < min_size->height) - continue; - - /* Determine maximal overlap amount */ - maximal_overlap_amount_for_compare = - MIN (rect->width, compare_rect->width) * - MIN (rect->height, compare_rect->height); - - /* See if this is the best rect so far */ - if (maximal_overlap_amount_for_compare > best_overlap) - { - best_rect = compare_rect; - best_overlap = maximal_overlap_amount_for_compare; - } - } - - /* Clamp rect appropriately */ - if (best_rect == NULL) - { - g_warning ("No rect whose size to clamp to found!"); - - /* If it doesn't fit, at least make it no bigger than it has to be */ - if (!(fixed_directions & FIXED_DIRECTION_X)) - rect->width = min_size->width; - if (!(fixed_directions & FIXED_DIRECTION_Y)) - rect->height = min_size->height; - } - else - { - rect->width = MIN (rect->width, best_rect->width); - rect->height = MIN (rect->height, best_rect->height); - } -} - -void -meta_rectangle_clip_to_region (const GList *spanning_rects, - FixedDirections fixed_directions, - MetaRectangle *rect) -{ - const GList *temp; - const MetaRectangle *best_rect = NULL; - int best_overlap = 0; - - /* First, find best rectangle from spanning_rects to which we will clip - * rect into. - */ - for (temp = spanning_rects; temp; temp = temp->next) - { - MetaRectangle *compare_rect = temp->data; - MetaRectangle overlap; - int maximal_overlap_amount_for_compare; - - /* If x is fixed and the entire width of rect doesn't fit in compare, - * skip the rectangle. - */ - if ((fixed_directions & FIXED_DIRECTION_X) && - (compare_rect->x > rect->x || - compare_rect->x + compare_rect->width < rect->x + rect->width)) - continue; - - /* If y is fixed and the entire height of rect doesn't fit in compare, - * skip the rectangle. - */ - if ((fixed_directions & FIXED_DIRECTION_Y) && - (compare_rect->y > rect->y || - compare_rect->y + compare_rect->height < rect->y + rect->height)) - continue; - - /* Determine maximal overlap amount */ - meta_rectangle_intersect (rect, compare_rect, &overlap); - maximal_overlap_amount_for_compare = meta_rectangle_area (&overlap); - - /* See if this is the best rect so far */ - if (maximal_overlap_amount_for_compare > best_overlap) - { - best_rect = compare_rect; - best_overlap = maximal_overlap_amount_for_compare; - } - } - - /* Clip rect appropriately */ - if (best_rect == NULL) - { - g_warning ("No rect to clip to found!"); - } - else - { - /* Extra precaution with checking fixed direction shouldn't be needed - * due to logic above, but it shouldn't hurt either. - */ - if (!(fixed_directions & FIXED_DIRECTION_X)) - { - /* Find the new left and right */ - int new_x = MAX (rect->x, best_rect->x); - rect->width = MIN ((rect->x + rect->width) - new_x, - (best_rect->x + best_rect->width) - new_x); - rect->x = new_x; - } - - /* Extra precaution with checking fixed direction shouldn't be needed - * due to logic above, but it shouldn't hurt either. - */ - if (!(fixed_directions & FIXED_DIRECTION_Y)) - { - /* Clip the top, if needed */ - int new_y = MAX (rect->y, best_rect->y); - rect->height = MIN ((rect->y + rect->height) - new_y, - (best_rect->y + best_rect->height) - new_y); - rect->y = new_y; - } - } -} - -void -meta_rectangle_shove_into_region (const GList *spanning_rects, - FixedDirections fixed_directions, - MetaRectangle *rect) -{ - const GList *temp; - const MetaRectangle *best_rect = NULL; - int best_overlap = 0; - int shortest_distance = G_MAXINT; - - /* First, find best rectangle from spanning_rects to which we will shove - * rect into. - */ - - for (temp = spanning_rects; temp; temp = temp->next) - { - MetaRectangle *compare_rect = temp->data; - int maximal_overlap_amount_for_compare; - int dist_to_compare; - - /* If x is fixed and the entire width of rect doesn't fit in compare, - * skip this rectangle. - */ - if ((fixed_directions & FIXED_DIRECTION_X) && - (compare_rect->x > rect->x || - compare_rect->x + compare_rect->width < rect->x + rect->width)) - continue; - - /* If y is fixed and the entire height of rect doesn't fit in compare, - * skip this rectangle. - */ - if ((fixed_directions & FIXED_DIRECTION_Y) && - (compare_rect->y > rect->y || - compare_rect->y + compare_rect->height < rect->y + rect->height)) - continue; - - /* Determine maximal overlap amount between rect & compare_rect */ - maximal_overlap_amount_for_compare = - MIN (rect->width, compare_rect->width) * - MIN (rect->height, compare_rect->height); - - /* Determine distance necessary to put rect into compare_rect */ - dist_to_compare = 0; - if (compare_rect->x > rect->x) - dist_to_compare += compare_rect->x - rect->x; - if (compare_rect->x + compare_rect->width < rect->x + rect->width) - dist_to_compare += (rect->x + rect->width) - - (compare_rect->x + compare_rect->width); - if (compare_rect->y > rect->y) - dist_to_compare += compare_rect->y - rect->y; - if (compare_rect->y + compare_rect->height < rect->y + rect->height) - dist_to_compare += (rect->y + rect->height) - - (compare_rect->y + compare_rect->height); - - /* See if this is the best rect so far */ - if ((maximal_overlap_amount_for_compare > best_overlap) || - (maximal_overlap_amount_for_compare == best_overlap && - dist_to_compare < shortest_distance)) - { - best_rect = compare_rect; - best_overlap = maximal_overlap_amount_for_compare; - shortest_distance = dist_to_compare; - } - } - - /* Shove rect appropriately */ - if (best_rect == NULL) - { - g_warning ("No rect to shove into found!"); - } - else - { - /* Extra precaution with checking fixed direction shouldn't be needed - * due to logic above, but it shouldn't hurt either. - */ - if (!(fixed_directions & FIXED_DIRECTION_X)) - { - /* Shove to the right, if needed */ - if (best_rect->x > rect->x) - rect->x = best_rect->x; - - /* Shove to the left, if needed */ - if (best_rect->x + best_rect->width < rect->x + rect->width) - rect->x = (best_rect->x + best_rect->width) - rect->width; - } - - /* Extra precaution with checking fixed direction shouldn't be needed - * due to logic above, but it shouldn't hurt either. - */ - if (!(fixed_directions & FIXED_DIRECTION_Y)) - { - /* Shove down, if needed */ - if (best_rect->y > rect->y) - rect->y = best_rect->y; - - /* Shove up, if needed */ - if (best_rect->y + best_rect->height < rect->y + rect->height) - rect->y = (best_rect->y + best_rect->height) - rect->height; - } - } -} - -void -meta_rectangle_find_linepoint_closest_to_point (double x1, - double y1, - double x2, - double y2, - double px, - double py, - double *valx, - double *valy) -{ - /* I'll use the shorthand rx, ry for the return values, valx & valy. - * Now, we need (rx,ry) to be on the line between (x1,y1) and (x2,y2). - * For that to happen, we first need the slope of the line from (x1,y1) - * to (rx,ry) must match the slope of (x1,y1) to (x2,y2), i.e.: - * (ry-y1) (y2-y1) - * ------- = ------- - * (rx-x1) (x2-x1) - * If x1==x2, though, this gives divide by zero errors, so we want to - * rewrite the equation by multiplying both sides by (rx-x1)*(x2-x1): - * (ry-y1)(x2-x1) = (y2-y1)(rx-x1) - * This is a valid requirement even when x1==x2 (when x1==x2, this latter - * equation will basically just mean that rx must be equal to both x1 and - * x2) - * - * The other requirement that we have is that the line from (rx,ry) to - * (px,py) must be perpendicular to the line from (x1,y1) to (x2,y2). So - * we just need to get a vector in the direction of each line, take the - * dot product of the two, and ensure that the result is 0: - * (rx-px)*(x2-x1) + (ry-py)*(y2-y1) = 0. - * - * This gives us two equations and two unknowns: - * - * (ry-y1)(x2-x1) = (y2-y1)(rx-x1) - * (rx-px)*(x2-x1) + (ry-py)*(y2-y1) = 0. - * - * This particular pair of equations is always solvable so long as - * (x1,y1) and (x2,y2) are not the same point (and note that anyone who - * calls this function that way is braindead because it means that they - * really didn't specify a line after all). However, the caller should - * be careful to avoid making (x1,y1) and (x2,y2) too close (e.g. like - * 10^{-8} apart in each coordinate), otherwise roundoff error could - * cause issues. Solving these equations by hand (or using Maple(TM) or - * Mathematica(TM) or whatever) results in slightly messy expressions, - * but that's all the below few lines do. - */ - - double diffx, diffy, den; - diffx = x2 - x1; - diffy = y2 - y1; - den = diffx * diffx + diffy * diffy; - - *valx = (py * diffx * diffy + px * diffx * diffx + - y2 * x1 * diffy - y1 * x2 * diffy) / den; - *valy = (px * diffx * diffy + py * diffy * diffy + - x2 * y1 * diffx - x1 * y2 * diffx) / den; -} - -/***************************************************************************/ -/* */ -/* Switching gears to code for edges instead of just rectangles */ -/* */ -/***************************************************************************/ - -gboolean -meta_rectangle_edge_aligns (const MetaRectangle *rect, const MetaEdge *edge) -{ - /* The reason for the usage of <= below instead of < is because we are - * interested in in-the-way-or-adject'ness. So, a left (i.e. vertical - * edge) occupying y positions 0-9 (which has a y of 0 and a height of - * 10) and a rectangle with top at y=10 would be considered to "align" by - * this function. - */ - switch (edge->side_type) - { - case META_SIDE_LEFT: - case META_SIDE_RIGHT: - return BOX_TOP (*rect) <= BOX_BOTTOM (edge->rect) && - BOX_TOP (edge->rect) <= BOX_BOTTOM (*rect); - case META_SIDE_TOP: - case META_SIDE_BOTTOM: - return BOX_LEFT (*rect) <= BOX_RIGHT (edge->rect) && - BOX_LEFT (edge->rect) <= BOX_RIGHT (*rect); - default: - g_assert_not_reached (); - return FALSE; - } -} - -static GList* -get_rect_minus_overlap (const GList *rect_in_list, - MetaRectangle *overlap) -{ - MetaRectangle *temp; - MetaRectangle *rect = rect_in_list->data; - GList *ret = NULL; - - if (BOX_LEFT (*rect) < BOX_LEFT (*overlap)) - { - temp = g_new (MetaRectangle, 1); - *temp = *rect; - temp->width = BOX_LEFT (*overlap) - BOX_LEFT (*rect); - ret = g_list_prepend (ret, temp); - } - if (BOX_RIGHT (*rect) > BOX_RIGHT (*overlap)) - { - temp = g_new (MetaRectangle, 1); - *temp = *rect; - temp->x = BOX_RIGHT (*overlap); - temp->width = BOX_RIGHT (*rect) - BOX_RIGHT (*overlap); - ret = g_list_prepend (ret, temp); - } - if (BOX_TOP (*rect) < BOX_TOP (*overlap)) - { - temp = g_new (MetaRectangle, 1); - temp->x = overlap->x; - temp->width = overlap->width; - temp->y = BOX_TOP (*rect); - temp->height = BOX_TOP (*overlap) - BOX_TOP (*rect); - ret = g_list_prepend (ret, temp); - } - if (BOX_BOTTOM (*rect) > BOX_BOTTOM (*overlap)) - { - temp = g_new (MetaRectangle, 1); - temp->x = overlap->x; - temp->width = overlap->width; - temp->y = BOX_BOTTOM (*overlap); - temp->height = BOX_BOTTOM (*rect) - BOX_BOTTOM (*overlap); - ret = g_list_prepend (ret, temp); - } - - return ret; -} - -static GList* -replace_rect_with_list (GList *old_element, - GList *new_list) -{ - GList *ret; - g_assert (old_element != NULL); - - if (!new_list) - { - /* If there is no new list, just remove the old_element */ - ret = g_list_remove_link (old_element, old_element); - } - else - { - /* Fix up the prev and next pointers everywhere */ - ret = new_list; - if (old_element->prev) - { - old_element->prev->next = new_list; - new_list->prev = old_element->prev; - } - if (old_element->next) - { - GList *tmp = g_list_last (new_list); - old_element->next->prev = tmp; - tmp->next = old_element->next; - } - } - - /* Free the old_element and return the appropriate "next" point */ - g_free (old_element->data); - g_list_free_1 (old_element); - return ret; -} - -/* Make a copy of the strut list, make sure that copy only contains parts - * of the old_struts that intersect with the region rect, and then do some - * magic to make all the new struts disjoint (okay, we we break up struts - * that aren't disjoint in a way that the overlapping part is only included - * once, so it's not really magic...). - */ -static GList* -get_disjoint_strut_rect_list_in_region (const GSList *old_struts, - const MetaRectangle *region) -{ - GList *strut_rects; - GList *tmp; - - /* First, copy the list */ - strut_rects = NULL; - while (old_struts) - { - MetaRectangle *cur = &((MetaStrut*)old_struts->data)->rect; - MetaRectangle *copy = g_new (MetaRectangle, 1); - *copy = *cur; - if (meta_rectangle_intersect (copy, region, copy)) - strut_rects = g_list_prepend (strut_rects, copy); - else - g_free (copy); - - old_struts = old_struts->next; - } - - /* Now, loop over the list and check for intersections, fixing things up - * where they do intersect. - */ - tmp = strut_rects; - while (tmp) - { - GList *compare; - - MetaRectangle *cur = tmp->data; - - compare = tmp->next; - while (compare) - { - MetaRectangle *comp = compare->data; - MetaRectangle overlap; - - if (meta_rectangle_intersect (cur, comp, &overlap)) - { - /* Get a list of rectangles for each strut that don't overlap - * the intersection region. - */ - GList *cur_leftover = get_rect_minus_overlap (tmp, &overlap); - GList *comp_leftover = get_rect_minus_overlap (compare, &overlap); - - /* Add the intersection region to cur_leftover */ - MetaRectangle *overlap_allocated = g_new (MetaRectangle, 1); - *overlap_allocated = overlap; - cur_leftover = g_list_prepend (cur_leftover, overlap_allocated); - - /* Fix up tmp, compare, and cur -- maybe struts too */ - if (strut_rects == tmp) - { - strut_rects = replace_rect_with_list (tmp, cur_leftover); - tmp = strut_rects; - } - else - tmp = replace_rect_with_list (tmp, cur_leftover); - compare = replace_rect_with_list (compare, comp_leftover); - - if (compare == NULL) - break; - - cur = tmp->data; - } - - compare = compare->next; - } - - tmp = tmp->next; - } - - return strut_rects; -} - -gint -meta_rectangle_edge_cmp_ignore_type (gconstpointer a, gconstpointer b) -{ - const MetaEdge *a_edge_rect = (gconstpointer) a; - const MetaEdge *b_edge_rect = (gconstpointer) b; - int a_compare, b_compare; - - /* Edges must be both vertical or both horizontal, or it doesn't make - * sense to compare them. - */ - g_assert ((a_edge_rect->rect.width == 0 && b_edge_rect->rect.width == 0) || - (a_edge_rect->rect.height == 0 && b_edge_rect->rect.height == 0)); - - a_compare = b_compare = 0; /* gcc-3.4.2 sucks at figuring initialized'ness */ - - if (a_edge_rect->side_type == META_SIDE_LEFT || - a_edge_rect->side_type == META_SIDE_RIGHT) - { - a_compare = a_edge_rect->rect.x; - b_compare = b_edge_rect->rect.x; - if (a_compare == b_compare) - { - a_compare = a_edge_rect->rect.y; - b_compare = b_edge_rect->rect.y; - } - } - else if (a_edge_rect->side_type == META_SIDE_TOP || - a_edge_rect->side_type == META_SIDE_BOTTOM) - { - a_compare = a_edge_rect->rect.y; - b_compare = b_edge_rect->rect.y; - if (a_compare == b_compare) - { - a_compare = a_edge_rect->rect.x; - b_compare = b_edge_rect->rect.x; - } - } - else - g_assert ("Some idiot wanted to sort sides of different types."); - - return a_compare - b_compare; /* positive value denotes a > b ... */ -} - -/* To make things easily testable, provide a nice way of sorting edges */ -gint -meta_rectangle_edge_cmp (gconstpointer a, gconstpointer b) -{ - const MetaEdge *a_edge_rect = (gconstpointer) a; - const MetaEdge *b_edge_rect = (gconstpointer) b; - - int a_compare, b_compare; - - a_compare = a_edge_rect->side_type; - b_compare = b_edge_rect->side_type; - - if (a_compare == b_compare) - return meta_rectangle_edge_cmp_ignore_type (a, b); - - return a_compare - b_compare; /* positive value denotes a > b ... */ -} - -/* Determine whether two given edges overlap */ -static gboolean -edges_overlap (const MetaEdge *edge1, - const MetaEdge *edge2) -{ - if (edge1->rect.width == 0 && edge2->rect.width == 0) - { - return meta_rectangle_vert_overlap (&edge1->rect, &edge2->rect) && - edge1->rect.x == edge2->rect.x; - } - else if (edge1->rect.height == 0 && edge2->rect.height == 0) - { - return meta_rectangle_horiz_overlap (&edge1->rect, &edge2->rect) && - edge1->rect.y == edge2->rect.y; - } - else - { - return FALSE; - } -} - -static gboolean -rectangle_and_edge_intersection (const MetaRectangle *rect, - const MetaEdge *edge, - MetaEdge *overlap, - int *handle_type) -{ - const MetaRectangle *rect2 = &edge->rect; - MetaRectangle *result = &overlap->rect; - gboolean intersect = TRUE; - - /* We don't know how to set these, so set them to invalid values */ - overlap->edge_type = -1; - overlap->side_type = -1; - - /* Figure out what the intersection is */ - result->x = MAX (rect->x, rect2->x); - result->y = MAX (rect->y, rect2->y); - result->width = MIN (BOX_RIGHT (*rect), BOX_RIGHT (*rect2)) - result->x; - result->height = MIN (BOX_BOTTOM (*rect), BOX_BOTTOM (*rect2)) - result->y; - - /* Find out if the intersection is empty; have to do it this way since - * edges have a thickness of 0 - */ - if ((result->width < 0 || result->height < 0) || - (result->width == 0 && result->height == 0)) - { - result->width = 0; - result->height = 0; - intersect = FALSE; - } - else - { - /* Need to figure out the handle_type, a somewhat weird quantity: - * 0 - overlap is in middle of rect - * -1 - overlap is at the side of rect, and is on the opposite side - * of rect than the edge->side_type side - * 1 - overlap is at the side of rect, and the side of rect it is - * on is the edge->side_type side - */ - switch (edge->side_type) - { - case META_SIDE_LEFT: - if (result->x == rect->x) - *handle_type = 1; - else if (result->x == BOX_RIGHT (*rect)) - *handle_type = -1; - else - *handle_type = 0; - break; - case META_SIDE_RIGHT: - if (result->x == rect->x) - *handle_type = -1; - else if (result->x == BOX_RIGHT (*rect)) - *handle_type = 1; - else - *handle_type = 0; - break; - case META_SIDE_TOP: - if (result->y == rect->y) - *handle_type = 1; - else if (result->y == BOX_BOTTOM (*rect)) - *handle_type = -1; - else - *handle_type = 0; - break; - case META_SIDE_BOTTOM: - if (result->y == rect->y) - *handle_type = -1; - else if (result->y == BOX_BOTTOM (*rect)) - *handle_type = 1; - else - *handle_type = 0; - break; - default: - g_assert_not_reached (); - } - } - return intersect; -} - -/* Add all edges of the given rect to cur_edges and return the result. If - * rect_is_internal is false, the side types are switched (LEFT<->RIGHT and - * TOP<->BOTTOM). - */ -static GList* -add_edges (GList *cur_edges, - const MetaRectangle *rect, - gboolean rect_is_internal) -{ - MetaEdge *temp_edge; - int i; - - for (i=0; i<4; i++) - { - temp_edge = g_new (MetaEdge, 1); - temp_edge->rect = *rect; - switch (i) - { - case 0: - temp_edge->side_type = - rect_is_internal ? META_SIDE_LEFT : META_SIDE_RIGHT; - temp_edge->rect.width = 0; - break; - case 1: - temp_edge->side_type = - rect_is_internal ? META_SIDE_RIGHT : META_SIDE_LEFT; - temp_edge->rect.x += temp_edge->rect.width; - temp_edge->rect.width = 0; - break; - case 2: - temp_edge->side_type = - rect_is_internal ? META_SIDE_TOP : META_SIDE_BOTTOM; - temp_edge->rect.height = 0; - break; - case 3: - temp_edge->side_type = - rect_is_internal ? META_SIDE_BOTTOM : META_SIDE_TOP; - temp_edge->rect.y += temp_edge->rect.height; - temp_edge->rect.height = 0; - break; - } - temp_edge->edge_type = META_EDGE_SCREEN; - cur_edges = g_list_prepend (cur_edges, temp_edge); - } - - return cur_edges; -} - -/* Remove any part of old_edge that intersects remove and add any resulting - * edges to cur_list. Return cur_list when finished. - */ -static GList* -split_edge (GList *cur_list, - const MetaEdge *old_edge, - const MetaEdge *remove) -{ - MetaEdge *temp_edge; - switch (old_edge->side_type) - { - case META_SIDE_LEFT: - case META_SIDE_RIGHT: - g_assert (meta_rectangle_vert_overlap (&old_edge->rect, &remove->rect)); - if (BOX_TOP (old_edge->rect) < BOX_TOP (remove->rect)) - { - temp_edge = g_new (MetaEdge, 1); - *temp_edge = *old_edge; - temp_edge->rect.height = BOX_TOP (remove->rect) - - BOX_TOP (old_edge->rect); - cur_list = g_list_prepend (cur_list, temp_edge); - } - if (BOX_BOTTOM (old_edge->rect) > BOX_BOTTOM (remove->rect)) - { - temp_edge = g_new (MetaEdge, 1); - *temp_edge = *old_edge; - temp_edge->rect.y = BOX_BOTTOM (remove->rect); - temp_edge->rect.height = BOX_BOTTOM (old_edge->rect) - - BOX_BOTTOM (remove->rect); - cur_list = g_list_prepend (cur_list, temp_edge); - } - break; - case META_SIDE_TOP: - case META_SIDE_BOTTOM: - g_assert (meta_rectangle_horiz_overlap (&old_edge->rect, &remove->rect)); - if (BOX_LEFT (old_edge->rect) < BOX_LEFT (remove->rect)) - { - temp_edge = g_new (MetaEdge, 1); - *temp_edge = *old_edge; - temp_edge->rect.width = BOX_LEFT (remove->rect) - - BOX_LEFT (old_edge->rect); - cur_list = g_list_prepend (cur_list, temp_edge); - } - if (BOX_RIGHT (old_edge->rect) > BOX_RIGHT (remove->rect)) - { - temp_edge = g_new (MetaEdge, 1); - *temp_edge = *old_edge; - temp_edge->rect.x = BOX_RIGHT (remove->rect); - temp_edge->rect.width = BOX_RIGHT (old_edge->rect) - - BOX_RIGHT (remove->rect); - cur_list = g_list_prepend (cur_list, temp_edge); - } - break; - default: - g_assert_not_reached (); - } - - return cur_list; -} - -/* Split up edge and remove preliminary edges from strut_edges depending on - * if and how rect and edge intersect. - */ -static void -fix_up_edges (MetaRectangle *rect, MetaEdge *edge, - GList **strut_edges, GList **edge_splits, - gboolean *edge_needs_removal) -{ - MetaEdge overlap; - int handle_type; - - if (!rectangle_and_edge_intersection (rect, edge, &overlap, &handle_type)) - return; - - if (handle_type == 0 || handle_type == 1) - { - /* Put the result of removing overlap from edge into edge_splits */ - *edge_splits = split_edge (*edge_splits, edge, &overlap); - *edge_needs_removal = TRUE; - } - - if (handle_type == -1 || handle_type == 1) - { - /* Remove the overlap from strut_edges */ - /* First, loop over the edges of the strut */ - GList *tmp = *strut_edges; - while (tmp) - { - MetaEdge *cur = tmp->data; - /* If this is the edge that overlaps, then we need to split it */ - if (edges_overlap (cur, &overlap)) - { - GList *delete_me = tmp; - - /* Split this edge into some new ones */ - *strut_edges = split_edge (*strut_edges, cur, &overlap); - - /* Delete the old one */ - tmp = tmp->next; - g_free (cur); - *strut_edges = g_list_delete_link (*strut_edges, delete_me); - } - else - tmp = tmp->next; - } - } -} - -/** - * meta_rectangle_remove_intersections_with_boxes_from_edges: (skip) - * - * This function removes intersections of edges with the rectangles from the - * list of edges. - */ -GList* -meta_rectangle_remove_intersections_with_boxes_from_edges ( - GList *edges, - const GSList *rectangles) -{ - const GSList *rect_iter; - const int opposing = 1; - - /* Now remove all intersections of rectangles with the edge list */ - rect_iter = rectangles; - while (rect_iter) - { - MetaRectangle *rect = rect_iter->data; - GList *edge_iter = edges; - while (edge_iter) - { - MetaEdge *edge = edge_iter->data; - MetaEdge overlap; - int handle; - gboolean edge_iter_advanced = FALSE; - - /* If this edge overlaps with this rect... */ - if (rectangle_and_edge_intersection (rect, edge, &overlap, &handle)) - { - - /* "Intersections" where the edges touch but are opposite - * sides (e.g. a left edge against the right edge) should not - * be split. Note that the comments in - * rectangle_and_edge_intersection() say that opposing edges - * occur when handle is -1, BUT you need to remember that we - * treat the left side of a window as a right edge because - * it's what the right side of the window being moved should - * be-resisted-by/snap-to. So opposing is really 1. Anyway, - * we just keep track of it in the opposing constant set up - * above and if handle isn't equal to that, then we know the - * edge should be split. - */ - if (handle != opposing) - { - /* Keep track of this edge so we can delete it below */ - GList *delete_me = edge_iter; - edge_iter = edge_iter->next; - edge_iter_advanced = TRUE; - - /* Split the edge and add the result to beginning of edges */ - edges = split_edge (edges, edge, &overlap); - - /* Now free the edge... */ - g_free (edge); - edges = g_list_delete_link (edges, delete_me); - } - } - - if (!edge_iter_advanced) - edge_iter = edge_iter->next; - } - - rect_iter = rect_iter->next; - } - - return edges; -} - -/** - * meta_rectangle_find_onscreen_edges: (skip) - * - * This function is trying to find all the edges of an onscreen region. - */ -GList* -meta_rectangle_find_onscreen_edges (const MetaRectangle *basic_rect, - const GSList *all_struts) -{ - GList *ret; - GList *fixed_strut_rects; - GList *edge_iter; - const GList *strut_rect_iter; - - /* The algorithm is basically as follows: - * Make sure the struts are disjoint - * Initialize the edge_set to the edges of basic_rect - * Foreach strut: - * Put together a preliminary new edge from the edges of the strut - * Foreach edge in edge_set: - * - Split the edge if it is partially contained inside the strut - * - If the edge matches an edge of the strut (i.e. a strut just - * against the edge of the screen or a not-next-to-edge-of-screen - * strut adjacent to another), then both the edge from the - * edge_set and the preliminary edge for the strut will need to - * be split - * Add any remaining "preliminary" strut edges to the edge_set - */ - - /* Make sure the struts are disjoint */ - fixed_strut_rects = - get_disjoint_strut_rect_list_in_region (all_struts, basic_rect); - - /* Start off the list with the edges of basic_rect */ - ret = add_edges (NULL, basic_rect, TRUE); - - strut_rect_iter = fixed_strut_rects; - while (strut_rect_iter) - { - MetaRectangle *strut_rect = (MetaRectangle*) strut_rect_iter->data; - - /* Get the new possible edges we may need to add from the strut */ - GList *new_strut_edges = add_edges (NULL, strut_rect, FALSE); - - edge_iter = ret; - while (edge_iter) - { - MetaEdge *cur_edge = edge_iter->data; - GList *splits_of_cur_edge = NULL; - gboolean edge_needs_removal = FALSE; - - fix_up_edges (strut_rect, cur_edge, - &new_strut_edges, &splits_of_cur_edge, - &edge_needs_removal); - - if (edge_needs_removal) - { - /* Delete the old edge */ - GList *delete_me = edge_iter; - edge_iter = edge_iter->next; - g_free (cur_edge); - ret = g_list_delete_link (ret, delete_me); - - /* Add the new split parts of the edge */ - ret = g_list_concat (splits_of_cur_edge, ret); - } - else - { - edge_iter = edge_iter->next; - } - - /* edge_iter was already advanced above */ - } - - ret = g_list_concat (new_strut_edges, ret); - strut_rect_iter = strut_rect_iter->next; - } - - /* Sort the list */ - ret = g_list_sort (ret, meta_rectangle_edge_cmp); - - /* Free the fixed struts list */ - meta_rectangle_free_list_and_elements (fixed_strut_rects); - - return ret; -} - -/** - * meta_rectangle_find_nonintersected_monitor_edges: (skip) - * - */ -GList* -meta_rectangle_find_nonintersected_monitor_edges ( - const GList *monitor_rects, - const GSList *all_struts) -{ - /* This function cannot easily be merged with - * meta_rectangle_find_onscreen_edges() because real screen edges - * and strut edges both are of the type "there ain't anything - * immediately on the other side"; monitor edges are different. - */ - GList *ret; - const GList *cur; - GSList *temp_rects; - - /* Initialize the return list to be empty */ - ret = NULL; - - /* start of ret with all the edges of monitors that are adjacent to - * another monitor. - */ - cur = monitor_rects; - while (cur) - { - MetaRectangle *cur_rect = cur->data; - const GList *compare = monitor_rects; - while (compare) - { - MetaRectangle *compare_rect = compare->data; - - /* Check if cur might be horizontally adjacent to compare */ - if (meta_rectangle_vert_overlap(cur_rect, compare_rect)) - { - MetaSide side_type; - int y = MAX (cur_rect->y, compare_rect->y); - int height = MIN (BOX_BOTTOM (*cur_rect) - y, - BOX_BOTTOM (*compare_rect) - y); - int width = 0; - int x; - - if (BOX_LEFT (*cur_rect) == BOX_RIGHT (*compare_rect)) - { - /* compare_rect is to the left of cur_rect */ - x = BOX_LEFT (*cur_rect); - side_type = META_SIDE_LEFT; - } - else if (BOX_RIGHT (*cur_rect) == BOX_LEFT (*compare_rect)) - { - /* compare_rect is to the right of cur_rect */ - x = BOX_RIGHT (*cur_rect); - side_type = META_SIDE_RIGHT; - } - else - /* These rectangles aren't adjacent after all */ - x = INT_MIN; - - /* If the rectangles really are adjacent */ - if (x != INT_MIN) - { - /* We need a left edge for the monitor on the right, and - * a right edge for the monitor on the left. Just fill - * up the edges and stick 'em on the list. - */ - MetaEdge *new_edge = g_new (MetaEdge, 1); - - new_edge->rect = meta_rect (x, y, width, height); - new_edge->side_type = side_type; - new_edge->edge_type = META_EDGE_MONITOR; - - ret = g_list_prepend (ret, new_edge); - } - } - - /* Check if cur might be vertically adjacent to compare */ - if (meta_rectangle_horiz_overlap(cur_rect, compare_rect)) - { - MetaSide side_type; - int x = MAX (cur_rect->x, compare_rect->x); - int width = MIN (BOX_RIGHT (*cur_rect) - x, - BOX_RIGHT (*compare_rect) - x); - int height = 0; - int y; - - if (BOX_TOP (*cur_rect) == BOX_BOTTOM (*compare_rect)) - { - /* compare_rect is to the top of cur_rect */ - y = BOX_TOP (*cur_rect); - side_type = META_SIDE_TOP; - } - else if (BOX_BOTTOM (*cur_rect) == BOX_TOP (*compare_rect)) - { - /* compare_rect is to the bottom of cur_rect */ - y = BOX_BOTTOM (*cur_rect); - side_type = META_SIDE_BOTTOM; - } - else - /* These rectangles aren't adjacent after all */ - y = INT_MIN; - - /* If the rectangles really are adjacent */ - if (y != INT_MIN) - { - /* We need a top edge for the monitor on the bottom, and - * a bottom edge for the monitor on the top. Just fill - * up the edges and stick 'em on the list. - */ - MetaEdge *new_edge = g_new (MetaEdge, 1); - - new_edge->rect = meta_rect (x, y, width, height); - new_edge->side_type = side_type; - new_edge->edge_type = META_EDGE_MONITOR; - - ret = g_list_prepend (ret, new_edge); - } - } - - compare = compare->next; - } - cur = cur->next; - } - - temp_rects = NULL; - for (; all_struts; all_struts = all_struts->next) - temp_rects = g_slist_prepend (temp_rects, - &((MetaStrut*)all_struts->data)->rect); - ret = meta_rectangle_remove_intersections_with_boxes_from_edges (ret, - temp_rects); - g_slist_free (temp_rects); - - /* Sort the list */ - ret = g_list_sort (ret, meta_rectangle_edge_cmp); - - return ret; -} - -gboolean -meta_rectangle_is_adjacent_to (MetaRectangle *rect, - MetaRectangle *other) -{ - int rect_x1 = rect->x; - int rect_y1 = rect->y; - int rect_x2 = rect->x + rect->width; - int rect_y2 = rect->y + rect->height; - int other_x1 = other->x; - int other_y1 = other->y; - int other_x2 = other->x + other->width; - int other_y2 = other->y + other->height; - - if ((rect_x1 == other_x2 || rect_x2 == other_x1) && - !(rect_y2 <= other_y1 || rect_y1 >= other_y2)) - return TRUE; - else if ((rect_y1 == other_y2 || rect_y2 == other_y1) && - !(rect_x2 <= other_x1 || rect_x1 >= other_x2)) - return TRUE; - else - return FALSE; -} - -void -meta_rectangle_scale_double (const MetaRectangle *rect, - double scale, - MetaRoundingStrategy rounding_strategy, - MetaRectangle *dest) -{ - graphene_rect_t tmp = GRAPHENE_RECT_INIT (rect->x, rect->y, - rect->width, rect->height); - - graphene_rect_scale (&tmp, scale, scale, &tmp); - meta_rectangle_from_graphene_rect (&tmp, rounding_strategy, dest); -} - -void -meta_rectangle_transform (const MetaRectangle *rect, - MetaMonitorTransform transform, - int width, - int height, - MetaRectangle *dest) -{ - switch (transform) - { - case META_MONITOR_TRANSFORM_NORMAL: - *dest = *rect; - break; - case META_MONITOR_TRANSFORM_90: - *dest = (MetaRectangle) { - .x = width - (rect->y + rect->height), - .y = rect->x, - .width = rect->height, - .height = rect->width, - }; - break; - case META_MONITOR_TRANSFORM_180: - *dest = (MetaRectangle) { - .x = width - (rect->x + rect->width), - .y = height - (rect->y + rect->height), - .width = rect->width, - .height = rect->height, - }; - break; - case META_MONITOR_TRANSFORM_270: - *dest = (MetaRectangle) { - .x = rect->y, - .y = height - (rect->x + rect->width), - .width = rect->height, - .height = rect->width, - }; - break; - case META_MONITOR_TRANSFORM_FLIPPED: - *dest = (MetaRectangle) { - .x = width - (rect->x + rect->width), - .y = rect->y, - .width = rect->width, - .height = rect->height, - }; - break; - case META_MONITOR_TRANSFORM_FLIPPED_90: - *dest = (MetaRectangle) { - .x = width - (rect->y + rect->height), - .y = height - (rect->x + rect->width), - .width = rect->height, - .height = rect->width, - }; - break; - case META_MONITOR_TRANSFORM_FLIPPED_180: - *dest = (MetaRectangle) { - .x = rect->x, - .y = height - (rect->y + rect->height), - .width = rect->width, - .height = rect->height, - }; - break; - case META_MONITOR_TRANSFORM_FLIPPED_270: - *dest = (MetaRectangle) { - .x = rect->y, - .y = rect->x, - .width = rect->height, - .height = rect->width, - }; - break; - } -} - -void -meta_rectangle_from_graphene_rect (const graphene_rect_t *rect, - MetaRoundingStrategy rounding_strategy, - MetaRectangle *dest) -{ - switch (rounding_strategy) - { - case META_ROUNDING_STRATEGY_SHRINK: - { - *dest = (MetaRectangle) { - .x = ceilf (rect->origin.x), - .y = ceilf (rect->origin.y), - .width = floorf (rect->size.width), - .height = floorf (rect->size.height), - }; - } - break; - case META_ROUNDING_STRATEGY_GROW: - { - graphene_rect_t clamped = *rect; - - graphene_rect_round_extents (&clamped, &clamped); - - *dest = (MetaRectangle) { - .x = clamped.origin.x, - .y = clamped.origin.y, - .width = clamped.size.width, - .height = clamped.size.height, - }; - } - break; - case META_ROUNDING_STRATEGY_ROUND: - { - *dest = (MetaRectangle) { - .x = roundf (rect->origin.x), - .y = roundf (rect->origin.y), - .width = roundf (rect->size.width), - .height = roundf (rect->size.height), - }; - } - } -} - -void -meta_rectangle_crop_and_scale (const MetaRectangle *rect, - graphene_rect_t *src_rect, - int dst_width, - int dst_height, - MetaRectangle *dest) -{ - graphene_rect_t tmp = GRAPHENE_RECT_INIT (rect->x, rect->y, - rect->width, rect->height); - - graphene_rect_scale (&tmp, - src_rect->size.width / dst_width, - src_rect->size.height / dst_height, - &tmp); - graphene_rect_offset (&tmp, src_rect->origin.x, src_rect->origin.y); - - meta_rectangle_from_graphene_rect (&tmp, META_ROUNDING_STRATEGY_GROW, dest); -} diff --git a/src/core/constraints.c b/src/core/constraints.c deleted file mode 100644 index 4b1d95338..000000000 --- a/src/core/constraints.c +++ /dev/null @@ -1,1878 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Mutter size/position constraints */ - -/* - * Copyright (C) 2002, 2003 Red Hat, Inc. - * Copyright (C) 2003, 2004 Rob Adams - * Copyright (C) 2005, 2006 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "config.h" - -#include "core/constraints.h" - -#include <stdlib.h> -#include <math.h> - -#include "backends/meta-backend-private.h" -#include "backends/meta-logical-monitor.h" -#include "backends/meta-monitor-manager-private.h" -#include "core/boxes-private.h" -#include "core/meta-workspace-manager-private.h" -#include "core/place.h" -#include "core/workspace-private.h" -#include "meta/prefs.h" - -#if 0 - // This is the short and sweet version of how to hack on this file; see - // doc/how-constraints-works.txt for the gory details. The basics of - // understanding this file can be shown by the steps needed to add a new - // constraint, which are: - // 1) Add a new entry in the ConstraintPriority enum; higher values - // have higher priority - // 2) Write a new function following the format of the example below, - // "constrain_whatever". - // 3) Add your function to the all_constraints and all_constraint_names - // arrays (the latter of which is for debugging purposes) - // - // An example constraint function, constrain_whatever: - // - // /* constrain_whatever does the following: - // * Quits (returning true) if priority is higher than PRIORITY_WHATEVER - // * If check_only is TRUE - // * Returns whether the constraint is satisfied or not - // * otherwise - // * Enforces the constraint - // * Note that the value of PRIORITY_WHATEVER is centralized with the - // * priorities of other constraints in the definition of ConstrainPriority - // * for easier maintenance and shuffling of priorities. - // */ - // static gboolean - // constrain_whatever (MetaWindow *window, - // ConstraintInfo *info, - // ConstraintPriority priority, - // gboolean check_only) - // { - // if (priority > PRIORITY_WHATEVER) - // return TRUE; - // - // /* Determine whether constraint applies; note that if the constraint - // * cannot possibly be satisfied, constraint_applies should be set to - // * false. If we don't do this, all constraints with a lesser priority - // * will be dropped along with this one, and we'd rather apply as many as - // * possible. - // */ - // if (!constraint_applies) - // return TRUE; - // - // /* Determine whether constraint is already satisfied; if we're only - // * checking the status of whether the constraint is satisfied, we end - // * here. - // */ - // if (check_only || constraint_already_satisfied) - // return constraint_already_satisfied; - // - // /* Enforce constraints */ - // return TRUE; /* Note that we exited early if check_only is FALSE; also, - // * we know we can return TRUE here because we exited early - // * if the constraint could not be satisfied; not that the - // * return value is heeded in this case... - // */ - // } -#endif - -typedef enum -{ - PRIORITY_MINIMUM = 0, /* Dummy value used for loop start = min(all priorities) */ - PRIORITY_ASPECT_RATIO = 0, - PRIORITY_ENTIRELY_VISIBLE_ON_SINGLE_MONITOR = 0, - PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA = 1, - PRIORITY_SIZE_HINTS_INCREMENTS = 1, - PRIORITY_MAXIMIZATION = 2, - PRIORITY_TILING = 2, - PRIORITY_FULLSCREEN = 2, - PRIORITY_SIZE_HINTS_LIMITS = 3, - PRIORITY_TITLEBAR_VISIBLE = 4, - PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA = 4, - PRIORITY_CUSTOM_RULE = 4, - PRIORITY_MAXIMUM = 4 /* Dummy value used for loop end = max(all priorities) */ -} ConstraintPriority; - -typedef enum -{ - ACTION_MOVE, - ACTION_RESIZE, - ACTION_MOVE_AND_RESIZE -} ActionType; - -typedef struct -{ - MetaRectangle orig; - MetaRectangle current; - MetaRectangle temporary; - int rel_x; - int rel_y; - ActionType action_type; - gboolean is_user_action; - - /* I know that these two things probably look similar at first, but they - * have much different uses. See doc/how-constraints-works.txt for for - * explanation of the differences and similarity between resize_gravity - * and fixed_directions - */ - MetaGravity resize_gravity; - FixedDirections fixed_directions; - - /* work_area_monitor - current monitor region minus struts - * entire_monitor - current monitor, including strut regions - */ - MetaRectangle work_area_monitor; - MetaRectangle entire_monitor; - - /* Spanning rectangles for the non-covered (by struts) region of the - * screen and also for just the current monitor - */ - GList *usable_screen_region; - GList *usable_monitor_region; - - MetaMoveResizeFlags flags; -} ConstraintInfo; - -static gboolean do_screen_and_monitor_relative_constraints (MetaWindow *window, - GList *region_spanning_rectangles, - ConstraintInfo *info, - gboolean check_only); -static gboolean constrain_custom_rule (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_modal_dialog (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_maximization (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_tiling (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_fullscreen (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_size_increments (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_size_limits (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_aspect_ratio (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_to_single_monitor (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_fully_onscreen (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_titlebar_visible (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_partially_onscreen (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); - -static void setup_constraint_info (ConstraintInfo *info, - MetaWindow *window, - MetaMoveResizeFlags flags, - MetaGravity resize_gravity, - const MetaRectangle *orig, - MetaRectangle *new); -static void place_window_if_needed (MetaWindow *window, - ConstraintInfo *info); -static void update_onscreen_requirements (MetaWindow *window, - ConstraintInfo *info); - -typedef gboolean (* ConstraintFunc) (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); - -typedef struct { - ConstraintFunc func; - const char* name; -} Constraint; - -static const Constraint all_constraints[] = { - {constrain_custom_rule, "constrain_custom_rule"}, - {constrain_modal_dialog, "constrain_modal_dialog"}, - {constrain_maximization, "constrain_maximization"}, - {constrain_tiling, "constrain_tiling"}, - {constrain_fullscreen, "constrain_fullscreen"}, - {constrain_size_increments, "constrain_size_increments"}, - {constrain_size_limits, "constrain_size_limits"}, - {constrain_aspect_ratio, "constrain_aspect_ratio"}, - {constrain_to_single_monitor, "constrain_to_single_monitor"}, - {constrain_fully_onscreen, "constrain_fully_onscreen"}, - {constrain_titlebar_visible, "constrain_titlebar_visible"}, - {constrain_partially_onscreen, "constrain_partially_onscreen"}, - {NULL, NULL} -}; - -static gboolean -do_all_constraints (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - const Constraint *constraint; - gboolean satisfied; - - constraint = &all_constraints[0]; - satisfied = TRUE; - while (constraint->func != NULL) - { - satisfied = satisfied && - (*constraint->func) (window, info, priority, check_only); - - if (!check_only) - { - /* Log how the constraint modified the position */ - meta_topic (META_DEBUG_GEOMETRY, - "info->current is %d,%d +%d,%d after %s", - info->current.x, info->current.y, - info->current.width, info->current.height, - constraint->name); - } - else if (!satisfied) - { - /* Log which constraint was not satisfied */ - meta_topic (META_DEBUG_GEOMETRY, - "constraint %s not satisfied.", - constraint->name); - return FALSE; - } - ++constraint; - } - - return TRUE; -} - -void -meta_window_constrain (MetaWindow *window, - MetaMoveResizeFlags flags, - MetaGravity resize_gravity, - const MetaRectangle *orig, - MetaRectangle *new, - MetaRectangle *temporary, - int *rel_x, - int *rel_y) -{ - ConstraintInfo info; - ConstraintPriority priority = PRIORITY_MINIMUM; - gboolean satisfied = FALSE; - - meta_topic (META_DEBUG_GEOMETRY, - "Constraining %s in move from %d,%d %dx%d to %d,%d %dx%d", - window->desc, - orig->x, orig->y, orig->width, orig->height, - new->x, new->y, new->width, new->height); - - setup_constraint_info (&info, - window, - flags, - resize_gravity, - orig, - new); - place_window_if_needed (window, &info); - - while (!satisfied && priority <= PRIORITY_MAXIMUM) { - gboolean check_only = TRUE; - - /* Individually enforce all the high-enough priority constraints */ - do_all_constraints (window, &info, priority, !check_only); - - /* Check if all high-enough priority constraints are simultaneously - * satisfied - */ - satisfied = do_all_constraints (window, &info, priority, check_only); - - /* Drop the least important constraints if we can't satisfy them all */ - priority++; - } - - /* Make sure we use the constrained position */ - *new = info.current; - *temporary = info.temporary; - *rel_x = info.rel_x; - *rel_y = info.rel_y; - - /* We may need to update window->require_fully_onscreen, - * window->require_on_single_monitor, and perhaps other quantities - * if this was a user move or user move-and-resize operation. - */ - update_onscreen_requirements (window, &info); -} - -static void -setup_constraint_info (ConstraintInfo *info, - MetaWindow *window, - MetaMoveResizeFlags flags, - MetaGravity resize_gravity, - const MetaRectangle *orig, - MetaRectangle *new) -{ - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - MetaLogicalMonitor *logical_monitor; - MetaWorkspace *cur_workspace; - - info->orig = *orig; - info->current = *new; - info->temporary = *orig; - info->rel_x = 0; - info->rel_y = 0; - info->flags = flags; - - if (info->current.width < 1) - info->current.width = 1; - if (info->current.height < 1) - info->current.height = 1; - - if (flags & META_MOVE_RESIZE_MOVE_ACTION && flags & META_MOVE_RESIZE_RESIZE_ACTION) - info->action_type = ACTION_MOVE_AND_RESIZE; - else if (flags & META_MOVE_RESIZE_RESIZE_ACTION) - info->action_type = ACTION_RESIZE; - else if (flags & META_MOVE_RESIZE_MOVE_ACTION) - info->action_type = ACTION_MOVE; - else - g_error ("BAD, BAD developer! No treat for you! (Fix your calls to " - "meta_window_move_resize_internal())."); - - info->is_user_action = (flags & META_MOVE_RESIZE_USER_ACTION); - - info->resize_gravity = resize_gravity; - - /* FIXME: fixed_directions might be more sane if we (a) made it - * depend on the grab_op type instead of current amount of movement - * (thus implying that it only has effect when user_action is true, - * and (b) ignored it for aspect ratio windows -- at least in those - * cases where both directions do actually change size. - */ - info->fixed_directions = FIXED_DIRECTION_NONE; - /* If x directions don't change but either y direction does */ - if ( orig->x == new->x && orig->x + orig->width == new->x + new->width && - (orig->y != new->y || orig->y + orig->height != new->y + new->height)) - { - info->fixed_directions = FIXED_DIRECTION_X; - } - /* If y directions don't change but either x direction does */ - if ( orig->y == new->y && orig->y + orig->height == new->y + new->height && - (orig->x != new->x || orig->x + orig->width != new->x + new->width )) - { - info->fixed_directions = FIXED_DIRECTION_Y; - } - /* The point of fixed directions is just that "move to nearest valid - * position" is sometimes a poorer choice than "move to nearest - * valid position but only change this coordinate" for windows the - * user is explicitly moving. This isn't ever true for things that - * aren't explicit user interaction, though, so just clear it out. - */ - if (!info->is_user_action) - info->fixed_directions = FIXED_DIRECTION_NONE; - - logical_monitor = - meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager, - &info->current); - meta_window_get_work_area_for_logical_monitor (window, - logical_monitor, - &info->work_area_monitor); - - if (window->fullscreen && meta_window_has_fullscreen_monitors (window)) - { - info->entire_monitor = window->fullscreen_monitors.top->rect; - meta_rectangle_union (&info->entire_monitor, - &window->fullscreen_monitors.bottom->rect, - &info->entire_monitor); - meta_rectangle_union (&info->entire_monitor, - &window->fullscreen_monitors.left->rect, - &info->entire_monitor); - meta_rectangle_union (&info->entire_monitor, - &window->fullscreen_monitors.right->rect, - &info->entire_monitor); - } - else - { - info->entire_monitor = logical_monitor->rect; - if (window->fullscreen) - meta_window_adjust_fullscreen_monitor_rect (window, &info->entire_monitor); - } - - cur_workspace = window->display->workspace_manager->active_workspace; - info->usable_screen_region = - meta_workspace_get_onscreen_region (cur_workspace); - info->usable_monitor_region = - meta_workspace_get_onmonitor_region (cur_workspace, logical_monitor); - - /* Log all this information for debugging */ - meta_topic (META_DEBUG_GEOMETRY, - "Setting up constraint info:\n" - " orig: %d,%d +%d,%d\n" - " new : %d,%d +%d,%d\n" - " action_type : %s\n" - " is_user_action : %s\n" - " resize_gravity : %s\n" - " fixed_directions: %s\n" - " work_area_monitor: %d,%d +%d,%d\n" - " entire_monitor : %d,%d +%d,%d", - info->orig.x, info->orig.y, info->orig.width, info->orig.height, - info->current.x, info->current.y, - info->current.width, info->current.height, - (info->action_type == ACTION_MOVE) ? "Move" : - (info->action_type == ACTION_RESIZE) ? "Resize" : - (info->action_type == ACTION_MOVE_AND_RESIZE) ? "Move&Resize" : - "Freakin' Invalid Stupid", - (info->is_user_action) ? "true" : "false", - meta_gravity_to_string (info->resize_gravity), - (info->fixed_directions == FIXED_DIRECTION_NONE) ? "None" : - (info->fixed_directions == FIXED_DIRECTION_X) ? "X fixed" : - (info->fixed_directions == FIXED_DIRECTION_Y) ? "Y fixed" : - "Freakin' Invalid Stupid", - info->work_area_monitor.x, info->work_area_monitor.y, - info->work_area_monitor.width, - info->work_area_monitor.height, - info->entire_monitor.x, info->entire_monitor.y, - info->entire_monitor.width, info->entire_monitor.height); -} - -static MetaRectangle * -get_start_rect_for_resize (MetaWindow *window, - ConstraintInfo *info) -{ - if (!info->is_user_action && info->action_type == ACTION_MOVE_AND_RESIZE) - return &info->current; - else - return &info->orig; -} - -static void -place_window_if_needed(MetaWindow *window, - ConstraintInfo *info) -{ - gboolean did_placement; - - /* Do placement if any, so we go ahead and apply position - * constraints in a move-only context. Don't place - * maximized/minimized/fullscreen windows until they are - * unmaximized, unminimized and unfullscreened. - */ - did_placement = FALSE; - if (!window->placed && - window->calc_placement && - !(window->maximized_horizontally || - window->maximized_vertically) && - !window->minimized && - !window->fullscreen) - { - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - MetaRectangle orig_rect; - MetaRectangle placed_rect; - MetaWorkspace *cur_workspace; - MetaLogicalMonitor *logical_monitor; - - placed_rect = (MetaRectangle) { - .x = window->rect.x, - .y = window->rect.y, - .width = info->current.width, - .height = info->current.height - }; - - orig_rect = info->orig; - - if (window->placement.rule) - { - meta_window_process_placement (window, - window->placement.rule, - &info->rel_x, &info->rel_y); - placed_rect.x = window->placement.rule->parent_rect.x + info->rel_x; - placed_rect.y = window->placement.rule->parent_rect.y + info->rel_y; - } - else - { - meta_window_place (window, orig_rect.x, orig_rect.y, - &placed_rect.x, &placed_rect.y); - } - did_placement = TRUE; - - /* placing the window may have changed the monitor. Find the - * new monitor and update the ConstraintInfo - */ - logical_monitor = - meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager, - &placed_rect); - info->entire_monitor = logical_monitor->rect; - meta_window_get_work_area_for_logical_monitor (window, - logical_monitor, - &info->work_area_monitor); - cur_workspace = window->display->workspace_manager->active_workspace; - info->usable_monitor_region = - meta_workspace_get_onmonitor_region (cur_workspace, logical_monitor); - - info->current.x = placed_rect.x; - info->current.y = placed_rect.y; - - /* Since we just barely placed the window, there's no reason to - * consider any of the directions fixed. - */ - info->fixed_directions = FIXED_DIRECTION_NONE; - } - - if (window->placed || did_placement) - { - if (window->maximize_horizontally_after_placement || - window->maximize_vertically_after_placement) - { - /* define a sane saved_rect so that the user can unmaximize to - * something reasonable. - */ - if (info->current.width >= info->work_area_monitor.width) - { - info->current.width = .75 * info->work_area_monitor.width; - info->current.x = info->work_area_monitor.x + - .125 * info->work_area_monitor.width; - } - if (info->current.height >= info->work_area_monitor.height) - { - info->current.height = .75 * info->work_area_monitor.height; - info->current.y = info->work_area_monitor.y + - .083 * info->work_area_monitor.height; - } - - /* idle_move_resize() uses the unconstrained_rect, so make sure it - * uses the placed coordinates (bug #556696). - */ - window->unconstrained_rect = info->current; - - if (window->maximize_horizontally_after_placement || - window->maximize_vertically_after_placement) - meta_window_maximize_internal (window, - (window->maximize_horizontally_after_placement ? - META_MAXIMIZE_HORIZONTAL : 0 ) | - (window->maximize_vertically_after_placement ? - META_MAXIMIZE_VERTICAL : 0), &info->current); - - window->maximize_horizontally_after_placement = FALSE; - window->maximize_vertically_after_placement = FALSE; - } - if (window->minimize_after_placement) - { - meta_window_minimize (window); - window->minimize_after_placement = FALSE; - } - } -} - -static void -update_onscreen_requirements (MetaWindow *window, - ConstraintInfo *info) -{ - gboolean old; - - /* We only apply the various onscreen requirements to normal windows */ - if (window->type == META_WINDOW_DESKTOP || - window->type == META_WINDOW_DOCK) - return; - - /* We don't want to update the requirements for fullscreen windows; - * fullscreen windows are specially handled anyway, and it updating - * the requirements when windows enter fullscreen mode mess up the - * handling of the window when it leaves that mode (especially when - * the application sends a bunch of configurerequest events). See - * #353699. - */ - if (window->fullscreen) - return; - - /* USABILITY NOTE: Naturally, I only want the require_fully_onscreen, - * require_on_single_monitor, and require_titlebar_visible flags to - * *become false* due to user interactions (which is allowed since - * certain constraints are ignored for user interactions regardless of - * the setting of these flags). However, whether to make these flags - * *become true* due to just an application interaction is a little - * trickier. It's possible that users may find not doing that strange - * since two application interactions that resize in opposite ways don't - * necessarily end up cancelling--but it may also be strange for the user - * to have an application resize the window so that it's onscreen, the - * user forgets about it, and then later the app is able to resize itself - * off the screen. Anyway, for now, I think the latter is the more - * problematic case but this may need to be revisited. - */ - - /* Update whether we want future constraint runs to require the - * window to be on fully onscreen. - */ - old = window->require_fully_onscreen; - window->require_fully_onscreen = - meta_rectangle_contained_in_region (info->usable_screen_region, - &info->current); - if (old != window->require_fully_onscreen) - meta_topic (META_DEBUG_GEOMETRY, - "require_fully_onscreen for %s toggled to %s", - window->desc, - window->require_fully_onscreen ? "TRUE" : "FALSE"); - - /* Update whether we want future constraint runs to require the - * window to be on a single monitor. - */ - old = window->require_on_single_monitor; - window->require_on_single_monitor = - meta_rectangle_contained_in_region (info->usable_monitor_region, - &info->current); - if (old != window->require_on_single_monitor) - meta_topic (META_DEBUG_GEOMETRY, - "require_on_single_monitor for %s toggled to %s", - window->desc, - window->require_on_single_monitor ? "TRUE" : "FALSE"); - - /* Update whether we want future constraint runs to require the - * titlebar to be visible. - */ - if (window->frame && window->decorated) - { - MetaRectangle titlebar_rect, frame_rect; - - meta_window_get_titlebar_rect (window, &titlebar_rect); - meta_window_get_frame_rect (window, &frame_rect); - - /* translate into screen coordinates */ - titlebar_rect.x = frame_rect.x; - titlebar_rect.y = frame_rect.y; - - old = window->require_titlebar_visible; - window->require_titlebar_visible = - meta_rectangle_overlaps_with_region (info->usable_screen_region, - &titlebar_rect); - if (old != window->require_titlebar_visible) - meta_topic (META_DEBUG_GEOMETRY, - "require_titlebar_visible for %s toggled to %s", - window->desc, - window->require_titlebar_visible ? "TRUE" : "FALSE"); - } -} - -static inline void -get_size_limits (MetaWindow *window, - MetaRectangle *min_size, - MetaRectangle *max_size) -{ - /* We pack the results into MetaRectangle structs just for convenience; we - * don't actually use the position of those rects. - */ - min_size->x = min_size->y = max_size->x = max_size->y = 0; - min_size->width = window->size_hints.min_width; - min_size->height = window->size_hints.min_height; - max_size->width = window->size_hints.max_width; - max_size->height = window->size_hints.max_height; - - meta_window_client_rect_to_frame_rect (window, min_size, min_size); - meta_window_client_rect_to_frame_rect (window, max_size, max_size); -} - -static void -placement_rule_flip_horizontally (MetaPlacementRule *placement_rule) -{ - if (placement_rule->anchor & META_PLACEMENT_ANCHOR_LEFT) - { - placement_rule->anchor &= ~META_PLACEMENT_ANCHOR_LEFT; - placement_rule->anchor |= META_PLACEMENT_ANCHOR_RIGHT; - } - else if (placement_rule->anchor & META_PLACEMENT_ANCHOR_RIGHT) - { - placement_rule->anchor &= ~META_PLACEMENT_ANCHOR_RIGHT; - placement_rule->anchor |= META_PLACEMENT_ANCHOR_LEFT; - } - - if (placement_rule->gravity & META_PLACEMENT_GRAVITY_LEFT) - { - placement_rule->gravity &= ~META_PLACEMENT_GRAVITY_LEFT; - placement_rule->gravity |= META_PLACEMENT_GRAVITY_RIGHT; - } - else if (placement_rule->gravity & META_PLACEMENT_GRAVITY_RIGHT) - { - placement_rule->gravity &= ~META_PLACEMENT_GRAVITY_RIGHT; - placement_rule->gravity |= META_PLACEMENT_GRAVITY_LEFT; - } -} - -static void -placement_rule_flip_vertically (MetaPlacementRule *placement_rule) -{ - if (placement_rule->anchor & META_PLACEMENT_ANCHOR_TOP) - { - placement_rule->anchor &= ~META_PLACEMENT_ANCHOR_TOP; - placement_rule->anchor |= META_PLACEMENT_ANCHOR_BOTTOM; - } - else if (placement_rule->anchor & META_PLACEMENT_ANCHOR_BOTTOM) - { - placement_rule->anchor &= ~META_PLACEMENT_ANCHOR_BOTTOM; - placement_rule->anchor |= META_PLACEMENT_ANCHOR_TOP; - } - - if (placement_rule->gravity & META_PLACEMENT_GRAVITY_TOP) - { - placement_rule->gravity &= ~META_PLACEMENT_GRAVITY_TOP; - placement_rule->gravity |= META_PLACEMENT_GRAVITY_BOTTOM; - } - else if (placement_rule->gravity & META_PLACEMENT_GRAVITY_BOTTOM) - { - placement_rule->gravity &= ~META_PLACEMENT_GRAVITY_BOTTOM; - placement_rule->gravity |= META_PLACEMENT_GRAVITY_TOP; - } -} - -static void -try_flip_window_position (MetaWindow *window, - ConstraintInfo *info, - MetaPlacementRule *placement_rule, - MetaPlacementConstraintAdjustment constraint_adjustment, - int parent_x, - int parent_y, - MetaRectangle *rect, - int *rel_x, - int *rel_y, - MetaRectangle *intersection) -{ - MetaPlacementRule flipped_rule = *placement_rule; - MetaRectangle flipped_rect; - MetaRectangle flipped_intersection; - int flipped_rel_x; - int flipped_rel_y; - - switch (constraint_adjustment) - { - case META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X: - placement_rule_flip_horizontally (&flipped_rule); - break; - case META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y: - placement_rule_flip_vertically (&flipped_rule); - break; - - default: - g_assert_not_reached (); - } - - flipped_rect = info->current; - meta_window_process_placement (window, &flipped_rule, - &flipped_rel_x, &flipped_rel_y); - flipped_rect.x = parent_x + flipped_rel_x; - flipped_rect.y = parent_y + flipped_rel_y; - meta_rectangle_intersect (&flipped_rect, &info->work_area_monitor, - &flipped_intersection); - - if ((constraint_adjustment == META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X && - flipped_intersection.width == flipped_rect.width) || - (constraint_adjustment == META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y && - flipped_intersection.height == flipped_rect.height)) - { - *placement_rule = flipped_rule; - *rect = flipped_rect; - *rel_x = flipped_rel_x; - *rel_y = flipped_rel_y; - *intersection = flipped_intersection; - } -} - -static gboolean -is_custom_rule_satisfied (MetaRectangle *rect, - MetaPlacementRule *placement_rule, - MetaRectangle *intersection) -{ - uint32_t x_constrain_actions, y_constrain_actions; - - x_constrain_actions = (META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_X | - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X); - y_constrain_actions = (META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_Y | - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y); - if ((placement_rule->constraint_adjustment & x_constrain_actions && - rect->width != intersection->width) || - (placement_rule->constraint_adjustment & y_constrain_actions && - rect->height != intersection->height)) - return FALSE; - else - return TRUE; -} - -static gboolean -constrain_custom_rule (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - MetaPlacementRule *placement_rule; - MetaRectangle intersection; - gboolean constraint_satisfied; - MetaRectangle temporary_rect; - MetaRectangle adjusted_unconstrained; - int adjusted_rel_x; - int adjusted_rel_y; - MetaPlacementRule current_rule; - MetaWindow *parent; - int parent_x, parent_y; - - if (priority > PRIORITY_CUSTOM_RULE) - return TRUE; - - placement_rule = meta_window_get_placement_rule (window); - if (!placement_rule) - return TRUE; - - parent = meta_window_get_transient_for (window); - if (window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_FINISHED) - { - placement_rule->parent_rect.x = parent->rect.x; - placement_rule->parent_rect.y = parent->rect.y; - } - parent_x = placement_rule->parent_rect.x; - parent_y = placement_rule->parent_rect.y; - - /* - * Calculate the temporary position, meaning a position that will be - * applied if the new constrained position requires asynchronous - * configuration of the window. This happens for example when the parent - * moves, causing this window to change relative position, meaning it can - * only have its newly constrained position applied when the configuration is - * acknowledged. - */ - - switch (window->placement.state) - { - case META_PLACEMENT_STATE_UNCONSTRAINED: - temporary_rect = info->current; - break; - case META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED: - case META_PLACEMENT_STATE_CONSTRAINED_PENDING: - case META_PLACEMENT_STATE_CONSTRAINED_FINISHED: - case META_PLACEMENT_STATE_INVALIDATED: - temporary_rect = (MetaRectangle) { - .x = parent->rect.x + window->placement.current.rel_x, - .y = parent->rect.y + window->placement.current.rel_y, - .width = info->current.width, - .height = info->current.height, - }; - break; - } - - /* - * Calculate an adjusted current position. Depending on the rule - * configuration and placement state, this may result in window being - * reconstrained. - */ - - adjusted_unconstrained = temporary_rect; - - if (window->placement.state == META_PLACEMENT_STATE_INVALIDATED || - window->placement.state == META_PLACEMENT_STATE_UNCONSTRAINED || - (window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_FINISHED && - placement_rule->is_reactive)) - { - meta_window_process_placement (window, placement_rule, - &adjusted_rel_x, - &adjusted_rel_y); - adjusted_unconstrained.x = parent_x + adjusted_rel_x; - adjusted_unconstrained.y = parent_y + adjusted_rel_y; - } - else if (window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_PENDING) - { - adjusted_rel_x = window->placement.pending.rel_x; - adjusted_rel_y = window->placement.pending.rel_y; - adjusted_unconstrained.x = window->placement.pending.x; - adjusted_unconstrained.y = window->placement.pending.y; - } - else - { - adjusted_rel_x = window->placement.current.rel_x; - adjusted_rel_y = window->placement.current.rel_y; - } - - meta_rectangle_intersect (&adjusted_unconstrained, &info->work_area_monitor, - &intersection); - - constraint_satisfied = (meta_rectangle_equal (&info->current, - &adjusted_unconstrained) && - is_custom_rule_satisfied (&adjusted_unconstrained, - placement_rule, - &intersection)); - - if (check_only) - return constraint_satisfied; - - info->current = adjusted_unconstrained; - info->rel_x = adjusted_rel_x; - info->rel_y = adjusted_rel_y; - info->temporary = temporary_rect; - - switch (window->placement.state) - { - case META_PLACEMENT_STATE_CONSTRAINED_FINISHED: - if (!placement_rule->is_reactive) - return TRUE; - break; - case META_PLACEMENT_STATE_CONSTRAINED_PENDING: - case META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED: - return TRUE; - case META_PLACEMENT_STATE_UNCONSTRAINED: - case META_PLACEMENT_STATE_INVALIDATED: - break; - } - - if (constraint_satisfied) - goto done; - - /* - * Process the placement rule in order either until constraints are - * satisfied, or there are no more rules to process. - */ - - current_rule = *placement_rule; - - if (info->current.width != intersection.width && - (current_rule.constraint_adjustment & - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X)) - { - try_flip_window_position (window, info, ¤t_rule, - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X, - parent_x, - parent_y, - &info->current, - &info->rel_x, - &info->rel_y, - &intersection); - } - if (info->current.height != intersection.height && - (current_rule.constraint_adjustment & - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y)) - { - try_flip_window_position (window, info, ¤t_rule, - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y, - parent_x, - parent_y, - &info->current, - &info->rel_x, - &info->rel_y, - &intersection); - } - - meta_rectangle_intersect (&info->current, &info->work_area_monitor, - &intersection); - constraint_satisfied = is_custom_rule_satisfied (&info->current, - placement_rule, - &intersection); - - if (constraint_satisfied) - goto done; - - if (current_rule.constraint_adjustment & - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_X) - { - int current_x2; - int work_area_monitor_x2; - int new_x; - - current_x2 = info->current.x + info->current.width; - work_area_monitor_x2 = (info->work_area_monitor.x + - info->work_area_monitor.width); - - if (current_x2 > work_area_monitor_x2) - { - new_x = MAX (info->work_area_monitor.x, - work_area_monitor_x2 - info->current.width); - } - else if (info->current.x < info->work_area_monitor.x) - { - new_x = info->work_area_monitor.x; - } - else - { - new_x = info->current.x; - } - - info->rel_x += new_x - info->current.x; - info->current.x = new_x; - } - if (current_rule.constraint_adjustment & - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_Y) - { - int current_y2; - int work_area_monitor_y2; - int new_y; - - current_y2 = info->current.y + info->current.height; - work_area_monitor_y2 = (info->work_area_monitor.y + - info->work_area_monitor.height); - - if (current_y2 > work_area_monitor_y2) - { - new_y = MAX (info->work_area_monitor.y, - work_area_monitor_y2 - info->current.height); - } - else if (info->current.y < info->work_area_monitor.y) - { - new_y = info->work_area_monitor.y; - } - else - { - new_y = info->current.y; - } - - info->rel_y += new_y - info->current.y; - info->current.y = new_y; - } - - meta_rectangle_intersect (&info->current, &info->work_area_monitor, - &intersection); - constraint_satisfied = is_custom_rule_satisfied (&info->current, - placement_rule, - &intersection); - - if (constraint_satisfied) - goto done; - - if (current_rule.constraint_adjustment & - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_X) - { - int new_x; - new_x = intersection.x; - info->current.width = intersection.width; - info->rel_x += new_x - info->current.x; - info->current.x = new_x; - } - if (current_rule.constraint_adjustment & - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_Y) - { - int new_y; - new_y = intersection.y; - info->current.height = intersection.height; - info->rel_y += new_y - info->current.y; - info->current.y = new_y; - } - -done: - window->placement.state = META_PLACEMENT_STATE_CONSTRAINED_PENDING; - - window->placement.pending.rel_x = info->rel_x; - window->placement.pending.rel_y = info->rel_y; - window->placement.pending.x = info->current.x; - window->placement.pending.y = info->current.y; - - return TRUE; -} - -static gboolean -constrain_modal_dialog (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - int x, y; - MetaWindow *parent = meta_window_get_transient_for (window); - MetaRectangle child_rect, parent_rect; - gboolean constraint_already_satisfied; - - if (!parent || - !meta_window_is_attached_dialog (window) || - meta_window_get_placement_rule (window)) - return TRUE; - - /* We want to center the dialog on the parent, including the decorations - for both of them. info->current is in client X window coordinates, so we need - to convert them to frame coordinates, apply the centering and then - convert back to client. - */ - - child_rect = info->current; - - meta_window_get_frame_rect (parent, &parent_rect); - - child_rect.x = parent_rect.x + (parent_rect.width / 2 - child_rect.width / 2); - child_rect.y = parent_rect.y + (parent_rect.height / 2 - child_rect.height / 2); - x = child_rect.x; - y = child_rect.y; - - constraint_already_satisfied = (x == info->current.x) && (y == info->current.y); - - if (check_only || constraint_already_satisfied) - return constraint_already_satisfied; - - info->current.y = y; - info->current.x = x; - /* The calculated position above may need adjustment to make sure the - * dialog does not end up partially off-screen */ - return do_screen_and_monitor_relative_constraints (window, - info->usable_screen_region, - info, - check_only); -} - -static gboolean -constrain_maximization (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - MetaRectangle target_size; - MetaRectangle min_size, max_size; - gboolean hminbad, vminbad; - gboolean horiz_equal, vert_equal; - gboolean constraint_already_satisfied; - - if (priority > PRIORITY_MAXIMIZATION) - return TRUE; - - /* Determine whether constraint applies; exit if it doesn't */ - if ((!window->maximized_horizontally && !window->maximized_vertically) || - META_WINDOW_TILED_SIDE_BY_SIDE (window)) - return TRUE; - - /* Calculate target_size = maximized size of (window + frame) */ - if (META_WINDOW_TILED_MAXIMIZED (window)) - { - meta_window_get_tile_area (window, window->tile_mode, &target_size); - } - else if (META_WINDOW_MAXIMIZED (window)) - { - target_size = info->work_area_monitor; - } - else - { - /* Amount of maximization possible in a single direction depends - * on which struts could occlude the window given its current - * position. For example, a vertical partial strut on the right - * is only relevant for a horizontally maximized window when the - * window is at a vertical position where it could be occluded - * by that partial strut. - */ - MetaDirection direction; - GSList *active_workspace_struts; - - if (window->maximized_horizontally) - direction = META_DIRECTION_HORIZONTAL; - else - direction = META_DIRECTION_VERTICAL; - active_workspace_struts = workspace_manager->active_workspace->all_struts; - - target_size = info->current; - meta_rectangle_expand_to_avoiding_struts (&target_size, - &info->entire_monitor, - direction, - active_workspace_struts); - } - - /* Check min size constraints; max size constraints are ignored for maximized - * windows, as per bug 327543. - */ - get_size_limits (window, &min_size, &max_size); - hminbad = target_size.width < min_size.width && window->maximized_horizontally; - vminbad = target_size.height < min_size.height && window->maximized_vertically; - if (hminbad || vminbad) - return TRUE; - - /* Determine whether constraint is already satisfied; exit if it is */ - horiz_equal = target_size.x == info->current.x && - target_size.width == info->current.width; - vert_equal = target_size.y == info->current.y && - target_size.height == info->current.height; - constraint_already_satisfied = - (horiz_equal || !window->maximized_horizontally) && - (vert_equal || !window->maximized_vertically); - if (check_only || constraint_already_satisfied) - return constraint_already_satisfied; - - /*** Enforce constraint ***/ - if (window->maximized_horizontally) - { - info->current.x = target_size.x; - info->current.width = target_size.width; - } - if (window->maximized_vertically) - { - info->current.y = target_size.y; - info->current.height = target_size.height; - } - return TRUE; -} - -static gboolean -constrain_tiling (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - MetaRectangle target_size; - MetaRectangle min_size, max_size; - gboolean hminbad, vminbad; - gboolean horiz_equal, vert_equal; - gboolean constraint_already_satisfied; - - if (priority > PRIORITY_TILING) - return TRUE; - - /* Determine whether constraint applies; exit if it doesn't */ - if (!META_WINDOW_TILED_SIDE_BY_SIDE (window)) - return TRUE; - - /* Calculate target_size - as the tile previews need this as well, we - * use an external function for the actual calculation - */ - meta_window_get_tile_area (window, window->tile_mode, &target_size); - - /* Check min size constraints; max size constraints are ignored as for - * maximized windows. - */ - get_size_limits (window, &min_size, &max_size); - hminbad = target_size.width < min_size.width; - vminbad = target_size.height < min_size.height; - if (hminbad || vminbad) - return TRUE; - - /* Determine whether constraint is already satisfied; exit if it is */ - horiz_equal = target_size.x == info->current.x && - target_size.width == info->current.width; - vert_equal = target_size.y == info->current.y && - target_size.height == info->current.height; - constraint_already_satisfied = horiz_equal && vert_equal; - if (check_only || constraint_already_satisfied) - return constraint_already_satisfied; - - /*** Enforce constraint ***/ - info->current.x = target_size.x; - info->current.width = target_size.width; - info->current.y = target_size.y; - info->current.height = target_size.height; - - return TRUE; -} - - -static gboolean -constrain_fullscreen (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - MetaRectangle min_size, max_size, monitor; - gboolean too_big, too_small, constraint_already_satisfied; - - if (priority > PRIORITY_FULLSCREEN) - return TRUE; - - /* Determine whether constraint applies; exit if it doesn't */ - if (!window->fullscreen) - return TRUE; - - monitor = info->entire_monitor; - - get_size_limits (window, &min_size, &max_size); - too_big = !meta_rectangle_could_fit_rect (&monitor, &min_size); - too_small = !meta_rectangle_could_fit_rect (&max_size, &monitor); - if (too_big || too_small) - return TRUE; - - /* Determine whether constraint is already satisfied; exit if it is */ - constraint_already_satisfied = - meta_rectangle_equal (&info->current, &monitor); - if (check_only || constraint_already_satisfied) - return constraint_already_satisfied; - - /*** Enforce constraint ***/ - info->current = monitor; - return TRUE; -} - -static gboolean -constrain_size_increments (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - int bh, hi, bw, wi, extra_height, extra_width; - int new_width, new_height; - gboolean constraint_already_satisfied; - MetaRectangle *start_rect; - MetaRectangle client_rect; - - if (priority > PRIORITY_SIZE_HINTS_INCREMENTS) - return TRUE; - - /* Determine whether constraint applies; exit if it doesn't */ - if (META_WINDOW_MAXIMIZED (window) || window->fullscreen || - META_WINDOW_TILED_SIDE_BY_SIDE (window) || - info->action_type == ACTION_MOVE) - return TRUE; - - meta_window_frame_rect_to_client_rect (window, &info->current, &client_rect); - - /* Determine whether constraint is already satisfied; exit if it is */ - bh = window->size_hints.base_height; - hi = window->size_hints.height_inc; - bw = window->size_hints.base_width; - wi = window->size_hints.width_inc; - extra_height = (client_rect.height - bh) % hi; - extra_width = (client_rect.width - bw) % wi; - /* ignore size increments for maximized windows */ - if (window->maximized_horizontally) - extra_width *= 0; - if (window->maximized_vertically) - extra_height *= 0; - /* constraint is satisfied iff there is no extra height or width */ - constraint_already_satisfied = - (extra_height == 0 && extra_width == 0); - - if (check_only || constraint_already_satisfied) - return constraint_already_satisfied; - - /*** Enforce constraint ***/ - new_width = client_rect.width - extra_width; - new_height = client_rect.height - extra_height; - - /* Adjusting down instead of up (as done in the above two lines) may - * violate minimum size constraints; fix the adjustment if this - * happens. - */ - if (new_width < window->size_hints.min_width) - new_width += ((window->size_hints.min_width - new_width)/wi + 1)*wi; - if (new_height < window->size_hints.min_height) - new_height += ((window->size_hints.min_height - new_height)/hi + 1)*hi; - - { - client_rect.width = new_width; - client_rect.height = new_height; - meta_window_client_rect_to_frame_rect (window, &client_rect, &client_rect); - new_width = client_rect.width; - new_height = client_rect.height; - } - - start_rect = get_start_rect_for_resize (window, info); - - /* Resize to the new size */ - meta_rectangle_resize_with_gravity (start_rect, - &info->current, - info->resize_gravity, - new_width, - new_height); - return TRUE; -} - -static gboolean -constrain_size_limits (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - MetaRectangle min_size, max_size; - gboolean too_big, too_small, constraint_already_satisfied; - int new_width, new_height; - MetaRectangle *start_rect; - - if (priority > PRIORITY_SIZE_HINTS_LIMITS) - return TRUE; - - /* Determine whether constraint applies; exit if it doesn't. - * - * Note: The old code didn't apply this constraint for fullscreen or - * maximized windows--but that seems odd to me. *shrug* - */ - if (info->action_type == ACTION_MOVE) - return TRUE; - - /* Determine whether constraint is already satisfied; exit if it is */ - get_size_limits (window, &min_size, &max_size); - /* We ignore max-size limits for maximized windows; see #327543 */ - if (window->maximized_horizontally) - max_size.width = MAX (max_size.width, info->current.width); - if (window->maximized_vertically) - max_size.height = MAX (max_size.height, info->current.height); - too_small = !meta_rectangle_could_fit_rect (&info->current, &min_size); - too_big = !meta_rectangle_could_fit_rect (&max_size, &info->current); - constraint_already_satisfied = !too_big && !too_small; - if (check_only || constraint_already_satisfied) - return constraint_already_satisfied; - - /*** Enforce constraint ***/ - new_width = CLAMP (info->current.width, min_size.width, max_size.width); - new_height = CLAMP (info->current.height, min_size.height, max_size.height); - - start_rect = get_start_rect_for_resize (window, info); - - meta_rectangle_resize_with_gravity (start_rect, - &info->current, - info->resize_gravity, - new_width, - new_height); - return TRUE; -} - -static gboolean -constrain_aspect_ratio (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - double minr, maxr; - gboolean constraints_are_inconsistent, constraint_already_satisfied; - int fudge, new_width, new_height; - double best_width, best_height; - double alt_width, alt_height; - MetaRectangle *start_rect; - MetaRectangle client_rect; - - if (priority > PRIORITY_ASPECT_RATIO) - return TRUE; - - /* Determine whether constraint applies; exit if it doesn't. */ - minr = window->size_hints.min_aspect.x / - (double)window->size_hints.min_aspect.y; - maxr = window->size_hints.max_aspect.x / - (double)window->size_hints.max_aspect.y; - constraints_are_inconsistent = minr > maxr; - if (constraints_are_inconsistent || - META_WINDOW_MAXIMIZED (window) || window->fullscreen || - META_WINDOW_TILED_SIDE_BY_SIDE (window) || - info->action_type == ACTION_MOVE) - return TRUE; - - /* Determine whether constraint is already satisfied; exit if it is. We - * need the following to hold: - * - * width - * minr <= ------ <= maxr - * height - * - * But we need to allow for some slight fudging since width and height - * are integers instead of floating point numbers (this is particularly - * important when minr == maxr), so we allow width and height to be off - * a little bit from strictly satisfying these equations. For just one - * sided resizing, we have to make the fudge factor a little bigger - * because of how meta_rectangle_resize_with_gravity treats those as - * being a resize increment (FIXME: I should handle real resize - * increments better here...) - */ - switch (info->resize_gravity) - { - case META_GRAVITY_WEST: - case META_GRAVITY_NORTH: - case META_GRAVITY_SOUTH: - case META_GRAVITY_EAST: - fudge = 2; - break; - - case META_GRAVITY_NORTH_WEST: - case META_GRAVITY_SOUTH_WEST: - case META_GRAVITY_CENTER: - case META_GRAVITY_NORTH_EAST: - case META_GRAVITY_SOUTH_EAST: - case META_GRAVITY_STATIC: - default: - fudge = 1; - break; - } - - meta_window_frame_rect_to_client_rect (window, &info->current, &client_rect); - - constraint_already_satisfied = - client_rect.width - (client_rect.height * minr ) > -minr*fudge && - client_rect.width - (client_rect.height * maxr ) < maxr*fudge; - if (check_only || constraint_already_satisfied) - return constraint_already_satisfied; - - /*** Enforce constraint ***/ - new_width = client_rect.width; - new_height = client_rect.height; - - switch (info->resize_gravity) - { - case META_GRAVITY_WEST: - case META_GRAVITY_EAST: - /* Yeah, I suck for doing implicit rounding -- sue me */ - new_height = CLAMP (new_height, new_width / maxr, new_width / minr); - break; - - case META_GRAVITY_NORTH: - case META_GRAVITY_SOUTH: - /* Yeah, I suck for doing implicit rounding -- sue me */ - new_width = CLAMP (new_width, new_height * minr, new_height * maxr); - break; - - case META_GRAVITY_NORTH_WEST: - case META_GRAVITY_SOUTH_WEST: - case META_GRAVITY_CENTER: - case META_GRAVITY_NORTH_EAST: - case META_GRAVITY_SOUTH_EAST: - case META_GRAVITY_STATIC: - default: - /* Find what width would correspond to new_height, and what height would - * correspond to new_width */ - alt_width = CLAMP (new_width, new_height * minr, new_height * maxr); - alt_height = CLAMP (new_height, new_width / maxr, new_width / minr); - - /* The line connecting the points (alt_width, new_height) and - * (new_width, alt_height) provide a range of - * valid-for-the-aspect-ratio-constraint sizes. We want the - * size in that range closest to the value requested, i.e. the - * point on the line which is closest to the point (new_width, - * new_height) - */ - meta_rectangle_find_linepoint_closest_to_point (alt_width, new_height, - new_width, alt_height, - new_width, new_height, - &best_width, &best_height); - - /* Yeah, I suck for doing implicit rounding -- sue me */ - new_width = best_width; - new_height = best_height; - - break; - } - - { - client_rect.width = new_width; - client_rect.height = new_height; - meta_window_client_rect_to_frame_rect (window, &client_rect, &client_rect); - new_width = client_rect.width; - new_height = client_rect.height; - } - - start_rect = get_start_rect_for_resize (window, info); - - meta_rectangle_resize_with_gravity (start_rect, - &info->current, - info->resize_gravity, - new_width, - new_height); - - return TRUE; -} - -static gboolean -do_screen_and_monitor_relative_constraints ( - MetaWindow *window, - GList *region_spanning_rectangles, - ConstraintInfo *info, - gboolean check_only) -{ - gboolean exit_early = FALSE, constraint_satisfied; - MetaRectangle how_far_it_can_be_smushed, min_size, max_size; - -#ifdef WITH_VERBOSE_MODE - if (meta_is_verbose ()) - { - /* First, log some debugging information */ - char spanning_region[1 + 28 * g_list_length (region_spanning_rectangles)]; - - meta_topic (META_DEBUG_GEOMETRY, - "screen/monitor constraint; region_spanning_rectangles: %s", - meta_rectangle_region_to_string (region_spanning_rectangles, ", ", - spanning_region)); - } -#endif - - /* Determine whether constraint applies; exit if it doesn't */ - how_far_it_can_be_smushed = info->current; - get_size_limits (window, &min_size, &max_size); - - if (info->action_type != ACTION_MOVE) - { - if (!(info->fixed_directions & FIXED_DIRECTION_X)) - how_far_it_can_be_smushed.width = min_size.width; - - if (!(info->fixed_directions & FIXED_DIRECTION_Y)) - how_far_it_can_be_smushed.height = min_size.height; - } - if (!meta_rectangle_could_fit_in_region (region_spanning_rectangles, - &how_far_it_can_be_smushed)) - exit_early = TRUE; - - /* Determine whether constraint is already satisfied; exit if it is */ - constraint_satisfied = - meta_rectangle_contained_in_region (region_spanning_rectangles, - &info->current); - if (exit_early || constraint_satisfied || check_only) - return constraint_satisfied; - - /* Enforce constraint */ - - /* Clamp rectangle size for resize or move+resize actions */ - if (info->action_type != ACTION_MOVE) - meta_rectangle_clamp_to_fit_into_region (region_spanning_rectangles, - info->fixed_directions, - &info->current, - &min_size); - - if (info->is_user_action && info->action_type == ACTION_RESIZE) - /* For user resize, clip to the relevant region */ - meta_rectangle_clip_to_region (region_spanning_rectangles, - info->fixed_directions, - &info->current); - else - /* For everything else, shove the rectangle into the relevant region */ - meta_rectangle_shove_into_region (region_spanning_rectangles, - info->fixed_directions, - &info->current); - - return TRUE; -} - -static gboolean -constrain_to_single_monitor (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - - if (priority > PRIORITY_ENTIRELY_VISIBLE_ON_SINGLE_MONITOR) - return TRUE; - - /* Exit early if we know the constraint won't apply--note that this constraint - * is only meant for normal windows (e.g. we don't want docks to be shoved - * "onscreen" by their own strut) and we can't apply it to frameless windows - * or else users will be unable to move windows such as XMMS across monitors. - */ - if (window->type == META_WINDOW_DESKTOP || - window->type == META_WINDOW_DOCK || - meta_monitor_manager_get_num_logical_monitors (monitor_manager) == 1 || - !window->require_on_single_monitor || - !window->frame || - info->is_user_action || - meta_window_get_placement_rule (window)) - return TRUE; - - /* Have a helper function handle the constraint for us */ - return do_screen_and_monitor_relative_constraints (window, - info->usable_monitor_region, - info, - check_only); -} - -static gboolean -constrain_fully_onscreen (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - if (priority > PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA) - return TRUE; - - /* Exit early if we know the constraint won't apply--note that this constraint - * is only meant for normal windows (e.g. we don't want docks to be shoved - * "onscreen" by their own strut). - */ - if (window->type == META_WINDOW_DESKTOP || - window->type == META_WINDOW_DOCK || - window->fullscreen || - !window->require_fully_onscreen || - info->is_user_action || - meta_window_get_placement_rule (window)) - return TRUE; - - /* Have a helper function handle the constraint for us */ - return do_screen_and_monitor_relative_constraints (window, - info->usable_screen_region, - info, - check_only); -} - -static gboolean -constrain_titlebar_visible (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - gboolean unconstrained_user_action; - gboolean retval; - int bottom_amount; - int horiz_amount_offscreen, vert_amount_offscreen; - int horiz_amount_onscreen, vert_amount_onscreen; - - if (priority > PRIORITY_TITLEBAR_VISIBLE) - return TRUE; - - /* Allow the titlebar beyond the top of the screen only if the user wasn't - * clicking on the frame to start the move. - */ - unconstrained_user_action = - info->is_user_action && !window->display->grab_frame_action; - - /* Exit early if we know the constraint won't apply--note that this constraint - * is only meant for normal windows (e.g. we don't want docks to be shoved - * "onscreen" by their own strut). - */ - if (window->type == META_WINDOW_DESKTOP || - window->type == META_WINDOW_DOCK || - window->fullscreen || - !window->require_titlebar_visible || - unconstrained_user_action || - meta_window_get_placement_rule (window)) - return TRUE; - - /* Determine how much offscreen things are allowed. We first need to - * figure out how much must remain on the screen. For that, we use 25% - * window width/height but clamp to the range of (10,75) pixels. This is - * somewhat of a seat of my pants random guess at what might look good. - * Then, the amount that is allowed off is just the window size minus - * this amount (but no less than 0 for tiny windows). - */ - horiz_amount_onscreen = info->current.width / 4; - vert_amount_onscreen = info->current.height / 4; - horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10, 75); - vert_amount_onscreen = CLAMP (vert_amount_onscreen, 10, 75); - horiz_amount_offscreen = info->current.width - horiz_amount_onscreen; - vert_amount_offscreen = info->current.height - vert_amount_onscreen; - horiz_amount_offscreen = MAX (horiz_amount_offscreen, 0); - vert_amount_offscreen = MAX (vert_amount_offscreen, 0); - /* Allow the titlebar to touch the bottom panel; If there is no titlebar, - * require vert_amount to remain on the screen. - */ - if (window->frame) - { - MetaFrameBorders borders; - meta_frame_calc_borders (window->frame, &borders); - - bottom_amount = info->current.height - borders.visible.top; - vert_amount_onscreen = borders.visible.top; - } - else - bottom_amount = vert_amount_offscreen; - - /* Extend the region, have a helper function handle the constraint, - * then return the region to its original size. - */ - meta_rectangle_expand_region_conditionally (info->usable_screen_region, - horiz_amount_offscreen, - horiz_amount_offscreen, - 0, /* Don't let titlebar off */ - bottom_amount, - horiz_amount_onscreen, - vert_amount_onscreen); - retval = - do_screen_and_monitor_relative_constraints (window, - info->usable_screen_region, - info, - check_only); - meta_rectangle_expand_region_conditionally (info->usable_screen_region, - -horiz_amount_offscreen, - -horiz_amount_offscreen, - 0, /* Don't let titlebar off */ - -bottom_amount, - horiz_amount_onscreen, - vert_amount_onscreen); - - return retval; -} - -static gboolean -constrain_partially_onscreen (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - gboolean retval; - int top_amount, bottom_amount; - int horiz_amount_offscreen, vert_amount_offscreen; - int horiz_amount_onscreen, vert_amount_onscreen; - - if (priority > PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA) - return TRUE; - - /* Exit early if we know the constraint won't apply--note that this constraint - * is only meant for normal windows (e.g. we don't want docks to be shoved - * "onscreen" by their own strut). - */ - if (window->type == META_WINDOW_DESKTOP || - window->type == META_WINDOW_DOCK || - meta_window_get_placement_rule (window)) - return TRUE; - - /* Determine how much offscreen things are allowed. We first need to - * figure out how much must remain on the screen. For that, we use 25% - * window width/height but clamp to the range of (10,75) pixels. This is - * somewhat of a seat of my pants random guess at what might look good. - * Then, the amount that is allowed off is just the window size minus - * this amount (but no less than 0 for tiny windows). - */ - horiz_amount_onscreen = info->current.width / 4; - vert_amount_onscreen = info->current.height / 4; - horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10, 75); - vert_amount_onscreen = CLAMP (vert_amount_onscreen, 10, 75); - horiz_amount_offscreen = info->current.width - horiz_amount_onscreen; - vert_amount_offscreen = info->current.height - vert_amount_onscreen; - horiz_amount_offscreen = MAX (horiz_amount_offscreen, 0); - vert_amount_offscreen = MAX (vert_amount_offscreen, 0); - top_amount = vert_amount_offscreen; - /* Allow the titlebar to touch the bottom panel; If there is no titlebar, - * require vert_amount to remain on the screen. - */ - if (window->frame) - { - MetaFrameBorders borders; - meta_frame_calc_borders (window->frame, &borders); - - bottom_amount = info->current.height - borders.visible.top; - vert_amount_onscreen = borders.visible.top; - } - else - bottom_amount = vert_amount_offscreen; - - /* Extend the region, have a helper function handle the constraint, - * then return the region to its original size. - */ - meta_rectangle_expand_region_conditionally (info->usable_screen_region, - horiz_amount_offscreen, - horiz_amount_offscreen, - top_amount, - bottom_amount, - horiz_amount_onscreen, - vert_amount_onscreen); - retval = - do_screen_and_monitor_relative_constraints (window, - info->usable_screen_region, - info, - check_only); - meta_rectangle_expand_region_conditionally (info->usable_screen_region, - -horiz_amount_offscreen, - -horiz_amount_offscreen, - -top_amount, - -bottom_amount, - horiz_amount_onscreen, - vert_amount_onscreen); - - return retval; -} diff --git a/src/core/constraints.h b/src/core/constraints.h deleted file mode 100644 index eaa4e4594..000000000 --- a/src/core/constraints.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Mutter size/position constraints */ - -/* - * Copyright (C) 2002 Red Hat, Inc. - * Copyright (C) 2005 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef META_CONSTRAINTS_H -#define META_CONSTRAINTS_H - -#include "core/frame.h" -#include "core/window-private.h" -#include "meta/util.h" - -void meta_window_constrain (MetaWindow *window, - MetaMoveResizeFlags flags, - MetaGravity resize_gravity, - const MetaRectangle *orig, - MetaRectangle *new, - MetaRectangle *intermediate, - int *rel_x, - int *rel_y); - -#endif /* META_CONSTRAINTS_H */ diff --git a/src/core/delete.c b/src/core/delete.c deleted file mode 100644 index 058764b08..000000000 --- a/src/core/delete.c +++ /dev/null @@ -1,124 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Mutter window deletion */ - -/* - * Copyright (C) 2001, 2002 Havoc Pennington - * Copyright (C) 2004 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#define _XOPEN_SOURCE /* for kill() */ - -#include "config.h" - -#include <errno.h> - -#include "compositor/compositor-private.h" -#include "core/util-private.h" -#include "core/window-private.h" -#include "meta/meta-x11-errors.h" -#include "meta/workspace.h" - -static void -close_dialog_response_cb (MetaCloseDialog *dialog, - MetaCloseDialogResponse response, - MetaWindow *window) -{ - if (response == META_CLOSE_DIALOG_RESPONSE_FORCE_CLOSE) - meta_window_kill (window); -} - -static void -meta_window_ensure_close_dialog (MetaWindow *window) -{ - MetaDisplay *display; - - if (window->close_dialog) - return; - - display = window->display; - window->close_dialog = meta_compositor_create_close_dialog (display->compositor, - window); - g_signal_connect (window->close_dialog, "response", - G_CALLBACK (close_dialog_response_cb), window); -} - -void -meta_window_set_alive (MetaWindow *window, - gboolean is_alive) -{ - if (is_alive && window->close_dialog) - { - meta_close_dialog_hide (window->close_dialog); - } - else if (!is_alive) - { - meta_window_ensure_close_dialog (window); - meta_close_dialog_show (window->close_dialog); - - if (window->display && - window->display->event_route == META_EVENT_ROUTE_NORMAL && - window == window->display->focus_window) - meta_close_dialog_focus (window->close_dialog); - } -} - -void -meta_window_check_alive (MetaWindow *window, - guint32 timestamp) -{ - meta_display_ping_window (window, timestamp); -} - -void -meta_window_delete (MetaWindow *window, - guint32 timestamp) -{ - META_WINDOW_GET_CLASS (window)->delete (window, timestamp); - - meta_window_check_alive (window, timestamp); -} - -void -meta_window_kill (MetaWindow *window) -{ - pid_t pid = meta_window_get_pid (window); - - if (pid > 0) - { - meta_topic (META_DEBUG_WINDOW_OPS, - "Killing %s with kill()", - window->desc); - - if (kill (pid, 9) == 0) - return; - - meta_topic (META_DEBUG_WINDOW_OPS, - "Failed to signal %s: %s", - window->desc, strerror (errno)); - } - - META_WINDOW_GET_CLASS (window)->kill (window); -} - -void -meta_window_free_delete_dialog (MetaWindow *window) -{ - if (window->close_dialog && - meta_close_dialog_is_visible (window->close_dialog)) - meta_close_dialog_hide (window->close_dialog); - g_clear_object (&window->close_dialog); -} diff --git a/src/core/display-private.h b/src/core/display-private.h deleted file mode 100644 index 8efac0b49..000000000 --- a/src/core/display-private.h +++ /dev/null @@ -1,434 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Mutter X display handler */ - -/* - * Copyright (C) 2001 Havoc Pennington - * Copyright (C) 2002 Red Hat, Inc. - * Copyright (C) 2003 Rob Adams - * Copyright (C) 2004-2006 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef META_DISPLAY_PRIVATE_H -#define META_DISPLAY_PRIVATE_H - -#include "meta/display.h" - -#include <glib.h> -#include <X11/extensions/sync.h> -#include <X11/Xlib.h> - -#include "clutter/clutter.h" -#include "core/keybindings-private.h" -#include "core/meta-gesture-tracker-private.h" -#include "core/meta-pad-action-mapper.h" -#include "core/stack-tracker.h" -#include "core/startup-notification-private.h" -#include "meta/barrier.h" -#include "meta/boxes.h" -#include "meta/common.h" -#include "meta/meta-selection.h" -#include "meta/prefs.h" - -typedef struct _MetaBell MetaBell; -typedef struct _MetaStack MetaStack; - -typedef struct MetaEdgeResistanceData MetaEdgeResistanceData; - -typedef enum -{ - META_LIST_DEFAULT = 0, /* normal windows */ - META_LIST_INCLUDE_OVERRIDE_REDIRECT = 1 << 0, /* normal and O-R */ - META_LIST_SORTED = 1 << 1, /* sort list by mru */ -} MetaListWindowsFlags; - -#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ -#define _NET_WM_STATE_ADD 1 /* add/set property */ -#define _NET_WM_STATE_TOGGLE 2 /* toggle property */ - -/* This is basically a bogus number, just has to be large enough - * to handle the expected case of the alt+tab operation, where - * we want to ignore serials from UnmapNotify on the tab popup, - * and the LeaveNotify/EnterNotify from the pointer ungrab. It - * also has to be big enough to hold ignored serials from the point - * where we reshape the stage to the point where we get events back. - */ -#define N_IGNORED_CROSSING_SERIALS 10 - -typedef enum -{ - META_TILE_NONE, - META_TILE_LEFT, - META_TILE_RIGHT, - META_TILE_MAXIMIZED -} MetaTileMode; - -typedef enum -{ - /* Normal interaction where you're interacting with windows. - * Events go to windows normally. */ - META_EVENT_ROUTE_NORMAL, - - /* In a window operation like moving or resizing. All events - * goes to MetaWindow, but not to the actual client window. */ - META_EVENT_ROUTE_WINDOW_OP, - - /* In a compositor grab operation. All events go to the - * compositor plugin. */ - META_EVENT_ROUTE_COMPOSITOR_GRAB, - - /* A Wayland application has a popup open. All events go to - * the Wayland application. */ - META_EVENT_ROUTE_WAYLAND_POPUP, - - /* The user is clicking on a window button. */ - META_EVENT_ROUTE_FRAME_BUTTON, -} MetaEventRoute; - -typedef void (* MetaDisplayWindowFunc) (MetaWindow *window, - gpointer user_data); - -struct _MetaDisplay -{ - GObject parent_instance; - - MetaX11Display *x11_display; - - int clutter_event_filter; - - /* 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), and the serial of the request - * or event that caused this. - */ - MetaWindow *focus_window; - - /* last timestamp passed to XSetInputFocus */ - guint32 last_focus_time; - - /* last user interaction time in any app */ - guint32 last_user_time; - - /* whether we're using mousenav (only relevant for sloppy&mouse focus modes; - * !mouse_mode means "keynav mode") - */ - guint mouse_mode : 1; - - /* Helper var used when focus_new_windows setting is 'strict'; only - * relevant in 'strict' mode and if the focus window is a terminal. - * In that case, we don't allow new windows to take focus away from - * a terminal, but if the user explicitly did something that should - * allow a different window to gain focus (e.g. global keybinding or - * clicking on a dock), then we will allow the transfer. - */ - guint allow_terminal_deactivation : 1; - - /*< private-ish >*/ - GHashTable *stamps; - GHashTable *wayland_windows; - - /* serials of leave/unmap events that may - * correspond to an enter event we should - * ignore - */ - unsigned long ignored_crossing_serials[N_IGNORED_CROSSING_SERIALS]; - - guint32 current_time; - - /* We maintain a sequence counter, incremented for each #MetaWindow - * created. This is exposed by meta_window_get_stable_sequence() - * but is otherwise not used inside mutter. - * - * It can be useful to plugins which want to sort windows in a - * stable fashion. - */ - guint32 window_sequence_counter; - - /* Pings which we're waiting for a reply from */ - GSList *pending_pings; - - /* Pending focus change */ - guint focus_timeout_id; - - /* Pending autoraise */ - guint autoraise_timeout_id; - MetaWindow* autoraise_window; - - /* Event routing */ - MetaEventRoute event_route; - - /* current window operation */ - MetaGrabOp grab_op; - MetaWindow *grab_window; - int grab_button; - int grab_anchor_root_x; - int grab_anchor_root_y; - MetaRectangle grab_anchor_window_pos; - MetaTileMode grab_tile_mode; - int grab_tile_monitor_number; - int grab_latest_motion_x; - int grab_latest_motion_y; - guint grab_have_pointer : 1; - guint grab_have_keyboard : 1; - guint grab_frame_action : 1; - MetaRectangle grab_initial_window_pos; - int grab_initial_x, grab_initial_y; /* These are only relevant for */ - gboolean grab_threshold_movement_reached; /* raise_on_click == FALSE. */ - int64_t grab_last_moveresize_time; - MetaEdgeResistanceData *grab_edge_resistance_data; - unsigned int grab_last_edge_resistance_flags; - - int grab_resize_timeout_id; - - MetaKeyBindingManager key_binding_manager; - - /* Opening the display */ - unsigned int display_opening : 1; - - /* Closing down the display */ - int closing; - - /* Managed by compositor.c */ - MetaCompositor *compositor; - - MetaGestureTracker *gesture_tracker; - ClutterEventSequence *pointer_emulating_sequence; - - ClutterActor *current_pad_osd; - MetaPadActionMapper *pad_action_mapper; - - MetaStartupNotification *startup_notification; - - MetaCursor current_cursor; - - MetaStack *stack; - MetaStackTracker *stack_tracker; - - guint tile_preview_timeout_id; - guint preview_tile_mode : 2; - - GSList *startup_sequences; - - guint work_area_later; - guint check_fullscreen_later; - - MetaBell *bell; - MetaWorkspaceManager *workspace_manager; - - MetaSoundPlayer *sound_player; - - MetaSelectionSource *selection_source; - GBytes *saved_clipboard; - gchar *saved_clipboard_mimetype; - MetaSelection *selection; -}; - -struct _MetaDisplayClass -{ - GObjectClass parent_class; -}; - -#define XSERVER_TIME_IS_BEFORE_ASSUMING_REAL_TIMESTAMPS(time1, time2) \ - ( (( (time1) < (time2) ) && ( (time2) - (time1) < ((guint32)-1)/2 )) || \ - (( (time1) > (time2) ) && ( (time1) - (time2) > ((guint32)-1)/2 )) \ - ) -/** - * XSERVER_TIME_IS_BEFORE: - * - * See the docs for meta_display_xserver_time_is_before(). - */ -#define XSERVER_TIME_IS_BEFORE(time1, time2) \ - ( (time1) == 0 || \ - (XSERVER_TIME_IS_BEFORE_ASSUMING_REAL_TIMESTAMPS(time1, time2) && \ - (time2) != 0) \ - ) - -MetaDisplay * meta_display_new (MetaContext *context, - GError **error); - -void meta_display_manage_all_xwindows (MetaDisplay *display); -void meta_display_unmanage_windows (MetaDisplay *display, - guint32 timestamp); - -/* Utility function to compare the stacking of two windows */ -int meta_display_stack_cmp (const void *a, - const void *b); - -/* Each MetaWindow is uniquely identified by a 64-bit "stamp"; unlike a - * a MetaWindow *, a stamp will never be recycled - */ -MetaWindow* meta_display_lookup_stamp (MetaDisplay *display, - guint64 stamp); -void meta_display_register_stamp (MetaDisplay *display, - guint64 *stampp, - MetaWindow *window); -void meta_display_unregister_stamp (MetaDisplay *display, - guint64 stamp); - -/* A "stack id" is a XID or a stamp */ -#define META_STACK_ID_IS_X11(id) ((id) < G_GUINT64_CONSTANT(0x100000000)) - -META_EXPORT_TEST -MetaWindow* meta_display_lookup_stack_id (MetaDisplay *display, - guint64 stack_id); - -/* for debug logging only; returns a human-description of the stack - * ID - a small number of buffers are recycled, so the result must - * be used immediately or copied */ -const char *meta_display_describe_stack_id (MetaDisplay *display, - guint64 stack_id); - -void meta_display_register_wayland_window (MetaDisplay *display, - MetaWindow *window); -void meta_display_unregister_wayland_window (MetaDisplay *display, - MetaWindow *window); - -void meta_display_notify_window_created (MetaDisplay *display, - MetaWindow *window); - -META_EXPORT_TEST -GSList* meta_display_list_windows (MetaDisplay *display, - MetaListWindowsFlags flags); - -MetaDisplay* meta_display_for_x_display (Display *xdisplay); - -META_EXPORT_TEST -MetaDisplay* meta_get_display (void); - -void meta_display_reload_cursor (MetaDisplay *display); -void meta_display_update_cursor (MetaDisplay *display); - -void meta_display_check_threshold_reached (MetaDisplay *display, - int x, - int y); -void meta_display_grab_window_buttons (MetaDisplay *display, - Window xwindow); -void meta_display_ungrab_window_buttons (MetaDisplay *display, - Window xwindow); - -void meta_display_grab_focus_window_button (MetaDisplay *display, - MetaWindow *window); -void meta_display_ungrab_focus_window_button (MetaDisplay *display, - MetaWindow *window); - -/* Next function is defined in edge-resistance.c */ -void meta_display_cleanup_edges (MetaDisplay *display); - -/* utility goo */ -const char* meta_event_mode_to_string (int m); -const char* meta_event_detail_to_string (int d); - -void meta_display_queue_retheme_all_windows (MetaDisplay *display); - -void meta_display_ping_window (MetaWindow *window, - guint32 serial); -void meta_display_pong_for_serial (MetaDisplay *display, - guint32 serial); - -MetaGravity meta_resize_gravity_from_grab_op (MetaGrabOp op); - -gboolean meta_grab_op_is_moving (MetaGrabOp op); -gboolean meta_grab_op_is_resizing (MetaGrabOp op); -gboolean meta_grab_op_is_mouse (MetaGrabOp op); -gboolean meta_grab_op_is_keyboard (MetaGrabOp op); - -void meta_display_queue_autoraise_callback (MetaDisplay *display, - MetaWindow *window); -void meta_display_remove_autoraise_callback (MetaDisplay *display); - -void meta_display_overlay_key_activate (MetaDisplay *display); -void meta_display_accelerator_activate (MetaDisplay *display, - guint action, - ClutterKeyEvent *event); -gboolean meta_display_modifiers_accelerator_activate (MetaDisplay *display); - -void meta_display_sync_wayland_input_focus (MetaDisplay *display); -void meta_display_update_focus_window (MetaDisplay *display, - MetaWindow *window); - -void meta_display_sanity_check_timestamps (MetaDisplay *display, - guint32 timestamp); -gboolean meta_display_timestamp_too_old (MetaDisplay *display, - guint32 *timestamp); - -void meta_display_remove_pending_pings_for_window (MetaDisplay *display, - MetaWindow *window); - -MetaGestureTracker * meta_display_get_gesture_tracker (MetaDisplay *display); - -gboolean meta_display_show_restart_message (MetaDisplay *display, - const char *message); -gboolean meta_display_request_restart (MetaDisplay *display); - -gboolean meta_display_show_resize_popup (MetaDisplay *display, - gboolean show, - MetaRectangle *rect, - int display_w, - int display_h); - -void meta_set_is_restart (gboolean whether); - -void meta_display_cancel_touch (MetaDisplay *display); - -gboolean meta_display_windows_are_interactable (MetaDisplay *display); - -void meta_display_show_tablet_mapping_notification (MetaDisplay *display, - ClutterInputDevice *pad, - const gchar *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); - -void meta_display_foreach_window (MetaDisplay *display, - MetaListWindowsFlags flags, - MetaDisplayWindowFunc func, - gpointer data); - -void meta_display_restacked (MetaDisplay *display); - - -void meta_display_update_tile_preview (MetaDisplay *display, - gboolean delay); -void meta_display_hide_tile_preview (MetaDisplay *display); - -gboolean meta_display_apply_startup_properties (MetaDisplay *display, - MetaWindow *window); - -void meta_display_queue_workarea_recalc (MetaDisplay *display); -void meta_display_queue_check_fullscreen (MetaDisplay *display); - -MetaWindow *meta_display_get_pointer_window (MetaDisplay *display, - MetaWindow *not_this_one); - -MetaWindow *meta_display_get_window_from_id (MetaDisplay *display, - uint64_t window_id); -uint64_t meta_display_generate_window_id (MetaDisplay *display); - -void meta_display_init_x11 (MetaDisplay *display, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean meta_display_init_x11_finish (MetaDisplay *display, - GAsyncResult *result, - GError **error); - -void meta_display_shutdown_x11 (MetaDisplay *display); - -#endif diff --git a/src/core/display.c b/src/core/display.c deleted file mode 100644 index 5d4a4da21..000000000 --- a/src/core/display.c +++ /dev/null @@ -1,3852 +0,0 @@ -/* -*- 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 <http://www.gnu.org/licenses/>. - */ - -/** - * SECTION:display - * @title: MetaDisplay - * @short_description: Mutter display representation - * - * The display is represented as a #MetaDisplay struct. - */ - -#include "config.h" - -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <X11/Xatom.h> -#include <X11/Xcursor/Xcursor.h> -#include <X11/extensions/shape.h> -#include <X11/extensions/Xcomposite.h> -#include <X11/extensions/Xdamage.h> -#include <X11/extensions/Xfixes.h> - -#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-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; -} diff --git a/src/core/edge-resistance.c b/src/core/edge-resistance.c deleted file mode 100644 index 3e42936e7..000000000 --- a/src/core/edge-resistance.c +++ /dev/null @@ -1,1310 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Edge resistance for move/resize operations */ - -/* - * Copyright (C) 2005, 2006 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "config.h" - -#include "core/edge-resistance.h" - -#include "core/boxes-private.h" -#include "core/display-private.h" -#include "core/meta-workspace-manager-private.h" -#include "core/workspace-private.h" - -/* A simple macro for whether a given window's edges are potentially - * relevant for resistance/snapping during a move/resize operation - */ -#define WINDOW_EDGES_RELEVANT(window, display) \ - meta_window_should_be_showing (window) && \ - window != display->grab_window && \ - window->type != META_WINDOW_DESKTOP && \ - window->type != META_WINDOW_MENU && \ - window->type != META_WINDOW_SPLASHSCREEN - -struct ResistanceDataForAnEdge -{ - gboolean timeout_setup; - guint timeout_id; - int timeout_edge_pos; - gboolean timeout_over; - GSourceFunc timeout_func; - MetaWindow *window; - int keyboard_buildup; -}; -typedef struct ResistanceDataForAnEdge ResistanceDataForAnEdge; - -struct MetaEdgeResistanceData -{ - GArray *left_edges; - GArray *right_edges; - GArray *top_edges; - GArray *bottom_edges; - - ResistanceDataForAnEdge left_data; - ResistanceDataForAnEdge right_data; - ResistanceDataForAnEdge top_data; - ResistanceDataForAnEdge bottom_data; -}; - -static void compute_resistance_and_snapping_edges (MetaDisplay *display); - -/* !WARNING!: this function can return invalid indices (namely, either -1 or - * edges->len); this is by design, but you need to remember this. - */ -static int -find_index_of_edge_near_position (const GArray *edges, - int position, - gboolean want_interval_min, - gboolean horizontal) -{ - /* This is basically like a binary search, except that we're trying to - * find a range instead of an exact value. So, if we have in our array - * Value: 3 27 316 316 316 505 522 800 1213 - * Index: 0 1 2 3 4 5 6 7 8 - * and we call this function with position=500 & want_interval_min=TRUE - * then we should get 5 (because 505 is the first value bigger than 500). - * If we call this function with position=805 and want_interval_min=FALSE - * then we should get 7 (because 800 is the last value smaller than 800). - * A couple more, to make things clear: - * position want_interval_min correct_answer - * 316 TRUE 2 - * 316 FALSE 4 - * 2 FALSE -1 - * 2000 TRUE 9 - */ - int low, high, mid; - int compare; - MetaEdge *edge; - - /* Initialize mid, edge, & compare in the off change that the array only - * has one element. - */ - mid = 0; - edge = g_array_index (edges, MetaEdge*, mid); - compare = horizontal ? edge->rect.x : edge->rect.y; - - /* Begin the search... */ - low = 0; - high = edges->len - 1; - while (low < high) - { - mid = low + (high - low)/2; - edge = g_array_index (edges, MetaEdge*, mid); - compare = horizontal ? edge->rect.x : edge->rect.y; - - if (compare == position) - break; - - if (compare > position) - high = mid - 1; - else - low = mid + 1; - } - - /* mid should now be _really_ close to the index we want, so we start - * linearly searching. However, note that we don't know if mid is less - * than or greater than what we need and it's possible that there are - * several equal values equal to what we were searching for and we ended - * up in the middle of them instead of at the end. So we may need to - * move mid multiple locations over. - */ - if (want_interval_min) - { - while (compare >= position && mid > 0) - { - mid--; - edge = g_array_index (edges, MetaEdge*, mid); - compare = horizontal ? edge->rect.x : edge->rect.y; - } - while (compare < position && mid < (int)edges->len - 1) - { - mid++; - edge = g_array_index (edges, MetaEdge*, mid); - compare = horizontal ? edge->rect.x : edge->rect.y; - } - - /* Special case for no values in array big enough */ - if (compare < position) - return edges->len; - - /* Return the found value */ - return mid; - } - else - { - while (compare <= position && mid < (int)edges->len - 1) - { - mid++; - edge = g_array_index (edges, MetaEdge*, mid); - compare = horizontal ? edge->rect.x : edge->rect.y; - } - while (compare > position && mid > 0) - { - mid--; - edge = g_array_index (edges, MetaEdge*, mid); - compare = horizontal ? edge->rect.x : edge->rect.y; - } - - /* Special case for no values in array small enough */ - if (compare > position) - return -1; - - /* Return the found value */ - return mid; - } -} - -static gboolean -points_on_same_side (int ref, int pt1, int pt2) -{ - return (pt1 - ref) * (pt2 - ref) > 0; -} - -static int -find_nearest_position (const GArray *edges, - int position, - int old_position, - const MetaRectangle *new_rect, - gboolean horizontal, - gboolean only_forward) -{ - /* This is basically just a binary search except that we're looking - * for the value closest to position, rather than finding that - * actual value. Also, we ignore any edges that aren't relevant - * given the horizontal/vertical position of new_rect. - */ - int low, high, mid; - int compare; - MetaEdge *edge; - int best, best_dist, i; - gboolean edges_align; - - /* Initialize mid, edge, & compare in the off change that the array only - * has one element. - */ - mid = 0; - edge = g_array_index (edges, MetaEdge*, mid); - compare = horizontal ? edge->rect.x : edge->rect.y; - - /* Begin the search... */ - low = 0; - high = edges->len - 1; - while (low < high) - { - mid = low + (high - low)/2; - edge = g_array_index (edges, MetaEdge*, mid); - compare = horizontal ? edge->rect.x : edge->rect.y; - - if (compare == position) - break; - - if (compare > position) - high = mid - 1; - else - low = mid + 1; - } - - /* mid should now be _really_ close to the index we want, so we - * start searching nearby for something that overlaps and is closer - * than the original position. - */ - best = old_position; - best_dist = INT_MAX; - - /* Start the search at mid */ - edge = g_array_index (edges, MetaEdge*, mid); - compare = horizontal ? edge->rect.x : edge->rect.y; - edges_align = meta_rectangle_edge_aligns (new_rect, edge); - if (edges_align && - (!only_forward || !points_on_same_side (position, compare, old_position))) - { - int dist = ABS (compare - position); - if (dist < best_dist) - { - best = compare; - best_dist = dist; - } - } - - /* Now start searching higher than mid */ - for (i = mid + 1; i < (int)edges->len; i++) - { - edge = g_array_index (edges, MetaEdge*, i); - compare = horizontal ? edge->rect.x : edge->rect.y; - - edges_align = horizontal ? - meta_rectangle_vert_overlap (&edge->rect, new_rect) : - meta_rectangle_horiz_overlap (&edge->rect, new_rect); - - if (edges_align && - (!only_forward || - !points_on_same_side (position, compare, old_position))) - { - int dist = ABS (compare - position); - if (dist < best_dist) - { - best = compare; - best_dist = dist; - } - break; - } - } - - /* Now start searching lower than mid */ - for (i = mid-1; i >= 0; i--) - { - edge = g_array_index (edges, MetaEdge*, i); - compare = horizontal ? edge->rect.x : edge->rect.y; - - edges_align = horizontal ? - meta_rectangle_vert_overlap (&edge->rect, new_rect) : - meta_rectangle_horiz_overlap (&edge->rect, new_rect); - - if (edges_align && - (!only_forward || - !points_on_same_side (position, compare, old_position))) - { - int dist = ABS (compare - position); - if (dist < best_dist) - { - best = compare; - best_dist = dist; - } - break; - } - } - - /* Return the best one found */ - return best; -} - -static gboolean -movement_towards_edge (MetaSide side, int increment) -{ - switch (side) - { - case META_SIDE_LEFT: - case META_SIDE_TOP: - return increment < 0; - case META_SIDE_RIGHT: - case META_SIDE_BOTTOM: - return increment > 0; - default: - g_assert_not_reached (); - return FALSE; - } -} - -static gboolean -edge_resistance_timeout (gpointer data) -{ - ResistanceDataForAnEdge *resistance_data = data; - - resistance_data->timeout_over = TRUE; - resistance_data->timeout_id = 0; - (*resistance_data->timeout_func)(resistance_data->window); - - return FALSE; -} - -static int -apply_edge_resistance (MetaWindow *window, - int old_pos, - int new_pos, - const MetaRectangle *old_rect, - const MetaRectangle *new_rect, - GArray *edges, - ResistanceDataForAnEdge *resistance_data, - GSourceFunc timeout_func, - gboolean xdir, - gboolean include_windows, - gboolean keyboard_op) -{ - int i, begin, end; - int last_edge; - gboolean increasing = new_pos > old_pos; - int increment = increasing ? 1 : -1; - - const int PIXEL_DISTANCE_THRESHOLD_TOWARDS_WINDOW = 16; - const int PIXEL_DISTANCE_THRESHOLD_AWAYFROM_WINDOW = 0; - const int PIXEL_DISTANCE_THRESHOLD_TOWARDS_MONITOR = 32; - const int PIXEL_DISTANCE_THRESHOLD_AWAYFROM_MONITOR = 0; - const int PIXEL_DISTANCE_THRESHOLD_TOWARDS_SCREEN = 32; - const int PIXEL_DISTANCE_THRESHOLD_AWAYFROM_SCREEN = 0; - const int TIMEOUT_RESISTANCE_LENGTH_MS_WINDOW = 0; - const int TIMEOUT_RESISTANCE_LENGTH_MS_MONITOR = 0; - const int TIMEOUT_RESISTANCE_LENGTH_MS_SCREEN = 0; - - /* Quit if no movement was specified */ - if (old_pos == new_pos) - return new_pos; - - /* Remove the old timeout if it's no longer relevant */ - if (resistance_data->timeout_setup && - ((resistance_data->timeout_edge_pos > old_pos && - resistance_data->timeout_edge_pos > new_pos) || - (resistance_data->timeout_edge_pos < old_pos && - resistance_data->timeout_edge_pos < new_pos))) - { - resistance_data->timeout_setup = FALSE; - g_clear_handle_id (&resistance_data->timeout_id, g_source_remove); - } - - /* Get the range of indices in the edge array that we move past/to. */ - begin = find_index_of_edge_near_position (edges, old_pos, increasing, xdir); - end = find_index_of_edge_near_position (edges, new_pos, !increasing, xdir); - - /* begin and end can be outside the array index, if the window is partially - * off the screen - */ - last_edge = edges->len - 1; - begin = CLAMP (begin, 0, last_edge); - end = CLAMP (end, 0, last_edge); - - /* Loop over all these edges we're moving past/to. */ - i = begin; - while ((increasing && i <= end) || - (!increasing && i >= end)) - { - gboolean edges_align; - MetaEdge *edge = g_array_index (edges, MetaEdge*, i); - int compare = xdir ? edge->rect.x : edge->rect.y; - - /* Find out if this edge is relevant */ - edges_align = meta_rectangle_edge_aligns (new_rect, edge) || - meta_rectangle_edge_aligns (old_rect, edge); - - /* Nothing to do unless the edges align */ - if (!edges_align) - { - /* Go to the next edge in the range */ - i += increment; - continue; - } - - /* Rest is easier to read if we split on keyboard vs. mouse op */ - if (keyboard_op) - { - if ((old_pos < compare && compare < new_pos) || - (old_pos > compare && compare > new_pos)) - return compare; - } - else /* mouse op */ - { - int threshold; - - /* TIMEOUT RESISTANCE: If the edge is relevant and we're moving - * towards it, then we may want to have some kind of time delay - * before the user can move past this edge. - */ - if (movement_towards_edge (edge->side_type, increment)) - { - /* First, determine the length of time for the resistance */ - int timeout_length_ms = 0; - switch (edge->edge_type) - { - case META_EDGE_WINDOW: - if (include_windows) - timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_WINDOW; - break; - case META_EDGE_MONITOR: - timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_MONITOR; - break; - case META_EDGE_SCREEN: - timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_SCREEN; - break; - } - - if (!resistance_data->timeout_setup && - timeout_length_ms != 0) - { - resistance_data->timeout_id = - g_timeout_add (timeout_length_ms, - edge_resistance_timeout, - resistance_data); - g_source_set_name_by_id (resistance_data->timeout_id, - "[mutter] edge_resistance_timeout"); - resistance_data->timeout_setup = TRUE; - resistance_data->timeout_edge_pos = compare; - resistance_data->timeout_over = FALSE; - resistance_data->timeout_func = timeout_func; - resistance_data->window = window; - } - if (!resistance_data->timeout_over && - timeout_length_ms != 0) - return compare; - } - - /* PIXEL DISTANCE MOUSE RESISTANCE: If the edge matters and the - * user hasn't moved at least threshold pixels past this edge, - * stop movement at this edge. (Note that this is different from - * keyboard resistance precisely because keyboard move ops are - * relative to previous positions, whereas mouse move ops are - * relative to differences in mouse position and mouse position - * is an absolute quantity rather than a relative quantity) - */ - - /* First, determine the threshold */ - threshold = 0; - switch (edge->edge_type) - { - case META_EDGE_WINDOW: - if (!include_windows) - break; - if (movement_towards_edge (edge->side_type, increment)) - threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_WINDOW; - else - threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_WINDOW; - break; - case META_EDGE_MONITOR: - if (movement_towards_edge (edge->side_type, increment)) - threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_MONITOR; - else - threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_MONITOR; - break; - case META_EDGE_SCREEN: - if (movement_towards_edge (edge->side_type, increment)) - threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_SCREEN; - else - threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_SCREEN; - break; - } - - if (ABS (compare - new_pos) < threshold) - return compare; - } - - /* Go to the next edge in the range */ - i += increment; - } - - return new_pos; -} - -static int -apply_edge_snapping (int old_pos, - int new_pos, - const MetaRectangle *new_rect, - GArray *edges, - gboolean xdir, - gboolean keyboard_op) -{ - int snap_to; - - if (old_pos == new_pos) - return new_pos; - - snap_to = find_nearest_position (edges, - new_pos, - old_pos, - new_rect, - xdir, - keyboard_op); - - /* If mouse snap-moving, the user could easily accidentally move just a - * couple pixels in a direction they didn't mean to move; so ignore snap - * movement in those cases unless it's only a small number of pixels - * anyway. - */ - if (!keyboard_op && - ABS (snap_to - old_pos) >= 8 && - ABS (new_pos - old_pos) < 8) - return old_pos; - else - /* Otherwise, return the snapping position found */ - return snap_to; -} - -/* This function takes the position (including any frame) of the window and - * a proposed new position (ignoring edge resistance/snapping), and then - * applies edge resistance to EACH edge (separately) updating new_outer. - * It returns true if new_outer is modified, false otherwise. - * - * display->grab_edge_resistance_data MUST already be setup or calling this - * function will cause a crash. - */ -static gboolean -apply_edge_resistance_to_each_side (MetaDisplay *display, - MetaWindow *window, - const MetaRectangle *old_outer, - MetaRectangle *new_outer, - GSourceFunc timeout_func, - MetaEdgeResistanceFlags flags, - gboolean is_resize) -{ - MetaEdgeResistanceData *edge_data; - MetaRectangle modified_rect; - gboolean modified; - int new_left, new_right, new_top, new_bottom; - gboolean auto_snap, keyboard_op; - - auto_snap = flags & META_EDGE_RESISTANCE_SNAP; - keyboard_op = flags & META_EDGE_RESISTANCE_KEYBOARD_OP; - - if (display->grab_edge_resistance_data == NULL) - compute_resistance_and_snapping_edges (display); - - edge_data = display->grab_edge_resistance_data; - - if (auto_snap && !META_WINDOW_TILED_SIDE_BY_SIDE (window)) - { - /* Do the auto snapping instead of normal edge resistance; in all - * cases, we allow snapping to opposite kinds of edges (e.g. left - * sides of windows to both left and right edges. - */ - - new_left = apply_edge_snapping (BOX_LEFT (*old_outer), - BOX_LEFT (*new_outer), - new_outer, - edge_data->left_edges, - TRUE, - keyboard_op); - - new_right = apply_edge_snapping (BOX_RIGHT (*old_outer), - BOX_RIGHT (*new_outer), - new_outer, - edge_data->right_edges, - TRUE, - keyboard_op); - - new_top = apply_edge_snapping (BOX_TOP (*old_outer), - BOX_TOP (*new_outer), - new_outer, - edge_data->top_edges, - FALSE, - keyboard_op); - - new_bottom = apply_edge_snapping (BOX_BOTTOM (*old_outer), - BOX_BOTTOM (*new_outer), - new_outer, - edge_data->bottom_edges, - FALSE, - keyboard_op); - } - else if (auto_snap && META_WINDOW_TILED_SIDE_BY_SIDE (window)) - { - MetaRectangle workarea; - guint i; - - const gfloat tile_edges[] = - { - 1./4., - 1./3., - 1./2., - 2./3., - 3./4., - }; - - meta_window_get_work_area_current_monitor (window, &workarea); - - new_left = new_outer->x; - new_top = new_outer->y; - new_right = new_outer->x + new_outer->width; - new_bottom = new_outer->y + new_outer->height; - - /* When snapping tiled windows, we don't really care about the - * x and y position, only about the width and height. Also, it - * is special-cased (instead of relying on edge_data) because - * we don't really care for other windows when calculating the - * snapping points of tiled windows - we only care about the - * work area and the target position. - */ - for (i = 0; i < G_N_ELEMENTS (tile_edges); i++) - { - guint horizontal_point = workarea.x + floor (workarea.width * tile_edges[i]); - - if (ABS (horizontal_point - new_left) < 16) - { - new_left = horizontal_point; - new_right = workarea.x + workarea.width; - } - else if (ABS (horizontal_point - new_right) < 16) - { - new_left = workarea.x; - new_right = horizontal_point; - } - } - } - else - { - gboolean include_windows = flags & META_EDGE_RESISTANCE_WINDOWS; - - /* Disable edge resistance for resizes when windows have size - * increment hints; see #346782. For all other cases, apply - * them. - */ - if (!is_resize || window->size_hints.width_inc == 1) - { - /* Now, apply the normal horizontal edge resistance */ - new_left = apply_edge_resistance (window, - BOX_LEFT (*old_outer), - BOX_LEFT (*new_outer), - old_outer, - new_outer, - edge_data->left_edges, - &edge_data->left_data, - timeout_func, - TRUE, - include_windows, - keyboard_op); - new_right = apply_edge_resistance (window, - BOX_RIGHT (*old_outer), - BOX_RIGHT (*new_outer), - old_outer, - new_outer, - edge_data->right_edges, - &edge_data->right_data, - timeout_func, - TRUE, - include_windows, - keyboard_op); - } - else - { - new_left = new_outer->x; - new_right = new_outer->x + new_outer->width; - } - /* Same for vertical resizes... */ - if (!is_resize || window->size_hints.height_inc == 1) - { - new_top = apply_edge_resistance (window, - BOX_TOP (*old_outer), - BOX_TOP (*new_outer), - old_outer, - new_outer, - edge_data->top_edges, - &edge_data->top_data, - timeout_func, - FALSE, - include_windows, - keyboard_op); - new_bottom = apply_edge_resistance (window, - BOX_BOTTOM (*old_outer), - BOX_BOTTOM (*new_outer), - old_outer, - new_outer, - edge_data->bottom_edges, - &edge_data->bottom_data, - timeout_func, - FALSE, - include_windows, - keyboard_op); - } - else - { - new_top = new_outer->y; - new_bottom = new_outer->y + new_outer->height; - } - } - - /* Determine whether anything changed, and save the changes */ - modified_rect = meta_rect (new_left, - new_top, - new_right - new_left, - new_bottom - new_top); - modified = !meta_rectangle_equal (new_outer, &modified_rect); - *new_outer = modified_rect; - return modified; -} - -void -meta_display_cleanup_edges (MetaDisplay *display) -{ - guint i,j; - MetaEdgeResistanceData *edge_data = display->grab_edge_resistance_data; - GHashTable *edges_to_be_freed; - - if (edge_data == NULL) /* Not currently cached */ - return; - - /* We first need to clean out any window edges */ - edges_to_be_freed = g_hash_table_new_full (g_direct_hash, g_direct_equal, - g_free, NULL); - for (i = 0; i < 4; i++) - { - GArray *tmp = NULL; - MetaSide side; - switch (i) - { - case 0: - tmp = edge_data->left_edges; - side = META_SIDE_LEFT; - break; - case 1: - tmp = edge_data->right_edges; - side = META_SIDE_RIGHT; - break; - case 2: - tmp = edge_data->top_edges; - side = META_SIDE_TOP; - break; - case 3: - tmp = edge_data->bottom_edges; - side = META_SIDE_BOTTOM; - break; - default: - g_assert_not_reached (); - } - - for (j = 0; j < tmp->len; j++) - { - MetaEdge *edge = g_array_index (tmp, MetaEdge*, j); - if (edge->edge_type == META_EDGE_WINDOW && - edge->side_type == side) - { - /* The same edge will appear in two arrays, and we can't free - * it yet we still need to compare edge->side_type for the other - * array that it is in. So store it in a hash table for later - * freeing. Could also do this in a simple linked list. - */ - g_hash_table_insert (edges_to_be_freed, edge, edge); - } - } - } - - /* Now free all the window edges (the key destroy function is g_free) */ - g_hash_table_destroy (edges_to_be_freed); - - /* Now free the arrays and data */ - g_array_free (edge_data->left_edges, TRUE); - g_array_free (edge_data->right_edges, TRUE); - g_array_free (edge_data->top_edges, TRUE); - g_array_free (edge_data->bottom_edges, TRUE); - edge_data->left_edges = NULL; - edge_data->right_edges = NULL; - edge_data->top_edges = NULL; - edge_data->bottom_edges = NULL; - - /* Cleanup the timeouts */ - if (edge_data->left_data.timeout_setup) - g_clear_handle_id (&edge_data->left_data.timeout_id, g_source_remove); - if (edge_data->right_data.timeout_setup) - g_clear_handle_id (&edge_data->right_data.timeout_id, g_source_remove); - if (edge_data->top_data.timeout_setup) - g_clear_handle_id (&edge_data->top_data.timeout_id, g_source_remove); - if (edge_data->bottom_data.timeout_setup) - g_clear_handle_id (&edge_data->bottom_data.timeout_id, g_source_remove); - - g_free (display->grab_edge_resistance_data); - display->grab_edge_resistance_data = NULL; -} - -static int -stupid_sort_requiring_extra_pointer_dereference (gconstpointer a, - gconstpointer b) -{ - const MetaEdge * const *a_edge = a; - const MetaEdge * const *b_edge = b; - return meta_rectangle_edge_cmp_ignore_type (*a_edge, *b_edge); -} - -static void -cache_edges (MetaDisplay *display, - GList *window_edges, - GList *monitor_edges, - GList *screen_edges) -{ - MetaEdgeResistanceData *edge_data; - GList *tmp; - int num_left, num_right, num_top, num_bottom; - int i; - - /* - * 0th: Print debugging information to the log about the edges - */ -#ifdef WITH_VERBOSE_MODE - if (meta_is_verbose()) - { - int max_edges = MAX (MAX( g_list_length (window_edges), - g_list_length (monitor_edges)), - g_list_length (screen_edges)); - char big_buffer[(EDGE_LENGTH+2)*max_edges]; - - meta_rectangle_edge_list_to_string (window_edges, ", ", big_buffer); - meta_topic (META_DEBUG_EDGE_RESISTANCE, - "Window edges for resistance : %s", big_buffer); - - meta_rectangle_edge_list_to_string (monitor_edges, ", ", big_buffer); - meta_topic (META_DEBUG_EDGE_RESISTANCE, - "Monitor edges for resistance: %s", big_buffer); - - meta_rectangle_edge_list_to_string (screen_edges, ", ", big_buffer); - meta_topic (META_DEBUG_EDGE_RESISTANCE, - "Screen edges for resistance : %s", big_buffer); - } -#endif - - /* - * 1st: Get the total number of each kind of edge - */ - num_left = num_right = num_top = num_bottom = 0; - for (i = 0; i < 3; i++) - { - tmp = NULL; - switch (i) - { - case 0: - tmp = window_edges; - break; - case 1: - tmp = monitor_edges; - break; - case 2: - tmp = screen_edges; - break; - default: - g_assert_not_reached (); - } - - while (tmp) - { - MetaEdge *edge = tmp->data; - switch (edge->side_type) - { - case META_SIDE_LEFT: - num_left++; - break; - case META_SIDE_RIGHT: - num_right++; - break; - case META_SIDE_TOP: - num_top++; - break; - case META_SIDE_BOTTOM: - num_bottom++; - break; - default: - g_assert_not_reached (); - } - tmp = tmp->next; - } - } - - /* - * 2nd: Allocate the edges - */ - g_assert (display->grab_edge_resistance_data == NULL); - display->grab_edge_resistance_data = g_new0 (MetaEdgeResistanceData, 1); - edge_data = display->grab_edge_resistance_data; - edge_data->left_edges = g_array_sized_new (FALSE, - FALSE, - sizeof(MetaEdge*), - num_left + num_right); - edge_data->right_edges = g_array_sized_new (FALSE, - FALSE, - sizeof(MetaEdge*), - num_left + num_right); - edge_data->top_edges = g_array_sized_new (FALSE, - FALSE, - sizeof(MetaEdge*), - num_top + num_bottom); - edge_data->bottom_edges = g_array_sized_new (FALSE, - FALSE, - sizeof(MetaEdge*), - num_top + num_bottom); - - /* - * 3rd: Add the edges to the arrays - */ - for (i = 0; i < 3; i++) - { - tmp = NULL; - switch (i) - { - case 0: - tmp = window_edges; - break; - case 1: - tmp = monitor_edges; - break; - case 2: - tmp = screen_edges; - break; - default: - g_assert_not_reached (); - } - - while (tmp) - { - MetaEdge *edge = tmp->data; - switch (edge->side_type) - { - case META_SIDE_LEFT: - case META_SIDE_RIGHT: - g_array_append_val (edge_data->left_edges, edge); - g_array_append_val (edge_data->right_edges, edge); - break; - case META_SIDE_TOP: - case META_SIDE_BOTTOM: - g_array_append_val (edge_data->top_edges, edge); - g_array_append_val (edge_data->bottom_edges, edge); - break; - default: - g_assert_not_reached (); - } - tmp = tmp->next; - } - } - - /* - * 4th: Sort the arrays (FIXME: This is kinda dumb since the arrays were - * individually sorted earlier and we could have done this faster and - * avoided this sort by sticking them into the array with some simple - * merging of the lists). - */ - g_array_sort (display->grab_edge_resistance_data->left_edges, - stupid_sort_requiring_extra_pointer_dereference); - g_array_sort (display->grab_edge_resistance_data->right_edges, - stupid_sort_requiring_extra_pointer_dereference); - g_array_sort (display->grab_edge_resistance_data->top_edges, - stupid_sort_requiring_extra_pointer_dereference); - g_array_sort (display->grab_edge_resistance_data->bottom_edges, - stupid_sort_requiring_extra_pointer_dereference); -} - -static void -initialize_grab_edge_resistance_data (MetaDisplay *display) -{ - MetaEdgeResistanceData *edge_data = display->grab_edge_resistance_data; - - edge_data->left_data.timeout_setup = FALSE; - edge_data->right_data.timeout_setup = FALSE; - edge_data->top_data.timeout_setup = FALSE; - edge_data->bottom_data.timeout_setup = FALSE; - - edge_data->left_data.keyboard_buildup = 0; - edge_data->right_data.keyboard_buildup = 0; - edge_data->top_data.keyboard_buildup = 0; - edge_data->bottom_data.keyboard_buildup = 0; -} - -static void -compute_resistance_and_snapping_edges (MetaDisplay *display) -{ - GList *stacked_windows; - GList *cur_window_iter; - GList *edges; - /* Lists of window positions (rects) and their relative stacking positions */ - int stack_position; - GSList *obscuring_windows, *window_stacking; - /* The portions of the above lists that still remain at the stacking position - * in the layer that we are working on - */ - GSList *rem_windows, *rem_win_stacking; - MetaWorkspaceManager *workspace_manager = display->workspace_manager; - - g_assert (display->grab_window != NULL); - meta_topic (META_DEBUG_WINDOW_OPS, - "Computing edges to resist-movement or snap-to for %s.", - display->grab_window->desc); - - /* - * 1st: Get the list of relevant windows, from bottom to top - */ - stacked_windows = - meta_stack_list_windows (display->stack, - workspace_manager->active_workspace); - - /* - * 2nd: we need to separate that stacked list into a list of windows that - * can obscure other edges. To make sure we only have windows obscuring - * those below it instead of going both ways, we also need to keep a - * counter list. Messy, I know. - */ - obscuring_windows = window_stacking = NULL; - cur_window_iter = stacked_windows; - stack_position = 0; - while (cur_window_iter != NULL) - { - MetaWindow *cur_window = cur_window_iter->data; - if (WINDOW_EDGES_RELEVANT (cur_window, display)) - { - MetaRectangle *new_rect; - new_rect = g_new (MetaRectangle, 1); - meta_window_get_frame_rect (cur_window, new_rect); - obscuring_windows = g_slist_prepend (obscuring_windows, new_rect); - window_stacking = - g_slist_prepend (window_stacking, GINT_TO_POINTER (stack_position)); - } - - stack_position++; - cur_window_iter = cur_window_iter->next; - } - /* Put 'em in bottom to top order */ - rem_windows = obscuring_windows = g_slist_reverse (obscuring_windows); - rem_win_stacking = window_stacking = g_slist_reverse (window_stacking); - - /* - * 3rd: loop over the windows again, this time getting the edges from - * them and removing intersections with the relevant obscuring_windows & - * obscuring_docks. - */ - edges = NULL; - stack_position = 0; - cur_window_iter = stacked_windows; - while (cur_window_iter != NULL) - { - MetaRectangle cur_rect; - MetaWindow *cur_window = cur_window_iter->data; - meta_window_get_frame_rect (cur_window, &cur_rect); - - /* Check if we want to use this window's edges for edge - * resistance (note that dock edges are considered screen edges - * which are handled separately - */ - if (WINDOW_EDGES_RELEVANT (cur_window, display) && - cur_window->type != META_WINDOW_DOCK) - { - GList *new_edges; - MetaEdge *new_edge; - MetaRectangle display_rect = { 0 }; - MetaRectangle reduced; - - meta_display_get_size (display, - &display_rect.width, &display_rect.height); - - /* We don't care about snapping to any portion of the window that - * is offscreen (we also don't care about parts of edges covered - * by other windows or DOCKS, but that's handled below). - */ - meta_rectangle_intersect (&cur_rect, - &display_rect, - &reduced); - - new_edges = NULL; - - /* Left side of this window is resistance for the right edge of - * the window being moved. - */ - new_edge = g_new (MetaEdge, 1); - new_edge->rect = reduced; - new_edge->rect.width = 0; - new_edge->side_type = META_SIDE_RIGHT; - new_edge->edge_type = META_EDGE_WINDOW; - new_edges = g_list_prepend (new_edges, new_edge); - - /* Right side of this window is resistance for the left edge of - * the window being moved. - */ - new_edge = g_new (MetaEdge, 1); - new_edge->rect = reduced; - new_edge->rect.x += new_edge->rect.width; - new_edge->rect.width = 0; - new_edge->side_type = META_SIDE_LEFT; - new_edge->edge_type = META_EDGE_WINDOW; - new_edges = g_list_prepend (new_edges, new_edge); - - /* Top side of this window is resistance for the bottom edge of - * the window being moved. - */ - new_edge = g_new (MetaEdge, 1); - new_edge->rect = reduced; - new_edge->rect.height = 0; - new_edge->side_type = META_SIDE_BOTTOM; - new_edge->edge_type = META_EDGE_WINDOW; - new_edges = g_list_prepend (new_edges, new_edge); - - /* Top side of this window is resistance for the bottom edge of - * the window being moved. - */ - new_edge = g_new (MetaEdge, 1); - new_edge->rect = reduced; - new_edge->rect.y += new_edge->rect.height; - new_edge->rect.height = 0; - new_edge->side_type = META_SIDE_TOP; - new_edge->edge_type = META_EDGE_WINDOW; - new_edges = g_list_prepend (new_edges, new_edge); - - /* Update the remaining windows to only those at a higher - * stacking position than this one. - */ - while (rem_win_stacking && - stack_position >= GPOINTER_TO_INT (rem_win_stacking->data)) - { - rem_windows = rem_windows->next; - rem_win_stacking = rem_win_stacking->next; - } - - /* Remove edge portions overlapped by rem_windows and rem_docks */ - new_edges = - meta_rectangle_remove_intersections_with_boxes_from_edges ( - new_edges, - rem_windows); - - /* Save the new edges */ - edges = g_list_concat (new_edges, edges); - } - - stack_position++; - cur_window_iter = cur_window_iter->next; - } - - /* - * 4th: Free the extra memory not needed and sort the list - */ - g_list_free (stacked_windows); - /* Free the memory used by the obscuring windows/docks lists */ - g_slist_free (window_stacking); - g_slist_free_full (obscuring_windows, g_free); - - /* Sort the list. FIXME: Should I bother with this sorting? I just - * sort again later in cache_edges() anyway... - */ - edges = g_list_sort (edges, meta_rectangle_edge_cmp); - - /* - * 5th: Cache the combination of these edges with the onscreen and - * monitor edges in an array for quick access. Free the edges since - * they've been cached elsewhere. - */ - cache_edges (display, - edges, - workspace_manager->active_workspace->monitor_edges, - workspace_manager->active_workspace->screen_edges); - g_list_free (edges); - - /* - * 6th: Initialize the resistance timeouts and buildups - */ - initialize_grab_edge_resistance_data (display); -} - -void -meta_window_edge_resistance_for_move (MetaWindow *window, - int *new_x, - int *new_y, - GSourceFunc timeout_func, - MetaEdgeResistanceFlags flags) -{ - MetaRectangle old_outer, proposed_outer, new_outer; - MetaEdgeResistanceFlags saved_flags; - gboolean is_resize, is_keyboard_op, snap; - - meta_window_get_frame_rect (window, &old_outer); - - proposed_outer = old_outer; - proposed_outer.x = *new_x; - proposed_outer.y = *new_y; - new_outer = proposed_outer; - - snap = flags & META_EDGE_RESISTANCE_SNAP; - is_keyboard_op = flags & META_EDGE_RESISTANCE_KEYBOARD_OP; - saved_flags = flags & ~META_EDGE_RESISTANCE_KEYBOARD_OP; - - window->display->grab_last_edge_resistance_flags = saved_flags; - is_resize = FALSE; - if (apply_edge_resistance_to_each_side (window->display, - window, - &old_outer, - &new_outer, - timeout_func, - flags, - is_resize)) - { - /* apply_edge_resistance_to_each_side independently applies - * resistance to both the right and left edges of new_outer as both - * could meet areas of resistance. But we don't want a resize, so we - * just have both edges move according to the stricter of the - * resistances. Same thing goes for top & bottom edges. - */ - MetaRectangle *reference; - int left_change, right_change, smaller_x_change; - int top_change, bottom_change, smaller_y_change; - - if (snap && !is_keyboard_op) - reference = &proposed_outer; - else - reference = &old_outer; - - left_change = BOX_LEFT (new_outer) - BOX_LEFT (*reference); - right_change = BOX_RIGHT (new_outer) - BOX_RIGHT (*reference); - if ( snap && is_keyboard_op && left_change == 0) - smaller_x_change = right_change; - else if (snap && is_keyboard_op && right_change == 0) - smaller_x_change = left_change; - else if (ABS (left_change) < ABS (right_change)) - smaller_x_change = left_change; - else - smaller_x_change = right_change; - - top_change = BOX_TOP (new_outer) - BOX_TOP (*reference); - bottom_change = BOX_BOTTOM (new_outer) - BOX_BOTTOM (*reference); - if ( snap && is_keyboard_op && top_change == 0) - smaller_y_change = bottom_change; - else if (snap && is_keyboard_op && bottom_change == 0) - smaller_y_change = top_change; - else if (ABS (top_change) < ABS (bottom_change)) - smaller_y_change = top_change; - else - smaller_y_change = bottom_change; - - *new_x = old_outer.x + smaller_x_change + - (BOX_LEFT (*reference) - BOX_LEFT (old_outer)); - *new_y = old_outer.y + smaller_y_change + - (BOX_TOP (*reference) - BOX_TOP (old_outer)); - - meta_topic (META_DEBUG_EDGE_RESISTANCE, - "outer x & y move-to coordinate changed from %d,%d to %d,%d", - proposed_outer.x, proposed_outer.y, - *new_x, *new_y); - } -} - -void -meta_window_edge_resistance_for_resize (MetaWindow *window, - int *new_width, - int *new_height, - MetaGravity gravity, - GSourceFunc timeout_func, - MetaEdgeResistanceFlags flags) -{ - MetaRectangle old_outer, new_outer; - MetaEdgeResistanceFlags saved_flags; - int proposed_outer_width, proposed_outer_height; - - meta_window_get_frame_rect (window, &old_outer); - proposed_outer_width = *new_width; - proposed_outer_height = *new_height; - meta_rectangle_resize_with_gravity (&old_outer, - &new_outer, - gravity, - proposed_outer_width, - proposed_outer_height); - - saved_flags = flags & ~META_EDGE_RESISTANCE_KEYBOARD_OP; - window->display->grab_last_edge_resistance_flags = saved_flags; - - if (apply_edge_resistance_to_each_side (window->display, - window, - &old_outer, - &new_outer, - timeout_func, - flags, - TRUE)) - { - *new_width = new_outer.width; - *new_height = new_outer.height; - - meta_topic (META_DEBUG_EDGE_RESISTANCE, - "outer width & height got changed from %d,%d to %d,%d", - proposed_outer_width, proposed_outer_height, - new_outer.width, new_outer.height); - } -} diff --git a/src/core/edge-resistance.h b/src/core/edge-resistance.h deleted file mode 100644 index 4eb6abdca..000000000 --- a/src/core/edge-resistance.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Edge resistance for move/resize operations */ - -/* - * Copyright (C) 2005 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef META_EDGE_RESISTANCE_H -#define META_EDGE_RESISTANCE_H - -#include "core/window-private.h" - -void meta_window_edge_resistance_for_move (MetaWindow *window, - int *new_x, - int *new_y, - GSourceFunc timeout_func, - MetaEdgeResistanceFlags flags); -void meta_window_edge_resistance_for_resize (MetaWindow *window, - int *new_width, - int *new_height, - MetaGravity gravity, - GSourceFunc timeout_func, - MetaEdgeResistanceFlags flags); - -#endif /* META_EDGE_RESISTANCE_H */ - diff --git a/src/core/events.c b/src/core/events.c deleted file mode 100644 index 8afc720ef..000000000 --- a/src/core/events.c +++ /dev/null @@ -1,509 +0,0 @@ -/* -*- 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 <http://www.gnu.org/licenses/>. - */ - -#include "config.h" - -#include "core/events.h" - -#include "backends/meta-cursor-tracker-private.h" -#include "backends/meta-idle-manager.h" -#include "backends/x11/meta-backend-x11.h" -#include "backends/x11/meta-input-device-x11.h" -#include "compositor/meta-window-actor-private.h" -#include "core/display-private.h" -#include "core/window-private.h" -#include "meta/meta-backend.h" - -#ifdef HAVE_NATIVE_BACKEND -#include "backends/native/meta-backend-native.h" -#endif - -#ifdef HAVE_WAYLAND -#include "wayland/meta-wayland-private.h" -#endif - -#define IS_GESTURE_EVENT(e) ((e)->type == CLUTTER_TOUCHPAD_SWIPE || \ - (e)->type == CLUTTER_TOUCHPAD_PINCH || \ - (e)->type == CLUTTER_TOUCH_BEGIN || \ - (e)->type == CLUTTER_TOUCH_UPDATE || \ - (e)->type == CLUTTER_TOUCH_END || \ - (e)->type == CLUTTER_TOUCH_CANCEL) - -#define IS_KEY_EVENT(e) ((e)->type == CLUTTER_KEY_PRESS || \ - (e)->type == CLUTTER_KEY_RELEASE) - -typedef enum -{ - EVENTS_UNFREEZE_SYNC, - EVENTS_UNFREEZE_REPLAY, -} EventsUnfreezeMethod; - -static gboolean -stage_has_key_focus (void) -{ - MetaBackend *backend = meta_get_backend (); - ClutterActor *stage = meta_backend_get_stage (backend); - - return clutter_stage_get_key_focus (CLUTTER_STAGE (stage)) == stage; -} - -static MetaWindow * -get_window_for_event (MetaDisplay *display, - const ClutterEvent *event) -{ - switch (display->event_route) - { - case META_EVENT_ROUTE_NORMAL: - { - ClutterActor *source; - MetaWindowActor *window_actor; - - /* Always use the key focused window for key events. */ - if (IS_KEY_EVENT (event)) - return stage_has_key_focus () ? display->focus_window : NULL; - - source = clutter_event_get_source (event); - window_actor = meta_window_actor_from_actor (source); - if (window_actor) - return meta_window_actor_get_meta_window (window_actor); - else - return NULL; - } - case META_EVENT_ROUTE_WINDOW_OP: - case META_EVENT_ROUTE_COMPOSITOR_GRAB: - case META_EVENT_ROUTE_WAYLAND_POPUP: - case META_EVENT_ROUTE_FRAME_BUTTON: - return display->grab_window; - default: - g_assert_not_reached (); - return NULL; - } -} - -static void -handle_idletime_for_event (const ClutterEvent *event) -{ - MetaBackend *backend = meta_get_backend (); - MetaIdleManager *idle_manager; - - if (clutter_event_get_device (event) == NULL) - return; - - if (event->any.flags & CLUTTER_EVENT_FLAG_SYNTHETIC || - event->type == CLUTTER_ENTER || - event->type == CLUTTER_LEAVE) - return; - - idle_manager = meta_backend_get_idle_manager (backend); - meta_idle_manager_reset_idle_time (idle_manager); -} - -static gboolean -sequence_is_pointer_emulated (MetaDisplay *display, - const ClutterEvent *event) -{ - ClutterEventSequence *sequence; - - sequence = clutter_event_get_event_sequence (event); - - if (!sequence) - return FALSE; - - if (clutter_event_is_pointer_emulated (event)) - return TRUE; - -#ifdef HAVE_NATIVE_BACKEND - MetaBackend *backend = meta_get_backend (); - - /* When using Clutter's native input backend there is no concept of - * pointer emulating sequence, we still must make up our own to be - * able to implement single-touch (hence pointer alike) behavior. - * - * This is implemented similarly to X11, where only the first touch - * on screen gets the "pointer emulated" flag, and it won't get assigned - * to another sequence until the next first touch on an idle touchscreen. - */ - if (META_IS_BACKEND_NATIVE (backend)) - { - MetaGestureTracker *tracker; - - tracker = meta_display_get_gesture_tracker (display); - - if (event->type == CLUTTER_TOUCH_BEGIN && - meta_gesture_tracker_get_n_current_touches (tracker) == 0) - return TRUE; - } -#endif /* HAVE_NATIVE_BACKEND */ - - return FALSE; -} - -static void -maybe_unfreeze_pointer_events (MetaBackend *backend, - const ClutterEvent *event, - EventsUnfreezeMethod unfreeze_method) -{ - ClutterInputDevice *device; - Display *xdisplay; - int event_mode; - int device_id; - - if (event->type != CLUTTER_BUTTON_PRESS) - return; - - if (!META_IS_BACKEND_X11 (backend)) - return; - - device = clutter_event_get_device (event); - device_id = meta_input_device_x11_get_device_id (device); - switch (unfreeze_method) - { - case EVENTS_UNFREEZE_SYNC: - event_mode = XISyncDevice; - meta_verbose ("Syncing events time %u device %i", - (unsigned int) event->button.time, device_id); - break; - case EVENTS_UNFREEZE_REPLAY: - event_mode = XIReplayDevice; - meta_verbose ("Replaying events time %u device %i", - (unsigned int) event->button.time, device_id); - break; - default: - g_assert_not_reached (); - return; - } - - xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); - XIAllowEvents (xdisplay, device_id, event_mode, event->button.time); -} - -static gboolean -meta_display_handle_event (MetaDisplay *display, - const ClutterEvent *event) -{ - MetaBackend *backend = meta_get_backend (); - MetaWindow *window; - gboolean bypass_clutter = FALSE; - G_GNUC_UNUSED gboolean bypass_wayland = FALSE; - MetaGestureTracker *gesture_tracker; - ClutterEventSequence *sequence; - - sequence = clutter_event_get_event_sequence (event); - - /* Set the pointer emulating sequence on touch begin, if eligible */ - if (event->type == CLUTTER_TOUCH_BEGIN) - { - if (sequence_is_pointer_emulated (display, event)) - { - /* This is the new pointer emulating sequence */ - display->pointer_emulating_sequence = sequence; - } - else if (display->pointer_emulating_sequence == sequence) - { - /* This sequence was "pointer emulating" in a prior incarnation, - * but now it isn't. We unset the pointer emulating sequence at - * this point so the current sequence is not mistaken as pointer - * emulating, while we've ensured that it's been deemed - * "pointer emulating" throughout all of the event processing - * of the previous incarnation. - */ - display->pointer_emulating_sequence = NULL; - } - } - -#ifdef HAVE_WAYLAND - MetaWaylandCompositor *compositor = NULL; - if (meta_is_wayland_compositor ()) - { - compositor = meta_wayland_compositor_get_default (); - meta_wayland_compositor_update (compositor, event); - } -#endif - - if (event->type == CLUTTER_PAD_BUTTON_PRESS || - event->type == CLUTTER_PAD_BUTTON_RELEASE || - event->type == CLUTTER_PAD_RING || - event->type == CLUTTER_PAD_STRIP) - { - gboolean handle_pad_event; - gboolean is_mode_switch = FALSE; - - if (event->type == CLUTTER_PAD_BUTTON_PRESS || - event->type == CLUTTER_PAD_BUTTON_RELEASE) - { - ClutterInputDevice *pad; - uint32_t button; - - pad = clutter_event_get_source_device (event); - button = clutter_event_get_button (event); - - is_mode_switch = - clutter_input_device_get_mode_switch_button_group (pad, button) >= 0; - } - - handle_pad_event = !display->current_pad_osd || is_mode_switch; - - if (handle_pad_event && - meta_pad_action_mapper_handle_event (display->pad_action_mapper, event)) - { - bypass_wayland = bypass_clutter = TRUE; - goto out; - } - } - - if (event->type != CLUTTER_DEVICE_ADDED && - event->type != CLUTTER_DEVICE_REMOVED) - { - ClutterInputDevice *source; - - handle_idletime_for_event (event); - source = clutter_event_get_source_device (event); - - if (source) - meta_backend_update_last_device (backend, source); - } - -#ifdef HAVE_WAYLAND - if (meta_is_wayland_compositor () && event->type == CLUTTER_MOTION) - { - MetaCursorRenderer *cursor_renderer; - ClutterInputDevice *device; - - device = clutter_event_get_device (event); - cursor_renderer = meta_backend_get_cursor_renderer_for_device (backend, - device); - if (cursor_renderer) - meta_cursor_renderer_update_position (cursor_renderer); - - if (device == clutter_seat_get_pointer (clutter_input_device_get_seat (device))) - { - MetaCursorTracker *cursor_tracker = - meta_backend_get_cursor_tracker (backend); - - meta_cursor_tracker_invalidate_position (cursor_tracker); - } - } -#endif - - window = get_window_for_event (display, event); - - display->current_time = event->any.time; - - if (window && !window->override_redirect && - (event->type == CLUTTER_KEY_PRESS || - event->type == CLUTTER_BUTTON_PRESS || - event->type == CLUTTER_TOUCH_BEGIN)) - { - if (META_CURRENT_TIME == display->current_time) - { - /* We can't use missing (i.e. invalid) timestamps to set user time, - * nor do we want to use them to sanity check other timestamps. - * See bug 313490 for more details. - */ - meta_warning ("Event has no timestamp! You may be using a broken " - "program such as xse. Please ask the authors of that " - "program to fix it."); - } - else - { - meta_window_set_user_time (window, display->current_time); - meta_display_sanity_check_timestamps (display, display->current_time); - } - } - - gesture_tracker = meta_display_get_gesture_tracker (display); - - if (meta_gesture_tracker_handle_event (gesture_tracker, event)) - { - bypass_wayland = bypass_clutter = TRUE; - goto out; - } - - if (display->event_route == META_EVENT_ROUTE_WINDOW_OP) - { - if (meta_window_handle_mouse_grab_op_event (window, event)) - { - bypass_clutter = TRUE; - bypass_wayland = TRUE; - goto out; - } - } - - /* For key events, it's important to enforce single-handling, or - * we can get into a confused state. So if a keybinding is - * handled (because it's one of our hot-keys, or because we are - * in a keyboard-grabbed mode like moving a window, we don't - * want to pass the key event to the compositor or Wayland at all. - */ - if (meta_keybindings_process_event (display, window, event)) - { - bypass_clutter = TRUE; - bypass_wayland = TRUE; - goto out; - } - - /* Do not pass keyboard events to Wayland if key focus is not on the - * stage in normal mode (e.g. during keynav in the panel) - */ - if (display->event_route == META_EVENT_ROUTE_NORMAL) - { - if (IS_KEY_EVENT (event) && !stage_has_key_focus ()) - { - bypass_wayland = TRUE; - goto out; - } - } - - if (meta_is_wayland_compositor () && - event->type == CLUTTER_SCROLL && - meta_prefs_get_mouse_button_mods () > 0) - { - ClutterModifierType grab_mods; - - grab_mods = meta_display_get_compositor_modifiers (display); - if ((clutter_event_get_state (event) & grab_mods) != 0) - { - bypass_wayland = TRUE; - goto out; - } - } - - if (display->current_pad_osd) - { - bypass_wayland = TRUE; - goto out; - } - - if (window) - { - /* Events that are likely to trigger compositor gestures should - * be known to clutter so they can propagate along the hierarchy. - * Gesture-wise, there's two groups of events we should be getting - * here: - * - CLUTTER_TOUCH_* with a touch sequence that's not yet accepted - * by the gesture tracker, these might trigger gesture actions - * into recognition. Already accepted touch sequences are handled - * directly by meta_gesture_tracker_handle_event(). - * - CLUTTER_TOUCHPAD_* events over windows. These can likewise - * trigger ::captured-event handlers along the way. - */ - bypass_clutter = !IS_GESTURE_EVENT (event); - - /* When double clicking to un-maximize an X11 window under Wayland, - * there is a race between X11 and Wayland protocols and the X11 - * XConfigureWindow may be processed by Xwayland before the button - * press event is forwarded via the Wayland protocol. - * As a result, the second click may reach another X11 window placed - * immediately underneath in the X11 stack. - * The following is to make sure we do not forward the button press - * event to Wayland if it was handled by the frame UI. - * See: https://gitlab.gnome.org/GNOME/mutter/issues/88 - */ - if (meta_window_handle_ui_frame_event (window, event)) - bypass_wayland = (event->type == CLUTTER_BUTTON_PRESS || - event->type == CLUTTER_TOUCH_BEGIN); - else - meta_window_handle_ungrabbed_event (window, event); - - /* This might start a grab op. If it does, then filter out the - * event, and if it doesn't, replay the event to release our - * own sync grab. */ - - if (display->event_route == META_EVENT_ROUTE_WINDOW_OP || - display->event_route == META_EVENT_ROUTE_FRAME_BUTTON) - { - bypass_clutter = TRUE; - bypass_wayland = TRUE; - } - else - { - /* Only replay button press events, since that's where we - * have the synchronous grab. */ - maybe_unfreeze_pointer_events (backend, event, EVENTS_UNFREEZE_REPLAY); - - /* If the focus window has an active close dialog let clutter - * events go through, so fancy clutter dialogs can get to handle - * all events. - */ - if (window->close_dialog && - meta_close_dialog_is_visible (window->close_dialog)) - { - bypass_wayland = TRUE; - bypass_clutter = FALSE; - } - } - - goto out; - } - else - { - /* We could not match the event with a window, make sure we sync - * the pointer to discard the sequence and don't keep events frozen. - */ - maybe_unfreeze_pointer_events (backend, event, EVENTS_UNFREEZE_SYNC); - } - - out: - /* If the compositor has a grab, don't pass that through to Wayland */ - if (display->event_route == META_EVENT_ROUTE_COMPOSITOR_GRAB) - bypass_wayland = TRUE; - - /* If a Wayland client has a grab, don't pass that through to Clutter */ - if (display->event_route == META_EVENT_ROUTE_WAYLAND_POPUP) - bypass_clutter = TRUE; - -#ifdef HAVE_WAYLAND - if (compositor && !bypass_wayland) - { - if (meta_wayland_compositor_handle_event (compositor, event)) - bypass_clutter = TRUE; - } -#endif - - display->current_time = META_CURRENT_TIME; - return bypass_clutter; -} - -static gboolean -event_callback (const ClutterEvent *event, - gpointer data) -{ - MetaDisplay *display = data; - - return meta_display_handle_event (display, event); -} - -void -meta_display_init_events (MetaDisplay *display) -{ - display->clutter_event_filter = clutter_event_add_filter (NULL, - event_callback, - NULL, - display); -} - -void -meta_display_free_events (MetaDisplay *display) -{ - clutter_event_remove_filter (display->clutter_event_filter); - display->clutter_event_filter = 0; -} diff --git a/src/core/events.h b/src/core/events.h deleted file mode 100644 index 6338119ae..000000000 --- a/src/core/events.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- 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 <http://www.gnu.org/licenses/>. - */ - -#ifndef META_EVENTS_H -#define META_EVENTS_H - -#include "meta/display.h" - -void meta_display_init_events (MetaDisplay *display); -void meta_display_free_events (MetaDisplay *display); - -#endif diff --git a/src/core/frame.c b/src/core/frame.c deleted file mode 100644 index 9c8cbb946..000000000 --- a/src/core/frame.c +++ /dev/null @@ -1,427 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Mutter X window decorations */ - -/* - * Copyright (C) 2001 Havoc Pennington - * Copyright (C) 2003, 2004 Red Hat, Inc. - * Copyright (C) 2005 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "config.h" - -#include "core/frame.h" - -#include "backends/x11/meta-backend-x11.h" -#include "core/bell.h" -#include "core/keybindings-private.h" -#include "meta/meta-x11-errors.h" -#include "x11/meta-x11-display-private.h" - -#define EVENT_MASK (SubstructureRedirectMask | \ - StructureNotifyMask | SubstructureNotifyMask | \ - ExposureMask | FocusChangeMask) - -void -meta_window_ensure_frame (MetaWindow *window) -{ - MetaFrame *frame; - XSetWindowAttributes attrs; - gulong create_serial; - MetaX11Display *x11_display; - - if (window->frame) - return; - - x11_display = window->display->x11_display; - - frame = g_new (MetaFrame, 1); - - frame->window = window; - frame->xwindow = None; - - frame->rect = window->rect; - frame->child_x = 0; - frame->child_y = 0; - frame->bottom_height = 0; - frame->right_width = 0; - frame->current_cursor = 0; - - frame->borders_cached = FALSE; - - meta_verbose ("Frame geometry %d,%d %dx%d", - frame->rect.x, frame->rect.y, - frame->rect.width, frame->rect.height); - - frame->ui_frame = meta_ui_create_frame (x11_display->ui, - x11_display->xdisplay, - frame->window, - window->xvisual, - frame->rect.x, - frame->rect.y, - frame->rect.width, - frame->rect.height, - &create_serial); - frame->xwindow = frame->ui_frame->xwindow; - - meta_stack_tracker_record_add (window->display->stack_tracker, - frame->xwindow, - create_serial); - - meta_verbose ("Frame for %s is 0x%lx", frame->window->desc, frame->xwindow); - attrs.event_mask = EVENT_MASK; - XChangeWindowAttributes (x11_display->xdisplay, - frame->xwindow, CWEventMask, &attrs); - - meta_x11_display_register_x_window (x11_display, &frame->xwindow, window); - - meta_x11_error_trap_push (x11_display); - if (window->mapped) - { - window->mapped = FALSE; /* the reparent will unmap the window, - * we don't want to take that as a withdraw - */ - meta_topic (META_DEBUG_WINDOW_STATE, - "Incrementing unmaps_pending on %s for reparent", window->desc); - window->unmaps_pending += 1; - } - - meta_stack_tracker_record_remove (window->display->stack_tracker, - window->xwindow, - XNextRequest (x11_display->xdisplay)); - XReparentWindow (x11_display->xdisplay, - window->xwindow, - frame->xwindow, - frame->child_x, - frame->child_y); - window->reparents_pending += 1; - /* FIXME handle this error */ - meta_x11_error_trap_pop (x11_display); - - /* Ensure focus is restored after the unmap/map events triggered - * by XReparentWindow(). - */ - if (meta_window_has_focus (window)) - window->restore_focus_on_map = TRUE; - - /* stick frame to the window */ - window->frame = frame; - - /* Now that frame->xwindow is registered with window, we can set its - * style and background. - */ - meta_frame_update_style (frame); - meta_frame_update_title (frame); - - meta_ui_map_frame (x11_display->ui, frame->xwindow); - - { - MetaBackend *backend = meta_get_backend (); - if (META_IS_BACKEND_X11 (backend)) - { - Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); - - /* Since the backend selects for events on another connection, - * make sure to sync the GTK+ connection to ensure that the - * frame window has been created on the server at this point. */ - XSync (x11_display->xdisplay, False); - - unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; - XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; - - XISelectEvents (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), - frame->xwindow, &mask, 1); - - XISetMask (mask.mask, XI_ButtonPress); - XISetMask (mask.mask, XI_ButtonRelease); - XISetMask (mask.mask, XI_Motion); - XISetMask (mask.mask, XI_Enter); - XISetMask (mask.mask, XI_Leave); - - XISelectEvents (xdisplay, frame->xwindow, &mask, 1); - } - } - - /* Move keybindings to frame instead of window */ - meta_window_grab_keys (window); -} - -void -meta_window_destroy_frame (MetaWindow *window) -{ - MetaFrame *frame; - MetaFrameBorders borders; - MetaX11Display *x11_display; - - if (window->frame == NULL) - return; - - x11_display = window->display->x11_display; - - meta_verbose ("Unframing window %s", window->desc); - - frame = window->frame; - - meta_frame_calc_borders (frame, &borders); - - /* Unparent the client window; it may be destroyed, - * thus the error trap. - */ - meta_x11_error_trap_push (x11_display); - if (window->mapped) - { - window->mapped = FALSE; /* Keep track of unmapping it, so we - * can identify a withdraw initiated - * by the client. - */ - meta_topic (META_DEBUG_WINDOW_STATE, - "Incrementing unmaps_pending on %s for reparent back to root", window->desc); - window->unmaps_pending += 1; - } - - if (!x11_display->closing) - { - meta_stack_tracker_record_add (window->display->stack_tracker, - window->xwindow, - XNextRequest (x11_display->xdisplay)); - - XReparentWindow (x11_display->xdisplay, - window->xwindow, - x11_display->xroot, - /* Using anything other than client root window coordinates - * coordinates here means we'll need to ensure a configure - * notify event is sent; see bug 399552. - */ - window->frame->rect.x + borders.invisible.left, - window->frame->rect.y + borders.invisible.top); - window->reparents_pending += 1; - } - - meta_x11_error_trap_pop (x11_display); - - meta_ui_frame_unmanage (frame->ui_frame); - - /* Ensure focus is restored after the unmap/map events triggered - * by XReparentWindow(). - */ - if (meta_window_has_focus (window)) - window->restore_focus_on_map = TRUE; - - meta_x11_display_unregister_x_window (x11_display, frame->xwindow); - - window->frame = NULL; - if (window->frame_bounds) - { - cairo_region_destroy (window->frame_bounds); - window->frame_bounds = NULL; - } - - /* Move keybindings to window instead of frame */ - meta_window_grab_keys (window); - - g_free (frame); - - /* Put our state back where it should be */ - meta_window_queue (window, META_QUEUE_CALC_SHOWING); - meta_window_queue (window, META_QUEUE_MOVE_RESIZE); -} - - -MetaFrameFlags -meta_frame_get_flags (MetaFrame *frame) -{ - MetaFrameFlags flags; - - flags = 0; - - if (frame->window->border_only) - { - ; /* FIXME this may disable the _function_ as well as decor - * in some cases, which is sort of wrong. - */ - } - else - { - flags |= META_FRAME_ALLOWS_MENU; - - if (frame->window->has_close_func) - flags |= META_FRAME_ALLOWS_DELETE; - - if (frame->window->has_maximize_func) - flags |= META_FRAME_ALLOWS_MAXIMIZE; - - if (frame->window->has_minimize_func) - flags |= META_FRAME_ALLOWS_MINIMIZE; - - if (frame->window->has_shade_func) - flags |= META_FRAME_ALLOWS_SHADE; - } - - if (META_WINDOW_ALLOWS_MOVE (frame->window)) - flags |= META_FRAME_ALLOWS_MOVE; - - if (META_WINDOW_ALLOWS_HORIZONTAL_RESIZE (frame->window)) - flags |= META_FRAME_ALLOWS_HORIZONTAL_RESIZE; - - if (META_WINDOW_ALLOWS_VERTICAL_RESIZE (frame->window)) - flags |= META_FRAME_ALLOWS_VERTICAL_RESIZE; - - if (meta_window_appears_focused (frame->window)) - flags |= META_FRAME_HAS_FOCUS; - - if (frame->window->shaded) - flags |= META_FRAME_SHADED; - - if (frame->window->on_all_workspaces_requested) - flags |= META_FRAME_STUCK; - - /* FIXME: Should we have some kind of UI for windows that are just vertically - * maximized or just horizontally maximized? - */ - if (META_WINDOW_MAXIMIZED (frame->window)) - flags |= META_FRAME_MAXIMIZED; - - if (META_WINDOW_TILED_LEFT (frame->window)) - flags |= META_FRAME_TILED_LEFT; - - if (META_WINDOW_TILED_RIGHT (frame->window)) - flags |= META_FRAME_TILED_RIGHT; - - if (frame->window->fullscreen) - flags |= META_FRAME_FULLSCREEN; - - if (frame->window->wm_state_above) - flags |= META_FRAME_ABOVE; - - return flags; -} - -void -meta_frame_borders_clear (MetaFrameBorders *self) -{ - self->visible.top = self->invisible.top = self->total.top = 0; - self->visible.bottom = self->invisible.bottom = self->total.bottom = 0; - self->visible.left = self->invisible.left = self->total.left = 0; - self->visible.right = self->invisible.right = self->total.right = 0; -} - -void -meta_frame_calc_borders (MetaFrame *frame, - MetaFrameBorders *borders) -{ - /* Save on if statements and potential uninitialized values - * in callers -- if there's no frame, then zero the borders. */ - if (frame == NULL) - meta_frame_borders_clear (borders); - else - { - if (!frame->borders_cached) - { - meta_ui_frame_get_borders (frame->ui_frame, &frame->cached_borders); - frame->borders_cached = TRUE; - } - - *borders = frame->cached_borders; - } -} - -void -meta_frame_clear_cached_borders (MetaFrame *frame) -{ - frame->borders_cached = FALSE; -} - -gboolean -meta_frame_sync_to_window (MetaFrame *frame, - gboolean need_resize) -{ - meta_topic (META_DEBUG_GEOMETRY, - "Syncing frame geometry %d,%d %dx%d (SE: %d,%d)", - frame->rect.x, frame->rect.y, - frame->rect.width, frame->rect.height, - frame->rect.x + frame->rect.width, - frame->rect.y + frame->rect.height); - - meta_ui_frame_move_resize (frame->ui_frame, - frame->rect.x, - frame->rect.y, - frame->rect.width, - frame->rect.height); - - return need_resize; -} - -cairo_region_t * -meta_frame_get_frame_bounds (MetaFrame *frame) -{ - return meta_ui_frame_get_bounds (frame->ui_frame); -} - -void -meta_frame_get_mask (MetaFrame *frame, - cairo_rectangle_int_t *frame_rect, - cairo_t *cr) -{ - meta_ui_frame_get_mask (frame->ui_frame, frame_rect, cr); -} - -void -meta_frame_queue_draw (MetaFrame *frame) -{ - meta_ui_frame_queue_draw (frame->ui_frame); -} - -void -meta_frame_set_screen_cursor (MetaFrame *frame, - MetaCursor cursor) -{ - MetaX11Display *x11_display; - Cursor xcursor; - if (cursor == frame->current_cursor) - return; - - frame->current_cursor = cursor; - x11_display = frame->window->display->x11_display; - - if (cursor == META_CURSOR_DEFAULT) - XUndefineCursor (x11_display->xdisplay, frame->xwindow); - else - { - xcursor = meta_x11_display_create_x_cursor (x11_display, cursor); - XDefineCursor (x11_display->xdisplay, frame->xwindow, xcursor); - XFlush (x11_display->xdisplay); - XFreeCursor (x11_display->xdisplay, xcursor); - } -} - -Window -meta_frame_get_xwindow (MetaFrame *frame) -{ - return frame->xwindow; -} - -void -meta_frame_update_style (MetaFrame *frame) -{ - meta_ui_frame_update_style (frame->ui_frame); -} - -void -meta_frame_update_title (MetaFrame *frame) -{ - if (frame->window->title) - meta_ui_frame_set_title (frame->ui_frame, frame->window->title); -} diff --git a/src/core/frame.h b/src/core/frame.h deleted file mode 100644 index 61a5ca725..000000000 --- a/src/core/frame.h +++ /dev/null @@ -1,85 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Mutter X window decorations */ - -/* - * Copyright (C) 2001 Havoc Pennington - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef META_FRAME_PRIVATE_H -#define META_FRAME_PRIVATE_H - -#include "core/window-private.h" -#include "ui/frames.h" - -struct _MetaFrame -{ - /* window we frame */ - MetaWindow *window; - - /* reparent window */ - Window xwindow; - - MetaCursor current_cursor; - - /* This rect is trusted info from where we put the - * frame, not the result of ConfigureNotify - */ - MetaRectangle rect; - - MetaFrameBorders cached_borders; /* valid if borders_cached is set */ - - /* position of client, size of frame */ - int child_x; - int child_y; - int right_width; - int bottom_height; - - guint need_reapply_frame_shape : 1; - guint borders_cached : 1; - - MetaUIFrame *ui_frame; -}; - -void meta_window_ensure_frame (MetaWindow *window); -void meta_window_destroy_frame (MetaWindow *window); -void meta_frame_queue_draw (MetaFrame *frame); - -MetaFrameFlags meta_frame_get_flags (MetaFrame *frame); -Window meta_frame_get_xwindow (MetaFrame *frame); - -/* These should ONLY be called from meta_window_move_resize_internal */ -void meta_frame_calc_borders (MetaFrame *frame, - MetaFrameBorders *borders); - -gboolean meta_frame_sync_to_window (MetaFrame *frame, - gboolean need_resize); - -void meta_frame_clear_cached_borders (MetaFrame *frame); - -cairo_region_t *meta_frame_get_frame_bounds (MetaFrame *frame); - -void meta_frame_get_mask (MetaFrame *frame, - cairo_rectangle_int_t *frame_rect, - cairo_t *cr); - -void meta_frame_set_screen_cursor (MetaFrame *frame, - MetaCursor cursor); - -void meta_frame_update_style (MetaFrame *frame); -void meta_frame_update_title (MetaFrame *frame); - -#endif diff --git a/src/core/keybindings-private.h b/src/core/keybindings-private.h deleted file mode 100644 index 56792c200..000000000 --- a/src/core/keybindings-private.h +++ /dev/null @@ -1,160 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/** - * \file keybindings.h Grab and ungrab keys, and process the key events - * - * Performs global X grabs on the keys we need to be told about, like - * the one to close a window. It also deals with incoming key events. - */ - -/* - * Copyright (C) 2001 Havoc Pennington - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef META_KEYBINDINGS_PRIVATE_H -#define META_KEYBINDINGS_PRIVATE_H - -#include <gio/gio.h> -#include <xkbcommon/xkbcommon.h> - -#include "core/meta-accel-parse.h" -#include "meta/keybindings.h" - -typedef struct _MetaKeyHandler MetaKeyHandler; -struct _MetaKeyHandler -{ - char *name; - MetaKeyHandlerFunc func; - MetaKeyHandlerFunc default_func; - gint data, flags; - gpointer user_data; - GDestroyNotify user_data_free_func; -}; - -typedef struct _MetaResolvedKeyCombo { - xkb_keycode_t *keycodes; - int len; - xkb_mod_mask_t mask; -} MetaResolvedKeyCombo; - -/** - * MetaKeyCombo: - * @keysym: keysym - * @keycode: keycode - * @modifiers: modifiers - */ -struct _MetaKeyCombo -{ - unsigned int keysym; - unsigned int keycode; - MetaVirtualModifier modifiers; -}; - -struct _MetaKeyBinding -{ - const char *name; - MetaKeyCombo combo; - MetaResolvedKeyCombo resolved_combo; - gint flags; - MetaKeyHandler *handler; -}; - -typedef struct -{ - char *name; - GSettings *settings; - - MetaKeyBindingAction action; - - /* - * A list of MetaKeyCombos. Each of them is bound to - * this keypref. If one has keysym==modifiers==0, it is - * ignored. - */ - GSList *combos; - - /* for keybindings not added with meta_display_add_keybinding() */ - gboolean builtin:1; -} MetaKeyPref; - -typedef struct _MetaKeyBindingKeyboardLayout -{ - struct xkb_keymap *keymap; - xkb_layout_index_t index; - xkb_level_index_t n_levels; -} MetaKeyBindingKeyboardLayout; - -typedef struct -{ - MetaBackend *backend; - - GHashTable *key_bindings; - GHashTable *key_bindings_index; - xkb_mod_mask_t ignored_modifier_mask; - xkb_mod_mask_t hyper_mask; - xkb_mod_mask_t virtual_hyper_mask; - xkb_mod_mask_t super_mask; - xkb_mod_mask_t virtual_super_mask; - xkb_mod_mask_t meta_mask; - xkb_mod_mask_t virtual_meta_mask; - MetaKeyCombo overlay_key_combo; - MetaResolvedKeyCombo overlay_resolved_key_combo; - gboolean overlay_key_only_pressed; - MetaKeyCombo locate_pointer_key_combo; - MetaResolvedKeyCombo locate_pointer_resolved_key_combo; - gboolean locate_pointer_key_only_pressed; - MetaResolvedKeyCombo iso_next_group_combo[2]; - int n_iso_next_group_combos; - - /* - * A primary layout, and an optional secondary layout for when the - * primary layout does not use the latin alphabet. - */ - MetaKeyBindingKeyboardLayout active_layouts[2]; - - /* Alt+click button grabs */ - ClutterModifierType window_grab_modifiers; -} MetaKeyBindingManager; - -void meta_display_init_keys (MetaDisplay *display); -void meta_display_shutdown_keys (MetaDisplay *display); -void meta_window_grab_keys (MetaWindow *window); -void meta_window_ungrab_keys (MetaWindow *window); -gboolean meta_window_grab_all_keys (MetaWindow *window, - guint32 timestamp); -void meta_window_ungrab_all_keys (MetaWindow *window, - guint32 timestamp); -gboolean meta_keybindings_process_event (MetaDisplay *display, - MetaWindow *window, - const ClutterEvent *event); - -gboolean meta_prefs_add_keybinding (const char *name, - GSettings *settings, - MetaKeyBindingAction action, - MetaKeyBindingFlags flags); - -gboolean meta_prefs_remove_keybinding (const char *name); - -GList *meta_prefs_get_keybindings (void); -void meta_prefs_get_overlay_binding (MetaKeyCombo *combo); -void meta_prefs_get_locate_pointer_binding (MetaKeyCombo *combo); -const char *meta_prefs_get_iso_next_group_option (void); -gboolean meta_prefs_is_locate_pointer_enabled (void); - -void meta_x11_display_grab_keys (MetaX11Display *x11_display); -void meta_x11_display_ungrab_keys (MetaX11Display *x11_display); - -#endif diff --git a/src/core/keybindings.c b/src/core/keybindings.c deleted file mode 100644 index a6e16f084..000000000 --- a/src/core/keybindings.c +++ /dev/null @@ -1,4578 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Mutter Keybindings */ -/* - * Copyright (C) 2001 Havoc Pennington - * Copyright (C) 2002 Red Hat Inc. - * Copyright (C) 2003 Rob Adams - * Copyright (C) 2004-2006 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -/** - * SECTION:keybindings - * @Title: MetaKeybinding - * @Short_Description: Key bindings - */ - -#include "config.h" - -#include "backends/meta-backend-private.h" -#include "backends/meta-keymap-utils.h" -#include "backends/meta-logical-monitor.h" -#include "backends/meta-monitor-manager-private.h" -#include "backends/x11/meta-backend-x11.h" -#include "backends/x11/meta-input-device-x11.h" -#include "compositor/compositor-private.h" -#include "core/edge-resistance.h" -#include "core/frame.h" -#include "core/keybindings-private.h" -#include "core/meta-accel-parse.h" -#include "core/meta-workspace-manager-private.h" -#include "core/workspace-private.h" -#include "meta/compositor.h" -#include "meta/meta-x11-errors.h" -#include "meta/prefs.h" -#include "x11/meta-x11-display-private.h" -#include "x11/window-x11.h" - -#ifdef HAVE_NATIVE_BACKEND -#include "backends/native/meta-backend-native.h" -#endif - -#ifdef __linux__ -#include <linux/input.h> -#elif !defined KEY_GRAVE -#define KEY_GRAVE 0x29 /* assume the use of xf86-input-keyboard */ -#endif - -#define SCHEMA_COMMON_KEYBINDINGS "org.gnome.desktop.wm.keybindings" -#define SCHEMA_MUTTER_KEYBINDINGS "org.gnome.mutter.keybindings" -#define SCHEMA_MUTTER_WAYLAND_KEYBINDINGS "org.gnome.mutter.wayland.keybindings" - -#define META_KEY_BINDING_PRIMARY_LAYOUT 0 -#define META_KEY_BINDING_SECONDARY_LAYOUT 1 - -/* Only for special modifier keys */ -#define IGNORED_MODIFIERS (CLUTTER_LOCK_MASK | \ - CLUTTER_MOD2_MASK | \ - CLUTTER_BUTTON1_MASK | \ - CLUTTER_BUTTON2_MASK | \ - CLUTTER_BUTTON3_MASK | \ - CLUTTER_BUTTON4_MASK | \ - CLUTTER_BUTTON5_MASK) - -static gboolean add_builtin_keybinding (MetaDisplay *display, - const char *name, - GSettings *settings, - MetaKeyBindingFlags flags, - MetaKeyBindingAction action, - MetaKeyHandlerFunc handler, - int handler_arg); - -static void -resolved_key_combo_reset (MetaResolvedKeyCombo *resolved_combo) -{ - g_free (resolved_combo->keycodes); - resolved_combo->len = 0; - resolved_combo->keycodes = NULL; -} - -static void -resolved_key_combo_copy (MetaResolvedKeyCombo *from, - MetaResolvedKeyCombo *to) -{ - to->len = from->len; - to->keycodes = g_memdup2 (from->keycodes, - from->len * sizeof (xkb_keycode_t)); -} - -static gboolean -resolved_key_combo_has_keycode (MetaResolvedKeyCombo *resolved_combo, - int keycode) -{ - int i; - - for (i = 0; i < resolved_combo->len; i++) - if ((int) resolved_combo->keycodes[i] == keycode) - return TRUE; - - return FALSE; -} - -static gboolean -resolved_key_combo_intersect (MetaResolvedKeyCombo *a, - MetaResolvedKeyCombo *b) -{ - int i; - - for (i = 0; i < a->len; i++) - if (resolved_key_combo_has_keycode (b, a->keycodes[i])) - return TRUE; - - return FALSE; -} - -static void -meta_key_binding_free (MetaKeyBinding *binding) -{ - resolved_key_combo_reset (&binding->resolved_combo); - g_free (binding); -} - -static MetaKeyBinding * -meta_key_binding_copy (MetaKeyBinding *binding) -{ - MetaKeyBinding *clone = g_memdup2 (binding, sizeof (MetaKeyBinding)); - resolved_key_combo_copy (&binding->resolved_combo, - &clone->resolved_combo); - return clone; -} - -G_DEFINE_BOXED_TYPE(MetaKeyBinding, - meta_key_binding, - meta_key_binding_copy, - meta_key_binding_free) - -const char * -meta_key_binding_get_name (MetaKeyBinding *binding) -{ - return binding->name; -} - -MetaVirtualModifier -meta_key_binding_get_modifiers (MetaKeyBinding *binding) -{ - return binding->combo.modifiers; -} - -gboolean -meta_key_binding_is_reversed (MetaKeyBinding *binding) -{ - return (binding->handler->flags & META_KEY_BINDING_IS_REVERSED) != 0; -} - -guint -meta_key_binding_get_mask (MetaKeyBinding *binding) -{ - return binding->resolved_combo.mask; -} - -gboolean -meta_key_binding_is_builtin (MetaKeyBinding *binding) -{ - return binding->handler->flags & META_KEY_BINDING_BUILTIN; -} - -/* These can't be bound to anything, but they are used to handle - * various other events. TODO: Possibly we should include them as event - * handler functions and have some kind of flag to say they're unbindable. - */ - -static gboolean process_mouse_move_resize_grab (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event); - -static gboolean process_keyboard_move_grab (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event); - -static gboolean process_keyboard_resize_grab (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event); - -static void maybe_update_locate_pointer_keygrab (MetaDisplay *display, - gboolean grab); - -static GHashTable *key_handlers; -static GHashTable *external_grabs; - -#define HANDLER(name) g_hash_table_lookup (key_handlers, (name)) - -static void -key_handler_free (MetaKeyHandler *handler) -{ - g_free (handler->name); - if (handler->user_data_free_func && handler->user_data) - handler->user_data_free_func (handler->user_data); - g_free (handler); -} - -typedef struct _MetaKeyGrab MetaKeyGrab; -struct _MetaKeyGrab { - char *name; - guint action; - MetaKeyCombo combo; - gint flags; -}; - -static void -meta_key_grab_free (MetaKeyGrab *grab) -{ - g_free (grab->name); - g_free (grab); -} - -static guint32 -key_combo_key (MetaResolvedKeyCombo *resolved_combo, - int i) -{ - /* On X, keycodes are only 8 bits while libxkbcommon supports 32 bit - keycodes, but since we're using the same XKB keymaps that X uses, - we won't find keycodes bigger than 8 bits in practice. The bits - that mutter cares about in the modifier mask are also all in the - lower 8 bits both on X and clutter key events. This means that we - can use a 32 bit integer to safely concatenate both keycode and - mask and thus making it easy to use them as an index in a - GHashTable. */ - guint32 key = resolved_combo->keycodes[i] & 0xffff; - return (key << 16) | (resolved_combo->mask & 0xffff); -} - -static void -reload_modmap (MetaKeyBindingManager *keys) -{ - struct xkb_keymap *keymap = meta_backend_get_keymap (keys->backend); - struct xkb_state *scratch_state; - xkb_mod_mask_t scroll_lock_mask; - xkb_mod_mask_t dummy_mask; - - /* Modifiers to find. */ - struct { - const char *name; - xkb_mod_mask_t *mask_p; - xkb_mod_mask_t *virtual_mask_p; - } mods[] = { - { "ScrollLock", &scroll_lock_mask, &dummy_mask }, - { "Meta", &keys->meta_mask, &keys->virtual_meta_mask }, - { "Hyper", &keys->hyper_mask, &keys->virtual_hyper_mask }, - { "Super", &keys->super_mask, &keys->virtual_super_mask }, - }; - - scratch_state = xkb_state_new (keymap); - - gsize i; - for (i = 0; i < G_N_ELEMENTS (mods); i++) - { - xkb_mod_mask_t *mask_p = mods[i].mask_p; - xkb_mod_mask_t *virtual_mask_p = mods[i].virtual_mask_p; - xkb_mod_index_t idx = xkb_keymap_mod_get_index (keymap, mods[i].name); - - if (idx != XKB_MOD_INVALID) - { - xkb_mod_mask_t vmodmask = (1 << idx); - xkb_state_update_mask (scratch_state, vmodmask, 0, 0, 0, 0, 0); - *mask_p = xkb_state_serialize_mods (scratch_state, XKB_STATE_MODS_DEPRESSED) & ~vmodmask; - *virtual_mask_p = vmodmask; - } - else - { - *mask_p = 0; - *virtual_mask_p = 0; - } - } - - xkb_state_unref (scratch_state); - - keys->ignored_modifier_mask = (scroll_lock_mask | Mod2Mask | LockMask); - - meta_topic (META_DEBUG_KEYBINDINGS, - "Ignoring modmask 0x%x scroll lock 0x%x hyper 0x%x super 0x%x meta 0x%x", - keys->ignored_modifier_mask, - scroll_lock_mask, - keys->hyper_mask, - keys->super_mask, - keys->meta_mask); -} - -static gboolean -is_keycode_for_keysym (struct xkb_keymap *keymap, - xkb_layout_index_t layout, - xkb_level_index_t level, - xkb_keycode_t keycode, - xkb_keysym_t keysym) -{ - const xkb_keysym_t *syms; - int num_syms, k; - - num_syms = xkb_keymap_key_get_syms_by_level (keymap, keycode, layout, level, &syms); - for (k = 0; k < num_syms; k++) - { - if (syms[k] == keysym) - return TRUE; - } - - return FALSE; -} - -typedef struct -{ - GArray *keycodes; - xkb_keysym_t keysym; - xkb_layout_index_t layout; - xkb_level_index_t level; -} FindKeysymData; - -static void -get_keycodes_for_keysym_iter (struct xkb_keymap *keymap, - xkb_keycode_t keycode, - void *data) -{ - FindKeysymData *search_data = data; - GArray *keycodes = search_data->keycodes; - xkb_keysym_t keysym = search_data->keysym; - xkb_layout_index_t layout = search_data->layout; - xkb_level_index_t level = search_data->level; - - if (is_keycode_for_keysym (keymap, layout, level, keycode, keysym)) - { - guint i; - gboolean missing = TRUE; - - /* duplicate keycode detection */ - for (i = 0; i < keycodes->len; i++) - if (g_array_index (keycodes, xkb_keysym_t, i) == keycode) - { - missing = FALSE; - break; - } - - if (missing) - g_array_append_val (keycodes, keycode); - } -} - -static void -add_keysym_keycodes_from_layout (int keysym, - MetaKeyBindingKeyboardLayout *layout, - GArray *keycodes) -{ - xkb_level_index_t layout_level; - - for (layout_level = 0; - layout_level < layout->n_levels && keycodes->len == 0; - layout_level++) - { - FindKeysymData search_data = (FindKeysymData) { - .keycodes = keycodes, - .keysym = keysym, - .layout = layout->index, - .level = layout_level - }; - xkb_keymap_key_for_each (layout->keymap, - get_keycodes_for_keysym_iter, - &search_data); - } -} - -/* Original code from gdk_x11_keymap_get_entries_for_keyval() in - * gdkkeys-x11.c */ -static void -get_keycodes_for_keysym (MetaKeyBindingManager *keys, - int keysym, - MetaResolvedKeyCombo *resolved_combo) -{ - unsigned int i; - GArray *keycodes; - int keycode; - - keycodes = g_array_new (FALSE, FALSE, sizeof (xkb_keysym_t)); - - /* Special-case: Fake mutter keysym */ - if (keysym == META_KEY_ABOVE_TAB) - { - keycode = KEY_GRAVE + 8; - g_array_append_val (keycodes, keycode); - goto out; - } - - for (i = 0; i < G_N_ELEMENTS (keys->active_layouts); i++) - { - MetaKeyBindingKeyboardLayout *layout = &keys->active_layouts[i]; - - if (!layout->keymap) - continue; - - add_keysym_keycodes_from_layout (keysym, layout, keycodes); - } - - out: - resolved_combo->len = keycodes->len; - resolved_combo->keycodes = - (xkb_keycode_t *) g_array_free (keycodes, - keycodes->len == 0 ? TRUE : FALSE); -} - -typedef struct _CalculateLayoutLevelsState -{ - struct xkb_keymap *keymap; - xkb_layout_index_t layout_index; - - xkb_level_index_t out_n_levels; -} CalculateLayoutLevelState; - -static void -calculate_n_layout_levels_iter (struct xkb_keymap *keymap, - xkb_keycode_t keycode, - void *data) -{ - CalculateLayoutLevelState *state = data; - xkb_level_index_t n_levels; - - n_levels = xkb_keymap_num_levels_for_key (keymap, - keycode, - state->layout_index); - - state->out_n_levels = MAX (n_levels, state->out_n_levels); -} - -static xkb_level_index_t -calculate_n_layout_levels (struct xkb_keymap *keymap, - xkb_layout_index_t layout_index) - -{ - CalculateLayoutLevelState state = { - .keymap = keymap, - .layout_index = layout_index, - - .out_n_levels = 0 - }; - - xkb_keymap_key_for_each (keymap, calculate_n_layout_levels_iter, &state); - - return state.out_n_levels; -} - -static void -reload_iso_next_group_combos (MetaKeyBindingManager *keys) -{ - const char *iso_next_group_option; - int i; - - for (i = 0; i < keys->n_iso_next_group_combos; i++) - resolved_key_combo_reset (&keys->iso_next_group_combo[i]); - - keys->n_iso_next_group_combos = 0; - - iso_next_group_option = meta_prefs_get_iso_next_group_option (); - if (iso_next_group_option == NULL) - return; - - get_keycodes_for_keysym (keys, XKB_KEY_ISO_Next_Group, keys->iso_next_group_combo); - - if (keys->iso_next_group_combo[0].len == 0) - return; - - keys->n_iso_next_group_combos = 1; - - if (g_str_equal (iso_next_group_option, "toggle") || - g_str_equal (iso_next_group_option, "lalt_toggle") || - g_str_equal (iso_next_group_option, "lwin_toggle") || - g_str_equal (iso_next_group_option, "rwin_toggle") || - g_str_equal (iso_next_group_option, "lshift_toggle") || - g_str_equal (iso_next_group_option, "rshift_toggle") || - g_str_equal (iso_next_group_option, "lctrl_toggle") || - g_str_equal (iso_next_group_option, "rctrl_toggle") || - g_str_equal (iso_next_group_option, "sclk_toggle") || - g_str_equal (iso_next_group_option, "menu_toggle") || - g_str_equal (iso_next_group_option, "caps_toggle")) - { - keys->iso_next_group_combo[0].mask = 0; - } - else if (g_str_equal (iso_next_group_option, "shift_caps_toggle") || - g_str_equal (iso_next_group_option, "shifts_toggle")) - { - keys->iso_next_group_combo[0].mask = ShiftMask; - } - else if (g_str_equal (iso_next_group_option, "alt_caps_toggle") || - g_str_equal (iso_next_group_option, "alt_space_toggle")) - { - keys->iso_next_group_combo[0].mask = Mod1Mask; - } - else if (g_str_equal (iso_next_group_option, "ctrl_shift_toggle") || - g_str_equal (iso_next_group_option, "lctrl_lshift_toggle") || - g_str_equal (iso_next_group_option, "rctrl_rshift_toggle")) - { - resolved_key_combo_copy (&keys->iso_next_group_combo[0], - &keys->iso_next_group_combo[1]); - - keys->iso_next_group_combo[0].mask = ShiftMask; - keys->iso_next_group_combo[1].mask = ControlMask; - keys->n_iso_next_group_combos = 2; - } - else if (g_str_equal (iso_next_group_option, "ctrl_alt_toggle")) - { - resolved_key_combo_copy (&keys->iso_next_group_combo[0], - &keys->iso_next_group_combo[1]); - - keys->iso_next_group_combo[0].mask = Mod1Mask; - keys->iso_next_group_combo[1].mask = ControlMask; - keys->n_iso_next_group_combos = 2; - } - else if (g_str_equal (iso_next_group_option, "alt_shift_toggle") || - g_str_equal (iso_next_group_option, "lalt_lshift_toggle")) - { - resolved_key_combo_copy (&keys->iso_next_group_combo[0], - &keys->iso_next_group_combo[1]); - - keys->iso_next_group_combo[0].mask = Mod1Mask; - keys->iso_next_group_combo[1].mask = ShiftMask; - keys->n_iso_next_group_combos = 2; - } - else - { - resolved_key_combo_reset (keys->iso_next_group_combo); - keys->n_iso_next_group_combos = 0; - } -} - -static void -devirtualize_modifiers (MetaKeyBindingManager *keys, - MetaVirtualModifier modifiers, - unsigned int *mask) -{ - *mask = 0; - - if (modifiers & META_VIRTUAL_SHIFT_MASK) - *mask |= ShiftMask; - if (modifiers & META_VIRTUAL_CONTROL_MASK) - *mask |= ControlMask; - if (modifiers & META_VIRTUAL_ALT_MASK) - *mask |= Mod1Mask; - if (modifiers & META_VIRTUAL_META_MASK) - *mask |= keys->meta_mask; - if (modifiers & META_VIRTUAL_HYPER_MASK) - *mask |= keys->hyper_mask; - if (modifiers & META_VIRTUAL_SUPER_MASK) - *mask |= keys->super_mask; - if (modifiers & META_VIRTUAL_MOD2_MASK) - *mask |= Mod2Mask; - if (modifiers & META_VIRTUAL_MOD3_MASK) - *mask |= Mod3Mask; - if (modifiers & META_VIRTUAL_MOD4_MASK) - *mask |= Mod4Mask; - if (modifiers & META_VIRTUAL_MOD5_MASK) - *mask |= Mod5Mask; -} - -static void -index_binding (MetaKeyBindingManager *keys, - MetaKeyBinding *binding) -{ - int i; - - for (i = 0; i < binding->resolved_combo.len; i++) - { - MetaKeyBinding *existing; - guint32 index_key; - - index_key = key_combo_key (&binding->resolved_combo, i); - - existing = g_hash_table_lookup (keys->key_bindings_index, - GINT_TO_POINTER (index_key)); - if (existing != NULL) - { - /* Overwrite already indexed keycodes only for the first - * keycode, i.e. we give those primary keycodes precedence - * over non-first ones. */ - if (i > 0) - continue; - - meta_warning ("Overwriting existing binding of keysym %x" - " with keysym %x (keycode %x).", - binding->combo.keysym, - existing->combo.keysym, - binding->resolved_combo.keycodes[i]); - } - - g_hash_table_replace (keys->key_bindings_index, - GINT_TO_POINTER (index_key), binding); - } -} - -static void -resolve_key_combo (MetaKeyBindingManager *keys, - MetaKeyCombo *combo, - MetaResolvedKeyCombo *resolved_combo) -{ - - resolved_key_combo_reset (resolved_combo); - - if (combo->keysym != 0) - { - get_keycodes_for_keysym (keys, combo->keysym, resolved_combo); - } - else if (combo->keycode != 0) - { - resolved_combo->keycodes = g_new0 (xkb_keycode_t, 1); - resolved_combo->keycodes[0] = combo->keycode; - resolved_combo->len = 1; - } - - devirtualize_modifiers (keys, combo->modifiers, &resolved_combo->mask); -} - -static void -binding_reload_combos_foreach (gpointer key, - gpointer value, - gpointer data) -{ - MetaKeyBindingManager *keys = data; - MetaKeyBinding *binding = value; - - resolve_key_combo (keys, &binding->combo, &binding->resolved_combo); - index_binding (keys, binding); -} - -typedef struct _FindLatinKeysymsState -{ - MetaKeyBindingKeyboardLayout *layout; - gboolean *required_keysyms_found; - int n_required_keysyms; -} FindLatinKeysymsState; - -static void -find_latin_keysym (struct xkb_keymap *keymap, - xkb_keycode_t key, - void *data) -{ - FindLatinKeysymsState *state = data; - int n_keysyms, i; - const xkb_keysym_t *keysyms; - - n_keysyms = xkb_keymap_key_get_syms_by_level (state->layout->keymap, - key, - state->layout->index, - 0, - &keysyms); - for (i = 0; i < n_keysyms; i++) - { - xkb_keysym_t keysym = keysyms[i]; - - if (keysym >= XKB_KEY_a && keysym <= XKB_KEY_z) - { - unsigned int keysym_index = keysym - XKB_KEY_a; - - if (!state->required_keysyms_found[keysym_index]) - { - state->required_keysyms_found[keysym_index] = TRUE; - state->n_required_keysyms--; - } - } - } -} - -static gboolean -needs_secondary_layout (MetaKeyBindingKeyboardLayout *layout) -{ - gboolean required_keysyms_found[] = { - FALSE, /* XKB_KEY_a */ - FALSE, /* XKB_KEY_b */ - FALSE, /* XKB_KEY_c */ - FALSE, /* XKB_KEY_d */ - FALSE, /* XKB_KEY_e */ - FALSE, /* XKB_KEY_f */ - FALSE, /* XKB_KEY_g */ - FALSE, /* XKB_KEY_h */ - FALSE, /* XKB_KEY_i */ - FALSE, /* XKB_KEY_j */ - FALSE, /* XKB_KEY_k */ - FALSE, /* XKB_KEY_l */ - FALSE, /* XKB_KEY_m */ - FALSE, /* XKB_KEY_n */ - FALSE, /* XKB_KEY_o */ - FALSE, /* XKB_KEY_p */ - FALSE, /* XKB_KEY_q */ - FALSE, /* XKB_KEY_r */ - FALSE, /* XKB_KEY_s */ - FALSE, /* XKB_KEY_t */ - FALSE, /* XKB_KEY_u */ - FALSE, /* XKB_KEY_v */ - FALSE, /* XKB_KEY_w */ - FALSE, /* XKB_KEY_x */ - FALSE, /* XKB_KEY_y */ - FALSE, /* XKB_KEY_z */ - }; - FindLatinKeysymsState state = { - .layout = layout, - .required_keysyms_found = required_keysyms_found, - .n_required_keysyms = G_N_ELEMENTS (required_keysyms_found), - }; - - xkb_keymap_key_for_each (layout->keymap, find_latin_keysym, &state); - - return state.n_required_keysyms != 0; -} - -static void -clear_active_keyboard_layouts (MetaKeyBindingManager *keys) -{ - unsigned int i; - - for (i = 0; i < G_N_ELEMENTS (keys->active_layouts); i++) - { - MetaKeyBindingKeyboardLayout *layout = &keys->active_layouts[i]; - - g_clear_pointer (&layout->keymap, xkb_keymap_unref); - *layout = (MetaKeyBindingKeyboardLayout) { 0 }; - } -} - -static MetaKeyBindingKeyboardLayout -create_us_layout (void) -{ - struct xkb_rule_names names; - struct xkb_keymap *keymap; - struct xkb_context *context; - - names.rules = DEFAULT_XKB_RULES_FILE; - names.model = DEFAULT_XKB_MODEL; - names.layout = "us"; - names.variant = ""; - names.options = ""; - - context = meta_create_xkb_context (); - keymap = xkb_keymap_new_from_names (context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS); - xkb_context_unref (context); - - return (MetaKeyBindingKeyboardLayout) { - .keymap = keymap, - .n_levels = calculate_n_layout_levels (keymap, 0), - }; -} - -static void -reload_active_keyboard_layouts (MetaKeyBindingManager *keys) -{ - struct xkb_keymap *keymap; - xkb_layout_index_t layout_index; - MetaKeyBindingKeyboardLayout primary_layout; - - clear_active_keyboard_layouts (keys); - - keymap = meta_backend_get_keymap (keys->backend); - layout_index = meta_backend_get_keymap_layout_group (keys->backend); - primary_layout = (MetaKeyBindingKeyboardLayout) { - .keymap = xkb_keymap_ref (keymap), - .index = layout_index, - .n_levels = calculate_n_layout_levels (keymap, layout_index), - }; - - keys->active_layouts[META_KEY_BINDING_PRIMARY_LAYOUT] = primary_layout; - - if (needs_secondary_layout (&primary_layout)) - { - MetaKeyBindingKeyboardLayout us_layout; - - us_layout = create_us_layout (); - keys->active_layouts[META_KEY_BINDING_SECONDARY_LAYOUT] = us_layout; - } -} - -static void -reload_combos (MetaKeyBindingManager *keys) -{ - g_hash_table_remove_all (keys->key_bindings_index); - - reload_active_keyboard_layouts (keys); - - resolve_key_combo (keys, - &keys->overlay_key_combo, - &keys->overlay_resolved_key_combo); - - resolve_key_combo (keys, - &keys->locate_pointer_key_combo, - &keys->locate_pointer_resolved_key_combo); - - reload_iso_next_group_combos (keys); - - g_hash_table_foreach (keys->key_bindings, binding_reload_combos_foreach, keys); -} - -static void -rebuild_binding_table (MetaKeyBindingManager *keys, - GList *prefs, - GList *grabs) -{ - MetaKeyBinding *b; - GList *p, *g; - - g_hash_table_remove_all (keys->key_bindings); - - p = prefs; - while (p) - { - MetaKeyPref *pref = (MetaKeyPref*)p->data; - GSList *tmp = pref->combos; - - while (tmp) - { - MetaKeyCombo *combo = tmp->data; - - if (combo && (combo->keysym != None || combo->keycode != 0)) - { - MetaKeyHandler *handler = HANDLER (pref->name); - - b = g_new0 (MetaKeyBinding, 1); - b->name = pref->name; - b->handler = handler; - b->flags = handler->flags; - b->combo = *combo; - - g_hash_table_add (keys->key_bindings, b); - } - - tmp = tmp->next; - } - - p = p->next; - } - - g = grabs; - while (g) - { - MetaKeyGrab *grab = (MetaKeyGrab*)g->data; - if (grab->combo.keysym != None || grab->combo.keycode != 0) - { - MetaKeyHandler *handler = HANDLER ("external-grab"); - - b = g_new0 (MetaKeyBinding, 1); - b->name = grab->name; - b->handler = handler; - b->flags = grab->flags; - b->combo = grab->combo; - - g_hash_table_add (keys->key_bindings, b); - } - - g = g->next; - } - - meta_topic (META_DEBUG_KEYBINDINGS, - " %d bindings in table", - g_hash_table_size (keys->key_bindings)); -} - -static void -rebuild_key_binding_table (MetaKeyBindingManager *keys) -{ - GList *prefs, *grabs; - - meta_topic (META_DEBUG_KEYBINDINGS, - "Rebuilding key binding table from preferences"); - - prefs = meta_prefs_get_keybindings (); - grabs = g_hash_table_get_values (external_grabs); - rebuild_binding_table (keys, prefs, grabs); - g_list_free (prefs); - g_list_free (grabs); -} - -static void -rebuild_special_bindings (MetaKeyBindingManager *keys) -{ - MetaKeyCombo combo; - - meta_prefs_get_overlay_binding (&combo); - keys->overlay_key_combo = combo; - - meta_prefs_get_locate_pointer_binding (&combo); - keys->locate_pointer_key_combo = combo; -} - -static void -ungrab_key_bindings (MetaDisplay *display) -{ - GSList *windows, *l; - - if (display->x11_display) - meta_x11_display_ungrab_keys (display->x11_display); - - windows = meta_display_list_windows (display, META_LIST_DEFAULT); - for (l = windows; l; l = l->next) - { - MetaWindow *w = l->data; - meta_window_ungrab_keys (w); - } - - g_slist_free (windows); -} - -static void -grab_key_bindings (MetaDisplay *display) -{ - GSList *windows, *l; - - if (display->x11_display) - meta_x11_display_grab_keys (display->x11_display); - - windows = meta_display_list_windows (display, META_LIST_DEFAULT); - for (l = windows; l; l = l->next) - { - MetaWindow *w = l->data; - meta_window_grab_keys (w); - } - - g_slist_free (windows); -} - -static MetaKeyBinding * -get_keybinding (MetaKeyBindingManager *keys, - MetaResolvedKeyCombo *resolved_combo) -{ - MetaKeyBinding *binding = NULL; - int i; - - for (i = 0; i < resolved_combo->len; i++) - { - guint32 key; - - key = key_combo_key (resolved_combo, i); - binding = g_hash_table_lookup (keys->key_bindings_index, - GINT_TO_POINTER (key)); - - if (binding != NULL) - break; - } - - return binding; -} - -static guint -next_dynamic_keybinding_action (void) -{ - static guint num_dynamic_bindings = 0; - return META_KEYBINDING_ACTION_LAST + (++num_dynamic_bindings); -} - -static gboolean -add_keybinding_internal (MetaDisplay *display, - const char *name, - GSettings *settings, - MetaKeyBindingFlags flags, - MetaKeyBindingAction action, - MetaKeyHandlerFunc func, - int data, - gpointer user_data, - GDestroyNotify free_data) -{ - MetaKeyHandler *handler; - - if (!meta_prefs_add_keybinding (name, settings, action, flags)) - return FALSE; - - handler = g_new0 (MetaKeyHandler, 1); - handler->name = g_strdup (name); - handler->func = func; - handler->default_func = func; - handler->data = data; - handler->flags = flags; - handler->user_data = user_data; - handler->user_data_free_func = free_data; - - g_hash_table_insert (key_handlers, g_strdup (name), handler); - - return TRUE; -} - -static gboolean -add_builtin_keybinding (MetaDisplay *display, - const char *name, - GSettings *settings, - MetaKeyBindingFlags flags, - MetaKeyBindingAction action, - MetaKeyHandlerFunc handler, - int handler_arg) -{ - return add_keybinding_internal (display, name, settings, - flags | META_KEY_BINDING_BUILTIN, - action, handler, handler_arg, NULL, NULL); -} - -/** - * meta_display_add_keybinding: - * @display: a #MetaDisplay - * @name: the binding's name - * @settings: the #GSettings object where @name is stored - * @flags: flags to specify binding details - * @handler: function to run when the keybinding is invoked - * @user_data: the data to pass to @handler - * @free_data: function to free @user_data - * - * Add a keybinding at runtime. The key @name in @schema needs to be of - * type %G_VARIANT_TYPE_STRING_ARRAY, with each string describing a - * keybinding in the form of "<Control>a" or "<Shift><Alt>F1". The parser - * is fairly liberal and allows lower or upper case, and also abbreviations - * such as "<Ctl>" and "<Ctrl>". If the key is set to the empty list or a - * list with a single element of either "" or "disabled", the keybinding is - * disabled. - * - * Use meta_display_remove_keybinding() to remove the binding. - * - * Returns: the corresponding keybinding action if the keybinding was - * added successfully, otherwise %META_KEYBINDING_ACTION_NONE - */ -guint -meta_display_add_keybinding (MetaDisplay *display, - const char *name, - GSettings *settings, - MetaKeyBindingFlags flags, - MetaKeyHandlerFunc handler, - gpointer user_data, - GDestroyNotify free_data) -{ - guint new_action = next_dynamic_keybinding_action (); - - if (!add_keybinding_internal (display, name, settings, flags, new_action, - handler, 0, user_data, free_data)) - return META_KEYBINDING_ACTION_NONE; - - return new_action; -} - -/** - * meta_display_remove_keybinding: - * @display: the #MetaDisplay - * @name: name of the keybinding to remove - * - * Remove keybinding @name; the function will fail if @name is not a known - * keybinding or has not been added with meta_display_add_keybinding(). - * - * Returns: %TRUE if the binding has been removed successfully, - * otherwise %FALSE - */ -gboolean -meta_display_remove_keybinding (MetaDisplay *display, - const char *name) -{ - if (!meta_prefs_remove_keybinding (name)) - return FALSE; - - g_hash_table_remove (key_handlers, name); - - return TRUE; -} - -static guint -get_keybinding_action (MetaKeyBindingManager *keys, - MetaResolvedKeyCombo *resolved_combo) -{ - MetaKeyBinding *binding; - - /* This is much more vague than the MetaDisplay::overlay-key signal, - * which is only emitted if the overlay-key is the only key pressed; - * as this method is primarily intended for plugins to allow processing - * of mutter keybindings while holding a grab, the overlay-key-only-pressed - * tracking is left to the plugin here. - */ - if (resolved_key_combo_intersect (resolved_combo, - &keys->overlay_resolved_key_combo)) - return META_KEYBINDING_ACTION_OVERLAY_KEY; - - if (resolved_key_combo_intersect (resolved_combo, - &keys->locate_pointer_resolved_key_combo)) - return META_KEYBINDING_ACTION_LOCATE_POINTER_KEY; - - binding = get_keybinding (keys, resolved_combo); - if (binding) - { - MetaKeyGrab *grab = g_hash_table_lookup (external_grabs, binding->name); - if (grab) - return grab->action; - else - return (guint) meta_prefs_get_keybinding_action (binding->name); - } - else - { - return META_KEYBINDING_ACTION_NONE; - } -} - -static xkb_mod_mask_t -mask_from_event_params (MetaKeyBindingManager *keys, - unsigned long mask) -{ - return mask & 0xff & ~keys->ignored_modifier_mask; -} - -/** - * meta_display_get_keybinding_action: - * @display: A #MetaDisplay - * @keycode: Raw keycode - * @mask: Event mask - * - * Get the keybinding action bound to @keycode. Builtin keybindings - * have a fixed associated #MetaKeyBindingAction, for bindings added - * dynamically the function will return the keybinding action - * meta_display_add_keybinding() returns on registration. - * - * Returns: The action that should be taken for the given key, or - * %META_KEYBINDING_ACTION_NONE. - */ -guint -meta_display_get_keybinding_action (MetaDisplay *display, - unsigned int keycode, - unsigned long mask) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - xkb_keycode_t code = (xkb_keycode_t) keycode; - MetaResolvedKeyCombo resolved_combo = { &code, 1 }; - - resolved_combo.mask = mask_from_event_params (keys, mask); - return get_keybinding_action (keys, &resolved_combo); -} - -static void -reload_keybindings (MetaDisplay *display) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - - ungrab_key_bindings (display); - - /* Deciphering the modmap depends on the loaded keysyms to find out - * what modifiers is Super and so forth, so we need to reload it - * even when only the keymap changes */ - reload_modmap (keys); - - reload_combos (keys); - - grab_key_bindings (display); -} - -static GArray * -calc_grab_modifiers (MetaKeyBindingManager *keys, - unsigned int modmask) -{ - unsigned int ignored_mask; - XIGrabModifiers mods; - GArray *mods_array = g_array_new (FALSE, TRUE, sizeof (XIGrabModifiers)); - - /* The X server crashes if XIAnyModifier gets passed in with any - other bits. It doesn't make sense to ask for a grab of - XIAnyModifier plus other bits anyway so we avoid that. */ - if (modmask & XIAnyModifier) - { - mods = (XIGrabModifiers) { XIAnyModifier, 0 }; - g_array_append_val (mods_array, mods); - return mods_array; - } - - mods = (XIGrabModifiers) { modmask, 0 }; - g_array_append_val (mods_array, mods); - - for (ignored_mask = 1; - ignored_mask <= keys->ignored_modifier_mask; - ++ignored_mask) - { - if (ignored_mask & keys->ignored_modifier_mask) - { - mods = (XIGrabModifiers) { modmask | ignored_mask, 0 }; - g_array_append_val (mods_array, mods); - } - } - - return mods_array; -} - -static void -meta_change_button_grab (MetaKeyBindingManager *keys, - Window xwindow, - gboolean grab, - gboolean sync, - int button, - int modmask) -{ - if (meta_is_wayland_compositor ()) - return; - - MetaBackendX11 *backend = META_BACKEND_X11 (keys->backend); - Display *xdisplay = meta_backend_x11_get_xdisplay (backend); - - unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; - XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; - GArray *mods; - - XISetMask (mask.mask, XI_ButtonPress); - XISetMask (mask.mask, XI_ButtonRelease); - XISetMask (mask.mask, XI_Motion); - - mods = calc_grab_modifiers (keys, modmask); - - /* GrabModeSync means freeze until XAllowEvents */ - if (grab) - XIGrabButton (xdisplay, - META_VIRTUAL_CORE_POINTER_ID, - button, xwindow, None, - sync ? XIGrabModeSync : XIGrabModeAsync, - XIGrabModeAsync, False, - &mask, mods->len, (XIGrabModifiers *)mods->data); - else - XIUngrabButton (xdisplay, - META_VIRTUAL_CORE_POINTER_ID, - button, xwindow, mods->len, (XIGrabModifiers *)mods->data); - - g_array_free (mods, TRUE); -} - -ClutterModifierType -meta_display_get_compositor_modifiers (MetaDisplay *display) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - return keys->window_grab_modifiers; -} - -static void -meta_change_buttons_grab (MetaKeyBindingManager *keys, - Window xwindow, - gboolean grab, - gboolean sync, - int modmask) -{ -#define MAX_BUTTON 3 - - int i; - for (i = 1; i <= MAX_BUTTON; i++) - meta_change_button_grab (keys, xwindow, grab, sync, i, modmask); -} - -void -meta_display_grab_window_buttons (MetaDisplay *display, - Window xwindow) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - - /* Grab Alt + button1 for moving window. - * Grab Alt + button2 for resizing window. - * Grab Alt + button3 for popping up window menu. - * Grab Alt + Shift + button1 for snap-moving window. - */ - meta_verbose ("Grabbing window buttons for 0x%lx", xwindow); - - /* FIXME If we ignored errors here instead of spewing, we could - * put one big error trap around the loop and avoid a bunch of - * XSync() - */ - - if (keys->window_grab_modifiers != 0) - { - meta_change_buttons_grab (keys, xwindow, TRUE, FALSE, - keys->window_grab_modifiers); - - /* In addition to grabbing Alt+Button1 for moving the window, - * grab Alt+Shift+Button1 for snap-moving the window. See bug - * 112478. Unfortunately, this doesn't work with - * Shift+Alt+Button1 for some reason; so at least part of the - * order still matters, which sucks (please FIXME). - */ - meta_change_button_grab (keys, xwindow, - TRUE, - FALSE, - 1, keys->window_grab_modifiers | ShiftMask); - } -} - -void -meta_display_ungrab_window_buttons (MetaDisplay *display, - Window xwindow) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - - if (keys->window_grab_modifiers == 0) - return; - - meta_change_buttons_grab (keys, xwindow, FALSE, FALSE, - keys->window_grab_modifiers); -} - -static void -update_window_grab_modifiers (MetaDisplay *display) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - MetaVirtualModifier virtual_mods; - unsigned int mods; - - virtual_mods = meta_prefs_get_mouse_button_mods (); - devirtualize_modifiers (keys, virtual_mods, &mods); - - if (keys->window_grab_modifiers != mods) - { - keys->window_grab_modifiers = mods; - g_object_notify (G_OBJECT (display), "compositor-modifiers"); - } -} - -void -meta_display_grab_focus_window_button (MetaDisplay *display, - MetaWindow *window) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - - /* Grab button 1 for activating unfocused windows */ - meta_verbose ("Grabbing unfocused window buttons for %s", window->desc); - - if (window->have_focus_click_grab) - { - meta_verbose (" (well, not grabbing since we already have the grab)"); - return; - } - - /* FIXME If we ignored errors here instead of spewing, we could - * put one big error trap around the loop and avoid a bunch of - * XSync() - */ - - meta_change_buttons_grab (keys, window->xwindow, TRUE, TRUE, XIAnyModifier); - window->have_focus_click_grab = TRUE; -} - -void -meta_display_ungrab_focus_window_button (MetaDisplay *display, - MetaWindow *window) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - - meta_verbose ("Ungrabbing unfocused window buttons for %s", window->desc); - - if (!window->have_focus_click_grab) - return; - - meta_change_buttons_grab (keys, window->xwindow, FALSE, FALSE, XIAnyModifier); - window->have_focus_click_grab = FALSE; -} - -static void -prefs_changed_callback (MetaPreference pref, - void *data) -{ - MetaDisplay *display = data; - MetaKeyBindingManager *keys = &display->key_binding_manager; - - switch (pref) - { - case META_PREF_LOCATE_POINTER: - maybe_update_locate_pointer_keygrab (display, - meta_prefs_is_locate_pointer_enabled()); - break; - case META_PREF_KEYBINDINGS: - ungrab_key_bindings (display); - rebuild_key_binding_table (keys); - rebuild_special_bindings (keys); - reload_combos (keys); - grab_key_bindings (display); - break; - case META_PREF_MOUSE_BUTTON_MODS: - { - GSList *windows, *l; - windows = meta_display_list_windows (display, META_LIST_DEFAULT); - - for (l = windows; l; l = l->next) - { - MetaWindow *w = l->data; - meta_display_ungrab_window_buttons (display, w->xwindow); - } - - update_window_grab_modifiers (display); - - for (l = windows; l; l = l->next) - { - MetaWindow *w = l->data; - if (w->type != META_WINDOW_DOCK) - meta_display_grab_window_buttons (display, w->xwindow); - } - - g_slist_free (windows); - } - default: - break; - } -} - - -void -meta_display_shutdown_keys (MetaDisplay *display) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - - meta_prefs_remove_listener (prefs_changed_callback, display); - - g_hash_table_destroy (keys->key_bindings_index); - g_hash_table_destroy (keys->key_bindings); - - clear_active_keyboard_layouts (keys); -} - -/* Grab/ungrab, ignoring all annoying modifiers like NumLock etc. */ -static void -meta_change_keygrab (MetaKeyBindingManager *keys, - Window xwindow, - gboolean grab, - MetaResolvedKeyCombo *resolved_combo) -{ - unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; - XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; - - XISetMask (mask.mask, XI_KeyPress); - XISetMask (mask.mask, XI_KeyRelease); - - if (meta_is_wayland_compositor ()) - return; - - MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); - Display *xdisplay = meta_backend_x11_get_xdisplay (backend); - GArray *mods; - int i; - - /* Grab keycode/modmask, together with - * all combinations of ignored modifiers. - * X provides no better way to do this. - */ - - mods = calc_grab_modifiers (keys, resolved_combo->mask); - - for (i = 0; i < resolved_combo->len; i++) - { - xkb_keycode_t keycode = resolved_combo->keycodes[i]; - - meta_topic (META_DEBUG_KEYBINDINGS, - "%s keybinding keycode %d mask 0x%x on 0x%lx", - grab ? "Grabbing" : "Ungrabbing", - keycode, resolved_combo->mask, xwindow); - - if (grab) - XIGrabKeycode (xdisplay, - META_VIRTUAL_CORE_KEYBOARD_ID, - keycode, xwindow, - XIGrabModeSync, XIGrabModeAsync, - False, &mask, mods->len, (XIGrabModifiers *)mods->data); - else - XIUngrabKeycode (xdisplay, - META_VIRTUAL_CORE_KEYBOARD_ID, - keycode, xwindow, - mods->len, (XIGrabModifiers *)mods->data); - } - - g_array_free (mods, TRUE); -} - -typedef struct -{ - MetaKeyBindingManager *keys; - Window xwindow; - gboolean only_per_window; - gboolean grab; -} ChangeKeygrabData; - -static void -change_keygrab_foreach (gpointer key, - gpointer value, - gpointer user_data) -{ - ChangeKeygrabData *data = user_data; - MetaKeyBinding *binding = value; - gboolean binding_is_per_window = (binding->flags & META_KEY_BINDING_PER_WINDOW) != 0; - - if (data->only_per_window != binding_is_per_window) - return; - - /* Ignore the key bindings marked as META_KEY_BINDING_NO_AUTO_GRAB, - * those are handled separately - */ - if (binding->flags & META_KEY_BINDING_NO_AUTO_GRAB) - return; - - if (binding->resolved_combo.len == 0) - return; - - meta_change_keygrab (data->keys, data->xwindow, data->grab, &binding->resolved_combo); -} - -static void -change_binding_keygrabs (MetaKeyBindingManager *keys, - Window xwindow, - gboolean only_per_window, - gboolean grab) -{ - ChangeKeygrabData data; - - data.keys = keys; - data.xwindow = xwindow; - data.only_per_window = only_per_window; - data.grab = grab; - - g_hash_table_foreach (keys->key_bindings, change_keygrab_foreach, &data); -} - -static void -maybe_update_locate_pointer_keygrab (MetaDisplay *display, - gboolean grab) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - - if (!display->x11_display) - return; - - if (keys->locate_pointer_resolved_key_combo.len != 0) - meta_change_keygrab (keys, display->x11_display->xroot, - (!!grab & !!meta_prefs_is_locate_pointer_enabled()), - &keys->locate_pointer_resolved_key_combo); -} - -static void -meta_x11_display_change_keygrabs (MetaX11Display *x11_display, - gboolean grab) -{ - MetaKeyBindingManager *keys = &x11_display->display->key_binding_manager; - int i; - - if (keys->overlay_resolved_key_combo.len != 0) - meta_change_keygrab (keys, x11_display->xroot, - grab, &keys->overlay_resolved_key_combo); - - maybe_update_locate_pointer_keygrab (x11_display->display, grab); - - for (i = 0; i < keys->n_iso_next_group_combos; i++) - meta_change_keygrab (keys, x11_display->xroot, - grab, &keys->iso_next_group_combo[i]); - - change_binding_keygrabs (keys, x11_display->xroot, - FALSE, grab); -} - -void -meta_x11_display_grab_keys (MetaX11Display *x11_display) -{ - if (x11_display->keys_grabbed) - return; - - meta_x11_display_change_keygrabs (x11_display, TRUE); - - x11_display->keys_grabbed = TRUE; -} - -void -meta_x11_display_ungrab_keys (MetaX11Display *x11_display) -{ - if (!x11_display->keys_grabbed) - return; - - meta_x11_display_change_keygrabs (x11_display, FALSE); - - x11_display->keys_grabbed = FALSE; -} - -static void -change_window_keygrabs (MetaKeyBindingManager *keys, - Window xwindow, - gboolean grab) -{ - change_binding_keygrabs (keys, xwindow, TRUE, grab); -} - -void -meta_window_grab_keys (MetaWindow *window) -{ - MetaDisplay *display = window->display; - MetaKeyBindingManager *keys = &display->key_binding_manager; - - if (meta_is_wayland_compositor ()) - return; - if (window->all_keys_grabbed) - return; - - if (window->type == META_WINDOW_DOCK - || window->override_redirect) - { - if (window->keys_grabbed) - change_window_keygrabs (keys, window->xwindow, FALSE); - window->keys_grabbed = FALSE; - return; - } - - if (window->keys_grabbed) - { - if (window->frame && !window->grab_on_frame) - change_window_keygrabs (keys, window->xwindow, FALSE); - else if (window->frame == NULL && - window->grab_on_frame) - ; /* continue to regrab on client window */ - else - return; /* already all good */ - } - - change_window_keygrabs (keys, - meta_window_x11_get_toplevel_xwindow (window), - TRUE); - - window->keys_grabbed = TRUE; - window->grab_on_frame = window->frame != NULL; -} - -void -meta_window_ungrab_keys (MetaWindow *window) -{ - if (!meta_is_wayland_compositor () && window->keys_grabbed) - { - MetaDisplay *display = window->display; - MetaKeyBindingManager *keys = &display->key_binding_manager; - - if (window->grab_on_frame && - window->frame != NULL) - change_window_keygrabs (keys, window->frame->xwindow, FALSE); - else if (!window->grab_on_frame) - change_window_keygrabs (keys, window->xwindow, FALSE); - - window->keys_grabbed = FALSE; - } -} - -static void -handle_external_grab (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer user_data) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - guint action = get_keybinding_action (keys, &binding->resolved_combo); - meta_display_accelerator_activate (display, action, event); -} - - -guint -meta_display_grab_accelerator (MetaDisplay *display, - const char *accelerator, - MetaKeyBindingFlags flags) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - MetaKeyBinding *binding; - MetaKeyGrab *grab; - MetaKeyCombo combo = { 0 }; - MetaResolvedKeyCombo resolved_combo = { NULL, 0 }; - - if (!meta_parse_accelerator (accelerator, &combo)) - { - meta_topic (META_DEBUG_KEYBINDINGS, - "Failed to parse accelerator"); - meta_warning ("\"%s\" is not a valid accelerator", accelerator); - - return META_KEYBINDING_ACTION_NONE; - } - - resolve_key_combo (keys, &combo, &resolved_combo); - - if (resolved_combo.len == 0) - return META_KEYBINDING_ACTION_NONE; - - if (get_keybinding (keys, &resolved_combo)) - { - resolved_key_combo_reset (&resolved_combo); - return META_KEYBINDING_ACTION_NONE; - } - - if (!meta_is_wayland_compositor ()) - { - meta_change_keygrab (keys, display->x11_display->xroot, - TRUE, &resolved_combo); - } - - grab = g_new0 (MetaKeyGrab, 1); - grab->action = next_dynamic_keybinding_action (); - grab->name = meta_external_binding_name_for_action (grab->action); - grab->combo = combo; - grab->flags = flags; - - g_hash_table_insert (external_grabs, grab->name, grab); - - binding = g_new0 (MetaKeyBinding, 1); - binding->name = grab->name; - binding->handler = HANDLER ("external-grab"); - binding->combo = combo; - binding->resolved_combo = resolved_combo; - binding->flags = flags; - - g_hash_table_add (keys->key_bindings, binding); - index_binding (keys, binding); - - return grab->action; -} - -gboolean -meta_display_ungrab_accelerator (MetaDisplay *display, - guint action) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - MetaKeyBinding *binding; - MetaKeyGrab *grab; - g_autofree char *key = NULL; - MetaResolvedKeyCombo resolved_combo = { NULL, 0 }; - - g_return_val_if_fail (action != META_KEYBINDING_ACTION_NONE, FALSE); - - key = meta_external_binding_name_for_action (action); - grab = g_hash_table_lookup (external_grabs, key); - if (!grab) - return FALSE; - - resolve_key_combo (keys, &grab->combo, &resolved_combo); - binding = get_keybinding (keys, &resolved_combo); - if (binding) - { - int i; - - if (!meta_is_wayland_compositor ()) - { - meta_change_keygrab (keys, display->x11_display->xroot, - FALSE, &binding->resolved_combo); - } - - for (i = 0; i < binding->resolved_combo.len; i++) - { - guint32 index_key = key_combo_key (&binding->resolved_combo, i); - g_hash_table_remove (keys->key_bindings_index, GINT_TO_POINTER (index_key)); - } - - g_hash_table_remove (keys->key_bindings, binding); - } - - g_hash_table_remove (external_grabs, key); - resolved_key_combo_reset (&resolved_combo); - - return TRUE; -} - -static gboolean -grab_keyboard (Window xwindow, - guint32 timestamp, - int grab_mode) -{ - int grab_status; - - unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; - XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; - - XISetMask (mask.mask, XI_KeyPress); - XISetMask (mask.mask, XI_KeyRelease); - - if (meta_is_wayland_compositor ()) - return TRUE; - - /* Grab the keyboard, so we get key releases and all key - * presses - */ - - MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); - Display *xdisplay = meta_backend_x11_get_xdisplay (backend); - - /* Strictly, we only need to set grab_mode on the keyboard device - * while the pointer should always be XIGrabModeAsync. Unfortunately - * there is a bug in the X server, only fixed (link below) in 1.15, - * which swaps these arguments for keyboard devices. As such, we set - * both the device and the paired device mode which works around - * that bug and also works on fixed X servers. - * - * http://cgit.freedesktop.org/xorg/xserver/commit/?id=9003399708936481083424b4ff8f18a16b88b7b3 - */ - grab_status = XIGrabDevice (xdisplay, - META_VIRTUAL_CORE_KEYBOARD_ID, - xwindow, - timestamp, - None, - grab_mode, grab_mode, - False, /* owner_events */ - &mask); - - return (grab_status == Success); -} - -static void -ungrab_keyboard (guint32 timestamp) -{ - if (meta_is_wayland_compositor ()) - return; - - MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); - Display *xdisplay = meta_backend_x11_get_xdisplay (backend); - - XIUngrabDevice (xdisplay, META_VIRTUAL_CORE_KEYBOARD_ID, timestamp); -} - -gboolean -meta_window_grab_all_keys (MetaWindow *window, - guint32 timestamp) -{ - Window grabwindow; - gboolean retval = TRUE; - - if (window->all_keys_grabbed) - return FALSE; - - if (window->keys_grabbed) - meta_window_ungrab_keys (window); - - /* Make sure the window is focused, otherwise the grab - * won't do a lot of good. - */ - meta_topic (META_DEBUG_FOCUS, - "Focusing %s because we're grabbing all its keys", - window->desc); - meta_window_focus (window, timestamp); - - if (!meta_is_wayland_compositor ()) - { - grabwindow = meta_window_x11_get_toplevel_xwindow (window); - - meta_topic (META_DEBUG_KEYBINDINGS, - "Grabbing all keys on window %s", window->desc); - retval = grab_keyboard (grabwindow, timestamp, XIGrabModeAsync); - } - if (retval) - { - window->keys_grabbed = FALSE; - window->all_keys_grabbed = TRUE; - window->grab_on_frame = window->frame != NULL; - } - - return retval; -} - -void -meta_window_ungrab_all_keys (MetaWindow *window, - guint32 timestamp) -{ - if (window->all_keys_grabbed) - { - if (!meta_is_wayland_compositor()) - ungrab_keyboard (timestamp); - - window->grab_on_frame = FALSE; - window->all_keys_grabbed = FALSE; - window->keys_grabbed = FALSE; - - /* Re-establish our standard bindings */ - meta_window_grab_keys (window); - } -} - -void -meta_display_freeze_keyboard (MetaDisplay *display, guint32 timestamp) -{ - MetaBackend *backend = meta_get_backend (); - - if (!META_IS_BACKEND_X11 (backend)) - return; - - Window window = meta_backend_x11_get_xwindow (META_BACKEND_X11 (backend)); - grab_keyboard (window, timestamp, XIGrabModeSync); -} - -void -meta_display_ungrab_keyboard (MetaDisplay *display, guint32 timestamp) -{ - ungrab_keyboard (timestamp); -} - -void -meta_display_unfreeze_keyboard (MetaDisplay *display, guint32 timestamp) -{ - MetaBackend *backend = meta_get_backend (); - - if (!META_IS_BACKEND_X11 (backend)) - return; - - Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); - - XIAllowEvents (xdisplay, META_VIRTUAL_CORE_KEYBOARD_ID, - XIAsyncDevice, timestamp); - /* We shouldn't need to unfreeze the pointer device here, however we - * have to, due to the workaround we do in grab_keyboard(). - */ - XIAllowEvents (xdisplay, META_VIRTUAL_CORE_POINTER_ID, - XIAsyncDevice, timestamp); -} - -static gboolean -is_modifier (xkb_keysym_t keysym) -{ - switch (keysym) - { - case XKB_KEY_Shift_L: - case XKB_KEY_Shift_R: - case XKB_KEY_Control_L: - case XKB_KEY_Control_R: - case XKB_KEY_Caps_Lock: - case XKB_KEY_Shift_Lock: - case XKB_KEY_Meta_L: - case XKB_KEY_Meta_R: - case XKB_KEY_Alt_L: - case XKB_KEY_Alt_R: - case XKB_KEY_Super_L: - case XKB_KEY_Super_R: - case XKB_KEY_Hyper_L: - case XKB_KEY_Hyper_R: - return TRUE; - default: - return FALSE; - } -} - -static void -invoke_handler (MetaDisplay *display, - MetaKeyHandler *handler, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding) -{ - if (handler->func) - (* handler->func) (display, - handler->flags & META_KEY_BINDING_PER_WINDOW ? - window : NULL, - event, - binding, - handler->user_data); - else - (* handler->default_func) (display, - handler->flags & META_KEY_BINDING_PER_WINDOW ? - window: NULL, - event, - binding, - NULL); -} - -static gboolean -meta_key_binding_has_handler_func (MetaKeyBinding *binding) -{ - return (!!binding->handler->func || !!binding->handler->default_func); -} - -static gboolean -process_event (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - xkb_keycode_t keycode = (xkb_keycode_t) event->hardware_keycode; - MetaResolvedKeyCombo resolved_combo = { &keycode, 1 }; - MetaKeyBinding *binding; - - /* we used to have release-based bindings but no longer. */ - if (event->type == CLUTTER_KEY_RELEASE) - return FALSE; - - resolved_combo.mask = mask_from_event_params (keys, event->modifier_state); - - binding = get_keybinding (keys, &resolved_combo); - - if (!binding || - (!window && binding->flags & META_KEY_BINDING_PER_WINDOW)) - goto not_found; - - if (binding->handler == NULL) - meta_bug ("Binding %s has no handler", binding->name); - - if (!meta_key_binding_has_handler_func (binding)) - goto not_found; - - if (display->focus_window && - !(binding->handler->flags & META_KEY_BINDING_NON_MASKABLE)) - { - ClutterInputDevice *source; - - source = clutter_event_get_source_device ((ClutterEvent *) event); - if (meta_window_shortcuts_inhibited (display->focus_window, source)) - goto not_found; - } - - /* If the compositor filtered out the keybindings, that - * means they don't want the binding to trigger, so we do - * the same thing as if the binding didn't exist. */ - if (meta_compositor_filter_keybinding (display->compositor, binding)) - goto not_found; - - if (event->flags & CLUTTER_EVENT_FLAG_REPEATED && - binding->flags & META_KEY_BINDING_IGNORE_AUTOREPEAT) - { - meta_topic (META_DEBUG_KEYBINDINGS, - "Ignore autorepeat for handler %s", - binding->name); - return TRUE; - } - - meta_topic (META_DEBUG_KEYBINDINGS, - "Running handler for %s", - binding->name); - - /* Global keybindings count as a let-the-terminal-lose-focus - * due to new window mapping until the user starts - * interacting with the terminal again. - */ - display->allow_terminal_deactivation = TRUE; - - invoke_handler (display, binding->handler, window, event, binding); - - return TRUE; - - not_found: - meta_topic (META_DEBUG_KEYBINDINGS, - "No handler found for this event in this binding table"); - return FALSE; -} - -static gboolean -process_special_modifier_key (MetaDisplay *display, - ClutterKeyEvent *event, - MetaWindow *window, - gboolean *modifier_press_only, - MetaResolvedKeyCombo *resolved_key_combo, - GFunc trigger_callback) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - MetaBackend *backend = keys->backend; - Display *xdisplay; - - if (META_IS_BACKEND_X11 (backend)) - xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); - else - xdisplay = NULL; - - if (*modifier_press_only) - { - if (! resolved_key_combo_has_keycode (resolved_key_combo, - event->hardware_keycode)) - { - *modifier_press_only = FALSE; - - /* If this is a wayland session, we can avoid the shenanigans - * about passive grabs below, and let the event continue to - * be processed through the regular paths. - */ - if (!xdisplay) - return FALSE; - - /* OK, the user hit modifier+key rather than pressing and - * releasing the modifier key alone. We want to handle the key - * sequence "normally". Unfortunately, using - * XAllowEvents(..., ReplayKeyboard, ...) doesn't quite - * work, since global keybindings won't be activated ("this - * time, however, the function ignores any passive grabs at - * above (toward the root of) the grab_window of the grab - * just released.") So, we first explicitly check for one of - * our global keybindings, and if not found, we then replay - * the event. Other clients with global grabs will be out of - * luck. - */ - if (process_event (display, window, event)) - { - /* As normally, after we've handled a global key - * binding, we unfreeze the keyboard but keep the grab - * (this is important for something like cycling - * windows */ - - if (xdisplay) - XIAllowEvents (xdisplay, - meta_input_device_x11_get_device_id (event->device), - XIAsyncDevice, event->time); - } - else - { - /* Replay the event so it gets delivered to our - * per-window key bindings or to the application */ - if (xdisplay) - XIAllowEvents (xdisplay, - meta_input_device_x11_get_device_id (event->device), - XIReplayDevice, event->time); - } - } - else if (event->type == CLUTTER_KEY_RELEASE) - { - MetaKeyBinding *binding; - - *modifier_press_only = FALSE; - - /* We want to unfreeze events, but keep the grab so that if the user - * starts typing into the overlay we get all the keys */ - if (xdisplay) - XIAllowEvents (xdisplay, - meta_input_device_x11_get_device_id (event->device), - XIAsyncDevice, event->time); - - binding = get_keybinding (keys, resolved_key_combo); - if (binding && - meta_compositor_filter_keybinding (display->compositor, binding)) - return TRUE; - trigger_callback (display, NULL); - } - else - { - /* In some rare race condition, mutter might not receive the Super_L - * KeyRelease event because: - * - the compositor might end the modal mode and call XIUngrabDevice - * while the key is still down - * - passive grabs are only activated on KeyPress and not KeyRelease. - * - * In this case, modifier_press_only might be wrong. - * Mutter still ought to acknowledge events, otherwise the X server - * will not send the next events. - * - * https://bugzilla.gnome.org/show_bug.cgi?id=666101 - */ - if (xdisplay) - XIAllowEvents (xdisplay, - meta_input_device_x11_get_device_id (event->device), - XIAsyncDevice, event->time); - } - - return TRUE; - } - else if (event->type == CLUTTER_KEY_PRESS && - ((event->modifier_state & ~(IGNORED_MODIFIERS)) & CLUTTER_MODIFIER_MASK) == 0 && - resolved_key_combo_has_keycode (resolved_key_combo, - event->hardware_keycode)) - { - *modifier_press_only = TRUE; - /* We keep the keyboard frozen - this allows us to use ReplayKeyboard - * on the next event if it's not the release of the modifier key */ - if (xdisplay) - XIAllowEvents (xdisplay, - meta_input_device_x11_get_device_id (event->device), - XISyncDevice, event->time); - - return TRUE; - } - else - return FALSE; -} - - -static gboolean -process_overlay_key (MetaDisplay *display, - ClutterKeyEvent *event, - MetaWindow *window) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - - if (display->focus_window && !keys->overlay_key_only_pressed) - { - ClutterInputDevice *source; - - source = clutter_event_get_source_device ((ClutterEvent *) event); - if (meta_window_shortcuts_inhibited (display->focus_window, source)) - return FALSE; - } - - return process_special_modifier_key (display, - event, - window, - &keys->overlay_key_only_pressed, - &keys->overlay_resolved_key_combo, - (GFunc) meta_display_overlay_key_activate); -} - -static void -handle_locate_pointer (MetaDisplay *display) -{ - meta_compositor_locate_pointer (display->compositor); -} - -static gboolean -process_locate_pointer_key (MetaDisplay *display, - ClutterKeyEvent *event, - MetaWindow *window) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - - return process_special_modifier_key (display, - event, - window, - &keys->locate_pointer_key_only_pressed, - &keys->locate_pointer_resolved_key_combo, - (GFunc) handle_locate_pointer); -} - -static gboolean -process_iso_next_group (MetaDisplay *display, - ClutterKeyEvent *event) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - gboolean activate; - xkb_keycode_t keycode = (xkb_keycode_t) event->hardware_keycode; - xkb_mod_mask_t mask; - int i, j; - - if (event->type == CLUTTER_KEY_RELEASE) - return FALSE; - - activate = FALSE; - mask = mask_from_event_params (keys, event->modifier_state); - - for (i = 0; i < keys->n_iso_next_group_combos; ++i) - { - for (j = 0; j < keys->iso_next_group_combo[i].len; ++j) - { - if (keycode == keys->iso_next_group_combo[i].keycodes[j] && - mask == keys->iso_next_group_combo[i].mask) - { - /* If the signal handler returns TRUE the keyboard will - remain frozen. It's the signal handler's responsibility - to unfreeze it. */ - if (!meta_display_modifiers_accelerator_activate (display)) - meta_display_unfreeze_keyboard (display, event->time); - activate = TRUE; - break; - } - } - } - - return activate; -} - -static gboolean -process_key_event (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event) -{ - gboolean keep_grab; - gboolean all_keys_grabbed; - - all_keys_grabbed = window ? window->all_keys_grabbed : FALSE; - if (!all_keys_grabbed) - { - if (process_overlay_key (display, event, window)) - return TRUE; - - if (process_locate_pointer_key (display, event, window)) - return FALSE; /* Continue with the event even if handled */ - - if (process_iso_next_group (display, event)) - return TRUE; - } - - { - MetaBackend *backend = meta_get_backend (); - if (META_IS_BACKEND_X11 (backend)) - { - Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); - XIAllowEvents (xdisplay, - meta_input_device_x11_get_device_id (event->device), - XIAsyncDevice, event->time); - } - } - - keep_grab = TRUE; - if (all_keys_grabbed) - { - if (display->grab_op == META_GRAB_OP_NONE) - return TRUE; - - /* If we get here we have a global grab, because - * we're in some special keyboard mode such as window move - * mode. - */ - if (window == display->grab_window) - { - if (display->grab_op & META_GRAB_OP_WINDOW_FLAG_KEYBOARD) - { - if (display->grab_op == META_GRAB_OP_KEYBOARD_MOVING) - { - meta_topic (META_DEBUG_KEYBINDINGS, - "Processing event for keyboard move"); - keep_grab = process_keyboard_move_grab (display, window, event); - } - else - { - meta_topic (META_DEBUG_KEYBINDINGS, - "Processing event for keyboard resize"); - keep_grab = process_keyboard_resize_grab (display, window, event); - } - } - else - { - meta_topic (META_DEBUG_KEYBINDINGS, - "Processing event for mouse-only move/resize"); - keep_grab = process_mouse_move_resize_grab (display, window, event); - } - } - if (!keep_grab) - meta_display_end_grab_op (display, event->time); - - return TRUE; - } - - /* Do the normal keybindings */ - return process_event (display, window, event); -} - -/* Handle a key event. May be called recursively: some key events cause - * grabs to be ended and then need to be processed again in their own - * right. This cannot cause infinite recursion because we never call - * ourselves when there wasn't a grab, and we always clear the grab - * first; the invariant is enforced using an assertion. See #112560. - * - * The return value is whether we handled the key event. - * - * FIXME: We need to prove there are no race conditions here. - * FIXME: Does it correctly handle alt-Tab being followed by another - * grabbing keypress without letting go of alt? - * FIXME: An iterative solution would probably be simpler to understand - * (and help us solve the other fixmes). - */ -gboolean -meta_keybindings_process_event (MetaDisplay *display, - MetaWindow *window, - const ClutterEvent *event) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - - switch (event->type) - { - case CLUTTER_BUTTON_PRESS: - case CLUTTER_BUTTON_RELEASE: - case CLUTTER_TOUCH_BEGIN: - case CLUTTER_TOUCH_END: - case CLUTTER_SCROLL: - keys->overlay_key_only_pressed = FALSE; - keys->locate_pointer_key_only_pressed = FALSE; - return FALSE; - - case CLUTTER_KEY_PRESS: - case CLUTTER_KEY_RELEASE: - return process_key_event (display, window, (ClutterKeyEvent *) event); - - default: - return FALSE; - } -} - -static gboolean -process_mouse_move_resize_grab (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event) -{ - /* don't care about releases, but eat them, don't end grab */ - if (event->type == CLUTTER_KEY_RELEASE) - return TRUE; - - if (event->keyval == CLUTTER_KEY_Escape) - { - MetaTileMode tile_mode; - - /* Hide the tiling preview if necessary */ - if (display->preview_tile_mode != META_TILE_NONE) - meta_display_hide_tile_preview (display); - - /* Restore the original tile mode */ - tile_mode = display->grab_tile_mode; - window->tile_monitor_number = display->grab_tile_monitor_number; - - /* End move or resize and restore to original state. If the - * window was a maximized window that had been "shaken loose" we - * need to remaximize it. In normal cases, we need to do a - * moveresize now to get the position back to the original. - */ - if (window->shaken_loose || tile_mode == META_TILE_MAXIMIZED) - meta_window_maximize (window, META_MAXIMIZE_BOTH); - else if (tile_mode != META_TILE_NONE) - meta_window_restore_tile (window, - tile_mode, - display->grab_initial_window_pos.width, - display->grab_initial_window_pos.height); - else - meta_window_move_resize_frame (display->grab_window, - TRUE, - display->grab_initial_window_pos.x, - display->grab_initial_window_pos.y, - display->grab_initial_window_pos.width, - display->grab_initial_window_pos.height); - - /* End grab */ - return FALSE; - } - - return TRUE; -} - -static gboolean -process_keyboard_move_grab (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event) -{ - MetaEdgeResistanceFlags flags; - gboolean handled; - MetaRectangle frame_rect; - int x, y; - int incr; - - handled = FALSE; - - /* don't care about releases, but eat them, don't end grab */ - if (event->type == CLUTTER_KEY_RELEASE) - return TRUE; - - /* don't end grab on modifier key presses */ - if (is_modifier (event->keyval)) - return TRUE; - - meta_window_get_frame_rect (window, &frame_rect); - x = frame_rect.x; - y = frame_rect.y; - - flags = META_EDGE_RESISTANCE_KEYBOARD_OP | META_EDGE_RESISTANCE_WINDOWS; - - if ((event->modifier_state & CLUTTER_SHIFT_MASK) != 0) - flags |= META_EDGE_RESISTANCE_SNAP; - -#define SMALL_INCREMENT 1 -#define NORMAL_INCREMENT 10 - - if (flags & META_EDGE_RESISTANCE_SNAP) - incr = 1; - else if (event->modifier_state & CLUTTER_CONTROL_MASK) - incr = SMALL_INCREMENT; - else - incr = NORMAL_INCREMENT; - - if (event->keyval == CLUTTER_KEY_Escape) - { - /* End move and restore to original state. If the window was a - * maximized window that had been "shaken loose" we need to - * remaximize it. In normal cases, we need to do a moveresize - * now to get the position back to the original. - */ - if (window->shaken_loose) - meta_window_maximize (window, META_MAXIMIZE_BOTH); - else - meta_window_move_resize_frame (display->grab_window, - TRUE, - display->grab_initial_window_pos.x, - display->grab_initial_window_pos.y, - display->grab_initial_window_pos.width, - display->grab_initial_window_pos.height); - } - - /* When moving by increments, we still snap to edges if the move - * to the edge is smaller than the increment. This is because - * Shift + arrow to snap is sort of a hidden feature. This way - * people using just arrows shouldn't get too frustrated. - */ - switch (event->keyval) - { - case CLUTTER_KEY_KP_Home: - case CLUTTER_KEY_KP_Prior: - case CLUTTER_KEY_Up: - case CLUTTER_KEY_KP_Up: - y -= incr; - handled = TRUE; - break; - case CLUTTER_KEY_KP_End: - case CLUTTER_KEY_KP_Next: - case CLUTTER_KEY_Down: - case CLUTTER_KEY_KP_Down: - y += incr; - handled = TRUE; - break; - } - - switch (event->keyval) - { - case CLUTTER_KEY_KP_Home: - case CLUTTER_KEY_KP_End: - case CLUTTER_KEY_Left: - case CLUTTER_KEY_KP_Left: - x -= incr; - handled = TRUE; - break; - case CLUTTER_KEY_KP_Prior: - case CLUTTER_KEY_KP_Next: - case CLUTTER_KEY_Right: - case CLUTTER_KEY_KP_Right: - x += incr; - handled = TRUE; - break; - } - - if (handled) - { - meta_topic (META_DEBUG_KEYBINDINGS, - "Computed new window location %d,%d due to keypress", - x, y); - - meta_window_edge_resistance_for_move (window, - &x, - &y, - NULL, - flags); - - meta_window_move_frame (window, TRUE, x, y); - meta_window_update_keyboard_move (window); - } - - return handled; -} - -static gboolean -process_keyboard_resize_grab_op_change (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event) -{ - gboolean handled; - - handled = FALSE; - switch (display->grab_op) - { - case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN: - switch (event->keyval) - { - case CLUTTER_KEY_Up: - case CLUTTER_KEY_KP_Up: - display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N; - handled = TRUE; - break; - case CLUTTER_KEY_Down: - case CLUTTER_KEY_KP_Down: - display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S; - handled = TRUE; - break; - case CLUTTER_KEY_Left: - case CLUTTER_KEY_KP_Left: - display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W; - handled = TRUE; - break; - case CLUTTER_KEY_Right: - case CLUTTER_KEY_KP_Right: - display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E; - handled = TRUE; - break; - } - break; - - case META_GRAB_OP_KEYBOARD_RESIZING_S: - switch (event->keyval) - { - case CLUTTER_KEY_Left: - case CLUTTER_KEY_KP_Left: - display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W; - handled = TRUE; - break; - case CLUTTER_KEY_Right: - case CLUTTER_KEY_KP_Right: - display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E; - handled = TRUE; - break; - } - break; - - case META_GRAB_OP_KEYBOARD_RESIZING_N: - switch (event->keyval) - { - case CLUTTER_KEY_Left: - case CLUTTER_KEY_KP_Left: - display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W; - handled = TRUE; - break; - case CLUTTER_KEY_Right: - case CLUTTER_KEY_KP_Right: - display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E; - handled = TRUE; - break; - } - break; - - case META_GRAB_OP_KEYBOARD_RESIZING_W: - switch (event->keyval) - { - case CLUTTER_KEY_Up: - case CLUTTER_KEY_KP_Up: - display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N; - handled = TRUE; - break; - case CLUTTER_KEY_Down: - case CLUTTER_KEY_KP_Down: - display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S; - handled = TRUE; - break; - } - break; - - case META_GRAB_OP_KEYBOARD_RESIZING_E: - switch (event->keyval) - { - case CLUTTER_KEY_Up: - case CLUTTER_KEY_KP_Up: - display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N; - handled = TRUE; - break; - case CLUTTER_KEY_Down: - case CLUTTER_KEY_KP_Down: - display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S; - handled = TRUE; - break; - } - break; - - case META_GRAB_OP_KEYBOARD_RESIZING_SE: - case META_GRAB_OP_KEYBOARD_RESIZING_NE: - case META_GRAB_OP_KEYBOARD_RESIZING_SW: - case META_GRAB_OP_KEYBOARD_RESIZING_NW: - break; - - default: - g_assert_not_reached (); - break; - } - - if (handled) - { - meta_window_update_keyboard_resize (window, TRUE); - return TRUE; - } - - return FALSE; -} - -static gboolean -process_keyboard_resize_grab (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event) -{ - MetaRectangle frame_rect; - gboolean handled; - int height_inc; - int width_inc; - int width, height; - MetaEdgeResistanceFlags flags; - MetaGravity gravity; - - handled = FALSE; - - /* don't care about releases, but eat them, don't end grab */ - if (event->type == CLUTTER_KEY_RELEASE) - return TRUE; - - /* don't end grab on modifier key presses */ - if (is_modifier (event->keyval)) - return TRUE; - - if (event->keyval == CLUTTER_KEY_Escape) - { - /* End resize and restore to original state. */ - meta_window_move_resize_frame (display->grab_window, - TRUE, - display->grab_initial_window_pos.x, - display->grab_initial_window_pos.y, - display->grab_initial_window_pos.width, - display->grab_initial_window_pos.height); - - return FALSE; - } - - if (process_keyboard_resize_grab_op_change (display, window, event)) - return TRUE; - - width = window->rect.width; - height = window->rect.height; - - meta_window_get_frame_rect (window, &frame_rect); - width = frame_rect.width; - height = frame_rect.height; - - gravity = meta_resize_gravity_from_grab_op (display->grab_op); - - flags = META_EDGE_RESISTANCE_KEYBOARD_OP; - - if ((event->modifier_state & CLUTTER_SHIFT_MASK) != 0) - flags |= META_EDGE_RESISTANCE_SNAP; - -#define SMALL_INCREMENT 1 -#define NORMAL_INCREMENT 10 - - if (flags & META_EDGE_RESISTANCE_SNAP) - { - height_inc = 1; - width_inc = 1; - } - else if (event->modifier_state & CLUTTER_CONTROL_MASK) - { - width_inc = SMALL_INCREMENT; - height_inc = SMALL_INCREMENT; - } - else - { - width_inc = NORMAL_INCREMENT; - height_inc = NORMAL_INCREMENT; - } - - /* If this is a resize increment window, make the amount we resize - * the window by match that amount (well, unless snap resizing...) - */ - if (window->size_hints.width_inc > 1) - width_inc = window->size_hints.width_inc; - if (window->size_hints.height_inc > 1) - height_inc = window->size_hints.height_inc; - - switch (event->keyval) - { - case CLUTTER_KEY_Up: - case CLUTTER_KEY_KP_Up: - switch (gravity) - { - case META_GRAVITY_NORTH: - case META_GRAVITY_NORTH_WEST: - case META_GRAVITY_NORTH_EAST: - /* Move bottom edge up */ - height -= height_inc; - break; - - case META_GRAVITY_SOUTH: - case META_GRAVITY_SOUTH_WEST: - case META_GRAVITY_SOUTH_EAST: - /* Move top edge up */ - height += height_inc; - break; - - case META_GRAVITY_EAST: - case META_GRAVITY_WEST: - case META_GRAVITY_CENTER: - case META_GRAVITY_NONE: - case META_GRAVITY_STATIC: - g_assert_not_reached (); - break; - } - - handled = TRUE; - break; - - case CLUTTER_KEY_Down: - case CLUTTER_KEY_KP_Down: - switch (gravity) - { - case META_GRAVITY_NORTH: - case META_GRAVITY_NORTH_WEST: - case META_GRAVITY_NORTH_EAST: - /* Move bottom edge down */ - height += height_inc; - break; - - case META_GRAVITY_SOUTH: - case META_GRAVITY_SOUTH_WEST: - case META_GRAVITY_SOUTH_EAST: - /* Move top edge down */ - height -= height_inc; - break; - - case META_GRAVITY_EAST: - case META_GRAVITY_WEST: - case META_GRAVITY_CENTER: - case META_GRAVITY_NONE: - case META_GRAVITY_STATIC: - g_assert_not_reached (); - break; - } - - handled = TRUE; - break; - - case CLUTTER_KEY_Left: - case CLUTTER_KEY_KP_Left: - switch (gravity) - { - case META_GRAVITY_EAST: - case META_GRAVITY_SOUTH_EAST: - case META_GRAVITY_NORTH_EAST: - /* Move left edge left */ - width += width_inc; - break; - - case META_GRAVITY_WEST: - case META_GRAVITY_SOUTH_WEST: - case META_GRAVITY_NORTH_WEST: - /* Move right edge left */ - width -= width_inc; - break; - - case META_GRAVITY_NORTH: - case META_GRAVITY_SOUTH: - case META_GRAVITY_CENTER: - case META_GRAVITY_NONE: - case META_GRAVITY_STATIC: - g_assert_not_reached (); - break; - } - - handled = TRUE; - break; - - case CLUTTER_KEY_Right: - case CLUTTER_KEY_KP_Right: - switch (gravity) - { - case META_GRAVITY_EAST: - case META_GRAVITY_SOUTH_EAST: - case META_GRAVITY_NORTH_EAST: - /* Move left edge right */ - width -= width_inc; - break; - - case META_GRAVITY_WEST: - case META_GRAVITY_SOUTH_WEST: - case META_GRAVITY_NORTH_WEST: - /* Move right edge right */ - width += width_inc; - break; - - case META_GRAVITY_NORTH: - case META_GRAVITY_SOUTH: - case META_GRAVITY_CENTER: - case META_GRAVITY_NONE: - case META_GRAVITY_STATIC: - g_assert_not_reached (); - break; - } - - handled = TRUE; - break; - - default: - break; - } - - /* fixup hack (just paranoia, not sure it's required) */ - if (height < 1) - height = 1; - if (width < 1) - width = 1; - - if (handled) - { - meta_topic (META_DEBUG_KEYBINDINGS, - "Computed new window size due to keypress: " - "%dx%d, gravity %s", - width, height, meta_gravity_to_string (gravity)); - - /* Do any edge resistance/snapping */ - meta_window_edge_resistance_for_resize (window, - &width, - &height, - gravity, - NULL, - flags); - - meta_window_resize_frame_with_gravity (window, - TRUE, - width, - height, - gravity); - - meta_window_update_keyboard_resize (window, FALSE); - } - - return handled; -} - -static void -handle_switch_to_last_workspace (MetaDisplay *display, - MetaWindow *event_window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - MetaWorkspaceManager *workspace_manager = display->workspace_manager; - gint target = meta_workspace_manager_get_n_workspaces (workspace_manager) - 1; - MetaWorkspace *workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, target); - meta_workspace_activate (workspace, event->time); -} - -static void -handle_switch_to_workspace (MetaDisplay *display, - MetaWindow *event_window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - gint which = binding->handler->data; - MetaWorkspaceManager *workspace_manager = display->workspace_manager; - MetaWorkspace *workspace; - - if (which < 0) - { - /* Negative workspace numbers are directions with respect to the - * current workspace. - */ - - workspace = meta_workspace_get_neighbor (workspace_manager->active_workspace, - which); - } - else - { - workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, which); - } - - if (workspace) - { - meta_workspace_activate (workspace, event->time); - } - else - { - /* We could offer to create it I suppose */ - } -} - - -static void -handle_maximize_vertically (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - if (window->has_resize_func) - { - if (window->maximized_vertically) - meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL); - else - meta_window_maximize (window, META_MAXIMIZE_VERTICAL); - } -} - -static void -handle_maximize_horizontally (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - if (window->has_resize_func) - { - if (window->maximized_horizontally) - meta_window_unmaximize (window, META_MAXIMIZE_HORIZONTAL); - else - meta_window_maximize (window, META_MAXIMIZE_HORIZONTAL); - } -} - -static void -handle_always_on_top (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - if (window->wm_state_above == FALSE) - meta_window_make_above (window); - else - meta_window_unmake_above (window); -} - -static void -handle_move_to_corner_backend (MetaDisplay *display, - MetaWindow *window, - MetaGravity gravity) -{ - MetaRectangle work_area; - MetaRectangle frame_rect; - int new_x, new_y; - - if (!window->monitor) - return; - - meta_window_get_work_area_current_monitor (window, &work_area); - meta_window_get_frame_rect (window, &frame_rect); - - switch (gravity) - { - case META_GRAVITY_NORTH_WEST: - case META_GRAVITY_WEST: - case META_GRAVITY_SOUTH_WEST: - new_x = work_area.x; - break; - case META_GRAVITY_NORTH: - case META_GRAVITY_SOUTH: - new_x = frame_rect.x; - break; - case META_GRAVITY_NORTH_EAST: - case META_GRAVITY_EAST: - case META_GRAVITY_SOUTH_EAST: - new_x = work_area.x + work_area.width - frame_rect.width; - break; - default: - g_assert_not_reached (); - } - - switch (gravity) - { - case META_GRAVITY_NORTH_WEST: - case META_GRAVITY_NORTH: - case META_GRAVITY_NORTH_EAST: - new_y = work_area.y; - break; - case META_GRAVITY_WEST: - case META_GRAVITY_EAST: - new_y = frame_rect.y; - break; - case META_GRAVITY_SOUTH_WEST: - case META_GRAVITY_SOUTH: - case META_GRAVITY_SOUTH_EAST: - new_y = work_area.y + work_area.height - frame_rect.height; - break; - default: - g_assert_not_reached (); - } - - meta_window_move_frame (window, - TRUE, - new_x, - new_y); -} - -static void -handle_move_to_corner_nw (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - handle_move_to_corner_backend (display, window, META_GRAVITY_NORTH_WEST); -} - -static void -handle_move_to_corner_ne (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - handle_move_to_corner_backend (display, window, META_GRAVITY_NORTH_EAST); -} - -static void -handle_move_to_corner_sw (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - handle_move_to_corner_backend (display, window, META_GRAVITY_SOUTH_WEST); -} - -static void -handle_move_to_corner_se (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - handle_move_to_corner_backend (display, window, META_GRAVITY_SOUTH_EAST); -} - -static void -handle_move_to_side_n (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - handle_move_to_corner_backend (display, window, META_GRAVITY_NORTH); -} - -static void -handle_move_to_side_s (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - handle_move_to_corner_backend (display, window, META_GRAVITY_SOUTH); -} - -static void -handle_move_to_side_e (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - handle_move_to_corner_backend (display, window, META_GRAVITY_EAST); -} - -static void -handle_move_to_side_w (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - handle_move_to_corner_backend (display, window, META_GRAVITY_WEST); -} - -static void -handle_move_to_center (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - MetaRectangle work_area; - MetaRectangle frame_rect; - - meta_window_get_work_area_current_monitor (window, &work_area); - meta_window_get_frame_rect (window, &frame_rect); - - meta_window_move_frame (window, - TRUE, - work_area.x + (work_area.width - frame_rect.width ) / 2, - work_area.y + (work_area.height - frame_rect.height) / 2); -} - -static void -handle_show_desktop (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - MetaWorkspaceManager *workspace_manager = display->workspace_manager; - - if (workspace_manager->active_workspace->showing_desktop) - { - meta_workspace_manager_unshow_desktop (workspace_manager); - meta_workspace_focus_default_window (workspace_manager->active_workspace, - NULL, - event->time); - } - else - meta_workspace_manager_show_desktop (workspace_manager, event->time); -} - -static void -handle_panel (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - MetaKeyBindingAction action = binding->handler->data; - MetaX11Display *x11_display = display->x11_display; - Atom action_atom; - XClientMessageEvent ev; - - action_atom = None; - switch (action) - { - /* FIXME: The numbers are wrong */ - case META_KEYBINDING_ACTION_PANEL_MAIN_MENU: - action_atom = x11_display->atom__GNOME_PANEL_ACTION_MAIN_MENU; - break; - case META_KEYBINDING_ACTION_PANEL_RUN_DIALOG: - action_atom = x11_display->atom__GNOME_PANEL_ACTION_RUN_DIALOG; - break; - default: - return; - } - - ev.type = ClientMessage; - ev.window = x11_display->xroot; - ev.message_type = x11_display->atom__GNOME_PANEL_ACTION; - ev.format = 32; - ev.data.l[0] = action_atom; - ev.data.l[1] = event->time; - - meta_topic (META_DEBUG_KEYBINDINGS, - "Sending panel message with timestamp %u, and turning mouse_mode " - "off due to keybinding press", event->time); - display->mouse_mode = FALSE; - - meta_x11_error_trap_push (x11_display); - - /* Release the grab for the panel before sending the event */ - XUngrabKeyboard (x11_display->xdisplay, event->time); - - XSendEvent (x11_display->xdisplay, - x11_display->xroot, - False, - StructureNotifyMask, - (XEvent*) &ev); - - meta_x11_error_trap_pop (x11_display); -} - -static void -handle_activate_window_menu (MetaDisplay *display, - MetaWindow *event_window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - if (display->focus_window) - { - int x, y; - MetaRectangle frame_rect; - cairo_rectangle_int_t child_rect; - - meta_window_get_frame_rect (display->focus_window, &frame_rect); - meta_window_get_client_area_rect (display->focus_window, &child_rect); - - x = frame_rect.x + child_rect.x; - if (meta_get_locale_direction () == META_LOCALE_DIRECTION_RTL) - x += child_rect.width; - - y = frame_rect.y + child_rect.y; - meta_window_show_menu (display->focus_window, META_WINDOW_MENU_WM, x, y); - } -} - -static void -do_choose_window (MetaDisplay *display, - MetaWindow *event_window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gboolean backward) -{ - MetaWorkspaceManager *workspace_manager = display->workspace_manager; - MetaTabList type = binding->handler->data; - MetaWindow *window; - - meta_topic (META_DEBUG_KEYBINDINGS, - "Tab list = %u", type); - - window = meta_display_get_tab_next (display, - type, - workspace_manager->active_workspace, - NULL, - backward); - - if (window) - meta_window_activate (window, event->time); -} - -static void -handle_switch (MetaDisplay *display, - MetaWindow *event_window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - gboolean backwards = meta_key_binding_is_reversed (binding); - do_choose_window (display, event_window, event, binding, backwards); -} - -static void -handle_cycle (MetaDisplay *display, - MetaWindow *event_window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - gboolean backwards = meta_key_binding_is_reversed (binding); - do_choose_window (display, event_window, event, binding, backwards); -} - -static void -handle_toggle_fullscreen (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - if (window->fullscreen) - meta_window_unmake_fullscreen (window); - else if (window->has_fullscreen_func) - meta_window_make_fullscreen (window); -} - -static void -handle_toggle_above (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - if (window->wm_state_above) - meta_window_unmake_above (window); - else - meta_window_make_above (window); -} - -static void -handle_toggle_tiled (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - MetaTileMode mode = binding->handler->data; - - if ((META_WINDOW_TILED_LEFT (window) && mode == META_TILE_LEFT) || - (META_WINDOW_TILED_RIGHT (window) && mode == META_TILE_RIGHT)) - { - meta_window_untile (window); - } - else if (meta_window_can_tile_side_by_side (window)) - { - window->tile_monitor_number = window->monitor->number; - /* Maximization constraints beat tiling constraints, so if the window - * is maximized, tiling won't have any effect unless we unmaximize it - * horizontally first; rather than calling meta_window_unmaximize(), - * we just set the flag and rely on meta_window_tile() syncing it to - * save an additional roundtrip. - */ - window->maximized_horizontally = FALSE; - meta_window_tile (window, mode); - } -} - -static void -handle_toggle_maximized (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - if (META_WINDOW_MAXIMIZED (window)) - meta_window_unmaximize (window, META_MAXIMIZE_BOTH); - else if (window->has_maximize_func) - meta_window_maximize (window, META_MAXIMIZE_BOTH); -} - -static void -handle_maximize (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - if (window->has_maximize_func) - meta_window_maximize (window, META_MAXIMIZE_BOTH); -} - -static void -handle_unmaximize (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - if (window->maximized_vertically || window->maximized_horizontally) - meta_window_unmaximize (window, META_MAXIMIZE_BOTH); -} - -static void -handle_toggle_shaded (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - if (window->shaded) - meta_window_unshade (window, event->time); - else if (window->has_shade_func) - meta_window_shade (window, event->time); -} - -static void -handle_close (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - if (window->has_close_func) - meta_window_delete (window, event->time); -} - -static void -handle_minimize (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - if (window->has_minimize_func) - meta_window_minimize (window); -} - -static void -handle_begin_move (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - if (window->has_move_func) - { - meta_window_begin_grab_op (window, - META_GRAB_OP_KEYBOARD_MOVING, - FALSE, - event->time); - } -} - -static void -handle_begin_resize (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - if (window->has_resize_func) - { - meta_window_begin_grab_op (window, - META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN, - FALSE, - event->time); - } -} - -static void -handle_toggle_on_all_workspaces (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - if (window->on_all_workspaces_requested) - meta_window_unstick (window); - else - meta_window_stick (window); -} - -static void -handle_move_to_workspace_last (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - MetaWorkspaceManager *workspace_manager = display->workspace_manager; - gint which; - MetaWorkspace *workspace; - - if (window->always_sticky) - return; - - which = meta_workspace_manager_get_n_workspaces (workspace_manager) - 1; - workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, which); - meta_window_change_workspace (window, workspace); -} - - -static void -handle_move_to_workspace (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - MetaWorkspaceManager *workspace_manager = display->workspace_manager; - gint which = binding->handler->data; - gboolean flip = (which < 0); - MetaWorkspace *workspace; - - /* If which is zero or positive, it's a workspace number, and the window - * should move to the workspace with that number. - * - * However, if it's negative, it's a direction with respect to the current - * position; it's expressed as a member of the MetaMotionDirection enum, - * all of whose members are negative. Such a change is called a flip. - */ - - if (window->always_sticky) - return; - - workspace = NULL; - if (flip) - { - workspace = meta_workspace_get_neighbor (workspace_manager->active_workspace, - which); - } - else - { - workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, which); - } - - if (workspace) - { - /* Activate second, so the window is never unmapped */ - meta_window_change_workspace (window, workspace); - if (flip) - { - meta_topic (META_DEBUG_FOCUS, - "Resetting mouse_mode to FALSE due to " - "handle_move_to_workspace() call with flip set."); - meta_display_clear_mouse_mode (workspace->display); - meta_workspace_activate_with_focus (workspace, - window, - event->time); - } - } - else - { - /* We could offer to create it I suppose */ - } -} - -static void -handle_move_to_monitor (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - gint which = binding->handler->data; - MetaLogicalMonitor *current, *new; - - current = window->monitor; - new = meta_monitor_manager_get_logical_monitor_neighbor (monitor_manager, - current, which); - - if (new == NULL) - return; - - meta_window_move_to_monitor (window, new->number); -} - -static void -handle_raise_or_lower (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - /* Get window at pointer */ - - MetaWindow *above = NULL; - - /* Check if top */ - if (meta_stack_get_top (window->display->stack) == window) - { - meta_window_lower (window); - return; - } - - /* else check if windows in same layer are intersecting it */ - - above = meta_stack_get_above (window->display->stack, window, TRUE); - - while (above) - { - MetaRectangle tmp, win_rect, above_rect; - - if (above->mapped && meta_window_should_be_showing (above)) - { - meta_window_get_frame_rect (window, &win_rect); - meta_window_get_frame_rect (above, &above_rect); - - /* Check if obscured */ - if (meta_rectangle_intersect (&win_rect, &above_rect, &tmp)) - { - meta_window_raise (window); - return; - } - } - - above = meta_stack_get_above (window->display->stack, above, TRUE); - } - - /* window is not obscured */ - meta_window_lower (window); -} - -static void -handle_raise (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - meta_window_raise (window); -} - -static void -handle_lower (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - meta_window_lower (window); -} - -static void -handle_set_spew_mark (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - meta_verbose ("-- MARK MARK MARK MARK --"); -} - -#ifdef HAVE_NATIVE_BACKEND -static void -handle_switch_vt (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - gint vt = binding->handler->data; - GError *error = NULL; - - if (!meta_activate_vt (vt, &error)) - { - g_warning ("Failed to switch VT: %s", error->message); - g_error_free (error); - } -} -#endif /* HAVE_NATIVE_BACKEND */ - -static void -handle_switch_monitor (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - MetaMonitorSwitchConfigType config_type = - meta_monitor_manager_get_switch_config (monitor_manager); - - if (!meta_monitor_manager_can_switch_config (monitor_manager)) - return; - - config_type = (config_type + 1) % (META_MONITOR_SWITCH_CONFIG_UNKNOWN); - meta_monitor_manager_switch_config (monitor_manager, config_type); -} - -static void -handle_rotate_monitor (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - - meta_monitor_manager_rotate_monitor (monitor_manager); -} - -static void -handle_restore_shortcuts (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) -{ - ClutterInputDevice *source; - - if (!display->focus_window) - return; - - source = clutter_event_get_source_device ((ClutterEvent *) event); - - meta_topic (META_DEBUG_KEYBINDINGS, "Restoring normal keyboard shortcuts"); - - meta_window_force_restore_shortcuts (display->focus_window, source); -} - -/** - * meta_keybindings_set_custom_handler: - * @name: The name of the keybinding to set - * @handler: (nullable): The new handler function - * @user_data: User data to pass to the callback - * @free_data: Will be called when this handler is overridden. - * - * Allows users to register a custom handler for a - * builtin key binding. - * - * Returns: %TRUE if the binding known as @name was found, - * %FALSE otherwise. - */ -gboolean -meta_keybindings_set_custom_handler (const gchar *name, - MetaKeyHandlerFunc handler, - gpointer user_data, - GDestroyNotify free_data) -{ - MetaKeyHandler *key_handler = HANDLER (name); - - if (!key_handler) - return FALSE; - - if (key_handler->user_data_free_func && key_handler->user_data) - key_handler->user_data_free_func (key_handler->user_data); - - key_handler->func = handler; - key_handler->user_data = user_data; - key_handler->user_data_free_func = free_data; - - return TRUE; -} - -static void -init_builtin_key_bindings (MetaDisplay *display) -{ - GSettings *common_keybindings = g_settings_new (SCHEMA_COMMON_KEYBINDINGS); - GSettings *mutter_keybindings = g_settings_new (SCHEMA_MUTTER_KEYBINDINGS); - GSettings *mutter_wayland_keybindings = g_settings_new (SCHEMA_MUTTER_WAYLAND_KEYBINDINGS); - - add_builtin_keybinding (display, - "switch-to-workspace-1", - common_keybindings, - META_KEY_BINDING_NONE | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_WORKSPACE_1, - handle_switch_to_workspace, 0); - add_builtin_keybinding (display, - "switch-to-workspace-2", - common_keybindings, - META_KEY_BINDING_NONE | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_WORKSPACE_2, - handle_switch_to_workspace, 1); - add_builtin_keybinding (display, - "switch-to-workspace-3", - common_keybindings, - META_KEY_BINDING_NONE | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_WORKSPACE_3, - handle_switch_to_workspace, 2); - add_builtin_keybinding (display, - "switch-to-workspace-4", - common_keybindings, - META_KEY_BINDING_NONE | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_WORKSPACE_4, - handle_switch_to_workspace, 3); - add_builtin_keybinding (display, - "switch-to-workspace-5", - common_keybindings, - META_KEY_BINDING_NONE | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_WORKSPACE_5, - handle_switch_to_workspace, 4); - add_builtin_keybinding (display, - "switch-to-workspace-6", - common_keybindings, - META_KEY_BINDING_NONE | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_WORKSPACE_6, - handle_switch_to_workspace, 5); - add_builtin_keybinding (display, - "switch-to-workspace-7", - common_keybindings, - META_KEY_BINDING_NONE | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_WORKSPACE_7, - handle_switch_to_workspace, 6); - add_builtin_keybinding (display, - "switch-to-workspace-8", - common_keybindings, - META_KEY_BINDING_NONE | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_WORKSPACE_8, - handle_switch_to_workspace, 7); - add_builtin_keybinding (display, - "switch-to-workspace-9", - common_keybindings, - META_KEY_BINDING_NONE | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_WORKSPACE_9, - handle_switch_to_workspace, 8); - add_builtin_keybinding (display, - "switch-to-workspace-10", - common_keybindings, - META_KEY_BINDING_NONE | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_WORKSPACE_10, - handle_switch_to_workspace, 9); - add_builtin_keybinding (display, - "switch-to-workspace-11", - common_keybindings, - META_KEY_BINDING_NONE | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_WORKSPACE_11, - handle_switch_to_workspace, 10); - add_builtin_keybinding (display, - "switch-to-workspace-12", - common_keybindings, - META_KEY_BINDING_NONE | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_WORKSPACE_12, - handle_switch_to_workspace, 11); - - add_builtin_keybinding (display, - "switch-to-workspace-left", - common_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_WORKSPACE_LEFT, - handle_switch_to_workspace, META_MOTION_LEFT); - - add_builtin_keybinding (display, - "switch-to-workspace-right", - common_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_WORKSPACE_RIGHT, - handle_switch_to_workspace, META_MOTION_RIGHT); - - add_builtin_keybinding (display, - "switch-to-workspace-up", - common_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_WORKSPACE_UP, - handle_switch_to_workspace, META_MOTION_UP); - - add_builtin_keybinding (display, - "switch-to-workspace-down", - common_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_WORKSPACE_DOWN, - handle_switch_to_workspace, META_MOTION_DOWN); - - add_builtin_keybinding (display, - "switch-to-workspace-last", - common_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_WORKSPACE_LAST, - handle_switch_to_last_workspace, 0); - - - - /* The ones which have inverses. These can't be bound to any keystroke - * containing Shift because Shift will invert their "backward" state. - * - * TODO: "NORMAL" and "DOCKS" should be renamed to the same name as their - * action, for obviousness. - * - * TODO: handle_switch and handle_cycle should probably really be the - * same function checking a bit in the parameter for difference. - */ - - add_builtin_keybinding (display, - "switch-group", - common_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_SWITCH_GROUP, - handle_switch, META_TAB_LIST_GROUP); - - add_builtin_keybinding (display, - "switch-group-backward", - common_keybindings, - META_KEY_BINDING_IS_REVERSED, - META_KEYBINDING_ACTION_SWITCH_GROUP_BACKWARD, - handle_switch, META_TAB_LIST_GROUP); - - add_builtin_keybinding (display, - "switch-applications", - common_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_SWITCH_APPLICATIONS, - handle_switch, META_TAB_LIST_NORMAL); - - add_builtin_keybinding (display, - "switch-applications-backward", - common_keybindings, - META_KEY_BINDING_IS_REVERSED, - META_KEYBINDING_ACTION_SWITCH_APPLICATIONS_BACKWARD, - handle_switch, META_TAB_LIST_NORMAL); - - add_builtin_keybinding (display, - "switch-windows", - common_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_SWITCH_WINDOWS, - handle_switch, META_TAB_LIST_NORMAL); - - add_builtin_keybinding (display, - "switch-windows-backward", - common_keybindings, - META_KEY_BINDING_IS_REVERSED, - META_KEYBINDING_ACTION_SWITCH_WINDOWS_BACKWARD, - handle_switch, META_TAB_LIST_NORMAL); - - add_builtin_keybinding (display, - "switch-panels", - common_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_SWITCH_PANELS, - handle_switch, META_TAB_LIST_DOCKS); - - add_builtin_keybinding (display, - "switch-panels-backward", - common_keybindings, - META_KEY_BINDING_IS_REVERSED, - META_KEYBINDING_ACTION_SWITCH_PANELS_BACKWARD, - handle_switch, META_TAB_LIST_DOCKS); - - add_builtin_keybinding (display, - "cycle-group", - common_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_CYCLE_GROUP, - handle_cycle, META_TAB_LIST_GROUP); - - add_builtin_keybinding (display, - "cycle-group-backward", - common_keybindings, - META_KEY_BINDING_IS_REVERSED, - META_KEYBINDING_ACTION_CYCLE_GROUP_BACKWARD, - handle_cycle, META_TAB_LIST_GROUP); - - add_builtin_keybinding (display, - "cycle-windows", - common_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_CYCLE_WINDOWS, - handle_cycle, META_TAB_LIST_NORMAL); - - add_builtin_keybinding (display, - "cycle-windows-backward", - common_keybindings, - META_KEY_BINDING_IS_REVERSED, - META_KEYBINDING_ACTION_CYCLE_WINDOWS_BACKWARD, - handle_cycle, META_TAB_LIST_NORMAL); - - add_builtin_keybinding (display, - "cycle-panels", - common_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_CYCLE_PANELS, - handle_cycle, META_TAB_LIST_DOCKS); - - add_builtin_keybinding (display, - "cycle-panels-backward", - common_keybindings, - META_KEY_BINDING_IS_REVERSED, - META_KEYBINDING_ACTION_CYCLE_PANELS_BACKWARD, - handle_cycle, META_TAB_LIST_DOCKS); - - /***********************************/ - - add_builtin_keybinding (display, - "show-desktop", - common_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_SHOW_DESKTOP, - handle_show_desktop, 0); - - add_builtin_keybinding (display, - "panel-main-menu", - common_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_PANEL_MAIN_MENU, - handle_panel, META_KEYBINDING_ACTION_PANEL_MAIN_MENU); - - add_builtin_keybinding (display, - "panel-run-dialog", - common_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_PANEL_RUN_DIALOG, - handle_panel, META_KEYBINDING_ACTION_PANEL_RUN_DIALOG); - - add_builtin_keybinding (display, - "set-spew-mark", - common_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_SET_SPEW_MARK, - handle_set_spew_mark, 0); - - add_builtin_keybinding (display, - "switch-monitor", - mutter_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_SWITCH_MONITOR, - handle_switch_monitor, 0); - - add_builtin_keybinding (display, - "rotate-monitor", - mutter_keybindings, - META_KEY_BINDING_NONE, - META_KEYBINDING_ACTION_ROTATE_MONITOR, - handle_rotate_monitor, 0); - -#ifdef HAVE_NATIVE_BACKEND - MetaBackend *backend = meta_get_backend (); - if (META_IS_BACKEND_NATIVE (backend)) - { - add_builtin_keybinding (display, - "switch-to-session-1", - mutter_wayland_keybindings, - META_KEY_BINDING_NON_MASKABLE, - META_KEYBINDING_ACTION_NONE, - handle_switch_vt, 1); - - add_builtin_keybinding (display, - "switch-to-session-2", - mutter_wayland_keybindings, - META_KEY_BINDING_NON_MASKABLE, - META_KEYBINDING_ACTION_NONE, - handle_switch_vt, 2); - - add_builtin_keybinding (display, - "switch-to-session-3", - mutter_wayland_keybindings, - META_KEY_BINDING_NON_MASKABLE, - META_KEYBINDING_ACTION_NONE, - handle_switch_vt, 3); - - add_builtin_keybinding (display, - "switch-to-session-4", - mutter_wayland_keybindings, - META_KEY_BINDING_NON_MASKABLE, - META_KEYBINDING_ACTION_NONE, - handle_switch_vt, 4); - - add_builtin_keybinding (display, - "switch-to-session-5", - mutter_wayland_keybindings, - META_KEY_BINDING_NON_MASKABLE, - META_KEYBINDING_ACTION_NONE, - handle_switch_vt, 5); - - add_builtin_keybinding (display, - "switch-to-session-6", - mutter_wayland_keybindings, - META_KEY_BINDING_NON_MASKABLE, - META_KEYBINDING_ACTION_NONE, - handle_switch_vt, 6); - - add_builtin_keybinding (display, - "switch-to-session-7", - mutter_wayland_keybindings, - META_KEY_BINDING_NON_MASKABLE, - META_KEYBINDING_ACTION_NONE, - handle_switch_vt, 7); - - add_builtin_keybinding (display, - "switch-to-session-8", - mutter_wayland_keybindings, - META_KEY_BINDING_NON_MASKABLE, - META_KEYBINDING_ACTION_NONE, - handle_switch_vt, 8); - - add_builtin_keybinding (display, - "switch-to-session-9", - mutter_wayland_keybindings, - META_KEY_BINDING_NON_MASKABLE, - META_KEYBINDING_ACTION_NONE, - handle_switch_vt, 9); - - add_builtin_keybinding (display, - "switch-to-session-10", - mutter_wayland_keybindings, - META_KEY_BINDING_NON_MASKABLE, - META_KEYBINDING_ACTION_NONE, - handle_switch_vt, 10); - - add_builtin_keybinding (display, - "switch-to-session-11", - mutter_wayland_keybindings, - META_KEY_BINDING_NON_MASKABLE, - META_KEYBINDING_ACTION_NONE, - handle_switch_vt, 11); - - add_builtin_keybinding (display, - "switch-to-session-12", - mutter_wayland_keybindings, - META_KEY_BINDING_NON_MASKABLE, - META_KEYBINDING_ACTION_NONE, - handle_switch_vt, 12); - } -#endif /* HAVE_NATIVE_BACKEND */ - - add_builtin_keybinding (display, - "restore-shortcuts", - mutter_wayland_keybindings, - META_KEY_BINDING_NON_MASKABLE, - META_KEYBINDING_ACTION_NONE, - handle_restore_shortcuts, 0); - - /************************ PER WINDOW BINDINGS ************************/ - - /* These take a window as an extra parameter; they have no effect - * if no window is active. - */ - - add_builtin_keybinding (display, - "activate-window-menu", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_ACTIVATE_WINDOW_MENU, - handle_activate_window_menu, 0); - - add_builtin_keybinding (display, - "toggle-fullscreen", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_TOGGLE_FULLSCREEN, - handle_toggle_fullscreen, 0); - - add_builtin_keybinding (display, - "toggle-maximized", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_TOGGLE_MAXIMIZED, - handle_toggle_maximized, 0); - - add_builtin_keybinding (display, - "toggle-tiled-left", - mutter_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_TOGGLE_TILED_LEFT, - handle_toggle_tiled, META_TILE_LEFT); - - add_builtin_keybinding (display, - "toggle-tiled-right", - mutter_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_TOGGLE_TILED_RIGHT, - handle_toggle_tiled, META_TILE_RIGHT); - - add_builtin_keybinding (display, - "toggle-above", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_TOGGLE_ABOVE, - handle_toggle_above, 0); - - add_builtin_keybinding (display, - "maximize", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MAXIMIZE, - handle_maximize, 0); - - add_builtin_keybinding (display, - "unmaximize", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_UNMAXIMIZE, - handle_unmaximize, 0); - - add_builtin_keybinding (display, - "toggle-shaded", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_TOGGLE_SHADED, - handle_toggle_shaded, 0); - - add_builtin_keybinding (display, - "minimize", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MINIMIZE, - handle_minimize, 0); - - add_builtin_keybinding (display, - "close", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_CLOSE, - handle_close, 0); - - add_builtin_keybinding (display, - "begin-move", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_BEGIN_MOVE, - handle_begin_move, 0); - - add_builtin_keybinding (display, - "begin-resize", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_BEGIN_RESIZE, - handle_begin_resize, 0); - - add_builtin_keybinding (display, - "toggle-on-all-workspaces", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_TOGGLE_ON_ALL_WORKSPACES, - handle_toggle_on_all_workspaces, 0); - - add_builtin_keybinding (display, - "move-to-workspace-1", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_1, - handle_move_to_workspace, 0); - - add_builtin_keybinding (display, - "move-to-workspace-2", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_2, - handle_move_to_workspace, 1); - - add_builtin_keybinding (display, - "move-to-workspace-3", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_3, - handle_move_to_workspace, 2); - - add_builtin_keybinding (display, - "move-to-workspace-4", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_4, - handle_move_to_workspace, 3); - - add_builtin_keybinding (display, - "move-to-workspace-5", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_5, - handle_move_to_workspace, 4); - - add_builtin_keybinding (display, - "move-to-workspace-6", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_6, - handle_move_to_workspace, 5); - - add_builtin_keybinding (display, - "move-to-workspace-7", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_7, - handle_move_to_workspace, 6); - - add_builtin_keybinding (display, - "move-to-workspace-8", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_8, - handle_move_to_workspace, 7); - - add_builtin_keybinding (display, - "move-to-workspace-9", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_9, - handle_move_to_workspace, 8); - - add_builtin_keybinding (display, - "move-to-workspace-10", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_10, - handle_move_to_workspace, 9); - - add_builtin_keybinding (display, - "move-to-workspace-11", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_11, - handle_move_to_workspace, 10); - - add_builtin_keybinding (display, - "move-to-workspace-12", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_12, - handle_move_to_workspace, 11); - - add_builtin_keybinding (display, - "move-to-workspace-last", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_LAST, - handle_move_to_workspace_last, 0); - - add_builtin_keybinding (display, - "move-to-workspace-left", - common_keybindings, - META_KEY_BINDING_PER_WINDOW, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_LEFT, - handle_move_to_workspace, META_MOTION_LEFT); - - add_builtin_keybinding (display, - "move-to-workspace-right", - common_keybindings, - META_KEY_BINDING_PER_WINDOW, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_RIGHT, - handle_move_to_workspace, META_MOTION_RIGHT); - - add_builtin_keybinding (display, - "move-to-workspace-up", - common_keybindings, - META_KEY_BINDING_PER_WINDOW, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_UP, - handle_move_to_workspace, META_MOTION_UP); - - add_builtin_keybinding (display, - "move-to-workspace-down", - common_keybindings, - META_KEY_BINDING_PER_WINDOW, - META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_DOWN, - handle_move_to_workspace, META_MOTION_DOWN); - - add_builtin_keybinding (display, - "move-to-monitor-left", - common_keybindings, - META_KEY_BINDING_PER_WINDOW, - META_KEYBINDING_ACTION_MOVE_TO_MONITOR_LEFT, - handle_move_to_monitor, META_DISPLAY_LEFT); - - add_builtin_keybinding (display, - "move-to-monitor-right", - common_keybindings, - META_KEY_BINDING_PER_WINDOW, - META_KEYBINDING_ACTION_MOVE_TO_MONITOR_RIGHT, - handle_move_to_monitor, META_DISPLAY_RIGHT); - - add_builtin_keybinding (display, - "move-to-monitor-down", - common_keybindings, - META_KEY_BINDING_PER_WINDOW, - META_KEYBINDING_ACTION_MOVE_TO_MONITOR_DOWN, - handle_move_to_monitor, META_DISPLAY_DOWN); - - add_builtin_keybinding (display, - "move-to-monitor-up", - common_keybindings, - META_KEY_BINDING_PER_WINDOW, - META_KEYBINDING_ACTION_MOVE_TO_MONITOR_UP, - handle_move_to_monitor, META_DISPLAY_UP); - - add_builtin_keybinding (display, - "raise-or-lower", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_RAISE_OR_LOWER, - handle_raise_or_lower, 0); - - add_builtin_keybinding (display, - "raise", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_RAISE, - handle_raise, 0); - - add_builtin_keybinding (display, - "lower", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_LOWER, - handle_lower, 0); - - add_builtin_keybinding (display, - "maximize-vertically", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MAXIMIZE_VERTICALLY, - handle_maximize_vertically, 0); - - add_builtin_keybinding (display, - "maximize-horizontally", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MAXIMIZE_HORIZONTALLY, - handle_maximize_horizontally, 0); - - add_builtin_keybinding (display, - "always-on-top", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_ALWAYS_ON_TOP, - handle_always_on_top, 0); - - add_builtin_keybinding (display, - "move-to-corner-nw", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_CORNER_NW, - handle_move_to_corner_nw, 0); - - add_builtin_keybinding (display, - "move-to-corner-ne", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_CORNER_NE, - handle_move_to_corner_ne, 0); - - add_builtin_keybinding (display, - "move-to-corner-sw", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_CORNER_SW, - handle_move_to_corner_sw, 0); - - add_builtin_keybinding (display, - "move-to-corner-se", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_CORNER_SE, - handle_move_to_corner_se, 0); - - add_builtin_keybinding (display, - "move-to-side-n", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_SIDE_N, - handle_move_to_side_n, 0); - - add_builtin_keybinding (display, - "move-to-side-s", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_SIDE_S, - handle_move_to_side_s, 0); - - add_builtin_keybinding (display, - "move-to-side-e", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_SIDE_E, - handle_move_to_side_e, 0); - - add_builtin_keybinding (display, - "move-to-side-w", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_SIDE_W, - handle_move_to_side_w, 0); - - add_builtin_keybinding (display, - "move-to-center", - common_keybindings, - META_KEY_BINDING_PER_WINDOW | - META_KEY_BINDING_IGNORE_AUTOREPEAT, - META_KEYBINDING_ACTION_MOVE_TO_CENTER, - handle_move_to_center, 0); - - g_object_unref (common_keybindings); - g_object_unref (mutter_keybindings); - g_object_unref (mutter_wayland_keybindings); -} - -void -meta_display_init_keys (MetaDisplay *display) -{ - MetaKeyBindingManager *keys = &display->key_binding_manager; - MetaBackend *backend = meta_get_backend (); - MetaKeyHandler *handler; - - keys->backend = backend; - - /* Keybindings */ - keys->ignored_modifier_mask = 0; - keys->hyper_mask = 0; - keys->super_mask = 0; - keys->meta_mask = 0; - - keys->key_bindings = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) meta_key_binding_free); - keys->key_bindings_index = g_hash_table_new (NULL, NULL); - - reload_modmap (keys); - - key_handlers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, - (GDestroyNotify) key_handler_free); - - handler = g_new0 (MetaKeyHandler, 1); - handler->name = g_strdup ("overlay-key"); - handler->flags = META_KEY_BINDING_BUILTIN | META_KEY_BINDING_NO_AUTO_GRAB; - - g_hash_table_insert (key_handlers, g_strdup (handler->name), handler); - - handler = g_new0 (MetaKeyHandler, 1); - handler->name = g_strdup ("locate-pointer-key"); - handler->flags = META_KEY_BINDING_BUILTIN | META_KEY_BINDING_NO_AUTO_GRAB; - - g_hash_table_insert (key_handlers, g_strdup (handler->name), handler); - - handler = g_new0 (MetaKeyHandler, 1); - handler->name = g_strdup ("iso-next-group"); - handler->flags = META_KEY_BINDING_BUILTIN; - - g_hash_table_insert (key_handlers, g_strdup (handler->name), handler); - - handler = g_new0 (MetaKeyHandler, 1); - handler->name = g_strdup ("external-grab"); - handler->func = handle_external_grab; - handler->default_func = handle_external_grab; - - g_hash_table_insert (key_handlers, g_strdup (handler->name), handler); - - external_grabs = g_hash_table_new_full (g_str_hash, g_str_equal, - NULL, - (GDestroyNotify)meta_key_grab_free); - - init_builtin_key_bindings (display); - - rebuild_key_binding_table (keys); - rebuild_special_bindings (keys); - - reload_combos (keys); - - update_window_grab_modifiers (display); - - /* Keys are actually grabbed in meta_screen_grab_keys() */ - - meta_prefs_add_listener (prefs_changed_callback, display); - - g_signal_connect_swapped (backend, "keymap-changed", - G_CALLBACK (reload_keybindings), display); - g_signal_connect_swapped (backend, "keymap-layout-group-changed", - G_CALLBACK (reload_keybindings), display); -} diff --git a/src/core/meta-accel-parse.c b/src/core/meta-accel-parse.c deleted file mode 100644 index 0d34251af..000000000 --- a/src/core/meta-accel-parse.c +++ /dev/null @@ -1,358 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2014 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Written by: - * Jasper St. Pierre <jstpierre@mecheye.net> - */ - -#include "config.h" - -#include "core/meta-accel-parse.h" - -#include <stdlib.h> -#include <string.h> -#include <xkbcommon/xkbcommon.h> - -#include "core/keybindings-private.h" - -/* This is copied from GTK+ and modified to work with mutter's - * internal structures. Originating code comes from gtk/gtkaccelgroup.c - */ - -static inline gboolean -is_alt (const gchar *string) -{ - return ((string[0] == '<') && - (string[1] == 'a' || string[1] == 'A') && - (string[2] == 'l' || string[2] == 'L') && - (string[3] == 't' || string[3] == 'T') && - (string[4] == '>')); -} - -static inline gboolean -is_ctl (const gchar *string) -{ - return ((string[0] == '<') && - (string[1] == 'c' || string[1] == 'C') && - (string[2] == 't' || string[2] == 'T') && - (string[3] == 'l' || string[3] == 'L') && - (string[4] == '>')); -} - -static inline gboolean -is_modx (const gchar *string) -{ - return ((string[0] == '<') && - (string[1] == 'm' || string[1] == 'M') && - (string[2] == 'o' || string[2] == 'O') && - (string[3] == 'd' || string[3] == 'D') && - (string[4] >= '1' && string[4] <= '5') && - (string[5] == '>')); -} - -static inline gboolean -is_ctrl (const gchar *string) -{ - return ((string[0] == '<') && - (string[1] == 'c' || string[1] == 'C') && - (string[2] == 't' || string[2] == 'T') && - (string[3] == 'r' || string[3] == 'R') && - (string[4] == 'l' || string[4] == 'L') && - (string[5] == '>')); -} - -static inline gboolean -is_shft (const gchar *string) -{ - return ((string[0] == '<') && - (string[1] == 's' || string[1] == 'S') && - (string[2] == 'h' || string[2] == 'H') && - (string[3] == 'f' || string[3] == 'F') && - (string[4] == 't' || string[4] == 'T') && - (string[5] == '>')); -} - -static inline gboolean -is_shift (const gchar *string) -{ - return ((string[0] == '<') && - (string[1] == 's' || string[1] == 'S') && - (string[2] == 'h' || string[2] == 'H') && - (string[3] == 'i' || string[3] == 'I') && - (string[4] == 'f' || string[4] == 'F') && - (string[5] == 't' || string[5] == 'T') && - (string[6] == '>')); -} - -static inline gboolean -is_control (const gchar *string) -{ - return ((string[0] == '<') && - (string[1] == 'c' || string[1] == 'C') && - (string[2] == 'o' || string[2] == 'O') && - (string[3] == 'n' || string[3] == 'N') && - (string[4] == 't' || string[4] == 'T') && - (string[5] == 'r' || string[5] == 'R') && - (string[6] == 'o' || string[6] == 'O') && - (string[7] == 'l' || string[7] == 'L') && - (string[8] == '>')); -} - -static inline gboolean -is_meta (const gchar *string) -{ - return ((string[0] == '<') && - (string[1] == 'm' || string[1] == 'M') && - (string[2] == 'e' || string[2] == 'E') && - (string[3] == 't' || string[3] == 'T') && - (string[4] == 'a' || string[4] == 'A') && - (string[5] == '>')); -} - -static inline gboolean -is_super (const gchar *string) -{ - return ((string[0] == '<') && - (string[1] == 's' || string[1] == 'S') && - (string[2] == 'u' || string[2] == 'U') && - (string[3] == 'p' || string[3] == 'P') && - (string[4] == 'e' || string[4] == 'E') && - (string[5] == 'r' || string[5] == 'R') && - (string[6] == '>')); -} - -static inline gboolean -is_hyper (const gchar *string) -{ - return ((string[0] == '<') && - (string[1] == 'h' || string[1] == 'H') && - (string[2] == 'y' || string[2] == 'Y') && - (string[3] == 'p' || string[3] == 'P') && - (string[4] == 'e' || string[4] == 'E') && - (string[5] == 'r' || string[5] == 'R') && - (string[6] == '>')); -} - -static inline gboolean -is_primary (const gchar *string) -{ - return ((string[0] == '<') && - (string[1] == 'p' || string[1] == 'P') && - (string[2] == 'r' || string[2] == 'R') && - (string[3] == 'i' || string[3] == 'I') && - (string[4] == 'm' || string[4] == 'M') && - (string[5] == 'a' || string[5] == 'A') && - (string[6] == 'r' || string[6] == 'R') && - (string[7] == 'y' || string[7] == 'Y') && - (string[8] == '>')); -} - -static inline gboolean -is_keycode (const gchar *string) -{ - return (string[0] == '0' && - string[1] == 'x' && - g_ascii_isxdigit (string[2]) && - g_ascii_isxdigit (string[3])); -} - -static gboolean -accelerator_parse (const gchar *accelerator, - MetaKeyCombo *combo) -{ - guint keyval, keycode; - MetaVirtualModifier mods; - gint len; - - combo->keysym = 0; - combo->keycode = 0; - combo->modifiers = 0; - - if (accelerator == NULL) - return FALSE; - - keyval = 0; - keycode = 0; - mods = 0; - len = strlen (accelerator); - while (len) - { - if (*accelerator == '<') - { - if (len >= 9 && is_primary (accelerator)) - { - /* Primary is treated the same as Control */ - accelerator += 9; - len -= 9; - mods |= META_VIRTUAL_CONTROL_MASK; - } - else if (len >= 9 && is_control (accelerator)) - { - accelerator += 9; - len -= 9; - mods |= META_VIRTUAL_CONTROL_MASK; - } - else if (len >= 7 && is_shift (accelerator)) - { - accelerator += 7; - len -= 7; - mods |= META_VIRTUAL_SHIFT_MASK; - } - else if (len >= 6 && is_shft (accelerator)) - { - accelerator += 6; - len -= 6; - mods |= META_VIRTUAL_SHIFT_MASK; - } - else if (len >= 6 && is_ctrl (accelerator)) - { - accelerator += 6; - len -= 6; - mods |= META_VIRTUAL_CONTROL_MASK; - } - else if (len >= 6 && is_modx (accelerator)) - { - static const guint mod_vals[] = { - META_VIRTUAL_ALT_MASK, - META_VIRTUAL_MOD2_MASK, - META_VIRTUAL_MOD3_MASK, - META_VIRTUAL_MOD4_MASK, - META_VIRTUAL_MOD5_MASK, - }; - - len -= 6; - accelerator += 4; - mods |= mod_vals[*accelerator - '1']; - accelerator += 2; - } - else if (len >= 5 && is_ctl (accelerator)) - { - accelerator += 5; - len -= 5; - mods |= META_VIRTUAL_CONTROL_MASK; - } - else if (len >= 5 && is_alt (accelerator)) - { - accelerator += 5; - len -= 5; - mods |= META_VIRTUAL_ALT_MASK; - } - else if (len >= 6 && is_meta (accelerator)) - { - accelerator += 6; - len -= 6; - mods |= META_VIRTUAL_META_MASK; - } - else if (len >= 7 && is_hyper (accelerator)) - { - accelerator += 7; - len -= 7; - mods |= META_VIRTUAL_HYPER_MASK; - } - else if (len >= 7 && is_super (accelerator)) - { - accelerator += 7; - len -= 7; - mods |= META_VIRTUAL_SUPER_MASK; - } - else - { - gchar last_ch; - - last_ch = *accelerator; - while (last_ch && last_ch != '>') - { - last_ch = *accelerator; - accelerator += 1; - len -= 1; - } - } - } - else - { - if (len >= 4 && is_keycode (accelerator)) - { - keycode = strtoul (accelerator, NULL, 16); - goto out; - } - else if (strcmp (accelerator, "Above_Tab") == 0) - { - keyval = META_KEY_ABOVE_TAB; - goto out; - } - else - { - keyval = xkb_keysym_from_name (accelerator, XKB_KEYSYM_CASE_INSENSITIVE); - if (keyval == XKB_KEY_NoSymbol) - { - char *with_xf86 = g_strconcat ("XF86", accelerator, NULL); - keyval = xkb_keysym_from_name (with_xf86, XKB_KEYSYM_CASE_INSENSITIVE); - g_free (with_xf86); - - if (keyval == XKB_KEY_NoSymbol) - return FALSE; - } - } - - accelerator += len; - len -= len; - } - } - - out: - combo->keysym = keyval; - combo->keycode = keycode; - combo->modifiers = mods; - return TRUE; -} - -gboolean -meta_parse_accelerator (const char *accel, - MetaKeyCombo *combo) -{ - g_return_val_if_fail (combo != NULL, FALSE); - - *combo = (MetaKeyCombo) { 0 }; - - if (!accel[0] || strcmp (accel, "disabled") == 0) - return TRUE; - - return accelerator_parse (accel, combo); -} - -gboolean -meta_parse_modifier (const char *accel, - MetaVirtualModifier *mask) -{ - MetaKeyCombo combo = { 0 }; - - g_return_val_if_fail (mask != NULL, FALSE); - - *mask = 0; - - if (accel == NULL || !accel[0] || strcmp (accel, "disabled") == 0) - return TRUE; - - if (!accelerator_parse (accel, &combo)) - return FALSE; - - *mask = combo.modifiers; - return TRUE; -} diff --git a/src/core/meta-accel-parse.h b/src/core/meta-accel-parse.h deleted file mode 100644 index 12cf3f982..000000000 --- a/src/core/meta-accel-parse.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* - * Copyright (C) 2014 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Written by: - * Jasper St. Pierre <jstpierre@mecheye.net> - */ - -#ifndef META_ACCEL_PARSE_H -#define META_ACCEL_PARSE_H - -#include <glib.h> - -#include "meta/common.h" - -typedef struct _MetaKeyCombo MetaKeyCombo; - -/* Not a real key symbol but means "key above the tab key"; this is - * used as the default keybinding for cycle_group. - * 0x2xxxxxxx is a range not used by GDK or X. the remaining digits are - * randomly chosen */ -#define META_KEY_ABOVE_TAB 0x2f7259c9 - -gboolean meta_parse_accelerator (const char *accel, - MetaKeyCombo *combo); -gboolean meta_parse_modifier (const char *accel, - MetaVirtualModifier *mask); - -#endif /* META_ACCEL_PARSE_H */ diff --git a/src/core/meta-anonymous-file.c b/src/core/meta-anonymous-file.c deleted file mode 100644 index 95b63c9f0..000000000 --- a/src/core/meta-anonymous-file.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Copyright (C) 2020 Sebastian Wick - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Author: Sebastian Wick <sebastian@sebastianwick.net> - */ - -#include "config.h" - -#include <errno.h> -#include <fcntl.h> -#include <sys/mman.h> - -#include "core/meta-anonymous-file.h" - -struct _MetaAnonymousFile -{ - int fd; - size_t size; -}; - -#define READONLY_SEALS (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE) - -static int -create_tmpfile_cloexec (char *tmpname) -{ - int fd; - -#if defined(HAVE_MKOSTEMP) - fd = mkostemp (tmpname, O_CLOEXEC); - if (fd >= 0) - unlink (tmpname); -#else - fd = mkstemp (tmpname); - if (fd >= 0) - { - long flags; - - unlink (tmpname); - - flags = fcntl (fd, F_GETFD); - if (flags == -1 || - fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1) - { - close (fd); - return -1; - } - } -#endif - - return fd; -} - -/* - * Create a new, unique, anonymous file of the given size, and - * return the file descriptor for it. The file descriptor is set - * CLOEXEC. The file is immediately suitable for mmap()'ing - * the given size at offset zero. - * - * The file should not have a permanent backing store like a disk, - * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. - * - * The file name is deleted from the file system. - * - * The file is suitable for buffer sharing between processes by - * transmitting the file descriptor over Unix sockets using the - * SCM_RIGHTS methods. - * - * If the C library implements posix_fallocate(), it is used to - * guarantee that disk space is available for the file at the - * given size. If disk space is insufficient, errno is set to ENOSPC. - * If posix_fallocate() is not supported, program may receive - * SIGBUS on accessing mmap()'ed file contents instead. - * - * If the C library implements memfd_create(), it is used to create the - * file purely in memory, without any backing file name on the file - * system, and then sealing off the possibility of shrinking it. This - * can then be checked before accessing mmap()'ed file contents, to make - * sure SIGBUS can't happen. It also avoids requiring XDG_RUNTIME_DIR. - */ -static int -create_anonymous_file (off_t size) -{ - int fd, ret; - -#if defined(HAVE_MEMFD_CREATE) - fd = memfd_create ("mutter-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING); - if (fd >= 0) - { - /* We can add this seal before calling posix_fallocate(), as - * the file is currently zero-sized anyway. - * - * There is also no need to check for the return value, we - * couldn't do anything with it anyway. - */ - fcntl (fd, F_ADD_SEALS, F_SEAL_SHRINK); - } - else -#endif - { - static const char template[] = "/mutter-shared-XXXXXX"; - const char *path; - char *name; - - path = getenv ("XDG_RUNTIME_DIR"); - if (!path) - { - errno = ENOENT; - return -1; - } - - name = g_malloc (strlen (path) + sizeof (template)); - if (!name) - return -1; - - strcpy (name, path); - strcat (name, template); - - fd = create_tmpfile_cloexec (name); - - g_free (name); - - if (fd < 0) - return -1; - } - -#if defined(HAVE_POSIX_FALLOCATE) - do - { - ret = posix_fallocate (fd, 0, size); - } - while (ret == EINTR); - - if (ret != 0) - { - close (fd); - errno = ret; - return -1; - } -#else - do - { - ret = ftruncate (fd, size); - } - while (ret < 0 && errno == EINTR); - - if (ret < 0) - { - close (fd); - return -1; - } -#endif - - return fd; -} - -/** - * meta_anonymous_file_new: (skip) - * @size: The size of @data - * @data: The data of the file with the size @size - * - * Create a new anonymous read-only file of the given size and the given data - * The intended use-case is for sending mid-sized data from the compositor - * to clients. - * - * When done, free the data using meta_anonymous_file_free(). - * - * If this function fails errno is set. - * - * Returns: The newly created #MetaAnonymousFile, or NULL on failure. Use - * meta_anonymous_file_free() to free the resources when done. - */ -MetaAnonymousFile * -meta_anonymous_file_new (size_t size, - const uint8_t *data) -{ - MetaAnonymousFile *file; - void *map; - - file = g_malloc0 (sizeof *file); - if (!file) - { - errno = ENOMEM; - return NULL; - } - - file->size = size; - file->fd = create_anonymous_file (size); - if (file->fd == -1) - goto err_free; - - map = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, file->fd, 0); - if (map == MAP_FAILED) - goto err_close; - - memcpy (map, data, size); - - munmap (map, size); - -#if defined(HAVE_MEMFD_CREATE) - /* try to put seals on the file to make it read-only so that we can - * return the fd later directly when MAPMODE_SHARED is not set. - * meta_anonymous_file_open_fd can handle the fd even if it is not - * sealed read-only and will instead create a new anonymous file on - * each invocation. - */ - fcntl (file->fd, F_ADD_SEALS, READONLY_SEALS); -#endif - - return file; - -err_close: - close (file->fd); -err_free: - g_free (file); - return NULL; -} - - -/** - * meta_anonymous_file_free: (skip) - * @file: the #MetaAnonymousFile - * - * Free the resources used by an anonymous read-only file. - */ -void -meta_anonymous_file_free (MetaAnonymousFile *file) -{ - close (file->fd); - g_free (file); -} - -/** - * meta_anonymous_file_size: (skip) - * @file: the #MetaAnonymousFile - * - * Get the size of an anonymous read-only file. - * - * Returns: The size of the anonymous read-only file. - */ -size_t -meta_anonymous_file_size (MetaAnonymousFile *file) -{ - return file->size; -} - -/** - * meta_anonymous_file_open_fd: (skip) - * @file: the #MetaAnonymousFile to get a file descriptor for - * @mapmode: describes the ways in which the returned file descriptor can - * be used with mmap - * - * Returns a file descriptor for the given file, ready to be sent to a client. - * The returned file descriptor must not be shared between multiple clients. - * If @mapmode is %META_ANONYMOUS_FILE_MAPMODE_PRIVATE the file descriptor is - * only guaranteed to be mmapable with MAP_PRIVATE. If @mapmode is - * %META_ANONYMOUS_FILE_MAPMODE_SHARED the file descriptor can be mmaped with - * either MAP_PRIVATE or MAP_SHARED. - * - * In case %META_ANONYMOUS_FILE_MAPMODE_PRIVATE is used, it is important to - * only read the returned fd using mmap() since using read() will move the - * read cursor of the fd and thus may cause read() calls on other returned - * fds to fail. - * - * When done using the fd, it is required to call meta_anonymous_file_close_fd() - * instead of close(). - * - * If this function fails errno is set. - * - * Returns: A file descriptor for the given file that can be sent to a client - * or -1 on failure. Use meta_anonymous_file_close_fd() to release the fd - * when done. - */ -int -meta_anonymous_file_open_fd (MetaAnonymousFile *file, - MetaAnonymousFileMapmode mapmode) -{ - void *src, *dst; - int fd; - -#if defined(HAVE_MEMFD_CREATE) - int seals; - - seals = fcntl (file->fd, F_GET_SEALS); - - /* file was sealed for read-only and we don't have to support MAP_SHARED - * so we can simply pass the memfd fd - */ - if (seals != -1 && mapmode == META_ANONYMOUS_FILE_MAPMODE_PRIVATE && - (seals & READONLY_SEALS) == READONLY_SEALS) - return file->fd; -#endif - - /* for all other cases we create a new anonymous file that can be mapped - * with MAP_SHARED and copy the contents to it and return that instead - */ - fd = create_anonymous_file (file->size); - if (fd == -1) - return fd; - - src = mmap (NULL, file->size, PROT_READ, MAP_PRIVATE, file->fd, 0); - if (src == MAP_FAILED) - { - close (fd); - return -1; - } - - dst = mmap (NULL, file->size, PROT_WRITE, MAP_SHARED, fd, 0); - if (dst == MAP_FAILED) - { - close (fd); - munmap (src, file->size); - return -1; - } - - memcpy (dst, src, file->size); - munmap (src, file->size); - munmap (dst, file->size); - - return fd; -} - -/** - * meta_anonymous_file_close_fd: (skip) - * @fd: A file descriptor obtained using meta_anonymous_file_open_fd() - * - * Release a file descriptor returned by meta_anonymous_file_open_fd(). - * This function must be called for every file descriptor created with - * meta_anonymous_file_open_fd() to not leak any resources. - * - * If this function fails errno is set. - */ -void -meta_anonymous_file_close_fd (int fd) -{ -#if defined(HAVE_MEMFD_CREATE) - int seals; - - seals = fcntl (fd, F_GET_SEALS); - if (seals == -1 && errno != EINVAL) - { - g_warning ("Reading seals of anonymous file %d failed", fd); - return; - } - - /* The only case in which we do NOT have to close the file is when the file - * was sealed for read-only - */ - if (seals != -1 && (seals & READONLY_SEALS) == READONLY_SEALS) - return; -#endif - - close (fd); -} diff --git a/src/core/meta-anonymous-file.h b/src/core/meta-anonymous-file.h deleted file mode 100644 index 5289c7193..000000000 --- a/src/core/meta-anonymous-file.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2020 Sebastian Wick - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Author: Sebastian Wick <sebastian@sebastianwick.net> - */ - -#ifndef META_ANONYMOUS_FILE_H -#define META_ANONYMOUS_FILE_H - -#include "meta/common.h" -#include "core/util-private.h" - -typedef struct _MetaAnonymousFile MetaAnonymousFile; - -typedef enum _MetaAnonymousFileMapmode -{ - META_ANONYMOUS_FILE_MAPMODE_PRIVATE, - META_ANONYMOUS_FILE_MAPMODE_SHARED, -} MetaAnonymousFileMapmode; - -META_EXPORT_TEST -MetaAnonymousFile * meta_anonymous_file_new (size_t size, - const uint8_t *data); - -META_EXPORT_TEST -void meta_anonymous_file_free (MetaAnonymousFile *file); - -META_EXPORT_TEST -size_t meta_anonymous_file_size (MetaAnonymousFile *file); - -META_EXPORT_TEST -int meta_anonymous_file_open_fd (MetaAnonymousFile *file, - MetaAnonymousFileMapmode mapmode); - -META_EXPORT_TEST -void meta_anonymous_file_close_fd (int fd); - -#endif /* META_ANONYMOUS_FILE_H */ diff --git a/src/core/meta-border.c b/src/core/meta-border.c deleted file mode 100644 index 3c926c419..000000000 --- a/src/core/meta-border.c +++ /dev/null @@ -1,154 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2015 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Written by: - * Jonas Ådahl <jadahl@gmail.com> - */ - -#include "config.h" - -#include "core/meta-border.h" - -#include <math.h> - -static inline float -meta_vector2_cross_product (const MetaVector2 a, - const MetaVector2 b) -{ - return a.x * b.y - a.y * b.x; -} - -static inline MetaVector2 -meta_vector2_add (const MetaVector2 a, - const MetaVector2 b) -{ - return (MetaVector2) { - .x = a.x + b.x, - .y = a.y + b.y, - }; -} - -static inline MetaVector2 -meta_vector2_multiply_constant (const float c, - const MetaVector2 a) -{ - return (MetaVector2) { - .x = c * a.x, - .y = c * a.y, - }; -} - -gboolean -meta_line2_intersects_with (const MetaLine2 *line1, - const MetaLine2 *line2, - MetaVector2 *intersection) -{ - MetaVector2 p = line1->a; - MetaVector2 r = meta_vector2_subtract (line1->b, line1->a); - MetaVector2 q = line2->a; - MetaVector2 s = meta_vector2_subtract (line2->b, line2->a); - float rxs; - float sxr; - float t; - float u; - - /* - * The line (p, r) and (q, s) intersects where - * - * p + t r = q + u s - * - * Calculate t: - * - * (p + t r) × s = (q + u s) × s - * p × s + t (r × s) = q × s + u (s × s) - * p × s + t (r × s) = q × s - * t (r × s) = q × s - p × s - * t (r × s) = (q - p) × s - * t = ((q - p) × s) / (r × s) - * - * Using the same method, for u we get: - * - * u = ((p - q) × r) / (s × r) - */ - - rxs = meta_vector2_cross_product (r, s); - sxr = meta_vector2_cross_product (s, r); - - /* If r × s = 0 then the lines are either parallel or collinear. */ - if (fabsf (rxs) < FLT_MIN) - return FALSE; - - t = meta_vector2_cross_product (meta_vector2_subtract (q, p), s) / rxs; - u = meta_vector2_cross_product (meta_vector2_subtract (p, q), r) / sxr; - - /* The lines only intersect if 0 ≤ t ≤ 1 and 0 ≤ u ≤ 1. */ - if (t < 0.0 || t > 1.0 || u < 0.0 || u > 1.0) - return FALSE; - - *intersection = meta_vector2_add (p, meta_vector2_multiply_constant (t, r)); - - return TRUE; -} - -gboolean -meta_border_is_horizontal (MetaBorder *border) -{ - return border->line.a.y == border->line.b.y; -} - -gboolean -meta_border_is_blocking_directions (MetaBorder *border, - MetaBorderMotionDirection directions) -{ - if (meta_border_is_horizontal (border)) - { - if ((directions & (META_BORDER_MOTION_DIRECTION_POSITIVE_Y | - META_BORDER_MOTION_DIRECTION_NEGATIVE_Y)) == 0) - return FALSE; - } - else - { - if ((directions & (META_BORDER_MOTION_DIRECTION_POSITIVE_X | - META_BORDER_MOTION_DIRECTION_NEGATIVE_X)) == 0) - return FALSE; - } - - return (~border->blocking_directions & directions) != directions; -} - -unsigned int -meta_border_get_allows_directions (MetaBorder *border) -{ - return ~border->blocking_directions & - (META_BORDER_MOTION_DIRECTION_POSITIVE_X | - META_BORDER_MOTION_DIRECTION_POSITIVE_Y | - META_BORDER_MOTION_DIRECTION_NEGATIVE_X | - META_BORDER_MOTION_DIRECTION_NEGATIVE_Y); -} - -void -meta_border_set_allows_directions (MetaBorder *border, unsigned int directions) -{ - border->blocking_directions = - ~directions & (META_BORDER_MOTION_DIRECTION_POSITIVE_X | - META_BORDER_MOTION_DIRECTION_POSITIVE_Y | - META_BORDER_MOTION_DIRECTION_NEGATIVE_X | - META_BORDER_MOTION_DIRECTION_NEGATIVE_Y); -} diff --git a/src/core/meta-border.h b/src/core/meta-border.h deleted file mode 100644 index dd76db5c9..000000000 --- a/src/core/meta-border.h +++ /dev/null @@ -1,84 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2015 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Written by: - * Jonas Ådahl <jadahl@gmail.com> - */ - -#ifndef META_BORDER_H -#define META_BORDER_H - -#include <glib.h> - -typedef enum -{ - META_BORDER_MOTION_DIRECTION_POSITIVE_X = 1 << 0, - META_BORDER_MOTION_DIRECTION_POSITIVE_Y = 1 << 1, - META_BORDER_MOTION_DIRECTION_NEGATIVE_X = 1 << 2, - META_BORDER_MOTION_DIRECTION_NEGATIVE_Y = 1 << 3, -} MetaBorderMotionDirection; - -typedef struct _MetaVector2 -{ - float x; - float y; -} MetaVector2; - -typedef struct _MetaLine2 -{ - MetaVector2 a; - MetaVector2 b; -} MetaLine2; - -typedef struct _MetaBorder -{ - MetaLine2 line; - MetaBorderMotionDirection blocking_directions; -} MetaBorder; - -static inline MetaVector2 -meta_vector2_subtract (const MetaVector2 a, - const MetaVector2 b) -{ - return (MetaVector2) { - .x = a.x - b.x, - .y = a.y - b.y, - }; -} - -gboolean -meta_line2_intersects_with (const MetaLine2 *line1, - const MetaLine2 *line2, - MetaVector2 *intersection); - -gboolean -meta_border_is_horizontal (MetaBorder *border); - -gboolean -meta_border_is_blocking_directions (MetaBorder *border, - MetaBorderMotionDirection directions); - -unsigned int -meta_border_get_allows_directions (MetaBorder *border); - -void -meta_border_set_allows_directions (MetaBorder *border, unsigned int directions); - -#endif /* META_BORDER_H */ diff --git a/src/core/meta-clipboard-manager.c b/src/core/meta-clipboard-manager.c deleted file mode 100644 index 794195f41..000000000 --- a/src/core/meta-clipboard-manager.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Author: Carlos Garnacho <carlosg@gnome.org> - */ - -#include "config.h" - -#include "core/meta-clipboard-manager.h" -#include "meta/meta-selection-source-memory.h" - -#define MAX_TEXT_SIZE (4 * 1024 * 1024) /* 4MB */ -#define MAX_IMAGE_SIZE (200 * 1024 * 1024) /* 200MB */ - -/* Supported mimetype globs, from least to most preferred */ -static struct { - const char *mimetype_glob; - ssize_t max_transfer_size; -} supported_mimetypes[] = { - { "image/tiff", MAX_IMAGE_SIZE }, - { "image/bmp", MAX_IMAGE_SIZE }, - { "image/gif", MAX_IMAGE_SIZE }, - { "image/jpeg", MAX_IMAGE_SIZE }, - { "image/webp", MAX_IMAGE_SIZE }, - { "image/png", MAX_IMAGE_SIZE }, - { "image/svg+xml", MAX_IMAGE_SIZE }, - { "text/plain", MAX_TEXT_SIZE }, - { "text/plain;charset=utf-8", MAX_TEXT_SIZE }, -}; - -static gboolean -mimetype_match (const char *mimetype, - int *idx, - gssize *max_transfer_size) -{ - int i; - - for (i = 0; i < G_N_ELEMENTS (supported_mimetypes); i++) - { - if (g_pattern_match_simple (supported_mimetypes[i].mimetype_glob, mimetype)) - { - *max_transfer_size = supported_mimetypes[i].max_transfer_size; - *idx = i; - return TRUE; - } - } - - return FALSE; -} - -static void -transfer_cb (MetaSelection *selection, - GAsyncResult *result, - GOutputStream *output) -{ - MetaDisplay *display = meta_get_display (); - GError *error = NULL; - - if (!meta_selection_transfer_finish (selection, result, &error)) - { - g_warning ("Failed to store clipboard: %s", error->message); - g_error_free (error); - g_object_unref (output); - return; - } - - g_output_stream_close (output, NULL, NULL); - display->saved_clipboard = - g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (output)); - g_object_unref (output); -} - -static void -owner_changed_cb (MetaSelection *selection, - MetaSelectionType selection_type, - MetaSelectionSource *new_owner, - MetaDisplay *display) -{ - if (selection_type != META_SELECTION_CLIPBOARD) - return; - - if (new_owner && new_owner != display->selection_source) - { - GOutputStream *output; - GList *mimetypes, *l; - int best_idx = -1; - const char *best = NULL; - ssize_t transfer_size = -1; - - /* New selection source, find the best mimetype in order to - * keep a copy of it. - */ - g_clear_object (&display->selection_source); - g_clear_pointer (&display->saved_clipboard_mimetype, g_free); - g_clear_pointer (&display->saved_clipboard, g_bytes_unref); - - mimetypes = meta_selection_get_mimetypes (selection, selection_type); - - for (l = mimetypes; l; l = l->next) - { - gssize max_transfer_size; - int idx; - - if (!mimetype_match (l->data, &idx, &max_transfer_size)) - continue; - - if (best_idx < idx) - { - best_idx = idx; - best = l->data; - transfer_size = max_transfer_size; - } - } - - if (best_idx < 0) - { - g_list_free_full (mimetypes, g_free); - return; - } - - display->saved_clipboard_mimetype = g_strdup (best); - g_list_free_full (mimetypes, g_free); - output = g_memory_output_stream_new_resizable (); - meta_selection_transfer_async (selection, - META_SELECTION_CLIPBOARD, - display->saved_clipboard_mimetype, - transfer_size, - output, - NULL, - (GAsyncReadyCallback) transfer_cb, - output); - } - else if (!new_owner && display->saved_clipboard) - { - /* Old owner is gone, time to take over */ - new_owner = meta_selection_source_memory_new (display->saved_clipboard_mimetype, - display->saved_clipboard); - g_set_object (&display->selection_source, new_owner); - meta_selection_set_owner (selection, selection_type, new_owner); - g_object_unref (new_owner); - } -} - -void -meta_clipboard_manager_init (MetaDisplay *display) -{ - MetaSelection *selection; - - selection = meta_display_get_selection (display); - g_signal_connect_after (selection, "owner-changed", - G_CALLBACK (owner_changed_cb), display); -} - -void -meta_clipboard_manager_shutdown (MetaDisplay *display) -{ - MetaSelection *selection; - - g_clear_object (&display->selection_source); - g_clear_pointer (&display->saved_clipboard, g_bytes_unref); - g_clear_pointer (&display->saved_clipboard_mimetype, g_free); - selection = meta_display_get_selection (display); - g_signal_handlers_disconnect_by_func (selection, owner_changed_cb, display); -} diff --git a/src/core/meta-clipboard-manager.h b/src/core/meta-clipboard-manager.h deleted file mode 100644 index 2ba130f9b..000000000 --- a/src/core/meta-clipboard-manager.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Author: Carlos Garnacho <carlosg@gnome.org> - */ - -#ifndef META_CLIPBOARD_MANAGER_H -#define META_CLIPBOARD_MANAGER_H - -#include "core/display-private.h" - -void meta_clipboard_manager_init (MetaDisplay *display); -void meta_clipboard_manager_shutdown (MetaDisplay *display); - -#endif /* META_CLIPBOARD_MANAGER_H */ diff --git a/src/core/meta-close-dialog-default-private.h b/src/core/meta-close-dialog-default-private.h deleted file mode 100644 index f149d3686..000000000 --- a/src/core/meta-close-dialog-default-private.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2016 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - * Author: Carlos Garnacho <carlosg@gnome.org> - */ - -#ifndef META_CLOSE_DIALOG_DEFAULT_H -#define META_CLOSE_DIALOG_DEFAULT_H - -#include <glib-object.h> - -#include "meta/meta-plugin.h" - -#define META_TYPE_CLOSE_DIALOG_DEFAULT (meta_close_dialog_default_get_type ()) -G_DECLARE_FINAL_TYPE (MetaCloseDialogDefault, - meta_close_dialog_default, - META, CLOSE_DIALOG_DEFAULT, - GObject) - -MetaCloseDialog * meta_close_dialog_default_new (MetaWindow *window); - -#endif /* META_CLOSE_DIALOG_DEFAULT_H */ diff --git a/src/core/meta-close-dialog-default.c b/src/core/meta-close-dialog-default.c deleted file mode 100644 index 544a5b62d..000000000 --- a/src/core/meta-close-dialog-default.c +++ /dev/null @@ -1,281 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2001, 2002 Havoc Pennington - * Copyright (C) 2004 Elijah Newren - * Copyright (C) 2016 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - * Author: Carlos Garnacho <carlosg@gnome.org> - */ - -#define _XOPEN_SOURCE /* for kill() */ - -#include "config.h" - -#include "core/meta-close-dialog-default-private.h" -#include "meta/meta-close-dialog.h" - -#include <sys/types.h> -#include <sys/wait.h> -#include <signal.h> - -#include "core/util-private.h" -#include "core/window-private.h" -#include "x11/meta-x11-display-private.h" - -typedef struct _MetaCloseDialogDefaultPrivate MetaCloseDialogDefaultPrivate; - -struct _MetaCloseDialogDefault -{ - GObject parent_instance; - MetaWindow *window; - int dialog_pid; - guint child_watch_id; -}; - -enum -{ - PROP_0, - PROP_WINDOW, - N_PROPS -}; - -GParamSpec *pspecs[N_PROPS] = { NULL }; - -static void meta_close_dialog_iface_init (MetaCloseDialogInterface *iface); - -G_DEFINE_TYPE_WITH_CODE (MetaCloseDialogDefault, meta_close_dialog_default, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (META_TYPE_CLOSE_DIALOG, - meta_close_dialog_iface_init)) - -static void -dialog_exited (GPid pid, - int status, - gpointer user_data) -{ - MetaCloseDialogDefault *dialog = user_data; - - dialog->dialog_pid = -1; - - /* exit status of 0 means the user pressed "Force Quit" */ - if (WIFEXITED (status) && WEXITSTATUS (status) == 0) - g_signal_emit_by_name (dialog, "response", META_CLOSE_DIALOG_RESPONSE_FORCE_CLOSE); -} - -static void -present_existing_delete_dialog (MetaCloseDialogDefault *dialog) -{ - MetaWindow *window; - GSList *windows; - GSList *tmp; - - window = dialog->window; - - if (dialog->dialog_pid < 0) - return; - - meta_topic (META_DEBUG_PING, - "Presenting existing ping dialog for %s", - window->desc); - - /* Activate transient for window that belongs to - * mutter-dialog - */ - windows = meta_display_list_windows (window->display, META_LIST_DEFAULT); - tmp = windows; - - while (tmp != NULL) - { - MetaWindow *w = tmp->data; - - if (w->transient_for == window && w->res_class && - g_ascii_strcasecmp (w->res_class, "mutter-dialog") == 0) - { - meta_window_activate (w, CLUTTER_CURRENT_TIME); - break; - } - - tmp = tmp->next; - } - - g_slist_free (windows); -} - -static void -meta_close_dialog_default_show (MetaCloseDialog *dialog) -{ - MetaCloseDialogDefault *dialog_default = META_CLOSE_DIALOG_DEFAULT (dialog); - MetaWindow *window = dialog_default->window; - gchar *window_title, *window_content, *tmp; - GPid dialog_pid; - - if (dialog_default->dialog_pid >= 0) - { - present_existing_delete_dialog (dialog_default); - return; - } - - /* This is to get a better string if the title isn't representable - * in the locale encoding; actual conversion to UTF-8 is done inside - * meta_show_dialog */ - if (window->title && window->title[0]) - { - tmp = g_locale_from_utf8 (window->title, -1, NULL, NULL, NULL); - if (tmp == NULL) - window_title = NULL; - else - window_title = window->title; - g_free (tmp); - } - else - { - window_title = NULL; - } - - if (window_title) - /* Translators: %s is a window title */ - tmp = g_strdup_printf (_("“%s” is not responding."), window_title); - else - tmp = g_strdup (_("Application is not responding.")); - - window_content = g_strdup_printf ( - "<big><b>%s</b></big>\n\n%s", - tmp, - _("You may choose to wait a short while for it to " - "continue or force the application to quit entirely.")); - - dialog_pid = - meta_show_dialog ("--question", - window_content, NULL, - window->display->x11_display->screen_name, - _("_Force Quit"), _("_Wait"), - "face-sad-symbolic", window->xwindow, - NULL, NULL); - - g_free (window_content); - g_free (tmp); - - dialog_default->dialog_pid = dialog_pid; - g_child_watch_add (dialog_pid, dialog_exited, dialog); -} - -static void -meta_close_dialog_default_hide (MetaCloseDialog *dialog) -{ - MetaCloseDialogDefault *dialog_default; - - dialog_default = META_CLOSE_DIALOG_DEFAULT (dialog); - - g_clear_handle_id (&dialog_default->child_watch_id, g_source_remove); - - if (dialog_default->dialog_pid > -1) - { - kill (dialog_default->dialog_pid, SIGTERM); - dialog_default->dialog_pid = -1; - } -} - -static void -meta_close_dialog_iface_init (MetaCloseDialogInterface *iface) -{ - iface->show = meta_close_dialog_default_show; - iface->hide = meta_close_dialog_default_hide; -} - -static void -meta_close_dialog_default_finalize (GObject *object) -{ - MetaCloseDialogDefault *dialog; - - dialog = META_CLOSE_DIALOG_DEFAULT (object); - - g_clear_handle_id (&dialog->child_watch_id, g_source_remove); - - if (dialog->dialog_pid > -1) - { - kill (dialog->dialog_pid, SIGKILL); - dialog->dialog_pid = -1; - } - - G_OBJECT_CLASS (meta_close_dialog_default_parent_class)->finalize (object); -} - -static void -meta_close_dialog_default_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - MetaCloseDialogDefault *dialog; - - dialog = META_CLOSE_DIALOG_DEFAULT (object); - - switch (prop_id) - { - case PROP_WINDOW: - dialog->window = g_value_get_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -meta_close_dialog_default_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - MetaCloseDialogDefault *dialog; - - dialog = META_CLOSE_DIALOG_DEFAULT (object); - - switch (prop_id) - { - case PROP_WINDOW: - g_value_set_object (value, dialog->window); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -meta_close_dialog_default_class_init (MetaCloseDialogDefaultClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = meta_close_dialog_default_finalize; - object_class->set_property = meta_close_dialog_default_set_property; - object_class->get_property = meta_close_dialog_default_get_property; - - g_object_class_override_property (object_class, PROP_WINDOW, "window"); -} - -static void -meta_close_dialog_default_init (MetaCloseDialogDefault *dialog) -{ - dialog->dialog_pid = -1; -} - -MetaCloseDialog * -meta_close_dialog_default_new (MetaWindow *window) -{ - return g_object_new (META_TYPE_CLOSE_DIALOG_DEFAULT, - "window", window, - NULL); -} diff --git a/src/core/meta-close-dialog.c b/src/core/meta-close-dialog.c deleted file mode 100644 index 6d24fa570..000000000 --- a/src/core/meta-close-dialog.c +++ /dev/null @@ -1,144 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2016 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - * Author: Carlos Garnacho <carlosg@gnome.org> - */ - -#include "config.h" - -#include "core/window-private.h" -#include "meta/meta-close-dialog.h" -#include "meta/meta-enum-types.h" - -enum -{ - RESPONSE, - N_SIGNALS -}; - -guint dialog_signals[N_SIGNALS] = { 0 }; - -static GQuark quark_visible = 0; - -G_DEFINE_INTERFACE (MetaCloseDialog, meta_close_dialog, G_TYPE_OBJECT) - -static void -meta_close_dialog_default_init (MetaCloseDialogInterface *iface) -{ - g_object_interface_install_property (iface, - g_param_spec_object ("window", - "Window", - "Window", - META_TYPE_WINDOW, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY)); - dialog_signals[RESPONSE] = - g_signal_new ("response", - G_TYPE_FROM_INTERFACE (iface), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 1, META_TYPE_CLOSE_DIALOG_RESPONSE); - - quark_visible = g_quark_from_static_string ("meta-close-dialog-visible"); -} - -/** - * meta_close_dialog_show: - * @dialog: a #MetaCloseDialog - * - * Shows the close dialog. - **/ -void -meta_close_dialog_show (MetaCloseDialog *dialog) -{ - MetaCloseDialogInterface *iface; - - g_return_if_fail (META_IS_CLOSE_DIALOG (dialog)); - - iface = META_CLOSE_DIALOG_GET_IFACE (dialog); - iface->show (dialog); - g_object_set_qdata (G_OBJECT (dialog), quark_visible, GINT_TO_POINTER (TRUE)); -} - -/** - * meta_close_dialog_hide: - * @dialog: a #MetaCloseDialog - * - * Hides the close dialog. - **/ -void -meta_close_dialog_hide (MetaCloseDialog *dialog) -{ - MetaCloseDialogInterface *iface; - - g_return_if_fail (META_IS_CLOSE_DIALOG (dialog)); - - iface = META_CLOSE_DIALOG_GET_IFACE (dialog); - iface->hide (dialog); - g_object_steal_qdata (G_OBJECT (dialog), quark_visible); -} - -/** - * meta_close_dialog_response: - * @dialog: a #MetaCloseDialog - * @response: a #MetaCloseDialogResponse - * - * Responds and closes the dialog. To be called by #MetaCloseDialog - * implementations. - **/ -void -meta_close_dialog_response (MetaCloseDialog *dialog, - MetaCloseDialogResponse response) -{ - g_signal_emit (dialog, dialog_signals[RESPONSE], 0, response); - meta_close_dialog_hide (dialog); -} - -/** - * meta_close_dialog_is_visible: - * @dialog: a #MetaCloseDialog - * - * Returns whether @dialog is currently visible. - * - * Returns: #TRUE if @dialog is visible. - **/ -gboolean -meta_close_dialog_is_visible (MetaCloseDialog *dialog) -{ - return GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (dialog), quark_visible)); -} - -/** - * meta_close_dialog_focus: - * @dialog: a #MetaCloseDialog - * - * Call whenever @dialog should receive keyboard focus, - * usually when the window would. - **/ -void -meta_close_dialog_focus (MetaCloseDialog *dialog) -{ - MetaCloseDialogInterface *iface; - - g_return_if_fail (META_IS_CLOSE_DIALOG (dialog)); - - iface = META_CLOSE_DIALOG_GET_IFACE (dialog); - if (iface->focus) - iface->focus (dialog); -} diff --git a/src/core/meta-context-main.c b/src/core/meta-context-main.c deleted file mode 100644 index 8f669151a..000000000 --- a/src/core/meta-context-main.c +++ /dev/null @@ -1,702 +0,0 @@ -/* - * Copyright (C) 2001 Havoc Pennington - * Copyright (C) 2006 Elijah Newren - * Copyright (C) 2021 Red Hat Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - */ - -#include "config.h" - -#include "core/meta-context-main.h" - -#include <glib.h> -#include <gio/gio.h> - -#if defined(HAVE_NATIVE_BACKEND) && defined(HAVE_WAYLAND) -#include <systemd/sd-login.h> -#endif /* HAVE_WAYLAND && HAVE_NATIVE_BACKEND */ - -#include "backends/meta-monitor-manager-private.h" -#include "backends/meta-virtual-monitor.h" -#include "backends/x11/cm/meta-backend-x11-cm.h" -#include "meta/meta-backend.h" -#include "wayland/meta-wayland.h" -#include "x11/session.h" - -#ifdef HAVE_NATIVE_BACKEND -#include "backends/native/meta-backend-native.h" -#endif - -#ifdef HAVE_WAYLAND -#include "backends/x11/nested/meta-backend-x11-nested.h" -#endif - -typedef struct _MetaContextMainOptions -{ - struct { - char *display_name; - gboolean replace; - gboolean sync; - gboolean force; - } x11; - struct { - char *save_file; - char *client_id; - gboolean disable; - } sm; -#ifdef HAVE_WAYLAND - gboolean wayland; - gboolean nested; - gboolean no_x11; - char *wayland_display; -#endif -#ifdef HAVE_NATIVE_BACKEND - gboolean display_server; - gboolean headless; -#endif -#ifdef HAVE_NATIVE_BACKEND - GList *virtual_monitor_infos; -#endif -} MetaContextMainOptions; - -struct _MetaContextMain -{ - GObject parent; - - MetaContextMainOptions options; - - MetaCompositorType compositor_type; - - GList *persistent_virtual_monitors; -}; - -G_DEFINE_TYPE (MetaContextMain, meta_context_main, META_TYPE_CONTEXT) - -static gboolean -check_configuration (MetaContextMain *context_main, - GError **error) -{ -#ifdef HAVE_WAYLAND - if (context_main->options.x11.force && context_main->options.no_x11) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - "Can't run in X11 mode with no X11"); - return FALSE; - } - if (context_main->options.x11.force && context_main->options.wayland) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - "Can't run in X11 mode with Wayland enabled"); - return FALSE; - } - if (context_main->options.x11.force && context_main->options.nested) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - "Can't run in X11 mode nested"); - return FALSE; - } -#endif /* HAVE_WAYLAND */ - -#ifdef HAVE_NATIVE_BACKEND - if (context_main->options.x11.force && context_main->options.display_server) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - "Can't run in X11 mode as a display server"); - return FALSE; - } - - if (context_main->options.x11.force && context_main->options.headless) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - "Can't run in X11 mode headlessly"); - return FALSE; - } - - if (context_main->options.display_server && context_main->options.headless) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - "Can't run in display server mode headlessly"); - return FALSE; - } -#endif /* HAVE_NATIVE_BACKEND */ - - if (context_main->options.sm.save_file && - context_main->options.sm.client_id) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - "Can't specify both SM save file and SM client id"); - return FALSE; - } - - return TRUE; -} - -#if defined(HAVE_WAYLAND) && defined(HAVE_NATIVE_BACKEND) -static gboolean -session_type_is_supported (const char *session_type) -{ - return (g_strcmp0 (session_type, "x11") == 0) || - (g_strcmp0 (session_type, "wayland") == 0); -} - -static char * -find_session_type (GError **error) -{ - char **sessions = NULL; - char *session_id; - char *session_type; - const char *session_type_env; - gboolean is_tty = FALSE; - int ret, i; - - ret = sd_pid_get_session (0, &session_id); - if (ret == 0 && session_id != NULL) - { - ret = sd_session_get_type (session_id, &session_type); - free (session_id); - - if (ret == 0) - { - if (session_type_is_supported (session_type)) - goto out; - else - is_tty = g_strcmp0 (session_type, "tty") == 0; - free (session_type); - } - } - else if (sd_uid_get_sessions (getuid (), 1, &sessions) > 0) - { - for (i = 0; sessions[i] != NULL; i++) - { - ret = sd_session_get_type (sessions[i], &session_type); - - if (ret < 0) - continue; - - if (session_type_is_supported (session_type)) - { - g_strfreev (sessions); - goto out; - } - - free (session_type); - } - } - g_strfreev (sessions); - - session_type_env = g_getenv ("XDG_SESSION_TYPE"); - if (session_type_is_supported (session_type_env)) - { - /* The string should be freeable */ - session_type = strdup (session_type_env); - goto out; - } - - /* Legacy support for starting through xinit */ - if (is_tty && (g_getenv ("MUTTER_DISPLAY") || g_getenv ("DISPLAY"))) - { - session_type = strdup ("x11"); - goto out; - } - - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Unsupported session type"); - return NULL; - -out: - return session_type; -} -#else /* defined(HAVE_WAYLAND) && defined(HAVE_NATIVE_BACKEND) */ -static char * -find_session_type (GError **error) -{ - return g_strdup ("x11"); -} -#endif /* defined(HAVE_WAYLAND) && defined(HAVE_NATIVE_BACKEND) */ - -static MetaCompositorType -determine_compositor_type (MetaContextMain *context_main, - GError **error) -{ - g_autofree char *session_type = NULL; - -#ifdef HAVE_WAYLAND - if (context_main->options.wayland || -#ifdef HAVE_NATIVE_BACKEND - context_main->options.display_server || - context_main->options.headless || -#endif /* HAVE_NATIVE_BACKEND */ - context_main->options.nested) - return META_COMPOSITOR_TYPE_WAYLAND; -#endif /* HAVE_WAYLAND */ - - if (context_main->options.x11.force) - return META_COMPOSITOR_TYPE_X11; - - session_type = find_session_type (error); - if (!session_type) - return -1; - - if (strcmp (session_type, "x11") == 0) - return META_COMPOSITOR_TYPE_X11; -#ifdef HAVE_WAYLAND - else if (strcmp (session_type, "wayland") == 0) - return META_COMPOSITOR_TYPE_WAYLAND; -#endif - else - g_assert_not_reached (); -} - -static gboolean -meta_context_main_configure (MetaContext *context, - int *argc, - char ***argv, - GError **error) -{ - MetaContextMain *context_main = META_CONTEXT_MAIN (context); - MetaContextClass *context_class = - META_CONTEXT_CLASS (meta_context_main_parent_class); - - if (!context_class->configure (context, argc, argv, error)) - return FALSE; - - if (!check_configuration (context_main, error)) - return FALSE; - - context_main->compositor_type = determine_compositor_type (context_main, - error); - if (context_main->compositor_type == -1) - return FALSE; - -#ifdef HAVE_WAYLAND - if (context_main->options.wayland_display) - meta_wayland_override_display_name (context_main->options.wayland_display); -#endif - - return TRUE; -} - -static MetaCompositorType -meta_context_main_get_compositor_type (MetaContext *context) -{ - MetaContextMain *context_main = META_CONTEXT_MAIN (context); - - return context_main->compositor_type; -} - -static MetaX11DisplayPolicy -meta_context_main_get_x11_display_policy (MetaContext *context) -{ - MetaCompositorType compositor_type; -#ifdef HAVE_WAYLAND - MetaContextMain *context_main = META_CONTEXT_MAIN (context); - char *unit; -#endif - - compositor_type = meta_context_get_compositor_type (context); - switch (compositor_type) - { - case META_COMPOSITOR_TYPE_X11: - return META_X11_DISPLAY_POLICY_MANDATORY; - case META_COMPOSITOR_TYPE_WAYLAND: -#ifdef HAVE_WAYLAND - if (context_main->options.no_x11) - return META_X11_DISPLAY_POLICY_DISABLED; - else if (sd_pid_get_user_unit (0, &unit) < 0) - return META_X11_DISPLAY_POLICY_MANDATORY; - else - return META_X11_DISPLAY_POLICY_ON_DEMAND; -#else /* HAVE_WAYLAND */ - g_assert_not_reached (); -#endif /* HAVE_WAYLAND */ - } - - g_assert_not_reached (); -} - -static gboolean -meta_context_main_is_replacing (MetaContext *context) -{ - MetaContextMain *context_main = META_CONTEXT_MAIN (context); - - return context_main->options.x11.replace; -} - -#ifdef HAVE_NATIVE_BACKEND -static gboolean -add_persistent_virtual_monitors (MetaContextMain *context_main, - GError **error) -{ - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - GList *l; - - for (l = context_main->options.virtual_monitor_infos; l; l = l->next) - { - MetaVirtualMonitorInfo *info = l->data; - MetaVirtualMonitor *virtual_monitor; - - virtual_monitor = - meta_monitor_manager_create_virtual_monitor (monitor_manager, - info, - error); - if (!virtual_monitor) - { - g_prefix_error (error, "Failed to add virtual monitor: "); - return FALSE; - } - - context_main->persistent_virtual_monitors = - g_list_append (context_main->persistent_virtual_monitors, virtual_monitor); - } - - if (context_main->options.virtual_monitor_infos) - { - g_list_free_full (context_main->options.virtual_monitor_infos, - (GDestroyNotify) meta_virtual_monitor_info_free); - context_main->options.virtual_monitor_infos = NULL; - - meta_monitor_manager_reload (monitor_manager); - } - - return TRUE; -} -#endif - -static gboolean -meta_context_main_setup (MetaContext *context, - GError **error) -{ - MetaContextMain *context_main = META_CONTEXT_MAIN (context); - - if (!META_CONTEXT_CLASS (meta_context_main_parent_class)->setup (context, - error)) - return FALSE; - - meta_set_syncing (context_main->options.x11.sync || g_getenv ("MUTTER_SYNC")); - -#ifdef HAVE_NATIVE_BACKEND - if (!add_persistent_virtual_monitors (context_main, error)) - return FALSE; -#endif - - return TRUE; -} - -static MetaBackend * -create_x11_cm_backend (MetaContext *context, - GError **error) -{ - MetaContextMain *context_main = META_CONTEXT_MAIN (context); - -#ifdef HAVE_NATIVE_BACKEND - if (context_main->options.virtual_monitor_infos) - g_warning ("Ignoring added virtual monitors in X11 session"); -#endif - - return g_initable_new (META_TYPE_BACKEND_X11_CM, - NULL, error, - "context", context, - "display-name", context_main->options.x11.display_name, - NULL); -} - -#ifdef HAVE_WAYLAND -static MetaBackend * -create_nested_backend (MetaContext *context, - GError **error) -{ - return g_initable_new (META_TYPE_BACKEND_X11_NESTED, - NULL, error, - "context", context, - NULL); -} - -#ifdef HAVE_NATIVE_BACKEND -static MetaBackend * -create_headless_backend (MetaContext *context, - GError **error) -{ - return g_initable_new (META_TYPE_BACKEND_NATIVE, - NULL, error, - "context", context, - "headless", TRUE, - NULL); -} - -static MetaBackend * -create_native_backend (MetaContext *context, - GError **error) -{ - return g_initable_new (META_TYPE_BACKEND_NATIVE, - NULL, error, - "context", context, - NULL); -} -#endif /* HAVE_NATIVE_BACKEND */ -#endif /* HAVE_WAYLAND */ - -static MetaBackend * -meta_context_main_create_backend (MetaContext *context, - GError **error) -{ -#ifdef HAVE_WAYLAND - MetaContextMain *context_main = META_CONTEXT_MAIN (context); -#endif - MetaCompositorType compositor_type; - - compositor_type = meta_context_get_compositor_type (context); - switch (compositor_type) - { - case META_COMPOSITOR_TYPE_X11: - return create_x11_cm_backend (context, error); - case META_COMPOSITOR_TYPE_WAYLAND: -#ifdef HAVE_WAYLAND - if (context_main->options.nested) - return create_nested_backend (context, error); -#ifdef HAVE_NATIVE_BACKEND - else if (context_main->options.headless) - return create_headless_backend (context, error); - else - return create_native_backend (context, error); -#endif /* HAVE_NATIVE_BACKEND */ -#else /* HAVE_WAYLAND */ - g_assert_not_reached (); -#endif /* HAVE_WAYLAND */ - } - - g_assert_not_reached (); -} - -static void -meta_context_main_notify_ready (MetaContext *context) -{ - MetaContextMain *context_main = META_CONTEXT_MAIN (context); - - if (!context_main->options.sm.disable) - { - meta_session_init (context, - context_main->options.sm.client_id, - context_main->options.sm.save_file); - } - g_clear_pointer (&context_main->options.sm.client_id, g_free); - g_clear_pointer (&context_main->options.sm.save_file, g_free); -} - -#ifdef HAVE_NATIVE_BACKEND -static gboolean -add_virtual_monitor_cb (const char *option_name, - const char *value, - gpointer user_data, - GError **error) -{ - MetaContextMain *context_main = user_data; - int width, height; - float refresh_rate = 60.0; - - if (sscanf (value, "%dx%d@%f", - &width, &height, &refresh_rate) == 3 || - sscanf (value, "%dx%d", - &width, &height) == 2) - { - g_autofree char *serial = NULL; - MetaVirtualMonitorInfo *virtual_monitor; - int n_existing_virtual_monitor_infos; - - n_existing_virtual_monitor_infos = - g_list_length (context_main->options.virtual_monitor_infos); - serial = g_strdup_printf ("0x%.2x", n_existing_virtual_monitor_infos); - virtual_monitor = meta_virtual_monitor_info_new (width, - height, - refresh_rate, - "MetaVendor", - "MetaVirtualMonitor", - serial); - context_main->options.virtual_monitor_infos = - g_list_append (context_main->options.virtual_monitor_infos, - virtual_monitor); - return TRUE; - } - else - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - "Unrecognizable virtual monitor spec '%s'", value); - return FALSE; - } -} -#endif /* HAVE_NATIVE_BACKEND */ - -static void -meta_context_main_add_option_entries (MetaContextMain *context_main) -{ - MetaContext *context = META_CONTEXT (context_main); - GOptionEntry options[] = { - { - "replace", 'r', 0, G_OPTION_ARG_NONE, - &context_main->options.x11.replace, - N_("Replace the running window manager"), - NULL - }, - { - "display", 'd', 0, G_OPTION_ARG_STRING, - &context_main->options.x11.display_name, - N_("X Display to use"), - "DISPLAY" - }, - { - "sm-disable", 0, 0, G_OPTION_ARG_NONE, - &context_main->options.sm.disable, - N_("Disable connection to session manager"), - NULL - }, - { - "sm-client-id", 0, 0, G_OPTION_ARG_STRING, - &context_main->options.sm.client_id, - N_("Specify session management ID"), - "ID" - }, - { - "sm-save-file", 0, 0, G_OPTION_ARG_FILENAME, - &context_main->options.sm.save_file, - N_("Initialize session from savefile"), - "FILE" - }, - { - "sync", 0, 0, G_OPTION_ARG_NONE, - &context_main->options.x11.sync, - N_("Make X calls synchronous"), - NULL - }, -#ifdef HAVE_WAYLAND - { - "wayland", 0, 0, G_OPTION_ARG_NONE, - &context_main->options.wayland, - N_("Run as a wayland compositor"), - NULL - }, - { - "nested", 0, 0, G_OPTION_ARG_NONE, - &context_main->options.nested, - N_("Run as a nested compositor"), - NULL - }, - { - "no-x11", 0, 0, G_OPTION_ARG_NONE, - &context_main->options.no_x11, - N_("Run wayland compositor without starting Xwayland"), - NULL - }, - { - "wayland-display", 0, 0, G_OPTION_ARG_STRING, - &context_main->options.wayland_display, - N_("Specify Wayland display name to use"), - NULL - }, -#endif -#ifdef HAVE_NATIVE_BACKEND - { - "display-server", 0, 0, G_OPTION_ARG_NONE, - &context_main->options.display_server, - N_("Run as a full display server, rather than nested") - }, - { - "headless", 0, 0, G_OPTION_ARG_NONE, - &context_main->options.headless, - N_("Run as a headless display server") - }, - { - "virtual-monitor", 0, 0, G_OPTION_ARG_CALLBACK, - add_virtual_monitor_cb, - N_("Add persistent virtual monitor (WxH or WxH@R)") - }, -#endif - { - "x11", 0, 0, G_OPTION_ARG_NONE, - &context_main->options.x11.force, - N_("Run with X11 backend") - }, - { NULL } - }; - - meta_context_add_option_entries (context, options, GETTEXT_PACKAGE); -} - -/** - * meta_create_context: - * @name: Human readable name of display server or window manager - * - * Create a context. - * - * Returns: (transfer full): A new context instance. - */ -MetaContext * -meta_create_context (const char *name) -{ - return g_object_new (META_TYPE_CONTEXT_MAIN, - "name", name, - NULL); -} - -static void -meta_context_main_finalize (GObject *object) -{ -#ifdef HAVE_NATIVE_BACKEND - MetaContextMain *context_main = META_CONTEXT_MAIN (object); - - g_list_free_full (context_main->persistent_virtual_monitors, g_object_unref); - context_main->persistent_virtual_monitors = NULL; -#endif - - G_OBJECT_CLASS (meta_context_main_parent_class)->finalize (object); -} - -static void -meta_context_main_constructed (GObject *object) -{ - MetaContextMain *context_main = META_CONTEXT_MAIN (object); - - G_OBJECT_CLASS (meta_context_main_parent_class)->constructed (object); - - meta_context_main_add_option_entries (context_main); -} - -static void -meta_context_main_class_init (MetaContextMainClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - MetaContextClass *context_class = META_CONTEXT_CLASS (klass); - - object_class->finalize = meta_context_main_finalize; - object_class->constructed = meta_context_main_constructed; - - context_class->configure = meta_context_main_configure; - context_class->get_compositor_type = meta_context_main_get_compositor_type; - context_class->get_x11_display_policy = - meta_context_main_get_x11_display_policy; - context_class->is_replacing = meta_context_main_is_replacing; - context_class->setup = meta_context_main_setup; - context_class->create_backend = meta_context_main_create_backend; - context_class->notify_ready = meta_context_main_notify_ready; -} - -static void -meta_context_main_init (MetaContextMain *context_main) -{ - context_main->compositor_type = -1; -} diff --git a/src/core/meta-context-main.h b/src/core/meta-context-main.h deleted file mode 100644 index 9a8ce902b..000000000 --- a/src/core/meta-context-main.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2021 Red Hat Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - */ - -#ifndef META_CONTEXT_MAIN_H -#define META_CONTEXT_MAIN_H - -#include "core/meta-context-private.h" - -#define META_TYPE_CONTEXT_MAIN (meta_context_main_get_type ()) -G_DECLARE_FINAL_TYPE (MetaContextMain, meta_context_main, - META, CONTEXT_MAIN, - MetaContext) - -#endif /* META_CONTEXT_MAIN_H */ diff --git a/src/core/meta-context-private.h b/src/core/meta-context-private.h deleted file mode 100644 index a3b7f9b4b..000000000 --- a/src/core/meta-context-private.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2019 Red Hat Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - */ - -#ifndef META_CONTEXT_PRIVATE_H -#define META_CONTEXT_PRIVATE_H - -#include "core/meta-private-enums.h" -#include "core/util-private.h" -#include "meta/meta-backend.h" -#include "meta/meta-context.h" -#include "wayland/meta-wayland-types.h" - -struct _MetaContextClass -{ - GObjectClass parent_class; - - gboolean (* configure) (MetaContext *context, - int *argc, - char ***argv, - GError **error); - - MetaCompositorType (* get_compositor_type) (MetaContext *context); - - MetaX11DisplayPolicy (* get_x11_display_policy) (MetaContext *context); - - gboolean (* is_replacing) (MetaContext *context); - - gboolean (* setup) (MetaContext *context, - GError **error); - - MetaBackend * (* create_backend) (MetaContext *context, - GError **error); - - void (* notify_ready) (MetaContext *context); -}; - -const char * meta_context_get_name (MetaContext *context); - -const char * meta_context_get_gnome_wm_keybindings (MetaContext *context); - -META_EXPORT_TEST -MetaWaylandCompositor * meta_context_get_wayland_compositor (MetaContext *context); - -MetaX11DisplayPolicy meta_context_get_x11_display_policy (MetaContext *context); - -#endif /* META_CONTEXT_PRIVATE_H */ diff --git a/src/core/meta-context.c b/src/core/meta-context.c deleted file mode 100644 index 7bb0e5f46..000000000 --- a/src/core/meta-context.c +++ /dev/null @@ -1,568 +0,0 @@ -/* - * Copyright (C) 2019 Red Hat Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - */ - -#include "config.h" - -#include "core/meta-context-private.h" - -#include <locale.h> - -#include "backends/meta-backend-private.h" -#include "compositor/meta-plugin-manager.h" -#include "core/display-private.h" -#include "core/prefs-private.h" -#include "core/util-private.h" - -#ifdef HAVE_WAYLAND -#include "wayland/meta-wayland.h" -#endif - -enum -{ - PROP_0, - - PROP_NAME, - - N_PROPS -}; - -static GParamSpec *obj_props[N_PROPS]; - -typedef enum _MetaContextState -{ - META_CONTEXT_STATE_INIT, - META_CONTEXT_STATE_CONFIGURED, - META_CONTEXT_STATE_SETUP, - META_CONTEXT_STATE_STARTED, - META_CONTEXT_STATE_RUNNING, - META_CONTEXT_STATE_TERMINATED, -} MetaContextState; - -typedef struct _MetaContextPrivate -{ - char *name; - char *plugin_name; - GType plugin_gtype; - char *gnome_wm_keybindings; - - MetaContextState state; - - GOptionContext *option_context; - - MetaBackend *backend; - MetaDisplay *display; -#ifdef HAVE_WAYLAND - MetaWaylandCompositor *wayland_compositor; -#endif - - GMainLoop *main_loop; - GError *termination_error; -} MetaContextPrivate; - -G_DEFINE_TYPE_WITH_PRIVATE (MetaContext, meta_context, G_TYPE_OBJECT) - -void -meta_context_add_option_entries (MetaContext *context, - const GOptionEntry *entries, - const char *translation_domain) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - g_warn_if_fail (priv->state == META_CONTEXT_STATE_INIT); - - g_option_context_add_main_entries (priv->option_context, - entries, - translation_domain); -} - -void -meta_context_add_option_group (MetaContext *context, - GOptionGroup *group) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - g_warn_if_fail (priv->state == META_CONTEXT_STATE_INIT); - g_return_if_fail (priv->option_context); - - g_option_context_add_group (priv->option_context, group); -} - -void -meta_context_set_plugin_gtype (MetaContext *context, - GType plugin_gtype) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - g_return_if_fail (priv->state <= META_CONTEXT_STATE_CONFIGURED); - g_return_if_fail (!priv->plugin_name); - - priv->plugin_gtype = plugin_gtype; -} - -void -meta_context_set_plugin_name (MetaContext *context, - const char *plugin_name) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - g_return_if_fail (priv->state <= META_CONTEXT_STATE_CONFIGURED); - g_return_if_fail (priv->plugin_gtype == G_TYPE_NONE); - - priv->plugin_name = g_strdup (plugin_name); -} - -void -meta_context_set_gnome_wm_keybindings (MetaContext *context, - const char *wm_keybindings) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - g_return_if_fail (priv->state <= META_CONTEXT_STATE_CONFIGURED); - - g_clear_pointer (&priv->gnome_wm_keybindings, g_free); - priv->gnome_wm_keybindings = g_strdup (wm_keybindings); -} - -const char * -meta_context_get_gnome_wm_keybindings (MetaContext *context) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - return priv->gnome_wm_keybindings; -} - -void -meta_context_notify_ready (MetaContext *context) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - g_return_if_fail (priv->state == META_CONTEXT_STATE_STARTED || - priv->state == META_CONTEXT_STATE_RUNNING); - - META_CONTEXT_GET_CLASS (context)->notify_ready (context); -} - -const char * -meta_context_get_name (MetaContext *context) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - return priv->name; -} - -/** - * meta_context_get_backend: - * @context: The #MetaContext - * - * Returns: (transfer none): the #MetaBackend - */ -MetaBackend * -meta_context_get_backend (MetaContext *context) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - return priv->backend; -} - -/** - * meta_context_get_display: - * @context: The #MetaContext - * - * Returns: (transfer none): the #MetaDisplay - */ -MetaDisplay * -meta_context_get_display (MetaContext *context) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - return priv->display; -} - -#ifdef HAVE_WAYLAND -MetaWaylandCompositor * -meta_context_get_wayland_compositor (MetaContext *context) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - return priv->wayland_compositor; -} -#endif - -MetaCompositorType -meta_context_get_compositor_type (MetaContext *context) -{ - return META_CONTEXT_GET_CLASS (context)->get_compositor_type (context); -} - -gboolean -meta_context_is_replacing (MetaContext *context) -{ - return META_CONTEXT_GET_CLASS (context)->is_replacing (context); -} - -MetaX11DisplayPolicy -meta_context_get_x11_display_policy (MetaContext *context) -{ - return META_CONTEXT_GET_CLASS (context)->get_x11_display_policy (context); -} - -static gboolean -meta_context_real_configure (MetaContext *context, - int *argc, - char ***argv, - GError **error) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - g_autoptr (GOptionContext) option_context = NULL; - - if (!priv->option_context) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Tried to configure multiple times"); - return FALSE; - } - - option_context = g_steal_pointer (&priv->option_context); - return g_option_context_parse (option_context, argc, argv, error); -} - -gboolean -meta_context_configure (MetaContext *context, - int *argc, - char ***argv, - GError **error) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - MetaCompositorType compositor_type; - - g_warn_if_fail (priv->state == META_CONTEXT_STATE_INIT); - - if (!META_CONTEXT_GET_CLASS (context)->configure (context, argc, argv, error)) - { - priv->state = META_CONTEXT_STATE_TERMINATED; - return FALSE; - } - - compositor_type = meta_context_get_compositor_type (context); - switch (compositor_type) - { - case META_COMPOSITOR_TYPE_WAYLAND: - meta_set_is_wayland_compositor (TRUE); - break; - case META_COMPOSITOR_TYPE_X11: - meta_set_is_wayland_compositor (FALSE); - break; - } - - priv->state = META_CONTEXT_STATE_CONFIGURED; - - return TRUE; -} - -static const char * -compositor_type_to_description (MetaCompositorType compositor_type) -{ - switch (compositor_type) - { - case META_COMPOSITOR_TYPE_WAYLAND: - return "Wayland display server"; - case META_COMPOSITOR_TYPE_X11: - return "X11 window and compositing manager"; - } - - g_assert_not_reached (); -} - -static void -init_introspection (MetaContext *context) -{ -#ifdef HAVE_INTROSPECTION - g_irepository_prepend_search_path (MUTTER_PKGLIBDIR); -#endif -} - -static gboolean -meta_context_real_setup (MetaContext *context, - GError **error) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - MetaBackend *backend; - - backend = META_CONTEXT_GET_CLASS (context)->create_backend (context, error); - if (!backend) - return FALSE; - - priv->backend = backend; - return TRUE; -} - -gboolean -meta_context_setup (MetaContext *context, - GError **error) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - MetaCompositorType compositor_type; - - g_warn_if_fail (priv->state == META_CONTEXT_STATE_CONFIGURED); - - if (!priv->plugin_name && priv->plugin_gtype == G_TYPE_NONE) - { - priv->state = META_CONTEXT_STATE_TERMINATED; - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "No compositor plugin set"); - return FALSE; - } - - meta_init_debug_utils (); - - compositor_type = meta_context_get_compositor_type (context); - g_message ("Running %s (using mutter %s) as a %s", - priv->name, VERSION, - compositor_type_to_description (compositor_type)); - - if (priv->plugin_name) - meta_plugin_manager_load (priv->plugin_name); - else - meta_plugin_manager_set_plugin_type (priv->plugin_gtype); - - init_introspection (context); - - if (!META_CONTEXT_GET_CLASS (context)->setup (context, error)) - { - priv->state = META_CONTEXT_STATE_TERMINATED; - return FALSE; - } - - priv->state = META_CONTEXT_STATE_SETUP; - return TRUE; -} - -gboolean -meta_context_start (MetaContext *context, - GError **error) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - g_warn_if_fail (priv->state == META_CONTEXT_STATE_SETUP); - - meta_prefs_init (); - -#ifdef HAVE_WAYLAND - if (meta_context_get_compositor_type (context) == - META_COMPOSITOR_TYPE_WAYLAND) - priv->wayland_compositor = meta_wayland_compositor_new (context); -#endif - - priv->display = meta_display_new (context, error); - if (!priv->display) - { - priv->state = META_CONTEXT_STATE_TERMINATED; - return FALSE; - } - - priv->main_loop = g_main_loop_new (NULL, FALSE); - - priv->state = META_CONTEXT_STATE_STARTED; - - return TRUE; -} - -gboolean -meta_context_run_main_loop (MetaContext *context, - GError **error) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - g_warn_if_fail (priv->state == META_CONTEXT_STATE_STARTED); - if (!priv->main_loop) - { - priv->state = META_CONTEXT_STATE_TERMINATED; - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Tried to run main loop without having started"); - return FALSE; - } - - priv->state = META_CONTEXT_STATE_RUNNING; - g_main_loop_run (priv->main_loop); - priv->state = META_CONTEXT_STATE_TERMINATED; - g_clear_pointer (&priv->main_loop, g_main_loop_unref); - - if (priv->termination_error) - { - g_propagate_error (error, g_steal_pointer (&priv->termination_error)); - return FALSE; - } - - return TRUE; -} - -void -meta_context_terminate (MetaContext *context) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - g_warn_if_fail (priv->state == META_CONTEXT_STATE_RUNNING); - g_warn_if_fail (g_main_loop_is_running (priv->main_loop)); - - g_main_loop_quit (priv->main_loop); -} - -void -meta_context_terminate_with_error (MetaContext *context, - GError *error) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - priv->termination_error = g_steal_pointer (&error); - meta_context_terminate (context); -} - -void -meta_context_destroy (MetaContext *context) -{ - g_object_run_dispose (G_OBJECT (context)); - g_object_unref (context); -} - -static void -meta_context_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - MetaContext *context = META_CONTEXT (object); - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - switch (prop_id) - { - case PROP_NAME: - g_value_set_string (value, priv->name); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -meta_context_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - MetaContext *context = META_CONTEXT (object); - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - switch (prop_id) - { - case PROP_NAME: - priv->name = g_value_dup_string (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -meta_context_dispose (GObject *object) -{ - MetaContext *context = META_CONTEXT (object); - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - if (priv->backend) - meta_backend_prepare_shutdown (priv->backend); - -#ifdef HAVE_WAYLAND - if (priv->wayland_compositor) - meta_wayland_compositor_prepare_shutdown (priv->wayland_compositor); -#endif - - if (priv->display) - meta_display_close (priv->display, META_CURRENT_TIME); - g_clear_object (&priv->display); - -#ifdef HAVE_WAYLAND - g_clear_object (&priv->wayland_compositor); -#endif - - g_clear_pointer (&priv->backend, meta_backend_destroy); - - g_clear_pointer (&priv->option_context, g_option_context_free); - g_clear_pointer (&priv->main_loop, g_main_loop_unref); - - G_OBJECT_CLASS (meta_context_parent_class)->dispose (object); -} - -static void -meta_context_finalize (GObject *object) -{ - MetaContext *context = META_CONTEXT (object); - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - g_clear_pointer (&priv->gnome_wm_keybindings, g_free); - g_clear_pointer (&priv->plugin_name, g_free); - g_clear_pointer (&priv->name, g_free); - - G_OBJECT_CLASS (meta_context_parent_class)->finalize (object); -} - -static void -meta_context_class_init (MetaContextClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->get_property = meta_context_get_property; - object_class->set_property = meta_context_set_property; - object_class->dispose = meta_context_dispose; - object_class->finalize = meta_context_finalize; - - klass->configure = meta_context_real_configure; - klass->setup = meta_context_real_setup; - - obj_props[PROP_NAME] = - g_param_spec_string ("name", - "name", - "Human readable name", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - g_object_class_install_properties (object_class, N_PROPS, obj_props); -} - -static void -meta_context_init (MetaContext *context) -{ - MetaContextPrivate *priv = meta_context_get_instance_private (context); - - priv->plugin_gtype = G_TYPE_NONE; - priv->gnome_wm_keybindings = g_strdup ("Mutter"); - - if (!setlocale (LC_ALL, "")) - g_warning ("Locale not understood by C library"); - bindtextdomain (GETTEXT_PACKAGE, MUTTER_LOCALEDIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); - - priv->option_context = g_option_context_new (NULL); - g_option_context_set_main_group (priv->option_context, - g_option_group_new (NULL, NULL, NULL, - context, NULL)); -} diff --git a/src/core/meta-fraction.c b/src/core/meta-fraction.c deleted file mode 100644 index 8090aa411..000000000 --- a/src/core/meta-fraction.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * 2000 Wim Taymans <wtay@chello.be> - * 2002 Thomas Vander Stichele <thomas@apestaart.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - * Fraction utility functions in this file comes from gstutils.c in gstreamer. - */ - -#include "config.h" - -#include "core/meta-fraction.h" - -#include <glib.h> -#include <math.h> - -#define MAX_TERMS 30 -#define MIN_DIVISOR 1.0e-10 -#define MAX_ERROR 1.0e-20 - -static int -greatest_common_divisor (int a, - int b) -{ - while (b != 0) - { - int temp = a; - - a = b; - b = temp % b; - } - - return ABS (a); -} - -MetaFraction -meta_fraction_from_double (double src) -{ - double V, F; /* double being converted */ - int N, D; /* will contain the result */ - int A; /* current term in continued fraction */ - int64_t N1, D1; /* numerator, denominator of last approx */ - int64_t N2, D2; /* numerator, denominator of previous approx */ - int i; - int gcd; - gboolean negative = FALSE; - - /* initialize fraction being converted */ - F = src; - if (F < 0.0) - { - F = -F; - negative = TRUE; - } - - V = F; - /* initialize fractions with 1/0, 0/1 */ - N1 = 1; - D1 = 0; - N2 = 0; - D2 = 1; - N = 1; - D = 1; - - for (i = 0; i < MAX_TERMS; i++) - { - /* get next term */ - A = (gint) F; /* no floor() needed, F is always >= 0 */ - /* get new divisor */ - F = F - A; - - /* calculate new fraction in temp */ - N2 = N1 * A + N2; - D2 = D1 * A + D2; - - /* guard against overflow */ - if (N2 > G_MAXINT || D2 > G_MAXINT) - break; - - N = N2; - D = D2; - - /* save last two fractions */ - N2 = N1; - D2 = D1; - N1 = N; - D1 = D; - - /* quit if dividing by zero or close enough to target */ - if (F < MIN_DIVISOR || fabs (V - ((gdouble) N) / D) < MAX_ERROR) - break; - - /* Take reciprocal */ - F = 1 / F; - } - - /* fix for overflow */ - if (D == 0) - { - N = G_MAXINT; - D = 1; - } - - /* fix for negative */ - if (negative) - N = -N; - - /* simplify */ - gcd = greatest_common_divisor (N, D); - if (gcd) - { - N /= gcd; - D /= gcd; - } - - return (MetaFraction) { - .num = N, - .denom = D - }; -} diff --git a/src/core/meta-fraction.h b/src/core/meta-fraction.h deleted file mode 100644 index f7ab294cd..000000000 --- a/src/core/meta-fraction.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2017 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef META_FRACTION_H -#define META_FRACTION_H - -typedef struct _MetaFraction -{ - int num; - int denom; -} MetaFraction; - -MetaFraction meta_fraction_from_double (double src); - -#endif /* META_FRACTION_H */ diff --git a/src/core/meta-gesture-tracker-private.h b/src/core/meta-gesture-tracker-private.h deleted file mode 100644 index e7bfc5472..000000000 --- a/src/core/meta-gesture-tracker-private.h +++ /dev/null @@ -1,70 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2014 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Author: Carlos Garnacho <carlosg@gnome.org> - */ - -#ifndef META_GESTURE_TRACKER_PRIVATE_H -#define META_GESTURE_TRACKER_PRIVATE_H - -#include <glib-object.h> - -#include "backends/meta-backend-private.h" -#include "clutter/clutter.h" -#include "meta/window.h" - -#define META_TYPE_GESTURE_TRACKER (meta_gesture_tracker_get_type ()) -#define META_GESTURE_TRACKER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_GESTURE_TRACKER, MetaGestureTracker)) -#define META_GESTURE_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_GESTURE_TRACKER, MetaGestureTrackerClass)) -#define META_IS_GESTURE_TRACKER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_GESTURE_TRACKER)) -#define META_IS_GESTURE_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_GESTURE_TRACKER)) -#define META_GESTURE_TRACKER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_GESTURE_TRACKER, MetaGestureTrackerClass)) - -typedef struct _MetaGestureTracker MetaGestureTracker; -typedef struct _MetaGestureTrackerClass MetaGestureTrackerClass; - -struct _MetaGestureTracker -{ - GObject parent_instance; -}; - -struct _MetaGestureTrackerClass -{ - GObjectClass parent_class; - - void (* state_changed) (MetaGestureTracker *tracker, - ClutterEventSequence *sequence, - MetaSequenceState state); -}; - -GType meta_gesture_tracker_get_type (void) G_GNUC_CONST; - -MetaGestureTracker * meta_gesture_tracker_new (void); - -gboolean meta_gesture_tracker_handle_event (MetaGestureTracker *tracker, - const ClutterEvent *event); -gboolean meta_gesture_tracker_set_sequence_state (MetaGestureTracker *tracker, - ClutterEventSequence *sequence, - MetaSequenceState state); -MetaSequenceState meta_gesture_tracker_get_sequence_state (MetaGestureTracker *tracker, - ClutterEventSequence *sequence); -gint meta_gesture_tracker_get_n_current_touches (MetaGestureTracker *tracker); - -#endif /* META_GESTURE_TRACKER_PRIVATE_H */ diff --git a/src/core/meta-gesture-tracker.c b/src/core/meta-gesture-tracker.c deleted file mode 100644 index 2badf8b82..000000000 --- a/src/core/meta-gesture-tracker.c +++ /dev/null @@ -1,581 +0,0 @@ -/* - * Copyright (C) 2014 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Author: Carlos Garnacho <carlosg@gnome.org> - */ - -/** - * SECTION:gesture-tracker - * @Title: MetaGestureTracker - * @Short_Description: Manages gestures on windows/desktop - * - * Forwards touch events to clutter actors, and accepts/rejects touch sequences - * based on the outcome of those. - */ - -#include "config.h" - -#include "core/meta-gesture-tracker-private.h" - -#include "compositor/meta-surface-actor.h" - -#define DISTANCE_THRESHOLD 30 - -typedef struct _MetaGestureTrackerPrivate MetaGestureTrackerPrivate; -typedef struct _GestureActionData GestureActionData; -typedef struct _MetaSequenceInfo MetaSequenceInfo; - -struct _MetaSequenceInfo -{ - MetaGestureTracker *tracker; - ClutterEventSequence *sequence; - MetaSequenceState state; - guint autodeny_timeout_id; - gfloat start_x; - gfloat start_y; -}; - -struct _GestureActionData -{ - ClutterGestureAction *gesture; - MetaSequenceState state; - gulong gesture_begin_id; - gulong gesture_end_id; - gulong gesture_cancel_id; -}; - -struct _MetaGestureTrackerPrivate -{ - GHashTable *sequences; /* Hashtable of ClutterEventSequence->MetaSequenceInfo */ - - MetaSequenceState stage_state; - GArray *stage_gestures; /* Array of GestureActionData */ - GList *listeners; /* List of ClutterGestureAction */ - guint autodeny_timeout; -}; - -enum -{ - PROP_0, - PROP_AUTODENY_TIMEOUT, - PROP_LAST, -}; - -static GParamSpec *obj_props[PROP_LAST]; - -enum -{ - STATE_CHANGED, - N_SIGNALS -}; - -static guint signals[N_SIGNALS] = { 0 }; - -#define DEFAULT_AUTODENY_TIMEOUT 150 - -static void meta_gesture_tracker_untrack_stage (MetaGestureTracker *tracker); - -G_DEFINE_TYPE_WITH_PRIVATE (MetaGestureTracker, meta_gesture_tracker, G_TYPE_OBJECT) - -static void -meta_gesture_tracker_finalize (GObject *object) -{ - MetaGestureTrackerPrivate *priv; - - priv = meta_gesture_tracker_get_instance_private (META_GESTURE_TRACKER (object)); - - g_hash_table_destroy (priv->sequences); - g_array_free (priv->stage_gestures, TRUE); - g_list_free (priv->listeners); - - G_OBJECT_CLASS (meta_gesture_tracker_parent_class)->finalize (object); -} - -static void -meta_gesture_tracker_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - MetaGestureTrackerPrivate *priv; - - priv = meta_gesture_tracker_get_instance_private (META_GESTURE_TRACKER (object)); - - switch (prop_id) - { - case PROP_AUTODENY_TIMEOUT: - priv->autodeny_timeout = g_value_get_uint (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -meta_gesture_tracker_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - MetaGestureTrackerPrivate *priv; - - priv = meta_gesture_tracker_get_instance_private (META_GESTURE_TRACKER (object)); - - switch (prop_id) - { - case PROP_AUTODENY_TIMEOUT: - g_value_set_uint (value, priv->autodeny_timeout); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -meta_gesture_tracker_class_init (MetaGestureTrackerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = meta_gesture_tracker_finalize; - object_class->set_property = meta_gesture_tracker_set_property; - object_class->get_property = meta_gesture_tracker_get_property; - - obj_props[PROP_AUTODENY_TIMEOUT] = g_param_spec_uint ("autodeny-timeout", - "Auto-deny timeout", - "Auto-deny timeout", - 0, G_MAXUINT, DEFAULT_AUTODENY_TIMEOUT, - G_PARAM_STATIC_STRINGS | - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY); - - g_object_class_install_properties (object_class, PROP_LAST, obj_props); - - signals[STATE_CHANGED] = - g_signal_new ("state-changed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MetaGestureTrackerClass, state_changed), - NULL, NULL, NULL, - G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT); -} - -static gboolean -autodeny_sequence (gpointer user_data) -{ - MetaSequenceInfo *info = user_data; - - /* Deny the sequence automatically after the given timeout */ - if (info->state == META_SEQUENCE_NONE) - meta_gesture_tracker_set_sequence_state (info->tracker, info->sequence, - META_SEQUENCE_REJECTED); - - info->autodeny_timeout_id = 0; - return G_SOURCE_REMOVE; -} - -static MetaSequenceInfo * -meta_sequence_info_new (MetaGestureTracker *tracker, - const ClutterEvent *event) -{ - MetaGestureTrackerPrivate *priv; - MetaSequenceInfo *info; - guint ms; - - priv = meta_gesture_tracker_get_instance_private (tracker); - ms = priv->autodeny_timeout; - - info = g_new0 (MetaSequenceInfo, 1); - info->tracker = tracker; - info->sequence = event->touch.sequence; - info->state = META_SEQUENCE_NONE; - info->autodeny_timeout_id = g_timeout_add (ms, autodeny_sequence, info); - - clutter_event_get_coords (event, &info->start_x, &info->start_y); - - return info; -} - -static void -meta_sequence_info_free (MetaSequenceInfo *info) -{ - g_clear_handle_id (&info->autodeny_timeout_id, g_source_remove); - - if (info->state == META_SEQUENCE_NONE) - meta_gesture_tracker_set_sequence_state (info->tracker, info->sequence, - META_SEQUENCE_REJECTED); - g_free (info); -} - -static gboolean -state_is_applicable (MetaSequenceState prev_state, - MetaSequenceState state) -{ - if (prev_state == META_SEQUENCE_PENDING_END) - return FALSE; - - /* Don't allow reverting to none */ - if (state == META_SEQUENCE_NONE) - return FALSE; - - /* PENDING_END state is final */ - if (prev_state == META_SEQUENCE_PENDING_END) - return FALSE; - - /* Sequences must be accepted/denied before PENDING_END */ - if (prev_state == META_SEQUENCE_NONE && - state == META_SEQUENCE_PENDING_END) - return FALSE; - - /* Make sequences stick to their accepted/denied state */ - if (state != META_SEQUENCE_PENDING_END && - prev_state != META_SEQUENCE_NONE) - return FALSE; - - return TRUE; -} - -static gboolean -meta_gesture_tracker_set_state (MetaGestureTracker *tracker, - MetaSequenceState state) -{ - MetaGestureTrackerPrivate *priv; - ClutterEventSequence *sequence; - GHashTableIter iter; - - priv = meta_gesture_tracker_get_instance_private (tracker); - - if (priv->stage_state != state && - !state_is_applicable (priv->stage_state, state)) - return FALSE; - - g_hash_table_iter_init (&iter, priv->sequences); - priv->stage_state = state; - - while (g_hash_table_iter_next (&iter, (gpointer*) &sequence, NULL)) - meta_gesture_tracker_set_sequence_state (tracker, sequence, state); - - return TRUE; -} - -static gboolean -gesture_begin_cb (ClutterGestureAction *gesture, - ClutterActor *actor, - MetaGestureTracker *tracker) -{ - MetaGestureTrackerPrivate *priv; - - priv = meta_gesture_tracker_get_instance_private (tracker); - - if (!g_list_find (priv->listeners, gesture) && - meta_gesture_tracker_set_state (tracker, META_SEQUENCE_ACCEPTED)) - priv->listeners = g_list_prepend (priv->listeners, gesture); - - return TRUE; -} - -static void -gesture_end_cb (ClutterGestureAction *gesture, - ClutterActor *actor, - MetaGestureTracker *tracker) -{ - MetaGestureTrackerPrivate *priv; - - priv = meta_gesture_tracker_get_instance_private (tracker); - priv->listeners = g_list_remove (priv->listeners, gesture); - - if (!priv->listeners) - meta_gesture_tracker_untrack_stage (tracker); -} - -static void -gesture_cancel_cb (ClutterGestureAction *gesture, - ClutterActor *actor, - MetaGestureTracker *tracker) -{ - MetaGestureTrackerPrivate *priv; - - priv = meta_gesture_tracker_get_instance_private (tracker); - - if (g_list_find (priv->listeners, gesture)) - { - priv->listeners = g_list_remove (priv->listeners, gesture); - - if (!priv->listeners) - meta_gesture_tracker_set_state (tracker, META_SEQUENCE_PENDING_END); - } -} - -static gboolean -cancel_and_unref_gesture_cb (ClutterGestureAction *action) -{ - clutter_gesture_action_cancel (action); - g_object_unref (action); - return G_SOURCE_REMOVE; -} - -static void -clear_gesture_data (GestureActionData *data) -{ - g_clear_signal_handler (&data->gesture_begin_id, data->gesture); - g_clear_signal_handler (&data->gesture_end_id, data->gesture); - g_clear_signal_handler (&data->gesture_cancel_id, data->gesture); - - /* Defer cancellation to an idle, as it may happen within event handling */ - g_idle_add ((GSourceFunc) cancel_and_unref_gesture_cb, data->gesture); -} - -static void -meta_gesture_tracker_init (MetaGestureTracker *tracker) -{ - MetaGestureTrackerPrivate *priv; - - priv = meta_gesture_tracker_get_instance_private (tracker); - priv->sequences = g_hash_table_new_full (NULL, NULL, NULL, - (GDestroyNotify) meta_sequence_info_free); - priv->stage_gestures = g_array_new (FALSE, FALSE, sizeof (GestureActionData)); - g_array_set_clear_func (priv->stage_gestures, (GDestroyNotify) clear_gesture_data); -} - -MetaGestureTracker * -meta_gesture_tracker_new (void) -{ - return g_object_new (META_TYPE_GESTURE_TRACKER, NULL); -} - -static void -meta_gesture_tracker_track_stage (MetaGestureTracker *tracker, - ClutterActor *stage) -{ - MetaGestureTrackerPrivate *priv; - GList *actions, *l; - - priv = meta_gesture_tracker_get_instance_private (tracker); - actions = clutter_actor_get_actions (stage); - - for (l = actions; l; l = l->next) - { - GestureActionData data; - - if (!CLUTTER_IS_GESTURE_ACTION (l->data)) - continue; - - data.gesture = g_object_ref (l->data); - data.state = META_SEQUENCE_NONE; - data.gesture_begin_id = - g_signal_connect (data.gesture, "gesture-begin", - G_CALLBACK (gesture_begin_cb), tracker); - data.gesture_end_id = - g_signal_connect (data.gesture, "gesture-end", - G_CALLBACK (gesture_end_cb), tracker); - data.gesture_cancel_id = - g_signal_connect (data.gesture, "gesture-cancel", - G_CALLBACK (gesture_cancel_cb), tracker); - g_array_append_val (priv->stage_gestures, data); - } - - g_list_free (actions); -} - -static void -meta_gesture_tracker_untrack_stage (MetaGestureTracker *tracker) -{ - MetaGestureTrackerPrivate *priv; - - priv = meta_gesture_tracker_get_instance_private (tracker); - priv->stage_state = META_SEQUENCE_NONE; - - g_hash_table_remove_all (priv->sequences); - - if (priv->stage_gestures->len > 0) - g_array_remove_range (priv->stage_gestures, 0, priv->stage_gestures->len); - - g_list_free (priv->listeners); - priv->listeners = NULL; -} - -gboolean -meta_gesture_tracker_handle_event (MetaGestureTracker *tracker, - const ClutterEvent *event) -{ - MetaGestureTrackerPrivate *priv; - ClutterEventSequence *sequence; - MetaSequenceState state; - MetaSequenceInfo *info; - ClutterActor *stage; - gfloat x, y; - - sequence = clutter_event_get_event_sequence (event); - - if (!sequence) - return FALSE; - - priv = meta_gesture_tracker_get_instance_private (tracker); - stage = CLUTTER_ACTOR (clutter_event_get_stage (event)); - - switch (event->type) - { - case CLUTTER_TOUCH_BEGIN: - if (g_hash_table_size (priv->sequences) == 0) - meta_gesture_tracker_track_stage (tracker, stage); - - info = meta_sequence_info_new (tracker, event); - g_hash_table_insert (priv->sequences, sequence, info); - - if (priv->stage_gestures->len == 0) - { - /* If no gestures are attached, reject the sequence right away */ - meta_gesture_tracker_set_sequence_state (tracker, sequence, - META_SEQUENCE_REJECTED); - } - else if (priv->stage_state != META_SEQUENCE_NONE) - { - /* Make the sequence state match the general state */ - meta_gesture_tracker_set_sequence_state (tracker, sequence, - priv->stage_state); - } - state = info->state; - break; - case CLUTTER_TOUCH_END: - info = g_hash_table_lookup (priv->sequences, sequence); - - if (!info) - return FALSE; - - /* If nothing was done yet about the sequence, reject it so X11 - * clients may see it - */ - if (info->state == META_SEQUENCE_NONE) - meta_gesture_tracker_set_sequence_state (tracker, sequence, - META_SEQUENCE_REJECTED); - - state = info->state; - g_hash_table_remove (priv->sequences, sequence); - - if (g_hash_table_size (priv->sequences) == 0) - meta_gesture_tracker_untrack_stage (tracker); - break; - case CLUTTER_TOUCH_UPDATE: - info = g_hash_table_lookup (priv->sequences, sequence); - - if (!info) - return FALSE; - - clutter_event_get_coords (event, &x, &y); - - if (info->state == META_SEQUENCE_NONE && - (ABS (info->start_x - x) > DISTANCE_THRESHOLD || - ABS (info->start_y - y) > DISTANCE_THRESHOLD)) - meta_gesture_tracker_set_sequence_state (tracker, sequence, - META_SEQUENCE_REJECTED); - state = info->state; - break; - default: - return FALSE; - break; - } - - /* As soon as a sequence is accepted, we replay it to - * the stage as a captured event, and make sure it's never - * propagated anywhere else. Since ClutterGestureAction does - * all its event handling from a captured-event handler on - * the stage, this effectively acts as a "sequence grab" on - * gesture actions. - * - * Sequences that aren't (yet or never) in an accepted state - * will go through, these events will get processed through - * the compositor, and eventually through clutter, still - * triggering the gestures capturing events on the stage, and - * possibly resulting in MetaSequenceState changes. - */ - if (state == META_SEQUENCE_ACCEPTED) - { - clutter_actor_event (CLUTTER_ACTOR (clutter_event_get_stage (event)), - event, TRUE); - return TRUE; - } - - return FALSE; -} - -gboolean -meta_gesture_tracker_set_sequence_state (MetaGestureTracker *tracker, - ClutterEventSequence *sequence, - MetaSequenceState state) -{ - MetaGestureTrackerPrivate *priv; - MetaSequenceInfo *info; - - g_return_val_if_fail (META_IS_GESTURE_TRACKER (tracker), FALSE); - - priv = meta_gesture_tracker_get_instance_private (tracker); - info = g_hash_table_lookup (priv->sequences, sequence); - - if (!info) - return FALSE; - else if (state == info->state) - return TRUE; - - if (!state_is_applicable (info->state, state)) - return FALSE; - - /* Unset autodeny timeout */ - g_clear_handle_id (&info->autodeny_timeout_id, g_source_remove); - - info->state = state; - g_signal_emit (tracker, signals[STATE_CHANGED], 0, sequence, info->state); - - /* If the sequence was denied, set immediately to PENDING_END after emission */ - if (state == META_SEQUENCE_REJECTED) - { - info->state = META_SEQUENCE_PENDING_END; - g_signal_emit (tracker, signals[STATE_CHANGED], 0, sequence, info->state); - } - - return TRUE; -} - -MetaSequenceState -meta_gesture_tracker_get_sequence_state (MetaGestureTracker *tracker, - ClutterEventSequence *sequence) -{ - MetaGestureTrackerPrivate *priv; - MetaSequenceInfo *info; - - g_return_val_if_fail (META_IS_GESTURE_TRACKER (tracker), META_SEQUENCE_PENDING_END); - - priv = meta_gesture_tracker_get_instance_private (tracker); - info = g_hash_table_lookup (priv->sequences, sequence); - - if (!info) - return META_SEQUENCE_PENDING_END; - - return info->state; -} - -gint -meta_gesture_tracker_get_n_current_touches (MetaGestureTracker *tracker) -{ - MetaGestureTrackerPrivate *priv; - - g_return_val_if_fail (META_IS_GESTURE_TRACKER (tracker), 0); - - priv = meta_gesture_tracker_get_instance_private (tracker); - return g_hash_table_size (priv->sequences); -} diff --git a/src/core/meta-inhibit-shortcuts-dialog-default-private.h b/src/core/meta-inhibit-shortcuts-dialog-default-private.h deleted file mode 100644 index 7f65494ef..000000000 --- a/src/core/meta-inhibit-shortcuts-dialog-default-private.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2017 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - */ - -#ifndef META_INHIBIT_SHORTCUTS_DIALOG_DEFAULT_H -#define META_INHIBIT_SHORTCUTS_DIALOG_DEFAULT_H - -#include <glib-object.h> - -#include "meta/meta-plugin.h" - -#define META_TYPE_INHIBIT_SHORTCUTS_DIALOG_DEFAULT (meta_inhibit_shortcuts_dialog_default_get_type ()) -G_DECLARE_FINAL_TYPE (MetaInhibitShortcutsDialogDefault, - meta_inhibit_shortcuts_dialog_default, - META, INHIBIT_SHORTCUTS_DIALOG_DEFAULT, - GObject) - -MetaInhibitShortcutsDialog * meta_inhibit_shortcuts_dialog_default_new (MetaWindow *window); - -#endif /* META_INHIBIT_SHORTCUTS_DIALOG_DEFAULT_H */ diff --git a/src/core/meta-inhibit-shortcuts-dialog-default.c b/src/core/meta-inhibit-shortcuts-dialog-default.c deleted file mode 100644 index d90234d6a..000000000 --- a/src/core/meta-inhibit-shortcuts-dialog-default.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2001, 2002 Havoc Pennington - * Copyright (C) 2004 Elijah Newren - * Copyright (C) 2017 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - */ - -#include "config.h" - -#include "core/meta-inhibit-shortcuts-dialog-default-private.h" -#include "core/util-private.h" -#include "core/window-private.h" -#include "meta/meta-inhibit-shortcuts-dialog.h" - -typedef struct _MetaInhibitShortcutsDialogDefaultPrivate MetaInhibitShortcutsDialogDefaultPrivate; - -struct _MetaInhibitShortcutsDialogDefault -{ - GObject parent_instance; - MetaWindow *window; -}; - -enum -{ - PROP_0, - PROP_WINDOW, - N_PROPS -}; - -static void meta_inhibit_shortcuts_dialog_iface_init (MetaInhibitShortcutsDialogInterface *iface); - -G_DEFINE_TYPE_WITH_CODE (MetaInhibitShortcutsDialogDefault, meta_inhibit_shortcuts_dialog_default, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (META_TYPE_INHIBIT_SHORTCUTS_DIALOG, - meta_inhibit_shortcuts_dialog_iface_init)) - -static void -meta_inhibit_shortcuts_dialog_default_show (MetaInhibitShortcutsDialog *dialog) -{ - /* Default to allow shortcuts inhibitor, but complain that no dialog is implemented */ - g_warning ("No MetaInhibitShortcutDialog implementation, falling back on allowing"); - meta_inhibit_shortcuts_dialog_response (dialog, META_INHIBIT_SHORTCUTS_DIALOG_RESPONSE_ALLOW); -} - -static void -meta_inhibit_shortcuts_dialog_default_hide (MetaInhibitShortcutsDialog *dialog) -{ -} - -static void -meta_inhibit_shortcuts_dialog_iface_init (MetaInhibitShortcutsDialogInterface *iface) -{ - iface->show = meta_inhibit_shortcuts_dialog_default_show; - iface->hide = meta_inhibit_shortcuts_dialog_default_hide; -} - -static void -meta_inhibit_shortcuts_dialog_default_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - MetaInhibitShortcutsDialogDefault *dialog; - - dialog = META_INHIBIT_SHORTCUTS_DIALOG_DEFAULT (object); - - switch (prop_id) - { - case PROP_WINDOW: - dialog->window = g_value_get_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -meta_inhibit_shortcuts_dialog_default_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - MetaInhibitShortcutsDialogDefault *dialog; - - dialog = META_INHIBIT_SHORTCUTS_DIALOG_DEFAULT (object); - - switch (prop_id) - { - case PROP_WINDOW: - g_value_set_object (value, dialog->window); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -meta_inhibit_shortcuts_dialog_default_class_init (MetaInhibitShortcutsDialogDefaultClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->set_property = meta_inhibit_shortcuts_dialog_default_set_property; - object_class->get_property = meta_inhibit_shortcuts_dialog_default_get_property; - - g_object_class_override_property (object_class, PROP_WINDOW, "window"); -} - -static void -meta_inhibit_shortcuts_dialog_default_init (MetaInhibitShortcutsDialogDefault *dialog) -{ -} - -MetaInhibitShortcutsDialog * -meta_inhibit_shortcuts_dialog_default_new (MetaWindow *window) -{ - return g_object_new (META_TYPE_INHIBIT_SHORTCUTS_DIALOG_DEFAULT, - "window", window, - NULL); -} diff --git a/src/core/meta-inhibit-shortcuts-dialog.c b/src/core/meta-inhibit-shortcuts-dialog.c deleted file mode 100644 index d5b4e5a05..000000000 --- a/src/core/meta-inhibit-shortcuts-dialog.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2017 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - */ - -#include "config.h" - -#include "core/window-private.h" -#include "meta/meta-inhibit-shortcuts-dialog.h" -#include "meta/meta-enum-types.h" - -enum -{ - RESPONSE, - LAST_SIGNAL -}; - -static guint inhibit_dialog_signals[LAST_SIGNAL] = { 0, }; - -G_DEFINE_INTERFACE (MetaInhibitShortcutsDialog, meta_inhibit_shortcuts_dialog, G_TYPE_OBJECT) - -static void -meta_inhibit_shortcuts_dialog_default_init (MetaInhibitShortcutsDialogInterface *iface) -{ - g_object_interface_install_property (iface, - g_param_spec_object ("window", - "Window", - "Window", - META_TYPE_WINDOW, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - inhibit_dialog_signals[RESPONSE] = - g_signal_new ("response", - G_TYPE_FROM_INTERFACE (iface), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 1, META_TYPE_INHIBIT_SHORTCUTS_DIALOG_RESPONSE); -} - -/** - * meta_inhibit_shortcuts_dialog_show: - * @dialog: a #MetaInhibitShortcutsDialog - * - * Shows the inhibit shortcuts dialog. - **/ -void -meta_inhibit_shortcuts_dialog_show (MetaInhibitShortcutsDialog *dialog) -{ - MetaInhibitShortcutsDialogInterface *iface; - - g_return_if_fail (META_IS_INHIBIT_SHORTCUTS_DIALOG (dialog)); - - iface = META_INHIBIT_SHORTCUTS_DIALOG_GET_IFACE (dialog); - iface->show (dialog); -} - -/** - * meta_inhibit_shortcuts_dialog_hide: - * @dialog: a #MetaInhibitShortcutsDialog - * - * Hides the inhibit shortcuts dialog. - **/ -void -meta_inhibit_shortcuts_dialog_hide (MetaInhibitShortcutsDialog *dialog) -{ - MetaInhibitShortcutsDialogInterface *iface; - - g_return_if_fail (META_IS_INHIBIT_SHORTCUTS_DIALOG (dialog)); - - iface = META_INHIBIT_SHORTCUTS_DIALOG_GET_IFACE (dialog); - iface->hide (dialog); -} - -/** - * meta_inhibit_shortcuts_dialog_response: - * @dialog: a #MetaInhibitShortcutsDialog - * @response: a #MetaInhibitShortcutsDialogResponse - * - * Responds and closes the dialog. To be called by #MetaInhibitShortcutsDialog - * implementations. - **/ -void -meta_inhibit_shortcuts_dialog_response (MetaInhibitShortcutsDialog *dialog, - MetaInhibitShortcutsDialogResponse response) -{ - g_signal_emit (dialog, inhibit_dialog_signals[RESPONSE], 0, response); - meta_inhibit_shortcuts_dialog_hide (dialog); -} diff --git a/src/core/meta-launch-context.c b/src/core/meta-launch-context.c deleted file mode 100644 index 984338c1b..000000000 --- a/src/core/meta-launch-context.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Author: Carlos Garnacho <carlosg@gnome.org> - */ - -#include "config.h" - -#include <gio/gdesktopappinfo.h> - -#include "core/display-private.h" -#include "meta/meta-launch-context.h" -#include "x11/meta-startup-notification-x11.h" - -typedef struct _MetaLaunchContext MetaLaunchContext; - -struct _MetaLaunchContext -{ - GAppLaunchContext parent_instance; - MetaDisplay *display; - MetaWorkspace *workspace; - uint32_t timestamp; -}; - -G_DEFINE_TYPE (MetaLaunchContext, meta_launch_context, - G_TYPE_APP_LAUNCH_CONTEXT) - -enum -{ - PROP_DISPLAY = 1, - PROP_WORKSPACE, - PROP_TIMESTAMP, - N_PROPS -}; - -static GParamSpec *props[N_PROPS] = { 0, }; - -static void -meta_launch_context_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - MetaLaunchContext *context = META_LAUNCH_CONTEXT (object); - - switch (prop_id) - { - case PROP_DISPLAY: - context->display = g_value_get_object (value); - break; - case PROP_WORKSPACE: - meta_launch_context_set_workspace (context, - g_value_get_object (value)); - break; - case PROP_TIMESTAMP: - meta_launch_context_set_timestamp (context, - g_value_get_uint (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -meta_launch_context_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - MetaLaunchContext *context = META_LAUNCH_CONTEXT (object); - - switch (prop_id) - { - case PROP_DISPLAY: - g_value_set_object (value, context->display); - break; - case PROP_WORKSPACE: - g_value_set_object (value, context->workspace); - break; - case PROP_TIMESTAMP: - g_value_set_uint (value, context->timestamp); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -meta_launch_context_finalize (GObject *object) -{ - G_OBJECT_CLASS (meta_launch_context_parent_class)->finalize (object); -} - -static void -meta_launch_context_constructed (GObject *object) -{ - MetaLaunchContext *context = META_LAUNCH_CONTEXT (object); - const char *x11_display, *wayland_display; - - G_OBJECT_CLASS (meta_launch_context_parent_class)->constructed (object); - - x11_display = getenv ("DISPLAY"); - wayland_display = getenv ("WAYLAND_DISPLAY"); - - if (x11_display) - { - g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (context), - "DISPLAY", x11_display); - } - - if (wayland_display) - { - g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (context), - "WAYLAND_DISPLAY", wayland_display); - } -} - -static gchar * -meta_launch_context_get_startup_notify_id (GAppLaunchContext *launch_context, - GAppInfo *info, - GList *files) -{ - MetaLaunchContext *context = META_LAUNCH_CONTEXT (launch_context); - MetaDisplay *display = context->display; - int workspace_idx = -1; - char *startup_id = NULL; - - if (context->workspace) - workspace_idx = meta_workspace_index (context->workspace); - - if (display->x11_display) - { - /* If there is a X11 display, we prefer going entirely through - * libsn, as SnMonitor expects to keep a view of the full lifetime - * of the startup sequence. We can't avoid it when launching and - * expect that a "remove" message from a X11 client will be handled. - */ - startup_id = - meta_x11_startup_notification_launch (display->x11_display, - info, - context->timestamp, - workspace_idx); - } - - if (!startup_id) - { - const char *application_id = NULL; - MetaStartupNotification *sn; - MetaStartupSequence *seq; - - startup_id = g_uuid_string_random (); - - /* Fallback through inserting our own startup sequence, this - * will be enough for wayland clients. - */ - if (G_IS_DESKTOP_APP_INFO (info)) - { - application_id = - g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (info)); - } - - sn = meta_display_get_startup_notification (context->display); - seq = g_object_new (META_TYPE_STARTUP_SEQUENCE, - "id", startup_id, - "application-id", application_id, - "name", g_app_info_get_name (info), - "workspace", workspace_idx, - "timestamp", context->timestamp, - NULL); - - meta_startup_notification_add_sequence (sn, seq); - g_object_unref (seq); - } - - return startup_id; -} - -static void -meta_launch_context_launch_failed (GAppLaunchContext *launch_context, - const gchar *startup_notify_id) -{ - MetaLaunchContext *context = META_LAUNCH_CONTEXT (launch_context); - MetaStartupNotification *sn; - MetaStartupSequence *seq; - - sn = meta_display_get_startup_notification (context->display); - seq = meta_startup_notification_lookup_sequence (sn, startup_notify_id); - - if (seq) - { - meta_startup_sequence_complete (seq); - meta_startup_notification_remove_sequence (sn, seq); - } -} - -static void -meta_launch_context_class_init (MetaLaunchContextClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GAppLaunchContextClass *ctx_class = G_APP_LAUNCH_CONTEXT_CLASS (klass); - - object_class->finalize = meta_launch_context_finalize; - object_class->constructed = meta_launch_context_constructed; - object_class->set_property = meta_launch_context_set_property; - object_class->get_property = meta_launch_context_get_property; - - ctx_class->get_startup_notify_id = meta_launch_context_get_startup_notify_id; - ctx_class->launch_failed = meta_launch_context_launch_failed; - - props[PROP_DISPLAY] = - g_param_spec_object ("display", - "display", - "Display", - META_TYPE_DISPLAY, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); - props[PROP_WORKSPACE] = - g_param_spec_object ("workspace", - "workspace", - "Workspace", - META_TYPE_WORKSPACE, - G_PARAM_READWRITE); - props[PROP_TIMESTAMP] = - g_param_spec_uint ("timestamp", - "timestamp", - "Timestamp", - 0, G_MAXUINT32, 0, - G_PARAM_READWRITE); - - g_object_class_install_properties (object_class, N_PROPS, props); -} - -static void -meta_launch_context_init (MetaLaunchContext *context) -{ -} - -void -meta_launch_context_set_workspace (MetaLaunchContext *context, - MetaWorkspace *workspace) -{ - g_return_if_fail (META_IS_LAUNCH_CONTEXT (context)); - g_return_if_fail (META_IS_WORKSPACE (workspace)); - - g_set_object (&context->workspace, workspace); -} - -void -meta_launch_context_set_timestamp (MetaLaunchContext *context, - uint32_t timestamp) -{ - g_return_if_fail (META_IS_LAUNCH_CONTEXT (context)); - - context->timestamp = timestamp; -} diff --git a/src/core/meta-pad-action-mapper.c b/src/core/meta-pad-action-mapper.c deleted file mode 100644 index 7c78c4014..000000000 --- a/src/core/meta-pad-action-mapper.c +++ /dev/null @@ -1,860 +0,0 @@ -/* - * Copyright (C) 2014-2020 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Written by: - * Carlos Garnacho <carlosg@gnome.org> - */ - -#include "config.h" - -#include <glib/gi18n-lib.h> -#include <gsettings-desktop-schemas/gdesktop-enums.h> - -#ifdef HAVE_LIBWACOM -#include <libwacom/libwacom.h> -#endif - -#include "core/meta-pad-action-mapper.h" -#include "backends/meta-input-device-private.h" -#include "backends/meta-logical-monitor.h" -#include "backends/meta-monitor.h" -#include "core/display-private.h" - -typedef struct _PadMappingInfo PadMappingInfo; - -struct _PadMappingInfo -{ - ClutterInputDevice *device; - GSettings *settings; - guint *group_modes; -}; - -typedef enum -{ - META_PAD_DIRECTION_NONE = -1, - META_PAD_DIRECTION_UP = 0, - META_PAD_DIRECTION_DOWN, - META_PAD_DIRECTION_CW, - META_PAD_DIRECTION_CCW, -} MetaPadDirection; - -struct _MetaPadActionMapper -{ - GObject parent_class; - - GHashTable *pads; - ClutterSeat *seat; - ClutterVirtualInputDevice *virtual_pad_keyboard; - MetaMonitorManager *monitor_manager; - - /* Pad ring/strip emission */ - struct { - ClutterInputDevice *pad; - MetaPadActionType action; - guint number; - double value; - } last_pad_action_info; -}; - -G_DEFINE_TYPE (MetaPadActionMapper, meta_pad_action_mapper, G_TYPE_OBJECT) - -static void -meta_pad_action_mapper_finalize (GObject *object) -{ - MetaPadActionMapper *mapper = META_PAD_ACTION_MAPPER (object); - - g_hash_table_unref (mapper->pads); - g_object_unref (mapper->monitor_manager); - g_clear_object (&mapper->virtual_pad_keyboard); - - G_OBJECT_CLASS (meta_pad_action_mapper_parent_class)->finalize (object); -} - -static void -meta_pad_action_mapper_class_init (MetaPadActionMapperClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = meta_pad_action_mapper_finalize; -} - -static GSettings * -lookup_device_settings (ClutterInputDevice *device) -{ - const char *vendor, *product; - GSettings *settings; - char *path; - - vendor = clutter_input_device_get_vendor_id (device); - product = clutter_input_device_get_product_id (device); - path = g_strdup_printf ("/org/gnome/desktop/peripherals/tablets/%s:%s/", - vendor, product); - - settings = g_settings_new_with_path ("org.gnome.desktop.peripherals.tablet", - path); - g_free (path); - - return settings; -} - -static PadMappingInfo * -pad_mapping_info_new (ClutterInputDevice *pad) -{ - PadMappingInfo *info; - - info = g_new0 (PadMappingInfo, 1); - info->device = pad; - info->settings = lookup_device_settings (pad); - info->group_modes = - g_new0 (guint, clutter_input_device_get_n_mode_groups (pad)); - - return info; -} - -static void -pad_mapping_info_free (PadMappingInfo *info) -{ - g_object_unref (info->settings); - g_free (info->group_modes); - g_free (info); -} - -static void -device_added (ClutterSeat *seat, - ClutterInputDevice *device, - MetaPadActionMapper *mapper) -{ - PadMappingInfo *info; - - if (clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE) - { - info = pad_mapping_info_new (device); - g_hash_table_insert (mapper->pads, device, info); - } -} - -static void -device_removed (ClutterSeat *seat, - ClutterInputDevice *device, - MetaPadActionMapper *mapper) -{ - g_hash_table_remove (mapper->pads, device); -} - -static void -meta_pad_action_mapper_init (MetaPadActionMapper *mapper) -{ - mapper->pads = g_hash_table_new_full (NULL, NULL, NULL, - (GDestroyNotify) pad_mapping_info_free); - - mapper->seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); - g_signal_connect (mapper->seat, "device-added", - G_CALLBACK (device_added), mapper); - g_signal_connect (mapper->seat, "device-removed", - G_CALLBACK (device_removed), mapper); -} - -MetaPadActionMapper * -meta_pad_action_mapper_new (MetaMonitorManager *monitor_manager) -{ - MetaPadActionMapper *action_mapper; - - action_mapper = g_object_new (META_TYPE_PAD_ACTION_MAPPER, NULL); - g_set_object (&action_mapper->monitor_manager, monitor_manager); - - return action_mapper; -} - -static GSettings * -lookup_pad_action_settings (ClutterInputDevice *device, - MetaPadActionType action, - guint number, - MetaPadDirection direction, - int mode) -{ - const char *vendor, *product, *action_type, *detail_type = NULL; - GSettings *settings; - GString *path; - char action_label; - - vendor = clutter_input_device_get_vendor_id (device); - product = clutter_input_device_get_product_id (device); - - action_label = 'A' + number; - - switch (action) - { - case META_PAD_ACTION_BUTTON: - action_type = "button"; - break; - case META_PAD_ACTION_RING: - g_assert (direction == META_PAD_DIRECTION_CW || - direction == META_PAD_DIRECTION_CCW); - action_type = "ring"; - detail_type = (direction == META_PAD_DIRECTION_CW) ? "cw" : "ccw"; - break; - case META_PAD_ACTION_STRIP: - g_assert (direction == META_PAD_DIRECTION_UP || - direction == META_PAD_DIRECTION_DOWN); - action_type = "strip"; - detail_type = (direction == META_PAD_DIRECTION_UP) ? "up" : "down"; - break; - default: - return NULL; - } - - path = g_string_new (NULL); - g_string_append_printf (path, "/org/gnome/desktop/peripherals/tablets/%s:%s/%s%c", - vendor, product, action_type, action_label); - - if (detail_type) - g_string_append_printf (path, "-%s", detail_type); - - if (mode >= 0) - g_string_append_printf (path, "-mode-%d", mode); - - g_string_append_c (path, '/'); - - settings = g_settings_new_with_path ("org.gnome.desktop.peripherals.tablet.pad-button", - path->str); - g_string_free (path, TRUE); - - return settings; -} - -static GDesktopPadButtonAction -meta_pad_action_mapper_get_button_action (MetaPadActionMapper *mapper, - ClutterInputDevice *pad, - guint button) -{ - GDesktopPadButtonAction action; - GSettings *settings; - - g_return_val_if_fail (META_IS_PAD_ACTION_MAPPER (mapper), - G_DESKTOP_PAD_BUTTON_ACTION_NONE); - g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (pad), - G_DESKTOP_PAD_BUTTON_ACTION_NONE); - - settings = lookup_pad_action_settings (pad, META_PAD_ACTION_BUTTON, - button, META_PAD_DIRECTION_NONE, -1); - action = g_settings_get_enum (settings, "action"); - g_object_unref (settings); - - return action; -} - -static gboolean -cycle_logical_monitors (MetaPadActionMapper *mapper, - gboolean skip_all_monitors, - MetaLogicalMonitor *current_logical_monitor, - MetaLogicalMonitor **next_logical_monitor) -{ - MetaMonitorManager *monitor_manager = mapper->monitor_manager; - GList *logical_monitors; - - /* We cycle between: - * - the span of all monitors (current_output = NULL), only for - * non-integrated devices. - * - each monitor individually. - */ - - logical_monitors = - meta_monitor_manager_get_logical_monitors (monitor_manager); - - if (!current_logical_monitor) - { - *next_logical_monitor = logical_monitors->data; - } - else - { - GList *l; - - l = g_list_find (logical_monitors, current_logical_monitor); - if (l->next) - *next_logical_monitor = l->next->data; - else if (skip_all_monitors) - *next_logical_monitor = logical_monitors->data; - else - *next_logical_monitor = NULL; - } - - return TRUE; -} - -static MetaMonitor * -logical_monitor_find_monitor (MetaLogicalMonitor *logical_monitor, - const char *vendor, - const char *product, - const char *serial) -{ - GList *monitors; - GList *l; - - monitors = meta_logical_monitor_get_monitors (logical_monitor); - for (l = monitors; l; l = l->next) - { - MetaMonitor *monitor = l->data; - - if (g_strcmp0 (meta_monitor_get_vendor (monitor), vendor) == 0 && - g_strcmp0 (meta_monitor_get_product (monitor), product) == 0 && - g_strcmp0 (meta_monitor_get_serial (monitor), serial) == 0) - return monitor; - } - - return NULL; -} - -static void -meta_pad_action_mapper_find_monitor (MetaPadActionMapper *mapper, - GSettings *settings, - ClutterInputDevice *device, - MetaMonitor **out_monitor, - MetaLogicalMonitor **out_logical_monitor) -{ - MetaMonitorManager *monitor_manager; - MetaMonitor *monitor; - guint n_values; - GList *logical_monitors; - GList *l; - char **edid; - - edid = g_settings_get_strv (settings, "output"); - n_values = g_strv_length (edid); - - if (n_values != 3) - { - g_warning ("EDID configuration for device '%s' " - "is incorrect, must have 3 values", - clutter_input_device_get_device_name (device)); - goto out; - } - - if (!*edid[0] && !*edid[1] && !*edid[2]) - goto out; - - monitor_manager = mapper->monitor_manager; - logical_monitors = - meta_monitor_manager_get_logical_monitors (monitor_manager); - for (l = logical_monitors; l; l = l->next) - { - MetaLogicalMonitor *logical_monitor = l->data; - - monitor = logical_monitor_find_monitor (logical_monitor, - edid[0], edid[1], edid[2]); - if (monitor) - { - if (out_monitor) - *out_monitor = monitor; - if (out_logical_monitor) - *out_logical_monitor = logical_monitor; - break; - } - } - -out: - g_strfreev (edid); -} - -static void -meta_pad_action_mapper_cycle_tablet_output (MetaPadActionMapper *mapper, - ClutterInputDevice *device) -{ - PadMappingInfo *info; - MetaLogicalMonitor *logical_monitor = NULL; - const char *edid[4] = { 0 }, *pretty_name = NULL; - gboolean is_integrated_device = FALSE; -#ifdef HAVE_LIBWACOM - WacomDevice *wacom_device; -#endif - - g_return_if_fail (META_IS_PAD_ACTION_MAPPER (mapper)); - g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); - g_return_if_fail (clutter_input_device_get_device_type (device) == CLUTTER_TABLET_DEVICE || - clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE); - - info = g_hash_table_lookup (mapper->pads, device); - g_return_if_fail (info != NULL); - -#ifdef HAVE_LIBWACOM - wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device)); - - if (wacom_device) - { - pretty_name = libwacom_get_name (wacom_device); - is_integrated_device = - libwacom_get_integration_flags (wacom_device) != WACOM_DEVICE_INTEGRATED_NONE; - } -#endif - - meta_pad_action_mapper_find_monitor (mapper, info->settings, device, - NULL, &logical_monitor); - - if (!cycle_logical_monitors (mapper, - is_integrated_device, - logical_monitor, - &logical_monitor)) - return; - - if (logical_monitor) - { - MetaMonitor *monitor; - - /* Pick an arbitrary monitor in the logical monitor to represent it. */ - monitor = meta_logical_monitor_get_monitors (logical_monitor)->data; - edid[0] = meta_monitor_get_vendor (monitor); - edid[1] = meta_monitor_get_product (monitor); - edid[2] = meta_monitor_get_serial (monitor); - } - else - { - edid[0] = ""; - edid[1] = ""; - edid[2] = ""; - } - - g_settings_set_strv (info->settings, "output", edid); - meta_display_show_tablet_mapping_notification (meta_get_display (), - device, pretty_name); -} - -gboolean -meta_pad_action_mapper_is_button_grabbed (MetaPadActionMapper *mapper, - ClutterInputDevice *pad, - guint button) -{ - g_return_val_if_fail (META_IS_PAD_ACTION_MAPPER (mapper), FALSE); - g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (pad), FALSE); - g_return_val_if_fail (clutter_input_device_get_device_type (pad) == - CLUTTER_PAD_DEVICE, FALSE); - - return (meta_pad_action_mapper_get_button_action (mapper, pad, button) != - G_DESKTOP_PAD_BUTTON_ACTION_NONE); -} - -static void -emulate_modifiers (ClutterVirtualInputDevice *device, - ClutterModifierType mods, - ClutterKeyState state) -{ - guint i; - struct { - ClutterModifierType mod; - guint keyval; - } mod_map[] = { - { CLUTTER_SHIFT_MASK, CLUTTER_KEY_Shift_L }, - { CLUTTER_CONTROL_MASK, CLUTTER_KEY_Control_L }, - { CLUTTER_MOD1_MASK, CLUTTER_KEY_Meta_L } - }; - - for (i = 0; i < G_N_ELEMENTS (mod_map); i++) - { - if ((mods & mod_map[i].mod) == 0) - continue; - - clutter_virtual_input_device_notify_keyval (device, - clutter_get_current_event_time (), - mod_map[i].keyval, state); - } -} - -static void -meta_pad_action_mapper_emulate_keybinding (MetaPadActionMapper *mapper, - const char *accel, - gboolean is_press) -{ - ClutterKeyState state; - guint key, mods; - - if (!accel || !*accel) - return; - - /* FIXME: This is appalling */ - gtk_accelerator_parse (accel, &key, &mods); - - if (!mapper->virtual_pad_keyboard) - { - ClutterBackend *backend; - ClutterSeat *seat; - - backend = clutter_get_default_backend (); - seat = clutter_backend_get_default_seat (backend); - - mapper->virtual_pad_keyboard = - clutter_seat_create_virtual_device (seat, - CLUTTER_KEYBOARD_DEVICE); - } - - state = is_press ? CLUTTER_KEY_STATE_PRESSED : CLUTTER_KEY_STATE_RELEASED; - - if (is_press) - emulate_modifiers (mapper->virtual_pad_keyboard, mods, state); - - clutter_virtual_input_device_notify_keyval (mapper->virtual_pad_keyboard, - clutter_get_current_event_time (), - key, state); - if (!is_press) - emulate_modifiers (mapper->virtual_pad_keyboard, mods, state); -} - -static gboolean -meta_pad_action_mapper_handle_button (MetaPadActionMapper *mapper, - ClutterInputDevice *pad, - const ClutterPadButtonEvent *event) -{ - GDesktopPadButtonAction action; - int button, group, mode, n_modes = 0; - gboolean is_press; - GSettings *settings; - char *accel; - - g_return_val_if_fail (META_IS_PAD_ACTION_MAPPER (mapper), FALSE); - g_return_val_if_fail (event->type == CLUTTER_PAD_BUTTON_PRESS || - event->type == CLUTTER_PAD_BUTTON_RELEASE, FALSE); - - button = event->button; - mode = event->mode; - group = clutter_input_device_get_mode_switch_button_group (pad, button); - is_press = event->type == CLUTTER_PAD_BUTTON_PRESS; - - if (group >= 0) - n_modes = clutter_input_device_get_group_n_modes (pad, group); - - if (is_press && n_modes > 0) - { - const char *pretty_name = NULL; - PadMappingInfo *info; -#ifdef HAVE_LIBWACOM - WacomDevice *wacom_device; -#endif - - info = g_hash_table_lookup (mapper->pads, pad); - -#ifdef HAVE_LIBWACOM - wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (pad)); - - if (wacom_device) - pretty_name = libwacom_get_name (wacom_device); -#endif - meta_display_notify_pad_group_switch (meta_get_display (), pad, - pretty_name, group, mode, n_modes); - info->group_modes[group] = mode; - } - - action = meta_pad_action_mapper_get_button_action (mapper, pad, button); - - switch (action) - { - case G_DESKTOP_PAD_BUTTON_ACTION_SWITCH_MONITOR: - if (is_press) - meta_pad_action_mapper_cycle_tablet_output (mapper, pad); - return TRUE; - case G_DESKTOP_PAD_BUTTON_ACTION_HELP: - if (is_press) - meta_display_request_pad_osd (meta_get_display (), pad, FALSE); - return TRUE; - case G_DESKTOP_PAD_BUTTON_ACTION_KEYBINDING: - settings = lookup_pad_action_settings (pad, META_PAD_ACTION_BUTTON, - button, META_PAD_DIRECTION_NONE, -1); - accel = g_settings_get_string (settings, "keybinding"); - meta_pad_action_mapper_emulate_keybinding (mapper, accel, is_press); - g_object_unref (settings); - g_free (accel); - return TRUE; - case G_DESKTOP_PAD_BUTTON_ACTION_NONE: - default: - return FALSE; - } -} - -static gboolean -meta_pad_action_mapper_handle_action (MetaPadActionMapper *mapper, - ClutterInputDevice *pad, - MetaPadActionType action, - guint number, - MetaPadDirection direction, - guint mode) -{ - GSettings *settings; - gboolean handled = FALSE; - char *accel; - - settings = lookup_pad_action_settings (pad, action, number, direction, mode); - accel = g_settings_get_string (settings, "keybinding"); - - if (accel && *accel) - { - meta_pad_action_mapper_emulate_keybinding (mapper, accel, TRUE); - meta_pad_action_mapper_emulate_keybinding (mapper, accel, FALSE); - handled = TRUE; - } - - g_object_unref (settings); - g_free (accel); - - return handled; -} - -static gboolean -meta_pad_action_mapper_get_action_direction (MetaPadActionMapper *mapper, - const ClutterEvent *event, - MetaPadDirection *direction) -{ - ClutterInputDevice *pad = clutter_event_get_device (event); - MetaPadActionType pad_action; - gboolean has_direction = FALSE; - MetaPadDirection inc_dir, dec_dir; - guint number; - double value; - - *direction = META_PAD_DIRECTION_NONE; - - switch (event->type) - { - case CLUTTER_PAD_RING: - pad_action = META_PAD_ACTION_RING; - number = event->pad_ring.ring_number; - value = event->pad_ring.angle; - inc_dir = META_PAD_DIRECTION_CW; - dec_dir = META_PAD_DIRECTION_CCW; - break; - case CLUTTER_PAD_STRIP: - pad_action = META_PAD_ACTION_STRIP; - number = event->pad_strip.strip_number; - value = event->pad_strip.value; - inc_dir = META_PAD_DIRECTION_DOWN; - dec_dir = META_PAD_DIRECTION_UP; - break; - default: - return FALSE; - } - - if (mapper->last_pad_action_info.pad == pad && - mapper->last_pad_action_info.action == pad_action && - mapper->last_pad_action_info.number == number && - value >= 0 && mapper->last_pad_action_info.value >= 0) - { - *direction = (value - mapper->last_pad_action_info.value) > 0 ? - inc_dir : dec_dir; - has_direction = TRUE; - } - - mapper->last_pad_action_info.pad = pad; - mapper->last_pad_action_info.action = pad_action; - mapper->last_pad_action_info.number = number; - mapper->last_pad_action_info.value = value; - return has_direction; -} - -gboolean -meta_pad_action_mapper_handle_event (MetaPadActionMapper *mapper, - const ClutterEvent *event) -{ - ClutterInputDevice *pad; - MetaPadDirection direction = META_PAD_DIRECTION_NONE; - - pad = clutter_event_get_source_device ((ClutterEvent *) event); - - switch (event->type) - { - case CLUTTER_PAD_BUTTON_PRESS: - case CLUTTER_PAD_BUTTON_RELEASE: - return meta_pad_action_mapper_handle_button (mapper, pad, - &event->pad_button); - case CLUTTER_PAD_RING: - if (!meta_pad_action_mapper_get_action_direction (mapper, - event, &direction)) - return FALSE; - return meta_pad_action_mapper_handle_action (mapper, pad, - META_PAD_ACTION_RING, - event->pad_ring.ring_number, - direction, - event->pad_ring.mode); - case CLUTTER_PAD_STRIP: - if (!meta_pad_action_mapper_get_action_direction (mapper, - event, &direction)) - return FALSE; - return meta_pad_action_mapper_handle_action (mapper, pad, - META_PAD_ACTION_STRIP, - event->pad_strip.strip_number, - direction, - event->pad_strip.mode); - default: - return FALSE; - } -} - - -static char * -compose_directional_action_label (GSettings *direction1, - GSettings *direction2) -{ - char *accel1, *accel2, *str = NULL; - - accel1 = g_settings_get_string (direction1, "keybinding"); - accel2 = g_settings_get_string (direction2, "keybinding"); - - if (accel1 && *accel1 && accel2 && *accel2) - str = g_strdup_printf ("%s / %s", accel1, accel2); - - g_free (accel1); - g_free (accel2); - - return str; -} - -static char * -meta_pad_action_mapper_get_ring_label (MetaPadActionMapper *mapper, - ClutterInputDevice *pad, - guint number, - guint mode) -{ - GSettings *settings1, *settings2; - char *label; - - /* We only allow keybinding actions with those */ - settings1 = lookup_pad_action_settings (pad, META_PAD_ACTION_RING, number, - META_PAD_DIRECTION_CW, mode); - settings2 = lookup_pad_action_settings (pad, META_PAD_ACTION_RING, number, - META_PAD_DIRECTION_CCW, mode); - label = compose_directional_action_label (settings1, settings2); - g_object_unref (settings1); - g_object_unref (settings2); - - return label; -} - -static char * -meta_pad_action_mapper_get_strip_label (MetaPadActionMapper *mapper, - ClutterInputDevice *pad, - guint number, - guint mode) -{ - GSettings *settings1, *settings2; - char *label; - - /* We only allow keybinding actions with those */ - settings1 = lookup_pad_action_settings (pad, META_PAD_ACTION_STRIP, number, - META_PAD_DIRECTION_UP, mode); - settings2 = lookup_pad_action_settings (pad, META_PAD_ACTION_STRIP, number, - META_PAD_DIRECTION_DOWN, mode); - label = compose_directional_action_label (settings1, settings2); - g_object_unref (settings1); - g_object_unref (settings2); - - return label; -} - -static char * -meta_pad_action_mapper_get_button_label (MetaPadActionMapper *mapper, - ClutterInputDevice *pad, - guint button) -{ - GDesktopPadButtonAction action; - int group; - - g_return_val_if_fail (META_IS_PAD_ACTION_MAPPER (mapper), NULL); - g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (pad), NULL); - g_return_val_if_fail (clutter_input_device_get_device_type (pad) == - CLUTTER_PAD_DEVICE, NULL); - - group = clutter_input_device_get_mode_switch_button_group (pad, button); - - if (group >= 0) - { - /* TRANSLATORS: This string refers to a button that switches between - * different modes. - */ - return g_strdup_printf (_("Mode Switch (Group %d)"), group); - } - - action = meta_pad_action_mapper_get_button_action (mapper, pad, button); - - switch (action) - { - case G_DESKTOP_PAD_BUTTON_ACTION_KEYBINDING: - { - GSettings *settings; - char *accel; - - settings = lookup_pad_action_settings (pad, META_PAD_ACTION_BUTTON, - button, META_PAD_DIRECTION_NONE, -1); - accel = g_settings_get_string (settings, "keybinding"); - g_object_unref (settings); - - return accel; - } - case G_DESKTOP_PAD_BUTTON_ACTION_SWITCH_MONITOR: - /* TRANSLATORS: This string refers to an action, cycles drawing tablets' - * mapping through the available outputs. - */ - return g_strdup (_("Switch monitor")); - case G_DESKTOP_PAD_BUTTON_ACTION_HELP: - return g_strdup (_("Show on-screen help")); - case G_DESKTOP_PAD_BUTTON_ACTION_NONE: - default: - return NULL; - } -} - -static guint -get_current_pad_mode (MetaPadActionMapper *mapper, - ClutterInputDevice *pad, - MetaPadActionType action_type, - guint number) -{ - PadMappingInfo *info; - guint group = 0, n_groups; - - info = g_hash_table_lookup (mapper->pads, pad); - n_groups = clutter_input_device_get_n_mode_groups (pad); - - if (!info->group_modes || n_groups == 0) - return 0; - - if (action_type == META_PAD_ACTION_RING || - action_type == META_PAD_ACTION_STRIP) - { - /* Assume features are evenly distributed in groups */ - group = number % n_groups; - } - - return info->group_modes[group]; -} - -char * -meta_pad_action_mapper_get_action_label (MetaPadActionMapper *mapper, - ClutterInputDevice *pad, - MetaPadActionType action_type, - guint number) -{ - guint mode; - - switch (action_type) - { - case META_PAD_ACTION_BUTTON: - return meta_pad_action_mapper_get_button_label (mapper, pad, number); - case META_PAD_ACTION_RING: - mode = get_current_pad_mode (mapper, pad, action_type, number); - return meta_pad_action_mapper_get_ring_label (mapper, pad, number, mode); - case META_PAD_ACTION_STRIP: - mode = get_current_pad_mode (mapper, pad, action_type, number); - return meta_pad_action_mapper_get_strip_label (mapper, pad, number, mode); - } - - return NULL; -} diff --git a/src/core/meta-pad-action-mapper.h b/src/core/meta-pad-action-mapper.h deleted file mode 100644 index 63fc2261b..000000000 --- a/src/core/meta-pad-action-mapper.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2020 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Written by: - * Carlos Garnacho <carlosg@gnome.org> - */ - -#ifndef META_PAD_ACTION_MAPPER_H -#define META_PAD_ACTION_MAPPER_H - -#include "clutter/clutter.h" -#include "meta/display.h" -#include "meta/meta-monitor-manager.h" - -#define META_TYPE_PAD_ACTION_MAPPER (meta_pad_action_mapper_get_type ()) -G_DECLARE_FINAL_TYPE (MetaPadActionMapper, meta_pad_action_mapper, - META, PAD_ACTION_MAPPER, GObject) - -MetaPadActionMapper * meta_pad_action_mapper_new (MetaMonitorManager *monitor_manager); - -gboolean meta_pad_action_mapper_is_button_grabbed (MetaPadActionMapper *mapper, - ClutterInputDevice *pad, - guint button); -gboolean meta_pad_action_mapper_handle_event (MetaPadActionMapper *mapper, - const ClutterEvent *event); -gchar * meta_pad_action_mapper_get_action_label (MetaPadActionMapper *mapper, - ClutterInputDevice *pad, - MetaPadActionType action, - guint number); - -#endif /* META_PAD_ACTION_MAPPER_H */ diff --git a/src/core/meta-private-enums.h b/src/core/meta-private-enums.h deleted file mode 100644 index 1a361c782..000000000 --- a/src/core/meta-private-enums.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2019-2021 Red Hat Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - */ - -#ifndef META_PRIVATE_ENUMS_H -#define META_PRIVATE_ENUMS_H - -typedef enum _MetaX11DisplayPolicy -{ - META_X11_DISPLAY_POLICY_MANDATORY, - META_X11_DISPLAY_POLICY_ON_DEMAND, - META_X11_DISPLAY_POLICY_DISABLED, -} MetaX11DisplayPolicy; - -#endif /* META_PRIVATE_ENUMS_H */ diff --git a/src/core/meta-selection-private.h b/src/core/meta-selection-private.h deleted file mode 100644 index de9caf9bf..000000000 --- a/src/core/meta-selection-private.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2020 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Written by: - * Carlos Garnacho <carlosg@gnome.org> - */ - -#ifndef META_SELECTION_PRIVATE_H -#define META_SELECTION_PRIVATE_H - -#include "meta/meta-selection.h" - -MetaSelectionSource * - meta_selection_get_current_owner (MetaSelection *selection, - MetaSelectionType selection_type); - -#endif /* META_SELECTION_PRIVATE_H */ diff --git a/src/core/meta-selection-source-memory.c b/src/core/meta-selection-source-memory.c deleted file mode 100644 index a52f50861..000000000 --- a/src/core/meta-selection-source-memory.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Author: Carlos Garnacho <carlosg@gnome.org> - */ - -#include "config.h" - -#include "meta/meta-selection-source-memory.h" - -struct _MetaSelectionSourceMemory -{ - MetaSelectionSource parent_instance; - char *mimetype; - GBytes *content; -}; - -G_DEFINE_TYPE (MetaSelectionSourceMemory, - meta_selection_source_memory, - META_TYPE_SELECTION_SOURCE) - -static void -meta_selection_source_memory_read_async (MetaSelectionSource *source, - const char *mimetype, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - MetaSelectionSourceMemory *source_mem = META_SELECTION_SOURCE_MEMORY (source); - GInputStream *stream; - g_autoptr (GTask) task = NULL; - - if (g_strcmp0 (mimetype, source_mem->mimetype) != 0) - { - g_task_report_new_error (source, callback, user_data, - meta_selection_source_memory_read_async, - G_IO_ERROR, G_IO_ERROR_FAILED, - "Mimetype not in selection"); - return; - } - - task = g_task_new (source, cancellable, callback, user_data); - g_task_set_source_tag (task, meta_selection_source_memory_read_async); - - stream = g_memory_input_stream_new_from_bytes (source_mem->content); - g_task_return_pointer (task, stream, g_object_unref); -} - -static GInputStream * -meta_selection_source_memory_read_finish (MetaSelectionSource *source, - GAsyncResult *result, - GError **error) -{ - g_assert (g_task_get_source_tag (G_TASK (result)) == - meta_selection_source_memory_read_async); - return g_task_propagate_pointer (G_TASK (result), error); -} - -static GList * -meta_selection_source_memory_get_mimetypes (MetaSelectionSource *source) -{ - MetaSelectionSourceMemory *source_mem = META_SELECTION_SOURCE_MEMORY (source); - - if (!source_mem->mimetype) - return NULL; - - return g_list_prepend (NULL, g_strdup (source_mem->mimetype)); -} - -static void -meta_selection_source_memory_finalize (GObject *object) -{ - MetaSelectionSourceMemory *source_mem = META_SELECTION_SOURCE_MEMORY (object); - - g_clear_pointer (&source_mem->content, g_bytes_unref); - g_free (source_mem->mimetype); - - G_OBJECT_CLASS (meta_selection_source_memory_parent_class)->finalize (object); -} - -static void -meta_selection_source_memory_class_init (MetaSelectionSourceMemoryClass *klass) -{ - MetaSelectionSourceClass *source_class = META_SELECTION_SOURCE_CLASS (klass); - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = meta_selection_source_memory_finalize; - - source_class->read_async = meta_selection_source_memory_read_async; - source_class->read_finish = meta_selection_source_memory_read_finish; - source_class->get_mimetypes = meta_selection_source_memory_get_mimetypes; -} - -static void -meta_selection_source_memory_init (MetaSelectionSourceMemory *source) -{ -} - -MetaSelectionSource * -meta_selection_source_memory_new (const char *mimetype, - GBytes *content) -{ - MetaSelectionSourceMemory *source; - - g_return_val_if_fail (mimetype != NULL, NULL); - g_return_val_if_fail (content != NULL, NULL); - - source = g_object_new (META_TYPE_SELECTION_SOURCE_MEMORY, NULL); - source->mimetype = g_strdup (mimetype); - source->content = g_bytes_ref (content); - - return META_SELECTION_SOURCE (source); -} diff --git a/src/core/meta-selection-source-remote.c b/src/core/meta-selection-source-remote.c deleted file mode 100644 index aa9f9e60a..000000000 --- a/src/core/meta-selection-source-remote.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2020 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - */ - -#include "config.h" - -#include "core/meta-selection-source-remote.h" - -#include <gio/gunixinputstream.h> - -#include "backends/meta-remote-desktop-session.h" - -struct _MetaSelectionSourceRemote -{ - MetaSelectionSource parent; - - MetaRemoteDesktopSession *session; - GList *mime_types; -}; - -G_DEFINE_TYPE (MetaSelectionSourceRemote, - meta_selection_source_remote, - META_TYPE_SELECTION_SOURCE) - -MetaSelectionSourceRemote * -meta_selection_source_remote_new (MetaRemoteDesktopSession *session, - GList *mime_types) -{ - MetaSelectionSourceRemote *source_remote; - - source_remote = g_object_new (META_TYPE_SELECTION_SOURCE_REMOTE, NULL); - source_remote->session = session; - source_remote->mime_types = mime_types; - - return source_remote; -} - -static void -meta_selection_source_remote_read_async (MetaSelectionSource *source, - const char *mimetype, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - MetaSelectionSourceRemote *source_remote = - META_SELECTION_SOURCE_REMOTE (source); - GTask *task; - - task = g_task_new (source, cancellable, callback, user_data); - g_task_set_source_tag (task, meta_selection_source_remote_read_async); - - meta_remote_desktop_session_request_transfer (source_remote->session, - mimetype, - task); -} - -static GInputStream * -meta_selection_source_remote_read_finish (MetaSelectionSource *source, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (g_task_is_valid (result, source), FALSE); - g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == - meta_selection_source_remote_read_async, FALSE); - - return g_task_propagate_pointer (G_TASK (result), error); -} - -void -meta_selection_source_remote_complete_transfer (MetaSelectionSourceRemote *source_remote, - int fd, - GTask *task) -{ - GInputStream *stream; - - stream = g_unix_input_stream_new (fd, TRUE); - g_task_return_pointer (task, stream, g_object_unref); - g_object_unref (task); -} - -void -meta_selection_source_remote_cancel_transfer (MetaSelectionSourceRemote *source_remote, - GTask *task) -{ - g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, - "Remote selection transfer was cancelled"); - g_object_unref (task); -} - -static GList * -meta_selection_source_remote_get_mimetypes (MetaSelectionSource *source) -{ - MetaSelectionSourceRemote *source_remote = - META_SELECTION_SOURCE_REMOTE (source); - - return g_list_copy_deep (source_remote->mime_types, - (GCopyFunc) g_strdup, - NULL); -} - -static void -meta_selection_source_remote_finalize (GObject *object) -{ - MetaSelectionSourceRemote *source_remote = - META_SELECTION_SOURCE_REMOTE (object); - - g_list_free_full (source_remote->mime_types, g_free); - - G_OBJECT_CLASS (meta_selection_source_remote_parent_class)->finalize (object); -} - -static void -meta_selection_source_remote_init (MetaSelectionSourceRemote *source_remote) -{ -} - -static void -meta_selection_source_remote_class_init (MetaSelectionSourceRemoteClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - MetaSelectionSourceClass *source_class = META_SELECTION_SOURCE_CLASS (klass); - - object_class->finalize = meta_selection_source_remote_finalize; - - source_class->read_async = meta_selection_source_remote_read_async; - source_class->read_finish = meta_selection_source_remote_read_finish; - source_class->get_mimetypes = meta_selection_source_remote_get_mimetypes; -} diff --git a/src/core/meta-selection-source-remote.h b/src/core/meta-selection-source-remote.h deleted file mode 100644 index 51e66957b..000000000 --- a/src/core/meta-selection-source-remote.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2020 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - */ - -#ifndef META_SELECTION_SOURCE_REMOTE_H -#define META_SELECTION_SOURCE_REMOTE_H - -#include "backends/meta-remote-desktop.h" -#include "meta/meta-selection-source.h" - -#define META_TYPE_SELECTION_SOURCE_REMOTE (meta_selection_source_remote_get_type ()) -G_DECLARE_FINAL_TYPE (MetaSelectionSourceRemote, - meta_selection_source_remote, - META, SELECTION_SOURCE_REMOTE, - MetaSelectionSource) - -void meta_selection_source_remote_complete_transfer (MetaSelectionSourceRemote *source_remote, - int fd, - GTask *task); - -void meta_selection_source_remote_cancel_transfer (MetaSelectionSourceRemote *source_remote, - GTask *task); - -MetaSelectionSourceRemote * meta_selection_source_remote_new (MetaRemoteDesktopSession *session, - GList *mime_types); - -#endif /* META_SELECTION_SOURCE_REMOTE_H */ diff --git a/src/core/meta-selection-source.c b/src/core/meta-selection-source.c deleted file mode 100644 index b076391ce..000000000 --- a/src/core/meta-selection-source.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Author: Carlos Garnacho <carlosg@gnome.org> - */ - -#include "config.h" - -#include "meta/meta-selection.h" -#include "meta/meta-selection-source.h" - -typedef struct MetaSelectionSourcePrivate MetaSelectionSourcePrivate; - -struct MetaSelectionSourcePrivate -{ - guint active : 1; -}; - -G_DEFINE_TYPE_WITH_PRIVATE (MetaSelectionSource, - meta_selection_source, - G_TYPE_OBJECT) - -enum -{ - ACTIVE, - INACTIVE, - N_SIGNALS -}; - -static guint signals[N_SIGNALS] = { 0 }; - -static void -meta_selection_source_activated (MetaSelectionSource *source) -{ - MetaSelectionSourcePrivate *priv = - meta_selection_source_get_instance_private (source); - - priv->active = TRUE; -} - -static void -meta_selection_source_deactivated (MetaSelectionSource *source) -{ - MetaSelectionSourcePrivate *priv = - meta_selection_source_get_instance_private (source); - - priv->active = FALSE; -} - -static void -meta_selection_source_class_init (MetaSelectionSourceClass *klass) -{ - klass->activated = meta_selection_source_activated; - klass->deactivated = meta_selection_source_deactivated; - - signals[ACTIVE] = - g_signal_new ("activated", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (MetaSelectionSourceClass, activated), - NULL, NULL, NULL, - G_TYPE_NONE, 0); - signals[INACTIVE] = - g_signal_new ("deactivated", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (MetaSelectionSourceClass, deactivated), - NULL, NULL, NULL, - G_TYPE_NONE, 0); -} - -static void -meta_selection_source_init (MetaSelectionSource *source) -{ -} - -void -meta_selection_source_read_async (MetaSelectionSource *source, - const gchar *mimetype, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_return_if_fail (META_IS_SELECTION_SOURCE (source)); - g_return_if_fail (mimetype != NULL); - g_return_if_fail (callback != NULL); - - META_SELECTION_SOURCE_GET_CLASS (source)->read_async (source, - mimetype, - cancellable, - callback, - user_data); -} - -/** - * meta_selection_source_read_finish: - * @source: The selection source - * @result: The async result - * @error: Location for returned error - * - * Finishes a read from the selection source. - * - * Returns: (transfer full): The resulting #GInputStream - */ -GInputStream * -meta_selection_source_read_finish (MetaSelectionSource *source, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (META_IS_SELECTION_SOURCE (source), NULL); - g_return_val_if_fail (g_task_is_valid (result, source), NULL); - - return META_SELECTION_SOURCE_GET_CLASS (source)->read_finish (source, - result, - error); -} - -/** - * meta_selection_source_get_mimetypes: - * @source: The selection source - * - * Returns the list of supported mimetypes. - * - * Returns: (element-type utf8) (transfer full): The supported mimetypes - */ -GList * -meta_selection_source_get_mimetypes (MetaSelectionSource *source) -{ - g_return_val_if_fail (META_IS_SELECTION_SOURCE (source), NULL); - - return META_SELECTION_SOURCE_GET_CLASS (source)->get_mimetypes (source); -} - -/** - * meta_selection_source_is_active: - * @source: the selection source - * - * Returns #TRUE if the source is active on a selection. - * - * Returns: #TRUE if the source owns a selection. - **/ -gboolean -meta_selection_source_is_active (MetaSelectionSource *source) -{ - MetaSelectionSourcePrivate *priv = - meta_selection_source_get_instance_private (source); - - g_return_val_if_fail (META_IS_SELECTION_SOURCE (source), FALSE); - - return priv->active; -} diff --git a/src/core/meta-selection.c b/src/core/meta-selection.c deleted file mode 100644 index 4e42e59a9..000000000 --- a/src/core/meta-selection.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Author: Carlos Garnacho <carlosg@gnome.org> - */ - -#include "config.h" - -#include "core/meta-selection-private.h" -#include "meta/meta-selection.h" - -typedef struct TransferRequest TransferRequest; - -struct _MetaSelection -{ - GObject parent_instance; - MetaDisplay *display; - MetaSelectionSource *owners[META_N_SELECTION_TYPES]; -}; - -struct TransferRequest -{ - MetaSelectionType selection_type; - GInputStream *istream; - GOutputStream *ostream; - gssize len; -}; - -enum -{ - OWNER_CHANGED, - N_SIGNALS -}; - -static guint signals[N_SIGNALS] = { 0 }; - -G_DEFINE_TYPE (MetaSelection, meta_selection, G_TYPE_OBJECT) - -static void read_selection_source_async (GTask *task, - TransferRequest *request); - -static void -meta_selection_dispose (GObject *object) -{ - MetaSelection *selection = META_SELECTION (object); - guint i; - - for (i = 0; i < META_N_SELECTION_TYPES; i++) - { - g_clear_object (&selection->owners[i]); - } - - G_OBJECT_CLASS (meta_selection_parent_class)->dispose (object); -} - -static void -meta_selection_class_init (MetaSelectionClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = meta_selection_dispose; - - signals[OWNER_CHANGED] = - g_signal_new ("owner-changed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 2, - G_TYPE_UINT, - META_TYPE_SELECTION_SOURCE); -} - -static void -meta_selection_init (MetaSelection *selection) -{ -} - -MetaSelection * -meta_selection_new (MetaDisplay *display) -{ - return g_object_new (META_TYPE_SELECTION, - NULL); -} - -/** - * meta_selection_set_owner: - * @selection: The selection manager - * @selection_type: Selection type - * @owner: New selection owner - * - * Sets @owner as the owner of the selection given by @selection_type, - * unsets any previous owner there was. - **/ -void -meta_selection_set_owner (MetaSelection *selection, - MetaSelectionType selection_type, - MetaSelectionSource *owner) -{ - g_return_if_fail (META_IS_SELECTION (selection)); - g_return_if_fail (selection_type < META_N_SELECTION_TYPES); - - if (selection->owners[selection_type] == owner) - return; - - if (selection->owners[selection_type]) - g_signal_emit_by_name (selection->owners[selection_type], "deactivated"); - - g_set_object (&selection->owners[selection_type], owner); - g_signal_emit_by_name (owner, "activated"); - g_signal_emit (selection, signals[OWNER_CHANGED], 0, selection_type, owner); -} - -/** - * meta_selection_unset_owner: - * @selection: The selection manager - * @selection_type: Selection type - * @owner: Owner to unset - * - * Unsets @owner as the owner the selection given by @selection_type. If - * @owner does not own the selection, nothing is done. - **/ -void -meta_selection_unset_owner (MetaSelection *selection, - MetaSelectionType selection_type, - MetaSelectionSource *owner) -{ - g_return_if_fail (META_IS_SELECTION (selection)); - g_return_if_fail (selection_type < META_N_SELECTION_TYPES); - - if (selection->owners[selection_type] == owner) - { - g_signal_emit_by_name (owner, "deactivated"); - g_clear_object (&selection->owners[selection_type]); - g_signal_emit (selection, signals[OWNER_CHANGED], 0, - selection_type, NULL); - } -} - -/** - * meta_selection_get_mimetypes: - * @selection: The selection manager - * @selection_type: Selection to query - * - * Returns the list of supported mimetypes for the given selection type. - * - * Returns: (element-type utf8) (transfer full): The supported mimetypes - */ -GList * -meta_selection_get_mimetypes (MetaSelection *selection, - MetaSelectionType selection_type) -{ - g_return_val_if_fail (META_IS_SELECTION (selection), NULL); - g_return_val_if_fail (selection_type < META_N_SELECTION_TYPES, NULL); - - if (!selection->owners[selection_type]) - return NULL; - - return meta_selection_source_get_mimetypes (selection->owners[selection_type]); -} - -static TransferRequest * -transfer_request_new (GOutputStream *ostream, - MetaSelectionType selection_type, - gssize len) -{ - TransferRequest *request; - - request = g_new0 (TransferRequest, 1); - request->ostream = g_object_ref (ostream); - request->selection_type = selection_type; - request->len = len; - return request; -} - -static void -transfer_request_free (TransferRequest *request) -{ - g_clear_object (&request->istream); - g_clear_object (&request->ostream); - g_free (request); -} - -static void -splice_cb (GOutputStream *stream, - GAsyncResult *result, - GTask *task) -{ - GError *error = NULL; - - g_output_stream_splice_finish (stream, result, &error); - if (error) - { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - g_task_return_boolean (task, TRUE); - g_object_unref (task); -} - -static void -write_cb (GOutputStream *stream, - GAsyncResult *result, - GTask *task) -{ - TransferRequest *request; - GError *error = NULL; - - g_output_stream_write_bytes_finish (stream, result, &error); - if (error) - { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - request = g_task_get_task_data (task); - - if (request->len > 0) - { - read_selection_source_async (task, request); - } - else - { - g_task_return_boolean (task, TRUE); - g_object_unref (task); - } -} - -static void -read_cb (GInputStream *stream, - GAsyncResult *result, - GTask *task) -{ - TransferRequest *request; - GError *error = NULL; - GBytes *bytes; - - bytes = g_input_stream_read_bytes_finish (stream, result, &error); - if (error) - { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - else if (g_bytes_get_size (bytes) == 0) - { - g_task_return_boolean (task, TRUE); - g_object_unref (task); - return; - } - - request = g_task_get_task_data (task); - - if (request->len < g_bytes_get_size (bytes)) - { - GBytes *copy; - - /* Trim content */ - copy = g_bytes_new_from_bytes (bytes, 0, request->len); - g_bytes_unref (bytes); - bytes = copy; - } - - request->len -= g_bytes_get_size (bytes); - g_output_stream_write_bytes_async (request->ostream, - bytes, - G_PRIORITY_DEFAULT, - g_task_get_cancellable (task), - (GAsyncReadyCallback) write_cb, - task); - g_bytes_unref (bytes); -} - -static void -read_selection_source_async (GTask *task, - TransferRequest *request) -{ - g_input_stream_read_bytes_async (request->istream, - (gsize) request->len, - G_PRIORITY_DEFAULT, - g_task_get_cancellable (task), - (GAsyncReadyCallback) read_cb, - task); -} - -static void -source_read_cb (MetaSelectionSource *source, - GAsyncResult *result, - GTask *task) -{ - TransferRequest *request; - GInputStream *stream; - GError *error = NULL; - - stream = meta_selection_source_read_finish (source, result, &error); - if (!stream) - { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - request = g_task_get_task_data (task); - request->istream = stream; - - if (request->len < 0) - { - g_output_stream_splice_async (request->ostream, - request->istream, - G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | - G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, - G_PRIORITY_DEFAULT, - g_task_get_cancellable (task), - (GAsyncReadyCallback) splice_cb, - task); - } - else - { - read_selection_source_async (task, request); - } -} - -/** - * meta_selection_transfer_async: - * @selection: The selection manager - * @selection_type: Selection type - * @mimetype: Mimetype to transfer - * @size: Maximum size to transfer, -1 for unlimited - * @output: Output stream to write contents to - * @cancellable: Cancellable - * @callback: User callback - * @user_data: User data - * - * Requests a transfer of @mimetype on the selection given by - * @selection_type. - **/ -void -meta_selection_transfer_async (MetaSelection *selection, - MetaSelectionType selection_type, - const char *mimetype, - gssize size, - GOutputStream *output, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GTask *task; - - g_return_if_fail (META_IS_SELECTION (selection)); - g_return_if_fail (selection_type < META_N_SELECTION_TYPES); - g_return_if_fail (G_IS_OUTPUT_STREAM (output)); - g_return_if_fail (mimetype != NULL); - - task = g_task_new (selection, cancellable, callback, user_data); - g_task_set_source_tag (task, meta_selection_transfer_async); - - g_task_set_task_data (task, - transfer_request_new (output, selection_type, size), - (GDestroyNotify) transfer_request_free); - meta_selection_source_read_async (selection->owners[selection_type], - mimetype, - cancellable, - (GAsyncReadyCallback) source_read_cb, - task); -} - -/** - * meta_selection_transfer_finish: - * @selection: The selection manager - * @result: The async result - * @error: Location for returned error, or %NULL - * - * Finishes the transfer of a queried mimetype. - * - * Returns: #TRUE if the transfer was successful. - **/ -gboolean -meta_selection_transfer_finish (MetaSelection *selection, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (g_task_is_valid (result, selection), FALSE); - g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == - meta_selection_transfer_async, FALSE); - - return g_task_propagate_boolean (G_TASK (result), error); -} - -MetaSelectionSource * -meta_selection_get_current_owner (MetaSelection *selection, - MetaSelectionType selection_type) -{ - g_return_val_if_fail (META_IS_SELECTION (selection), NULL); - g_return_val_if_fail (selection_type < META_N_SELECTION_TYPES, NULL); - - return selection->owners[selection_type]; -} diff --git a/src/core/meta-sound-player.c b/src/core/meta-sound-player.c deleted file mode 100644 index e2d103893..000000000 --- a/src/core/meta-sound-player.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Author: Carlos Garnacho <carlosg@gnome.org> - */ - -#include "config.h" - -#include <canberra.h> - -#include "meta/meta-sound-player.h" - -#define EVENT_SOUNDS_KEY "event-sounds" -#define THEME_NAME_KEY "theme-name" - -typedef struct _MetaPlayRequest MetaPlayRequest; - -struct _MetaSoundPlayer -{ - GObject parent; - GThreadPool *queue; - GSettings *settings; - ca_context *context; - uint32_t id_pool; -}; - -struct _MetaPlayRequest -{ - ca_proplist *props; - uint32_t id; - gulong cancel_id; - GCancellable *cancellable; - MetaSoundPlayer *player; -}; - -const char * const cache_allow_list[] = { - "bell-window-system", - "desktop-switch-left", - "desktop-switch-right", - "desktop-switch-up", - "desktop-switch-down", - NULL -}; - -G_DEFINE_TYPE (MetaSoundPlayer, meta_sound_player, G_TYPE_OBJECT) - -static MetaPlayRequest * -meta_play_request_new (MetaSoundPlayer *player, - ca_proplist *props, - GCancellable *cancellable) -{ - MetaPlayRequest *req; - - req = g_new0 (MetaPlayRequest, 1); - req->props = props; - req->player = player; - g_set_object (&req->cancellable, cancellable); - - return req; -} - -static void -meta_play_request_free (MetaPlayRequest *req) -{ - g_clear_object (&req->cancellable); - ca_proplist_destroy (req->props); - g_free (req); -} - -static void -meta_sound_player_finalize (GObject *object) -{ - MetaSoundPlayer *player = META_SOUND_PLAYER (object); - - g_object_unref (player->settings); - g_thread_pool_free (player->queue, FALSE, TRUE); - ca_context_destroy (player->context); - - G_OBJECT_CLASS (meta_sound_player_parent_class)->finalize (object); -} - -static void -meta_sound_player_class_init (MetaSoundPlayerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = meta_sound_player_finalize; -} - -static void -cancelled_cb (GCancellable *cancellable, - MetaPlayRequest *req) -{ - ca_context_cancel (req->player->context, req->id); -} - -static void -finish_cb (ca_context *context, - uint32_t id, - int error_code, - gpointer user_data) -{ - MetaPlayRequest *req = user_data; - - if (error_code != CA_ERROR_CANCELED) - g_cancellable_disconnect (req->cancellable, req->cancel_id); - else if (req->cancellable != NULL) - g_clear_signal_handler (&req->cancel_id, req->cancellable); - - meta_play_request_free (req); -} - -static void -play_sound (MetaPlayRequest *req, - MetaSoundPlayer *player) -{ - req->id = player->id_pool++; - - if (ca_context_play_full (player->context, req->id, req->props, - finish_cb, req) != CA_SUCCESS) - { - meta_play_request_free (req); - return; - } - - if (req->cancellable) - { - gulong cancel_id = - g_cancellable_connect (req->cancellable, - G_CALLBACK (cancelled_cb), req, NULL); - if (cancel_id) - req->cancel_id = cancel_id; - } -} - -static void -settings_changed_cb (GSettings *settings, - const char *key, - MetaSoundPlayer *player) -{ - if (strcmp (key, EVENT_SOUNDS_KEY) == 0) - { - gboolean enabled; - - enabled = g_settings_get_boolean (settings, EVENT_SOUNDS_KEY); - ca_context_change_props (player->context, CA_PROP_CANBERRA_ENABLE, - enabled ? "1" : "0", NULL); - } - else if (strcmp (key, THEME_NAME_KEY) == 0) - { - char *theme_name; - - theme_name = g_settings_get_string (settings, THEME_NAME_KEY); - ca_context_change_props (player->context, CA_PROP_CANBERRA_XDG_THEME_NAME, - theme_name, NULL); - g_free (theme_name); - } -} - -static ca_context * -create_context (GSettings *settings) -{ - ca_context *context; - ca_proplist *props; - gboolean enabled; - char *theme_name; - - if (ca_context_create (&context) != CA_SUCCESS) - return NULL; - - if (ca_proplist_create (&props) != CA_SUCCESS) - { - ca_context_destroy (context); - return NULL; - } - - ca_proplist_sets (props, CA_PROP_APPLICATION_NAME, "Mutter"); - - enabled = g_settings_get_boolean (settings, EVENT_SOUNDS_KEY); - ca_proplist_sets (props, CA_PROP_CANBERRA_ENABLE, enabled ? "1" : "0"); - - theme_name = g_settings_get_string (settings, THEME_NAME_KEY); - ca_proplist_sets (props, CA_PROP_CANBERRA_XDG_THEME_NAME, theme_name); - g_free (theme_name); - - ca_context_change_props_full (context, props); - ca_proplist_destroy (props); - - return context; -} - -static void -meta_sound_player_init (MetaSoundPlayer *player) -{ - player->queue = g_thread_pool_new ((GFunc) play_sound, - player, 1, FALSE, NULL); - player->settings = g_settings_new ("org.gnome.desktop.sound"); - player->context = create_context (player->settings); - - g_signal_connect (player->settings, "changed", - G_CALLBACK (settings_changed_cb), player); -} - -static void -build_ca_proplist (ca_proplist *props, - const char *event_property, - const char *event_id, - const char *event_description) -{ - ca_proplist_sets (props, event_property, event_id); - ca_proplist_sets (props, CA_PROP_EVENT_DESCRIPTION, event_description); -} - -/** - * meta_sound_player_play_from_theme: - * @player: a #MetaSoundPlayer - * @name: sound theme name of the event - * @description: description of the event - * @cancellable: cancellable for the request - * - * Plays a sound from the sound theme. - **/ -void -meta_sound_player_play_from_theme (MetaSoundPlayer *player, - const char *name, - const char *description, - GCancellable *cancellable) -{ - MetaPlayRequest *req; - ca_proplist *props; - - g_return_if_fail (META_IS_SOUND_PLAYER (player)); - g_return_if_fail (name != NULL); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - ca_proplist_create (&props); - build_ca_proplist (props, CA_PROP_EVENT_ID, name, description); - - if (g_strv_contains (cache_allow_list, name)) - ca_proplist_sets (props, CA_PROP_CANBERRA_CACHE_CONTROL, "permanent"); - else - ca_proplist_sets (props, CA_PROP_CANBERRA_CACHE_CONTROL, "volatile"); - - req = meta_play_request_new (player, props, cancellable); - g_thread_pool_push (player->queue, req, NULL); -} - -/** - * meta_sound_player_play_from_file: - * @player: a #MetaSoundPlayer - * @file: file to play - * @description: description of the played sound - * @cancellable: cancellable for the request - * - * Plays a sound from a file. - **/ -void -meta_sound_player_play_from_file (MetaSoundPlayer *player, - GFile *file, - const char *description, - GCancellable *cancellable) -{ - MetaPlayRequest *req; - ca_proplist *props; - char *path; - - g_return_if_fail (META_IS_SOUND_PLAYER (player)); - g_return_if_fail (G_IS_FILE (file)); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - path = g_file_get_path (file); - g_return_if_fail (path != NULL); - - ca_proplist_create (&props); - build_ca_proplist (props, CA_PROP_MEDIA_FILENAME, path, description); - ca_proplist_sets (props, CA_PROP_CANBERRA_CACHE_CONTROL, "volatile"); - g_free (path); - - req = meta_play_request_new (player, props, cancellable); - g_thread_pool_push (player->queue, req, NULL); -} diff --git a/src/core/meta-workspace-manager-private.h b/src/core/meta-workspace-manager-private.h deleted file mode 100644 index 261c4d47c..000000000 --- a/src/core/meta-workspace-manager-private.h +++ /dev/null @@ -1,96 +0,0 @@ -/* -*- 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 <http://www.gnu.org/licenses/>. - */ - -#ifndef META_WORKSPACE_MANAGER_PRIVATE_H -#define META_WORKSPACE_MANAGER_PRIVATE_H - -#include <glib.h> - -#include "core/display-private.h" -#include "meta/common.h" -#include "meta/types.h" -#include "meta/meta-workspace-manager.h" - -struct _MetaWorkspaceManager -{ - GObject parent; - - MetaDisplay *display; - MetaWorkspace *active_workspace; - - GList *workspaces; - - int rows_of_workspaces; - int columns_of_workspaces; - MetaDisplayCorner starting_corner; - guint vertical_workspaces : 1; - guint workspace_layout_overridden : 1; -}; - -MetaWorkspaceManager *meta_workspace_manager_new (MetaDisplay *display); - -void meta_workspace_manager_init_workspaces (MetaWorkspaceManager *workspace_manager); -void meta_workspace_manager_update_workspace_layout (MetaWorkspaceManager *workspace_manager, - MetaDisplayCorner starting_corner, - gboolean vertical_layout, - int n_rows, - int n_columns); - -void meta_workspace_manager_reload_work_areas (MetaWorkspaceManager *workspace_manager); - -typedef struct MetaWorkspaceLayout MetaWorkspaceLayout; - -struct MetaWorkspaceLayout -{ - int rows; - int cols; - int *grid; - int grid_area; - int current_row; - int current_col; -}; - -void meta_workspace_manager_calc_workspace_layout (MetaWorkspaceManager *workspace_manager, - int num_workspaces, - int current_space, - MetaWorkspaceLayout *layout); - -void meta_workspace_manager_free_workspace_layout (MetaWorkspaceLayout *layout); - -void meta_workspace_manager_minimize_all_on_active_workspace_except (MetaWorkspaceManager *workspace_manager, - MetaWindow *keep); - -/* Show/hide the desktop (temporarily hide all windows) */ -void meta_workspace_manager_show_desktop (MetaWorkspaceManager *workspace_manager, - guint32 timestamp); -void meta_workspace_manager_unshow_desktop (MetaWorkspaceManager *workspace_manager); - -void meta_workspace_manager_workspace_switched (MetaWorkspaceManager *workspace_manager, - int from, - int to, - MetaMotionDirection direction); - -void meta_workspace_manager_update_num_workspaces (MetaWorkspaceManager *workspace_manager, - guint32 timestamp, - int new_num); - -#endif /* META_WORKSPACE_MANAGER_PRIVATE_H */ diff --git a/src/core/meta-workspace-manager.c b/src/core/meta-workspace-manager.c deleted file mode 100644 index 61fbc0090..000000000 --- a/src/core/meta-workspace-manager.c +++ /dev/null @@ -1,1060 +0,0 @@ -/* -*- 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 <http://www.gnu.org/licenses/>. - */ - -#include "config.h" - -#include "core/meta-workspace-manager-private.h" - -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "core/window-private.h" -#include "core/workspace-private.h" -#include "meta/meta-enum-types.h" -#include "meta/prefs.h" -#include "meta/util.h" - -G_DEFINE_TYPE (MetaWorkspaceManager, meta_workspace_manager, G_TYPE_OBJECT) - -enum -{ - WORKSPACE_ADDED, - WORKSPACE_REMOVED, - WORKSPACE_SWITCHED, - WORKSPACES_REORDERED, - ACTIVE_WORKSPACE_CHANGED, - SHOWING_DESKTOP_CHANGED, - LAST_SIGNAL -}; - -enum -{ - PROP_0, - - PROP_LAYOUT_COLUMNS, - PROP_LAYOUT_ROWS, - - PROP_N_WORKSPACES -}; - -static guint workspace_manager_signals [LAST_SIGNAL] = { 0 }; - -static void prefs_changed_callback (MetaPreference pref, - gpointer data); - -static void -meta_workspace_manager_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - MetaWorkspaceManager *workspace_manager = META_WORKSPACE_MANAGER (object); - - switch (prop_id) - { - case PROP_LAYOUT_COLUMNS: - g_value_set_int (value, workspace_manager->columns_of_workspaces); - break; - case PROP_LAYOUT_ROWS: - g_value_set_int (value, workspace_manager->rows_of_workspaces); - break; - case PROP_N_WORKSPACES: - g_value_set_int (value, meta_workspace_manager_get_n_workspaces (workspace_manager)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -meta_workspace_manager_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_workspace_manager_finalize (GObject *object) -{ - MetaWorkspaceManager *workspace_manager = META_WORKSPACE_MANAGER (object); - - meta_prefs_remove_listener (prefs_changed_callback, workspace_manager); - - G_OBJECT_CLASS (meta_workspace_manager_parent_class)->finalize (object); -} - -static void -meta_workspace_manager_class_init (MetaWorkspaceManagerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->get_property = meta_workspace_manager_get_property; - object_class->set_property = meta_workspace_manager_set_property; - - object_class->finalize = meta_workspace_manager_finalize; - - workspace_manager_signals[WORKSPACE_ADDED] = - g_signal_new ("workspace-added", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, - 1, - G_TYPE_INT); - - workspace_manager_signals[WORKSPACE_REMOVED] = - g_signal_new ("workspace-removed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, - 1, - G_TYPE_INT); - - workspace_manager_signals[WORKSPACE_SWITCHED] = - g_signal_new ("workspace-switched", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, - 3, - G_TYPE_INT, - G_TYPE_INT, - META_TYPE_MOTION_DIRECTION); - - /* Emitted when calling meta_workspace_manager_reorder_workspace. - * - * This signal is emitted when a workspace has been reordered to - * a different index. Note that other workspaces can change - * their index too when reordering happens. - */ - workspace_manager_signals[WORKSPACES_REORDERED] = - g_signal_new ("workspaces-reordered", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 0); - - workspace_manager_signals[ACTIVE_WORKSPACE_CHANGED] = - g_signal_new ("active-workspace-changed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); - - workspace_manager_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); - - g_object_class_install_property (object_class, - PROP_LAYOUT_COLUMNS, - g_param_spec_int ("layout-columns", - "Layout columns", - "Number of columns in layout", - -1, G_MAXINT, 1, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, - PROP_LAYOUT_ROWS, - g_param_spec_int ("layout-rows", - "Layout rows", - "Number of rows in layout", - -1, G_MAXINT, -1, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, - PROP_N_WORKSPACES, - g_param_spec_int ("n-workspaces", - "N Workspaces", - "Number of workspaces", - 1, G_MAXINT, 1, - G_PARAM_READABLE)); -} - -static void -meta_workspace_manager_init (MetaWorkspaceManager *workspace_manager) -{ -} - -void -meta_workspace_manager_reload_work_areas (MetaWorkspaceManager *workspace_manager) -{ - GList *l; - - for (l = workspace_manager->workspaces; l; l = l->next) - { - MetaWorkspace *workspace = l->data; - - meta_workspace_invalidate_work_area (workspace); - } -} - -MetaWorkspaceManager * -meta_workspace_manager_new (MetaDisplay *display) -{ - MetaWorkspaceManager *workspace_manager; - - workspace_manager = g_object_new (META_TYPE_WORKSPACE_MANAGER, NULL); - - workspace_manager->display = display; - workspace_manager->active_workspace = NULL; - workspace_manager->workspaces = NULL; - workspace_manager->rows_of_workspaces = 1; - workspace_manager->columns_of_workspaces = -1; - workspace_manager->vertical_workspaces = FALSE; - workspace_manager->starting_corner = META_DISPLAY_TOPLEFT; - - /* This is the default layout extracted from default - * variable values in update_num_workspaces () - * This can be overridden using _NET_DESKTOP_LAYOUT in - * meta_x11_display_new (), if it's specified */ - meta_workspace_manager_update_workspace_layout (workspace_manager, - META_DISPLAY_TOPLEFT, - FALSE, - 1, - -1); - - /* There must be at least one workspace at all times, - * so create that required workspace. - */ - meta_workspace_new (workspace_manager); - - meta_workspace_manager_init_workspaces (workspace_manager); - - meta_prefs_add_listener (prefs_changed_callback, workspace_manager); - - return workspace_manager; -} - -void -meta_workspace_manager_init_workspaces (MetaWorkspaceManager *workspace_manager) -{ - int num; - - g_return_if_fail (META_IS_WORKSPACE_MANAGER (workspace_manager)); - - if (meta_prefs_get_dynamic_workspaces ()) - /* This will be properly updated using _NET_NUMBER_OF_DESKTOPS - * (if set) in meta_x11_display_new () */ - num = 1; - else - num = meta_prefs_get_num_workspaces (); - - meta_workspace_manager_update_num_workspaces (workspace_manager, META_CURRENT_TIME, num); - - meta_workspace_activate (workspace_manager->workspaces->data, META_CURRENT_TIME); - - meta_workspace_manager_reload_work_areas (workspace_manager); -} - -int -meta_workspace_manager_get_n_workspaces (MetaWorkspaceManager *workspace_manager) -{ - return g_list_length (workspace_manager->workspaces); -} - -/** - * meta_workspace_manager_get_workspace_by_index: - * @workspace_manager: a #MetaWorkspaceManager - * @index: index of one of the display's workspaces - * - * Gets the workspace object for one of a workspace manager's workspaces given the workspace - * index. It's valid to call this function with an out-of-range index and it - * will robustly return %NULL. - * - * Return value: (transfer none) (nullable): the workspace object with specified - * index, or %NULL if the index is out of range. - */ -MetaWorkspace * -meta_workspace_manager_get_workspace_by_index (MetaWorkspaceManager *workspace_manager, - int idx) -{ - return g_list_nth_data (workspace_manager->workspaces, idx); -} - -void -meta_workspace_manager_remove_workspace (MetaWorkspaceManager *workspace_manager, - MetaWorkspace *workspace, - guint32 timestamp) -{ - GList *l; - GList *next; - MetaWorkspace *neighbour = NULL; - int index; - int active_index; - gboolean active_index_changed; - int new_num; - - l = g_list_find (workspace_manager->workspaces, workspace); - if (!l) - return; - - next = l->next; - - if (l->prev) - neighbour = l->prev->data; - else if (l->next) - neighbour = l->next->data; - else - { - /* Cannot remove the only workspace! */ - return; - } - - meta_workspace_relocate_windows (workspace, neighbour); - - if (workspace == workspace_manager->active_workspace) - meta_workspace_activate (neighbour, timestamp); - - /* To emit the signal after removing the workspace */ - index = meta_workspace_index (workspace); - active_index = meta_workspace_manager_get_active_workspace_index (workspace_manager); - active_index_changed = index < active_index; - - /* This also removes the workspace from the displays list */ - meta_workspace_remove (workspace); - - new_num = g_list_length (workspace_manager->workspaces); - - if (!meta_prefs_get_dynamic_workspaces ()) - meta_prefs_set_num_workspaces (new_num); - - /* If deleting a workspace before the current workspace, the active - * workspace index changes, so we need to update that hint */ - if (active_index_changed) - g_signal_emit (workspace_manager, - workspace_manager_signals[ACTIVE_WORKSPACE_CHANGED], - 0, NULL); - - for (l = next; l; l = l->next) - { - MetaWorkspace *w = l->data; - meta_workspace_index_changed (w); - } - - meta_display_queue_workarea_recalc (workspace_manager->display); - - g_signal_emit (workspace_manager, - workspace_manager_signals[WORKSPACE_REMOVED], - 0, index); - g_object_notify (G_OBJECT (workspace_manager), "n-workspaces"); -} - -/** - * meta_workspace_manager_append_new_workspace: - * @workspace_manager: a #MetaWorkspaceManager - * @activate: %TRUE if the workspace should be switched to after creation - * @timestamp: if switching to a new workspace, timestamp to be used when - * focusing a window on the new workspace. (Doesn't hurt to pass a valid - * timestamp when available even if not switching workspaces.) - * - * Append a new workspace to the workspace manager and (optionally) switch to that - * display. - * - * Return value: (transfer none): the newly appended workspace. - */ -MetaWorkspace * -meta_workspace_manager_append_new_workspace (MetaWorkspaceManager *workspace_manager, - gboolean activate, - guint32 timestamp) -{ - MetaWorkspace *w; - int new_num; - - /* This also adds the workspace to the workspace manager list */ - w = meta_workspace_new (workspace_manager); - - if (!w) - return NULL; - - if (activate) - meta_workspace_activate (w, timestamp); - - new_num = g_list_length (workspace_manager->workspaces); - - if (!meta_prefs_get_dynamic_workspaces ()) - meta_prefs_set_num_workspaces (new_num); - - meta_display_queue_workarea_recalc (workspace_manager->display); - - g_signal_emit (workspace_manager, workspace_manager_signals[WORKSPACE_ADDED], - 0, meta_workspace_index (w)); - g_object_notify (G_OBJECT (workspace_manager), "n-workspaces"); - - return w; -} - -void -meta_workspace_manager_update_num_workspaces (MetaWorkspaceManager *workspace_manager, - guint32 timestamp, - int new_num) -{ - int old_num; - GList *l; - int i = 0; - GList *extras = NULL; - MetaWorkspace *last_remaining = NULL; - gboolean need_change_space = FALSE; - - g_assert (new_num > 0); - - if (g_list_length (workspace_manager->workspaces) == (guint) new_num) - return; - - for (l = workspace_manager->workspaces; l; l = l->next) - { - MetaWorkspace *w = l->data; - - if (i >= new_num) - extras = g_list_prepend (extras, w); - else - last_remaining = w; - - ++i; - } - old_num = i; - - g_assert (last_remaining); - - /* Get rid of the extra workspaces by moving all their windows - * to last_remaining, then activating last_remaining if - * one of the removed workspaces was active. This will be a bit - * wacky if the config tool for changing number of workspaces - * is on a removed workspace ;-) - */ - for (l = extras; l; l = l->next) - { - MetaWorkspace *w = l->data; - - meta_workspace_relocate_windows (w, last_remaining); - - if (w == workspace_manager->active_workspace) - need_change_space = TRUE; - } - - if (need_change_space) - meta_workspace_activate (last_remaining, timestamp); - - /* Should now be safe to free the workspaces */ - for (l = extras; l; l = l->next) - { - MetaWorkspace *w = l->data; - - meta_workspace_remove (w); - } - - g_list_free (extras); - - for (i = old_num; i < new_num; i++) - meta_workspace_new (workspace_manager); - - meta_display_queue_workarea_recalc (workspace_manager->display); - - for (i = old_num; i < new_num; i++) - g_signal_emit (workspace_manager, - workspace_manager_signals[WORKSPACE_ADDED], - 0, i); - - g_object_notify (G_OBJECT (workspace_manager), "n-workspaces"); -} - -/** - * meta_workspace_manager_reorder_workspace: - * @workspace_manager: a #MetaWorkspaceManager - * @workspace: a #MetaWorkspace to reorder - * @new_index: the new index of the passed workspace - * - * Reorder a workspace to a new index. If the workspace is currently active - * the "active-workspace-changed" signal will be emitted. - * If the workspace's index is the same as @new_index or the workspace - * will not be found in the list, this function will return. - * - * Calling this function will also emit the "workspaces-reordered" signal. - */ -void -meta_workspace_manager_reorder_workspace (MetaWorkspaceManager *workspace_manager, - MetaWorkspace *workspace, - int new_index) -{ - GList *l; - GList *from, *to; - int index; - int active_index, new_active_index; - - g_return_if_fail (META_IS_WORKSPACE_MANAGER (workspace_manager)); - g_return_if_fail (new_index >= 0 && - new_index < g_list_length (workspace_manager->workspaces)); - - l = g_list_find (workspace_manager->workspaces, workspace); - g_return_if_fail (l); - - index = meta_workspace_index (workspace); - - if (new_index == index) - return; - - active_index = - meta_workspace_manager_get_active_workspace_index (workspace_manager); - - workspace_manager->workspaces = - g_list_remove_link (workspace_manager->workspaces, l); - - workspace_manager->workspaces = - g_list_insert (workspace_manager->workspaces, l->data, new_index); - - g_list_free (l); - - new_active_index = - meta_workspace_manager_get_active_workspace_index (workspace_manager); - - if (active_index != new_active_index) - g_signal_emit (workspace_manager, - workspace_manager_signals[ACTIVE_WORKSPACE_CHANGED], - 0, NULL); - - from = g_list_nth (workspace_manager->workspaces, MIN (new_index, index)); - to = g_list_nth (workspace_manager->workspaces, MAX (new_index, index)); - for (l = from; l != to->next; l = l->next) - { - MetaWorkspace *w = l->data; - - meta_workspace_index_changed (w); - } - - meta_display_queue_workarea_recalc (workspace_manager->display); - g_signal_emit (workspace_manager, - workspace_manager_signals[WORKSPACES_REORDERED], 0, NULL); -} - -void -meta_workspace_manager_update_workspace_layout (MetaWorkspaceManager *workspace_manager, - MetaDisplayCorner starting_corner, - gboolean vertical_layout, - int n_rows, - int n_columns) -{ - g_return_if_fail (META_IS_WORKSPACE_MANAGER (workspace_manager)); - g_return_if_fail (n_rows > 0 || n_columns > 0); - g_return_if_fail (n_rows != 0 && n_columns != 0); - - if (workspace_manager->workspace_layout_overridden) - return; - - workspace_manager->vertical_workspaces = vertical_layout != FALSE; - workspace_manager->starting_corner = starting_corner; - workspace_manager->rows_of_workspaces = n_rows; - workspace_manager->columns_of_workspaces = n_columns; - - meta_verbose ("Workspace layout rows = %d cols = %d orientation = %d starting corner = %u", - workspace_manager->rows_of_workspaces, - workspace_manager->columns_of_workspaces, - workspace_manager->vertical_workspaces, - workspace_manager->starting_corner); - g_object_notify (G_OBJECT (workspace_manager), "layout-columns"); - g_object_notify (G_OBJECT (workspace_manager), "layout-rows"); -} - -/** - * meta_workspace_manager_override_workspace_layout: - * @workspace_manager: a #MetaWorkspaceManager - * @starting_corner: the corner at which the first workspace is found - * @vertical_layout: if %TRUE the workspaces are laid out in columns rather than rows - * @n_rows: number of rows of workspaces, or -1 to determine the number of rows from - * @n_columns and the total number of workspaces - * @n_columns: number of columns of workspaces, or -1 to determine the number of columns from - * @n_rows and the total number of workspaces - * - * Explicitly set the layout of workspaces. Once this has been called, the contents of the - * _NET_DESKTOP_LAYOUT property on the root window are completely ignored. - */ -void -meta_workspace_manager_override_workspace_layout (MetaWorkspaceManager *workspace_manager, - MetaDisplayCorner starting_corner, - gboolean vertical_layout, - int n_rows, - int n_columns) -{ - workspace_manager->workspace_layout_overridden = FALSE; - - meta_workspace_manager_update_workspace_layout (workspace_manager, - starting_corner, - vertical_layout, - n_rows, - n_columns); - - workspace_manager->workspace_layout_overridden = TRUE; -} - -#ifdef WITH_VERBOSE_MODE -static const char * -meta_workspace_manager_corner_to_string (MetaDisplayCorner corner) -{ - switch (corner) - { - case META_DISPLAY_TOPLEFT: - return "TopLeft"; - case META_DISPLAY_TOPRIGHT: - return "TopRight"; - case META_DISPLAY_BOTTOMLEFT: - return "BottomLeft"; - case META_DISPLAY_BOTTOMRIGHT: - return "BottomRight"; - } - - return "Unknown"; -} -#endif /* WITH_VERBOSE_MODE */ - -void -meta_workspace_manager_calc_workspace_layout (MetaWorkspaceManager *workspace_manager, - int num_workspaces, - int current_space, - MetaWorkspaceLayout *layout) -{ - int rows, cols; - int grid_area; - int *grid; - int i, r, c; - int current_row, current_col; - - rows = workspace_manager->rows_of_workspaces; - cols = workspace_manager->columns_of_workspaces; - if (rows <= 0 && cols <= 0) - cols = num_workspaces; - - if (rows <= 0) - rows = num_workspaces / cols + ((num_workspaces % cols) > 0 ? 1 : 0); - if (cols <= 0) - cols = num_workspaces / rows + ((num_workspaces % rows) > 0 ? 1 : 0); - - /* paranoia */ - if (rows < 1) - rows = 1; - if (cols < 1) - cols = 1; - - g_assert (rows != 0 && cols != 0); - - grid_area = rows * cols; - - meta_verbose ("Getting layout rows = %d cols = %d current = %d " - "num_spaces = %d vertical = %s corner = %s", - rows, cols, current_space, num_workspaces, - workspace_manager->vertical_workspaces ? "(true)" : "(false)", - meta_workspace_manager_corner_to_string (workspace_manager->starting_corner)); - - /* ok, we want to setup the distances in the workspace array to go - * in each direction. Remember, there are many ways that a workspace - * array can be setup. - * see http://www.freedesktop.org/standards/wm-spec/1.2/html/x109.html - * and look at the _NET_DESKTOP_LAYOUT section for details. - * For instance: - */ - /* starting_corner = META_DISPLAY_TOPLEFT - * vertical_workspaces = 0 vertical_workspaces=1 - * 1234 1357 - * 5678 2468 - * - * starting_corner = META_DISPLAY_TOPRIGHT - * vertical_workspaces = 0 vertical_workspaces=1 - * 4321 7531 - * 8765 8642 - * - * starting_corner = META_DISPLAY_BOTTOMLEFT - * vertical_workspaces = 0 vertical_workspaces=1 - * 5678 2468 - * 1234 1357 - * - * starting_corner = META_DISPLAY_BOTTOMRIGHT - * vertical_workspaces = 0 vertical_workspaces=1 - * 8765 8642 - * 4321 7531 - * - */ - /* keep in mind that we could have a ragged layout, e.g. the "8" - * in the above grids could be missing - */ - - - grid = g_new (int, grid_area); - - i = 0; - - switch (workspace_manager->starting_corner) - { - case META_DISPLAY_TOPLEFT: - if (workspace_manager->vertical_workspaces) - { - c = 0; - while (c < cols) - { - r = 0; - while (r < rows) - { - grid[r*cols+c] = i; - ++i; - ++r; - } - ++c; - } - } - else - { - r = 0; - while (r < rows) - { - c = 0; - while (c < cols) - { - grid[r*cols+c] = i; - ++i; - ++c; - } - ++r; - } - } - break; - case META_DISPLAY_TOPRIGHT: - if (workspace_manager->vertical_workspaces) - { - c = cols - 1; - while (c >= 0) - { - r = 0; - while (r < rows) - { - grid[r*cols+c] = i; - ++i; - ++r; - } - --c; - } - } - else - { - r = 0; - while (r < rows) - { - c = cols - 1; - while (c >= 0) - { - grid[r*cols+c] = i; - ++i; - --c; - } - ++r; - } - } - break; - case META_DISPLAY_BOTTOMLEFT: - if (workspace_manager->vertical_workspaces) - { - c = 0; - while (c < cols) - { - r = rows - 1; - while (r >= 0) - { - grid[r*cols+c] = i; - ++i; - --r; - } - ++c; - } - } - else - { - r = rows - 1; - while (r >= 0) - { - c = 0; - while (c < cols) - { - grid[r*cols+c] = i; - ++i; - ++c; - } - --r; - } - } - break; - case META_DISPLAY_BOTTOMRIGHT: - if (workspace_manager->vertical_workspaces) - { - c = cols - 1; - while (c >= 0) - { - r = rows - 1; - while (r >= 0) - { - grid[r*cols+c] = i; - ++i; - --r; - } - --c; - } - } - else - { - r = rows - 1; - while (r >= 0) - { - c = cols - 1; - while (c >= 0) - { - grid[r*cols+c] = i; - ++i; - --c; - } - --r; - } - } - break; - } - - if (i != grid_area) - meta_bug ("did not fill in the whole workspace grid in %s (%d filled)", - G_STRFUNC, i); - - current_row = 0; - current_col = 0; - r = 0; - while (r < rows) - { - c = 0; - while (c < cols) - { - if (grid[r*cols+c] == current_space) - { - current_row = r; - current_col = c; - } - else if (grid[r*cols+c] >= num_workspaces) - { - /* flag nonexistent spaces with -1 */ - grid[r*cols+c] = -1; - } - ++c; - } - ++r; - } - - layout->rows = rows; - layout->cols = cols; - layout->grid = grid; - layout->grid_area = grid_area; - layout->current_row = current_row; - layout->current_col = current_col; - -#ifdef WITH_VERBOSE_MODE - if (meta_is_verbose ()) - { - r = 0; - while (r < layout->rows) - { - meta_verbose (" "); - meta_push_no_msg_prefix (); - c = 0; - while (c < layout->cols) - { - if (r == layout->current_row && - c == layout->current_col) - meta_verbose ("*%2d ", layout->grid[r*layout->cols+c]); - else - meta_verbose ("%3d ", layout->grid[r*layout->cols+c]); - ++c; - } - meta_pop_no_msg_prefix (); - ++r; - } - } -#endif /* WITH_VERBOSE_MODE */ -} - -void -meta_workspace_manager_free_workspace_layout (MetaWorkspaceLayout *layout) -{ - g_free (layout->grid); -} - -static void -queue_windows_showing (MetaWorkspaceManager *workspace_manager) -{ - GSList *windows, *l; - - /* Must operate on all windows on display instead of just on the - * active_workspace's window list, because the active_workspace's - * window list may not contain the on_all_workspace windows. - */ - windows = meta_display_list_windows (workspace_manager->display, META_LIST_DEFAULT); - - for (l = windows; l; l = l->next) - { - MetaWindow *w = l->data; - - meta_window_queue (w, META_QUEUE_CALC_SHOWING); - } - - g_slist_free (windows); -} - -void -meta_workspace_manager_minimize_all_on_active_workspace_except (MetaWorkspaceManager *workspace_manager, - MetaWindow *keep) -{ - GList *l; - - for (l = workspace_manager->active_workspace->windows; l; l = l->next) - { - MetaWindow *w = l->data; - - if (w->has_minimize_func && w != keep) - meta_window_minimize (w); - } -} - -void -meta_workspace_manager_show_desktop (MetaWorkspaceManager *workspace_manager, - guint32 timestamp) -{ - GList *l; - - if (workspace_manager->active_workspace->showing_desktop) - return; - - workspace_manager->active_workspace->showing_desktop = TRUE; - - queue_windows_showing (workspace_manager); - - /* Focus the most recently used META_WINDOW_DESKTOP window, if there is one; - * see bug 159257. - */ - for (l = workspace_manager->active_workspace->mru_list; l; l = l->next) - { - MetaWindow *w = l->data; - - if (w->type == META_WINDOW_DESKTOP) - { - meta_window_focus (w, timestamp); - break; - } - } - - g_signal_emit (workspace_manager, - workspace_manager_signals[SHOWING_DESKTOP_CHANGED], - 0, NULL); -} - -void -meta_workspace_manager_unshow_desktop (MetaWorkspaceManager *workspace_manager) -{ - if (!workspace_manager->active_workspace->showing_desktop) - return; - - workspace_manager->active_workspace->showing_desktop = FALSE; - - queue_windows_showing (workspace_manager); - - g_signal_emit (workspace_manager, - workspace_manager_signals[SHOWING_DESKTOP_CHANGED], - 0, NULL); -} - -/** - * meta_workspace_manager_get_workspaces: (skip) - * @workspace_manager: a #MetaWorkspaceManager - * - * Returns: (transfer none) (element-type Meta.Workspace): The workspaces for @display - */ -GList * -meta_workspace_manager_get_workspaces (MetaWorkspaceManager *workspace_manager) -{ - return workspace_manager->workspaces; -} - -int -meta_workspace_manager_get_active_workspace_index (MetaWorkspaceManager *workspace_manager) -{ - MetaWorkspace *active = workspace_manager->active_workspace; - - if (!active) - return -1; - - return meta_workspace_index (active); -} - -/** - * meta_workspace_manager_get_active_workspace: - * @workspace_manager: A #MetaWorkspaceManager - * - * Returns: (transfer none): The current workspace - */ -MetaWorkspace * -meta_workspace_manager_get_active_workspace (MetaWorkspaceManager *workspace_manager) -{ - return workspace_manager->active_workspace; -} - -void -meta_workspace_manager_workspace_switched (MetaWorkspaceManager *workspace_manager, - int from, - int to, - MetaMotionDirection direction) -{ - g_signal_emit (workspace_manager, - workspace_manager_signals[WORKSPACE_SWITCHED], 0, - from, to, direction); -} - -static void -prefs_changed_callback (MetaPreference pref, - gpointer data) -{ - MetaWorkspaceManager *workspace_manager = data; - - if ((pref == META_PREF_NUM_WORKSPACES || - pref == META_PREF_DYNAMIC_WORKSPACES) && - !meta_prefs_get_dynamic_workspaces ()) - { - guint32 timestamp; - int new_num; - - timestamp = - meta_display_get_current_time_roundtrip (workspace_manager->display); - new_num = meta_prefs_get_num_workspaces (); - meta_workspace_manager_update_num_workspaces (workspace_manager, - timestamp, new_num); - } -} diff --git a/src/core/mutter.c b/src/core/mutter.c deleted file mode 100644 index 9d716014a..000000000 --- a/src/core/mutter.c +++ /dev/null @@ -1,97 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright 2011 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "config.h" - -#include <glib.h> -#include <glib/gi18n-lib.h> -#include <stdlib.h> - -#include "compositor/meta-plugin-manager.h" -#include "meta/main.h" -#include "meta/meta-context.h" -#include "meta/util.h" - -static gboolean -print_version (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - g_print ("mutter %s\n", VERSION); - exit (0); -} - -static const char *plugin = "libdefault"; - -GOptionEntry mutter_options[] = { - { - "version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, - print_version, - N_("Print version"), - NULL - }, - { - "mutter-plugin", 0, 0, G_OPTION_ARG_STRING, - &plugin, - N_("Mutter plugin to use"), - "PLUGIN", - }, - { NULL } -}; - -int -main (int argc, char **argv) -{ - g_autoptr (MetaContext) context = NULL; - g_autoptr (GError) error = NULL; - - context = meta_create_context ("Mutter"); - - meta_context_add_option_entries (context, mutter_options, GETTEXT_PACKAGE); - if (!meta_context_configure (context, &argc, &argv, &error)) - { - g_printerr ("Failed to configure: %s", error->message); - return EXIT_FAILURE; - } - - meta_context_set_plugin_name (context, plugin); - - if (!meta_context_setup (context, &error)) - { - g_printerr ("Failed to setup: %s", error->message); - return EXIT_FAILURE; - } - - if (!meta_context_start (context, &error)) - { - g_printerr ("Failed to start: %s", error->message); - return EXIT_FAILURE; - } - - meta_context_notify_ready (context); - - if (!meta_context_run_main_loop (context, &error)) - { - g_printerr ("Mutter terminated with a failure: %s", error->message); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} diff --git a/src/core/place.c b/src/core/place.c deleted file mode 100644 index 1075fe20d..000000000 --- a/src/core/place.c +++ /dev/null @@ -1,942 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Mutter window placement */ - -/* - * Copyright (C) 2001 Havoc Pennington - * Copyright (C) 2002, 2003 Red Hat, Inc. - * Copyright (C) 2003 Rob Adams - * Copyright (C) 2005 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "config.h" - -#include "core/place.h" - -#include <gdk/gdk.h> -#include <math.h> -#include <stdlib.h> - -#include "backends/meta-backend-private.h" -#include "backends/meta-logical-monitor.h" -#include "core/boxes-private.h" -#include "meta/meta-backend.h" -#include "meta/prefs.h" -#include "meta/workspace.h" - -typedef enum -{ - META_LEFT, - META_RIGHT, - META_TOP, - META_BOTTOM -} MetaWindowDirection; - -static gint -northwestcmp (gconstpointer a, gconstpointer b) -{ - MetaWindow *aw = (gpointer) a; - MetaWindow *bw = (gpointer) b; - MetaRectangle a_frame; - MetaRectangle b_frame; - int from_origin_a; - int from_origin_b; - int ax, ay, bx, by; - - meta_window_get_frame_rect (aw, &a_frame); - meta_window_get_frame_rect (bw, &b_frame); - ax = a_frame.x; - ay = a_frame.y; - bx = b_frame.x; - by = b_frame.y; - - /* probably there's a fast good-enough-guess we could use here. */ - from_origin_a = sqrt (ax * ax + ay * ay); - from_origin_b = sqrt (bx * bx + by * by); - - if (from_origin_a < from_origin_b) - return -1; - else if (from_origin_a > from_origin_b) - return 1; - else - return 0; -} - -static void -find_next_cascade (MetaWindow *window, - /* visible windows on relevant workspaces */ - GList *windows, - int x, - int y, - int *new_x, - int *new_y) -{ - MetaBackend *backend = meta_get_backend (); - GList *tmp; - GList *sorted; - int cascade_x, cascade_y; - MetaRectangle titlebar_rect; - int x_threshold, y_threshold; - MetaRectangle frame_rect; - int window_width, window_height; - int cascade_stage; - MetaRectangle work_area; - MetaLogicalMonitor *current; - - sorted = g_list_copy (windows); - sorted = g_list_sort (sorted, northwestcmp); - - /* This is a "fuzzy" cascade algorithm. - * For each window in the list, we find where we'd cascade a - * new window after it. If a window is already nearly at that - * position, we move on. - */ - - /* arbitrary-ish threshold, honors user attempts to - * manually cascade. - */ -#define CASCADE_FUZZ 15 - meta_window_get_titlebar_rect (window, &titlebar_rect); - x_threshold = MAX (titlebar_rect.x, CASCADE_FUZZ); - y_threshold = MAX (titlebar_rect.y, CASCADE_FUZZ); - - /* Find furthest-SE origin of all workspaces. - * cascade_x, cascade_y are the target position - * of NW corner of window frame. - */ - - current = meta_backend_get_current_logical_monitor (backend); - meta_window_get_work_area_for_logical_monitor (window, current, &work_area); - - cascade_x = MAX (0, work_area.x); - cascade_y = MAX (0, work_area.y); - - /* Find first cascade position that's not used. */ - - meta_window_get_frame_rect (window, &frame_rect); - window_width = frame_rect.width; - window_height = frame_rect.height; - - cascade_stage = 0; - tmp = sorted; - while (tmp != NULL) - { - MetaWindow *w; - MetaRectangle w_frame_rect; - int wx, wy; - - w = tmp->data; - - /* we want frame position, not window position */ - meta_window_get_frame_rect (w, &w_frame_rect); - wx = w_frame_rect.x; - wy = w_frame_rect.y; - - if (ABS (wx - cascade_x) < x_threshold && - ABS (wy - cascade_y) < y_threshold) - { - meta_window_get_titlebar_rect (w, &titlebar_rect); - - /* Cascade the window evenly by the titlebar height; this isn't a typo. */ - cascade_x = wx + titlebar_rect.height; - cascade_y = wy + titlebar_rect.height; - - /* If we go off the screen, start over with a new cascade */ - if (((cascade_x + window_width) > - (work_area.x + work_area.width)) || - ((cascade_y + window_height) > - (work_area.y + work_area.height))) - { - cascade_x = MAX (0, work_area.x); - cascade_y = MAX (0, work_area.y); - -#define CASCADE_INTERVAL 50 /* space between top-left corners of cascades */ - cascade_stage += 1; - cascade_x += CASCADE_INTERVAL * cascade_stage; - - /* start over with a new cascade translated to the right, unless - * we are out of space - */ - if ((cascade_x + window_width) < - (work_area.x + work_area.width)) - { - tmp = sorted; - continue; - } - else - { - /* All out of space, this cascade_x won't work */ - cascade_x = MAX (0, work_area.x); - break; - } - } - } - else - { - /* Keep searching for a further-down-the-diagonal window. */ - } - - tmp = tmp->next; - } - - /* cascade_x and cascade_y will match the last window in the list - * that was "in the way" (in the approximate cascade diagonal) - */ - - g_list_free (sorted); - - *new_x = cascade_x; - *new_y = cascade_y; -} - -static void -find_most_freespace (MetaWindow *window, - /* visible windows on relevant workspaces */ - MetaWindow *focus_window, - int x, - int y, - int *new_x, - int *new_y) -{ - MetaWindowDirection side; - int max_area; - int max_width, max_height, left, right, top, bottom; - int left_space, right_space, top_space, bottom_space; - MetaRectangle work_area; - MetaRectangle avoid; - MetaRectangle frame_rect; - - meta_window_get_work_area_current_monitor (focus_window, &work_area); - meta_window_get_frame_rect (focus_window, &avoid); - meta_window_get_frame_rect (window, &frame_rect); - - /* Find the areas of choosing the various sides of the focus window */ - max_width = MIN (avoid.width, frame_rect.width); - max_height = MIN (avoid.height, frame_rect.height); - left_space = avoid.x - work_area.x; - right_space = work_area.width - (avoid.x + avoid.width - work_area.x); - top_space = avoid.y - work_area.y; - bottom_space = work_area.height - (avoid.y + avoid.height - work_area.y); - left = MIN (left_space, frame_rect.width); - right = MIN (right_space, frame_rect.width); - top = MIN (top_space, frame_rect.height); - bottom = MIN (bottom_space, frame_rect.height); - - /* Find out which side of the focus_window can show the most of the window */ - side = META_LEFT; - max_area = left*max_height; - if (right*max_height > max_area) - { - side = META_RIGHT; - max_area = right*max_height; - } - if (top*max_width > max_area) - { - side = META_TOP; - max_area = top*max_width; - } - if (bottom*max_width > max_area) - { - side = META_BOTTOM; - max_area = bottom*max_width; - } - - /* Give up if there's no where to put it (i.e. focus window is maximized) */ - if (max_area == 0) - return; - - /* Place the window on the relevant side; if the whole window fits, - * make it adjacent to the focus window; if not, make sure the - * window doesn't go off the edge of the screen. - */ - switch (side) - { - case META_LEFT: - *new_y = avoid.y; - if (left_space > frame_rect.width) - *new_x = avoid.x - frame_rect.width; - else - *new_x = work_area.x; - break; - case META_RIGHT: - *new_y = avoid.y; - if (right_space > frame_rect.width) - *new_x = avoid.x + avoid.width; - else - *new_x = work_area.x + work_area.width - frame_rect.width; - break; - case META_TOP: - *new_x = avoid.x; - if (top_space > frame_rect.height) - *new_y = avoid.y - frame_rect.height; - else - *new_y = work_area.y; - break; - case META_BOTTOM: - *new_x = avoid.x; - if (bottom_space > frame_rect.height) - *new_y = avoid.y + avoid.height; - else - *new_y = work_area.y + work_area.height - frame_rect.height; - break; - } -} - -static gboolean -window_overlaps_focus_window (MetaWindow *window) -{ - MetaWindow *focus_window; - MetaRectangle window_frame, focus_frame, overlap; - - focus_window = window->display->focus_window; - if (focus_window == NULL) - return FALSE; - - meta_window_get_frame_rect (window, &window_frame); - meta_window_get_frame_rect (focus_window, &focus_frame); - - return meta_rectangle_intersect (&window_frame, - &focus_frame, - &overlap); -} - -static gboolean -window_place_centered (MetaWindow *window) -{ - MetaWindowType type; - - type = window->type; - - return (type == META_WINDOW_DIALOG || - type == META_WINDOW_MODAL_DIALOG || - type == META_WINDOW_SPLASHSCREEN || - (type == META_WINDOW_NORMAL && meta_prefs_get_center_new_windows ())); -} - -static void -avoid_being_obscured_as_second_modal_dialog (MetaWindow *window, - int *x, - int *y) -{ - /* We can't center this dialog if it was denied focus and it - * overlaps with the focus window and this dialog is modal and this - * dialog is in the same app as the focus window (*phew*...please - * don't make me say that ten times fast). See bug 307875 comment 11 - * and 12 for details, but basically it means this is probably a - * second modal dialog for some app while the focus window is the - * first modal dialog. We should probably make them simultaneously - * visible in general, but it becomes mandatory to do so due to - * buggy apps (e.g. those using gtk+ *sigh*) because in those cases - * this second modal dialog also happens to be modal to the first - * dialog in addition to the main window, while it has only let us - * know about the modal-to-the-main-window part. - */ - - MetaWindow *focus_window; - - focus_window = window->display->focus_window; - - /* denied_focus_and_not_transient is only set when focus_window != NULL */ - - if (window->denied_focus_and_not_transient && - window->type == META_WINDOW_MODAL_DIALOG && - meta_window_same_application (window, focus_window) && - window_overlaps_focus_window (window)) - { - find_most_freespace (window, focus_window, *x, *y, x, y); - meta_topic (META_DEBUG_PLACEMENT, - "Dialog window %s was denied focus but may be modal " - "to the focus window; had to move it to avoid the " - "focus window", - window->desc); - } -} - -static gboolean -rectangle_overlaps_some_window (MetaRectangle *rect, - GList *windows) -{ - GList *tmp; - MetaRectangle dest; - - tmp = windows; - while (tmp != NULL) - { - MetaWindow *other = tmp->data; - MetaRectangle other_rect; - - switch (other->type) - { - case META_WINDOW_DOCK: - case META_WINDOW_SPLASHSCREEN: - case META_WINDOW_DESKTOP: - case META_WINDOW_DIALOG: - case META_WINDOW_MODAL_DIALOG: - /* override redirect window types: */ - case META_WINDOW_DROPDOWN_MENU: - case META_WINDOW_POPUP_MENU: - case META_WINDOW_TOOLTIP: - case META_WINDOW_NOTIFICATION: - case META_WINDOW_COMBO: - case META_WINDOW_DND: - case META_WINDOW_OVERRIDE_OTHER: - break; - - case META_WINDOW_NORMAL: - case META_WINDOW_UTILITY: - case META_WINDOW_TOOLBAR: - case META_WINDOW_MENU: - meta_window_get_frame_rect (other, &other_rect); - - if (meta_rectangle_intersect (rect, &other_rect, &dest)) - return TRUE; - break; - } - - tmp = tmp->next; - } - - return FALSE; -} - -static gint -leftmost_cmp (gconstpointer a, gconstpointer b) -{ - MetaWindow *aw = (gpointer) a; - MetaWindow *bw = (gpointer) b; - MetaRectangle a_frame; - MetaRectangle b_frame; - int ax, bx; - - meta_window_get_frame_rect (aw, &a_frame); - meta_window_get_frame_rect (bw, &b_frame); - ax = a_frame.x; - bx = b_frame.x; - - if (ax < bx) - return -1; - else if (ax > bx) - return 1; - else - return 0; -} - -static gint -topmost_cmp (gconstpointer a, gconstpointer b) -{ - MetaWindow *aw = (gpointer) a; - MetaWindow *bw = (gpointer) b; - MetaRectangle a_frame; - MetaRectangle b_frame; - int ay, by; - - meta_window_get_frame_rect (aw, &a_frame); - meta_window_get_frame_rect (bw, &b_frame); - ay = a_frame.y; - by = b_frame.y; - - if (ay < by) - return -1; - else if (ay > by) - return 1; - else - return 0; -} - -static void -center_tile_rect_in_area (MetaRectangle *rect, - MetaRectangle *work_area) -{ - int fluff; - - /* The point here is to tile a window such that "extra" - * space is equal on either side (i.e. so a full screen - * of windows tiled this way would center the windows - * as a group) - */ - - fluff = (work_area->width % (rect->width+1)) / 2; - rect->x = work_area->x + fluff; - fluff = (work_area->height % (rect->height+1)) / 3; - rect->y = work_area->y + fluff; -} - -/* Find the leftmost, then topmost, empty area on the workspace - * that can contain the new window. - * - * Cool feature to have: if we can't fit the current window size, - * try shrinking the window (within geometry constraints). But - * beware windows such as Emacs with no sane minimum size, we - * don't want to create a 1x1 Emacs. - */ -static gboolean -find_first_fit (MetaWindow *window, - /* visible windows on relevant workspaces */ - GList *windows, - MetaLogicalMonitor *logical_monitor, - int x, - int y, - int *new_x, - int *new_y) -{ - /* This algorithm is limited - it just brute-force tries - * to fit the window in a small number of locations that are aligned - * with existing windows. It tries to place the window on - * the bottom of each existing window, and then to the right - * of each existing window, aligned with the left/top of the - * existing window in each of those cases. - */ - int retval; - GList *below_sorted; - GList *right_sorted; - GList *tmp; - MetaRectangle rect; - MetaRectangle work_area; - - retval = FALSE; - - /* Below each window */ - below_sorted = g_list_copy (windows); - below_sorted = g_list_sort (below_sorted, leftmost_cmp); - below_sorted = g_list_sort (below_sorted, topmost_cmp); - - /* To the right of each window */ - right_sorted = g_list_copy (windows); - right_sorted = g_list_sort (right_sorted, topmost_cmp); - right_sorted = g_list_sort (right_sorted, leftmost_cmp); - - meta_window_get_frame_rect (window, &rect); - -#ifdef WITH_VERBOSE_MODE - { - char monitor_location_string[RECT_LENGTH]; - - meta_rectangle_to_string (&logical_monitor->rect, - monitor_location_string); - meta_topic (META_DEBUG_PLACEMENT, - "Natural monitor is %s", - monitor_location_string); - } -#endif - - meta_window_get_work_area_for_logical_monitor (window, - logical_monitor, - &work_area); - - center_tile_rect_in_area (&rect, &work_area); - - if (meta_rectangle_contains_rect (&work_area, &rect) && - !rectangle_overlaps_some_window (&rect, windows)) - { - *new_x = rect.x; - *new_y = rect.y; - - retval = TRUE; - - goto out; - } - - /* try below each window */ - tmp = below_sorted; - while (tmp != NULL) - { - MetaWindow *w = tmp->data; - MetaRectangle frame_rect; - - meta_window_get_frame_rect (w, &frame_rect); - - rect.x = frame_rect.x; - rect.y = frame_rect.y + frame_rect.height; - - if (meta_rectangle_contains_rect (&work_area, &rect) && - !rectangle_overlaps_some_window (&rect, below_sorted)) - { - *new_x = rect.x; - *new_y = rect.y; - - retval = TRUE; - - goto out; - } - - tmp = tmp->next; - } - - /* try to the right of each window */ - tmp = right_sorted; - while (tmp != NULL) - { - MetaWindow *w = tmp->data; - MetaRectangle frame_rect; - - meta_window_get_frame_rect (w, &frame_rect); - - rect.x = frame_rect.x + frame_rect.width; - rect.y = frame_rect.y; - - if (meta_rectangle_contains_rect (&work_area, &rect) && - !rectangle_overlaps_some_window (&rect, right_sorted)) - { - *new_x = rect.x; - *new_y = rect.y; - - retval = TRUE; - - goto out; - } - - tmp = tmp->next; - } - - out: - g_list_free (below_sorted); - g_list_free (right_sorted); - return retval; -} - -void -meta_window_process_placement (MetaWindow *window, - MetaPlacementRule *placement_rule, - int *rel_x, - int *rel_y) -{ - MetaRectangle anchor_rect; - int window_width, window_height; - int x, y; - - window_width = placement_rule->width; - window_height = placement_rule->height; - - anchor_rect = placement_rule->anchor_rect; - - /* Place at anchor point. */ - if (placement_rule->anchor & META_PLACEMENT_ANCHOR_LEFT) - x = anchor_rect.x; - else if (placement_rule->anchor & META_PLACEMENT_ANCHOR_RIGHT) - x = anchor_rect.x + anchor_rect.width; - else - x = anchor_rect.x + (anchor_rect.width / 2); - if (placement_rule->anchor & META_PLACEMENT_ANCHOR_TOP) - y = anchor_rect.y; - else if (placement_rule->anchor & META_PLACEMENT_ANCHOR_BOTTOM) - y = anchor_rect.y + anchor_rect.height; - else - y = anchor_rect.y + (anchor_rect.height / 2); - - /* Shift according to gravity. */ - if (placement_rule->gravity & META_PLACEMENT_GRAVITY_LEFT) - x -= window_width; - else if (placement_rule->gravity & META_PLACEMENT_GRAVITY_RIGHT) - x = x; - else - x -= window_width / 2; - if (placement_rule->gravity & META_PLACEMENT_GRAVITY_TOP) - y -= window_height; - else if (placement_rule->gravity & META_PLACEMENT_GRAVITY_BOTTOM) - y = y; - else - y -= window_height / 2; - - /* Offset according to offset. */ - x += placement_rule->offset_x; - y += placement_rule->offset_y; - - *rel_x = x; - *rel_y = y; -} - -void -meta_window_place (MetaWindow *window, - int x, - int y, - int *new_x, - int *new_y) -{ - MetaBackend *backend = meta_get_backend (); - GList *windows = NULL; - MetaLogicalMonitor *logical_monitor; - - meta_topic (META_DEBUG_PLACEMENT, "Placing window %s", window->desc); - - g_return_if_fail (!window->placement.rule); - - switch (window->type) - { - /* Run placement algorithm on these. */ - case META_WINDOW_NORMAL: - case META_WINDOW_DIALOG: - case META_WINDOW_MODAL_DIALOG: - case META_WINDOW_SPLASHSCREEN: - break; - - /* Assume the app knows best how to place these, no placement - * algorithm ever (other than "leave them as-is") - */ - case META_WINDOW_DESKTOP: - case META_WINDOW_DOCK: - case META_WINDOW_TOOLBAR: - case META_WINDOW_MENU: - case META_WINDOW_UTILITY: - /* override redirect window types: */ - case META_WINDOW_DROPDOWN_MENU: - case META_WINDOW_POPUP_MENU: - case META_WINDOW_TOOLTIP: - case META_WINDOW_NOTIFICATION: - case META_WINDOW_COMBO: - case META_WINDOW_DND: - case META_WINDOW_OVERRIDE_OTHER: - goto done; - } - - if (meta_prefs_get_disable_workarounds ()) - { - switch (window->type) - { - /* Only accept USPosition on normal windows because the app is full - * of shit claiming the user set -geometry for a dialog or dock - */ - case META_WINDOW_NORMAL: - if (window->size_hints.flags & USPosition) - { - /* don't constrain with placement algorithm */ - meta_topic (META_DEBUG_PLACEMENT, - "Honoring USPosition for %s instead of using placement algorithm", - window->desc); - - goto done; - } - break; - - /* Ignore even USPosition on dialogs, splashscreen */ - case META_WINDOW_DIALOG: - case META_WINDOW_MODAL_DIALOG: - case META_WINDOW_SPLASHSCREEN: - break; - - /* Assume the app knows best how to place these. */ - case META_WINDOW_DESKTOP: - case META_WINDOW_DOCK: - case META_WINDOW_TOOLBAR: - case META_WINDOW_MENU: - case META_WINDOW_UTILITY: - /* override redirect window types: */ - case META_WINDOW_DROPDOWN_MENU: - case META_WINDOW_POPUP_MENU: - case META_WINDOW_TOOLTIP: - case META_WINDOW_NOTIFICATION: - case META_WINDOW_COMBO: - case META_WINDOW_DND: - case META_WINDOW_OVERRIDE_OTHER: - if (window->size_hints.flags & PPosition) - { - meta_topic (META_DEBUG_PLACEMENT, - "Not placing non-normal non-dialog window with PPosition set"); - goto done; - } - break; - } - } - else - { - /* workarounds enabled */ - - if ((window->size_hints.flags & PPosition) || - (window->size_hints.flags & USPosition)) - { - meta_topic (META_DEBUG_PLACEMENT, - "Not placing window with PPosition or USPosition set"); - avoid_being_obscured_as_second_modal_dialog (window, &x, &y); - goto done; - } - } - - if (window->type == META_WINDOW_DIALOG || - window->type == META_WINDOW_MODAL_DIALOG) - { - MetaWindow *parent = meta_window_get_transient_for (window); - - if (parent) - { - MetaRectangle frame_rect, parent_frame_rect; - - meta_window_get_frame_rect (window, &frame_rect); - meta_window_get_frame_rect (parent, &parent_frame_rect); - - y = parent_frame_rect.y; - - /* center of parent */ - x = parent_frame_rect.x + parent_frame_rect.width / 2; - /* center of child over center of parent */ - x -= frame_rect.width / 2; - - /* "visually" center window over parent, leaving twice as - * much space below as on top. - */ - y += (parent_frame_rect.height - frame_rect.height)/3; - - meta_topic (META_DEBUG_PLACEMENT, - "Centered window %s over transient parent", - window->desc); - - avoid_being_obscured_as_second_modal_dialog (window, &x, &y); - - goto done; - } - } - - /* FIXME UTILITY with transient set should be stacked up - * on the sides of the parent window or something. - */ - - if (window_place_centered (window)) - { - /* Center on current monitor */ - MetaRectangle work_area; - MetaRectangle frame_rect; - - /* Warning, this function is a round trip! */ - logical_monitor = meta_backend_get_current_logical_monitor (backend); - - meta_window_get_work_area_for_logical_monitor (window, - logical_monitor, - &work_area); - meta_window_get_frame_rect (window, &frame_rect); - - x = work_area.x + (work_area.width - frame_rect.width) / 2; - y = work_area.y + (work_area.height - frame_rect.height) / 2; - - meta_topic (META_DEBUG_PLACEMENT, "Centered window %s on monitor %d", - window->desc, logical_monitor->number); - - goto done_check_denied_focus; - } - - /* Find windows that matter (not minimized, on same workspace - * as placed window, may be shaded - if shaded we pretend it isn't - * for placement purposes) - */ - { - GSList *all_windows; - GSList *tmp; - - all_windows = meta_display_list_windows (window->display, META_LIST_DEFAULT); - - tmp = all_windows; - while (tmp != NULL) - { - MetaWindow *w = tmp->data; - - if (w != window && - meta_window_showing_on_its_workspace (w) && - (window->on_all_workspaces || - meta_window_located_on_workspace (w, window->workspace))) - windows = g_list_prepend (windows, w); - - tmp = tmp->next; - } - - g_slist_free (all_windows); - } - - /* Warning, on X11 this might be a round trip! */ - logical_monitor = meta_backend_get_current_logical_monitor (backend); - - /* Maximize windows if they are too big for their work area (bit of - * a hack here). Assume undecorated windows probably don't intend to - * be maximized. - */ - if (window->has_maximize_func && window->decorated && - !window->fullscreen) - { - MetaRectangle workarea; - MetaRectangle frame_rect; - - meta_window_get_work_area_for_logical_monitor (window, - logical_monitor, - &workarea); - meta_window_get_frame_rect (window, &frame_rect); - - /* If the window is bigger than the screen, then automaximize. Do NOT - * auto-maximize the directions independently. See #419810. - */ - if (frame_rect.width >= workarea.width && frame_rect.height >= workarea.height) - { - window->maximize_horizontally_after_placement = TRUE; - window->maximize_vertically_after_placement = TRUE; - } - } - - /* "Origin" placement algorithm */ - x = logical_monitor->rect.x; - y = logical_monitor->rect.y; - - if (find_first_fit (window, windows, - logical_monitor, - x, y, &x, &y)) - goto done_check_denied_focus; - - /* No good fit? Fall back to cascading... */ - find_next_cascade (window, windows, x, y, &x, &y); - - done_check_denied_focus: - /* If the window is being denied focus and isn't a transient of the - * focus window, we do NOT want it to overlap with the focus window - * if at all possible. This is guaranteed to only be called if the - * focus_window is non-NULL, and we try to avoid that window. - */ - if (window->denied_focus_and_not_transient) - { - MetaWindow *focus_window; - gboolean found_fit; - - focus_window = window->display->focus_window; - g_assert (focus_window != NULL); - - /* No need to do anything if the window doesn't overlap at all */ - found_fit = !window_overlaps_focus_window (window); - - /* Try to do a first fit again, this time only taking into account the - * focus window. - */ - if (!found_fit) - { - GList *focus_window_list; - focus_window_list = g_list_prepend (NULL, focus_window); - - /* Reset x and y ("origin" placement algorithm) */ - x = logical_monitor->rect.x; - y = logical_monitor->rect.y; - - found_fit = find_first_fit (window, focus_window_list, - logical_monitor, - x, y, &x, &y); - g_list_free (focus_window_list); - } - - /* If that still didn't work, just place it where we can see as much - * as possible. - */ - if (!found_fit) - find_most_freespace (window, focus_window, x, y, &x, &y); - } - - done: - if (windows) - g_list_free (windows); - - *new_x = x; - *new_y = y; -} diff --git a/src/core/place.h b/src/core/place.h deleted file mode 100644 index 2e2c81141..000000000 --- a/src/core/place.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Mutter window placement */ - -/* - * Copyright (C) 2001 Havoc Pennington - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef META_PLACE_H -#define META_PLACE_H - -#include "core/frame.h" -#include "core/window-private.h" - -void meta_window_process_placement (MetaWindow *window, - MetaPlacementRule *placement_rule, - int *rel_x, - int *rel_y); - -void meta_window_place (MetaWindow *window, - int x, - int y, - int *new_x, - int *new_y); - -#endif diff --git a/src/core/prefs-private.h b/src/core/prefs-private.h deleted file mode 100644 index e587bfaa1..000000000 --- a/src/core/prefs-private.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2021 Red Hat Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - */ - -#ifndef PREFS_PRIVATE_H -#define PREFS_PRIVATE_H - -void meta_prefs_init (void); - -#endif /* PREFS_PRIVATE_H */ diff --git a/src/core/prefs.c b/src/core/prefs.c deleted file mode 100644 index 536d9dd57..000000000 --- a/src/core/prefs.c +++ /dev/null @@ -1,2229 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2001 Havoc Pennington, Copyright (C) 2002 Red Hat Inc. - * Copyright (C) 2006 Elijah Newren - * Copyright (C) 2008 Thomas Thurman - * Copyright (C) 2010 Milan Bouchet-Valat, Copyright (C) 2011 Red Hat Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -/** - * SECTION:prefs - * @title: Preferences - * @short_description: Mutter preferences - */ - -#include "config.h" - -#include <glib.h> -#include <gio/gio.h> -#include <string.h> -#include <stdlib.h> - -#include "compositor/meta-plugin-manager.h" -#include "core/keybindings-private.h" -#include "core/meta-accel-parse.h" -#include "core/prefs-private.h" -#include "core/util-private.h" -#include "meta/prefs.h" -#include "x11/meta-x11-display-private.h" - -/* If you add a key, it needs updating in init() and in the gsettings - * notify listener and of course in the .schemas file. - * - * Keys which are handled by one of the unified handlers below are - * not given a name here, because the purpose of the unified handlers - * is that keys should be referred to exactly once. - */ -#define KEY_TITLEBAR_FONT "titlebar-font" -#define KEY_NUM_WORKSPACES "num-workspaces" -#define KEY_WORKSPACE_NAMES "workspace-names" - -/* Keys from "foreign" schemas */ -#define KEY_GNOME_ACCESSIBILITY "toolkit-accessibility" -#define KEY_GNOME_ANIMATIONS "enable-animations" -#define KEY_GNOME_CURSOR_THEME "cursor-theme" -#define KEY_GNOME_CURSOR_SIZE "cursor-size" -#define KEY_XKB_OPTIONS "xkb-options" - -#define KEY_OVERLAY_KEY "overlay-key" -#define KEY_WORKSPACES_ONLY_ON_PRIMARY "workspaces-only-on-primary" -#define KEY_LOCATE_POINTER "locate-pointer" - -/* These are the different schemas we are keeping - * a GSettings instance for */ -#define SCHEMA_GENERAL "org.gnome.desktop.wm.preferences" -#define SCHEMA_MUTTER "org.gnome.mutter" -#define SCHEMA_INTERFACE "org.gnome.desktop.interface" -#define SCHEMA_INPUT_SOURCES "org.gnome.desktop.input-sources" -#define SCHEMA_MOUSE "org.gnome.desktop.peripherals.mouse" - -#define SETTINGS(s) g_hash_table_lookup (settings_schemas, (s)) - -static GList *changes = NULL; -static guint changed_idle; -static GList *listeners = NULL; -static GHashTable *settings_schemas; - -static gboolean use_system_font = FALSE; -static PangoFontDescription *titlebar_font = NULL; -static MetaVirtualModifier mouse_button_mods = Mod1Mask; -static MetaKeyCombo overlay_key_combo = { 0, 0, 0 }; -static MetaKeyCombo locate_pointer_key_combo = { 0, 0, 0 }; -static GDesktopFocusMode focus_mode = G_DESKTOP_FOCUS_MODE_CLICK; -static GDesktopFocusNewWindows focus_new_windows = G_DESKTOP_FOCUS_NEW_WINDOWS_SMART; -static gboolean raise_on_click = TRUE; -static gboolean center_new_windows = FALSE; -static gboolean attach_modal_dialogs = FALSE; -static int num_workspaces = 4; -static GDesktopTitlebarAction action_double_click_titlebar = G_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE; -static GDesktopTitlebarAction action_middle_click_titlebar = G_DESKTOP_TITLEBAR_ACTION_LOWER; -static GDesktopTitlebarAction action_right_click_titlebar = G_DESKTOP_TITLEBAR_ACTION_MENU; -static gboolean dynamic_workspaces = FALSE; -static gboolean disable_workarounds = FALSE; -static gboolean auto_raise = FALSE; -static gboolean auto_raise_delay = 500; -static gboolean focus_change_on_pointer_rest = FALSE; -static gboolean bell_is_visible = FALSE; -static gboolean bell_is_audible = TRUE; -static gboolean gnome_accessibility = FALSE; -static gboolean gnome_animations = TRUE; -static gboolean locate_pointer_is_enabled = FALSE; -static unsigned int check_alive_timeout = 5000; -static char *cursor_theme = NULL; -/* cursor_size will, when running as an X11 compositing window manager, be the - * actual cursor size, multiplied with the global window scaling factor. On - * Wayland, it will be the actual cursor size retrieved from gsettings. - */ -static int cursor_size = 24; -static int draggable_border_width = 10; -static int drag_threshold; -static gboolean resize_with_right_button = FALSE; -static gboolean edge_tiling = FALSE; -static gboolean force_fullscreen = TRUE; -static gboolean auto_maximize = TRUE; -static gboolean show_fallback_app_menu = TRUE; - -static GDesktopVisualBellType visual_bell_type = G_DESKTOP_VISUAL_BELL_FULLSCREEN_FLASH; -static MetaButtonLayout button_layout; - -/* NULL-terminated array */ -static char **workspace_names = NULL; - -static gboolean workspaces_only_on_primary = FALSE; - -static char *iso_next_group_option = NULL; - -static void handle_preference_update_enum (GSettings *settings, - gchar *key); -static gboolean update_binding (MetaKeyPref *binding, - gchar **strokes); -static gboolean update_key_binding (const char *key, - gchar **strokes); - -static void settings_changed (GSettings *settings, - gchar *key, - gpointer data); -static void bindings_changed (GSettings *settings, - gchar *key, - gpointer data); - -static void queue_changed (MetaPreference pref); - -static void maybe_give_disable_workarounds_warning (void); - -static gboolean titlebar_handler (GVariant*, gpointer*, gpointer); -static gboolean mouse_button_mods_handler (GVariant*, gpointer*, gpointer); -static gboolean button_layout_handler (GVariant*, gpointer*, gpointer); -static gboolean overlay_key_handler (GVariant*, gpointer*, gpointer); -static gboolean locate_pointer_key_handler (GVariant*, gpointer*, gpointer); - -static gboolean iso_next_group_handler (GVariant*, gpointer*, gpointer); - -static void init_bindings (void); - -typedef struct -{ - MetaPrefsChangedFunc func; - gpointer data; -} MetaPrefsListener; - -typedef struct -{ - const char *key; - const char *schema; - MetaPreference pref; -} MetaBasePreference; - -typedef struct -{ - MetaBasePreference base; - gpointer target; -} MetaEnumPreference; - -typedef struct -{ - MetaBasePreference base; - gboolean *target; -} MetaBoolPreference; - - -/** - * MetaStringPreference: - * @handler: (nullable): A handler. Many of the string preferences - * aren't stored as strings and need parsing; others of them have - * default values which can't be solved in the general case. If you - * include a function pointer here, it will be called instead of writing - * the string value out to the target variable. - * The function will be passed to g_settings_get_mapped() and should - * return %TRUE if the mapping was successful and %FALSE otherwise. - * In the former case the function is expected to handle the result - * of the conversion itself and call queue_changed() appropriately; - * in particular the @result (out) parameter as returned by - * g_settings_get_mapped() will be ignored in all cases. - * This may be %NULL. If it is, see "target", below. - * @target: (nullable): Where to write the incoming string. - * This must be %NULL if the handler is non-%NULL. - * If the incoming string is %NULL, no change will be made. - */ -typedef struct -{ - MetaBasePreference base; - GSettingsGetMapping handler; - gchar **target; -} MetaStringPreference; - -typedef struct -{ - MetaBasePreference base; - GSettingsGetMapping handler; - gchar ***target; -} MetaStringArrayPreference; - -typedef struct -{ - MetaBasePreference base; - gint *target; -} MetaIntPreference; - -typedef struct -{ - MetaBasePreference base; - unsigned int *target; -} MetaUintPreference; - - -/* All preferences that are not keybindings must be listed here, - * plus in the GSettings schemas and the MetaPreference enum. - */ - -/* FIXMEs: */ -/* @@@ Don't use NULL lines at the end; glib can tell you how big it is */ - -static MetaEnumPreference preferences_enum[] = - { - { - { "focus-new-windows", - SCHEMA_GENERAL, - META_PREF_FOCUS_NEW_WINDOWS, - }, - &focus_new_windows, - }, - { - { "focus-mode", - SCHEMA_GENERAL, - META_PREF_FOCUS_MODE, - }, - &focus_mode, - }, - { - { "visual-bell-type", - SCHEMA_GENERAL, - META_PREF_VISUAL_BELL_TYPE, - }, - &visual_bell_type, - }, - { - { "action-double-click-titlebar", - SCHEMA_GENERAL, - META_PREF_ACTION_DOUBLE_CLICK_TITLEBAR, - }, - &action_double_click_titlebar, - }, - { - { "action-middle-click-titlebar", - SCHEMA_GENERAL, - META_PREF_ACTION_MIDDLE_CLICK_TITLEBAR, - }, - &action_middle_click_titlebar, - }, - { - { "action-right-click-titlebar", - SCHEMA_GENERAL, - META_PREF_ACTION_RIGHT_CLICK_TITLEBAR, - }, - &action_right_click_titlebar, - }, - { { NULL, 0, 0 }, NULL }, - }; - -static MetaBoolPreference preferences_bool[] = - { - { - { "attach-modal-dialogs", - SCHEMA_MUTTER, - META_PREF_ATTACH_MODAL_DIALOGS, - }, - &attach_modal_dialogs, - }, - { - { "center-new-windows", - SCHEMA_MUTTER, - META_PREF_CENTER_NEW_WINDOWS, - }, - ¢er_new_windows, - }, - { - { "raise-on-click", - SCHEMA_GENERAL, - META_PREF_RAISE_ON_CLICK, - }, - &raise_on_click, - }, - { - { "titlebar-uses-system-font", - SCHEMA_GENERAL, - META_PREF_TITLEBAR_FONT, /* note! shares a pref */ - }, - &use_system_font, - }, - { - { "dynamic-workspaces", - SCHEMA_MUTTER, - META_PREF_DYNAMIC_WORKSPACES, - }, - &dynamic_workspaces, - }, - { - { "disable-workarounds", - SCHEMA_GENERAL, - META_PREF_DISABLE_WORKAROUNDS, - }, - &disable_workarounds, - }, - { - { "auto-raise", - SCHEMA_GENERAL, - META_PREF_AUTO_RAISE, - }, - &auto_raise, - }, - { - { "focus-change-on-pointer-rest", - SCHEMA_MUTTER, - META_PREF_FOCUS_CHANGE_ON_POINTER_REST, - }, - &focus_change_on_pointer_rest - }, - { - { "visual-bell", - SCHEMA_GENERAL, - META_PREF_VISUAL_BELL, - }, - &bell_is_visible, /* FIXME: change the name: it's confusing */ - }, - { - { "audible-bell", - SCHEMA_GENERAL, - META_PREF_AUDIBLE_BELL, - }, - &bell_is_audible, /* FIXME: change the name: it's confusing */ - }, - { - { KEY_GNOME_ACCESSIBILITY, - SCHEMA_INTERFACE, - META_PREF_GNOME_ACCESSIBILITY, - }, - &gnome_accessibility, - }, - { - { KEY_GNOME_ANIMATIONS, - SCHEMA_INTERFACE, - META_PREF_GNOME_ANIMATIONS, - }, - &gnome_animations, - }, - { - { "resize-with-right-button", - SCHEMA_GENERAL, - META_PREF_RESIZE_WITH_RIGHT_BUTTON, - }, - &resize_with_right_button, - }, - { - { "edge-tiling", - SCHEMA_MUTTER, - META_PREF_EDGE_TILING, - }, - &edge_tiling, - }, - { - { "workspaces-only-on-primary", - SCHEMA_MUTTER, - META_PREF_WORKSPACES_ONLY_ON_PRIMARY, - }, - &workspaces_only_on_primary, - }, - { - { "auto-maximize", - SCHEMA_MUTTER, - META_PREF_AUTO_MAXIMIZE, - }, - &auto_maximize, - }, - { - { KEY_LOCATE_POINTER, - SCHEMA_INTERFACE, - META_PREF_LOCATE_POINTER, - }, - &locate_pointer_is_enabled, - }, - { { NULL, 0, 0 }, NULL }, - }; - -static MetaStringPreference preferences_string[] = - { - { - { "mouse-button-modifier", - SCHEMA_GENERAL, - META_PREF_MOUSE_BUTTON_MODS, - }, - mouse_button_mods_handler, - NULL, - }, - { - { KEY_TITLEBAR_FONT, - SCHEMA_GENERAL, - META_PREF_TITLEBAR_FONT, - }, - titlebar_handler, - NULL, - }, - { - { "button-layout", - SCHEMA_GENERAL, - META_PREF_BUTTON_LAYOUT, - }, - button_layout_handler, - NULL, - }, - { - { "cursor-theme", - SCHEMA_INTERFACE, - META_PREF_CURSOR_THEME, - }, - NULL, - &cursor_theme, - }, - { - { "overlay-key", - SCHEMA_MUTTER, - META_PREF_KEYBINDINGS, - }, - overlay_key_handler, - NULL, - }, - { - { "locate-pointer-key", - SCHEMA_MUTTER, - META_PREF_KEYBINDINGS, - }, - locate_pointer_key_handler, - NULL, - }, - { { NULL, 0, 0 }, NULL }, - }; - -static MetaStringArrayPreference preferences_string_array[] = - { - { - { KEY_WORKSPACE_NAMES, - SCHEMA_GENERAL, - META_PREF_WORKSPACE_NAMES, - }, - NULL, - &workspace_names, - }, - { - { KEY_XKB_OPTIONS, - SCHEMA_INPUT_SOURCES, - META_PREF_KEYBINDINGS, - }, - iso_next_group_handler, - NULL, - }, - { { NULL, 0, 0 }, NULL }, - }; - -static MetaIntPreference preferences_int[] = - { - { - { KEY_NUM_WORKSPACES, - SCHEMA_GENERAL, - META_PREF_NUM_WORKSPACES, - }, - &num_workspaces - }, - { - { "auto-raise-delay", - SCHEMA_GENERAL, - META_PREF_AUTO_RAISE_DELAY, - }, - &auto_raise_delay - }, - { - { "draggable-border-width", - SCHEMA_MUTTER, - META_PREF_DRAGGABLE_BORDER_WIDTH, - }, - &draggable_border_width - }, - { - { "drag-threshold", - SCHEMA_MOUSE, - META_PREF_DRAG_THRESHOLD, - }, - &drag_threshold - }, - { - { "cursor-size", - SCHEMA_INTERFACE, - META_PREF_CURSOR_SIZE, - }, - &cursor_size - }, - { { NULL, 0, 0 }, NULL }, - }; - -static MetaUintPreference preferences_uint[] = - { - { - { "check-alive-timeout", - SCHEMA_MUTTER, - META_PREF_CHECK_ALIVE_TIMEOUT, - }, - &check_alive_timeout, - }, - { { NULL, 0, 0 }, NULL }, - }; - -static void -handle_preference_init_enum (void) -{ - MetaEnumPreference *cursor = preferences_enum; - - while (cursor->base.key != NULL) - { - if (cursor->target==NULL) - continue; - - *((gint *) cursor->target) = - g_settings_get_enum (SETTINGS (cursor->base.schema), cursor->base.key); - - ++cursor; - } -} - -static void -handle_preference_init_bool (void) -{ - MetaBoolPreference *cursor = preferences_bool; - - while (cursor->base.key != NULL) - { - if (cursor->target!=NULL) - *cursor->target = - g_settings_get_boolean (SETTINGS (cursor->base.schema), - cursor->base.key); - - ++cursor; - } - - maybe_give_disable_workarounds_warning (); -} - -static void -handle_preference_init_string (void) -{ - MetaStringPreference *cursor = preferences_string; - - while (cursor->base.key != NULL) - { - char *value; - - /* Complex keys have a mapping function to check validity */ - if (cursor->handler) - { - if (cursor->target) - meta_bug ("%s has both a target and a handler", cursor->base.key); - - g_settings_get_mapped (SETTINGS (cursor->base.schema), - cursor->base.key, cursor->handler, NULL); - } - else - { - if (!cursor->target) - meta_bug ("%s must have handler or target", cursor->base.key); - - g_free (*(cursor->target)); - - value = g_settings_get_string (SETTINGS (cursor->base.schema), - cursor->base.key); - - *(cursor->target) = value; - } - - ++cursor; - } -} - -static void -handle_preference_init_string_array (void) -{ - MetaStringArrayPreference *cursor = preferences_string_array; - - while (cursor->base.key != NULL) - { - char **value; - - /* Complex keys have a mapping function to check validity */ - if (cursor->handler) - { - if (cursor->target) - meta_bug ("%s has both a target and a handler", cursor->base.key); - - g_settings_get_mapped (SETTINGS (cursor->base.schema), - cursor->base.key, cursor->handler, NULL); - } - else - { - if (!cursor->target) - meta_bug ("%s must have handler or target", cursor->base.key); - - if (*(cursor->target)) - g_strfreev (*(cursor->target)); - - value = g_settings_get_strv (SETTINGS (cursor->base.schema), - cursor->base.key); - - *(cursor->target) = value; - } - - ++cursor; - } -} - -static void -handle_preference_init_int (void) -{ - MetaIntPreference *cursor = preferences_int; - - - while (cursor->base.key != NULL) - { - if (cursor->target) - *cursor->target = g_settings_get_int (SETTINGS (cursor->base.schema), - cursor->base.key); - - ++cursor; - } -} - -static void -handle_preference_init_uint (void) -{ - MetaUintPreference *cursor = preferences_uint; - - while (cursor->base.key != NULL) - { - if (cursor->target) - *cursor->target = g_settings_get_uint (SETTINGS (cursor->base.schema), - cursor->base.key); - - ++cursor; - } -} - -static void -handle_preference_update_enum (GSettings *settings, - gchar *key) -{ - MetaEnumPreference *cursor = preferences_enum; - gint old_value; - - while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0) - ++cursor; - - if (cursor->base.key == NULL) - /* Didn't recognise that key. */ - return; - - /* We need to know whether the value changes, so - * store the current value away. - */ - - old_value = * ((gint *)cursor->target); - *((gint *)cursor->target) = - g_settings_get_enum (SETTINGS (cursor->base.schema), key); - - /* Did it change? If so, tell the listeners about it. */ - if (old_value != *((gint *)cursor->target)) - queue_changed (cursor->base.pref); -} - -static void -handle_preference_update_bool (GSettings *settings, - gchar *key) -{ - MetaBoolPreference *cursor = preferences_bool; - gboolean old_value; - - while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0) - ++cursor; - - if (cursor->base.key == NULL || cursor->target == NULL) - /* Unknown key or no work for us to do. */ - return; - - /* We need to know whether the value changes, so - * store the current value away. - */ - old_value = *((gboolean *) cursor->target); - - /* Now look it up... */ - *((gboolean *) cursor->target) = - g_settings_get_boolean (SETTINGS (cursor->base.schema), key); - - /* Did it change? If so, tell the listeners about it. */ - if (old_value != *((gboolean *)cursor->target)) - queue_changed (cursor->base.pref); - - if (cursor->base.pref==META_PREF_DISABLE_WORKAROUNDS) - maybe_give_disable_workarounds_warning (); -} - -static void -handle_preference_update_string (GSettings *settings, - gchar *key) -{ - MetaStringPreference *cursor = preferences_string; - char *value; - gboolean inform_listeners = FALSE; - - while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0) - ++cursor; - - if (cursor->base.key==NULL) - /* Didn't recognise that key. */ - return; - - /* Complex keys have a mapping function to check validity */ - if (cursor->handler) - { - if (cursor->target) - meta_bug ("%s has both a target and a handler", cursor->base.key); - - g_settings_get_mapped (SETTINGS (cursor->base.schema), - cursor->base.key, cursor->handler, NULL); - } - else - { - if (!cursor->target) - meta_bug ("%s must have handler or target", cursor->base.key); - - value = g_settings_get_string (SETTINGS (cursor->base.schema), - cursor->base.key); - - inform_listeners = (g_strcmp0 (value, *(cursor->target)) != 0); - - g_free(*(cursor->target)); - - *(cursor->target) = value; - } - - if (inform_listeners) - queue_changed (cursor->base.pref); -} - -static void -handle_preference_update_string_array (GSettings *settings, - gchar *key) -{ - MetaStringArrayPreference *cursor = preferences_string_array; - gboolean inform_listeners = FALSE; - - while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0) - ++cursor; - - if (cursor->base.key==NULL) - /* Didn't recognise that key. */ - return; - - /* Complex keys have a mapping function to check validity */ - if (cursor->handler) - { - if (cursor->target) - meta_bug ("%s has both a target and a handler", cursor->base.key); - - g_settings_get_mapped (SETTINGS (cursor->base.schema), - cursor->base.key, cursor->handler, NULL); - } - else - { - char **values, **previous; - int n_values, n_previous, i; - - if (!cursor->target) - meta_bug ("%s must have handler or target", cursor->base.key); - - values = g_settings_get_strv (SETTINGS (cursor->base.schema), - cursor->base.key); - n_values = g_strv_length (values); - previous = *(cursor->target); - n_previous = previous ? g_strv_length (previous) : 0; - - inform_listeners = n_previous != n_values; - for (i = 0; i < n_values && !inform_listeners; i++) - inform_listeners = g_strcmp0 (values[i], previous[i]) != 0; - - if (*(cursor->target)) - g_strfreev (*(cursor->target)); - *(cursor->target) = values; - } - - if (inform_listeners) - queue_changed (cursor->base.pref); -} - -static void -handle_preference_update_int (GSettings *settings, - gchar *key) -{ - MetaIntPreference *cursor = preferences_int; - gint new_value; - - while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0) - ++cursor; - - if (cursor->base.key == NULL || cursor->target == NULL) - /* Unknown key or no work for us to do. */ - return; - - new_value = g_settings_get_int (SETTINGS (cursor->base.schema), key); - - /* Did it change? If so, tell the listeners about it. */ - if (*cursor->target != new_value) - { - *cursor->target = new_value; - queue_changed (cursor->base.pref); - } -} - -static void -handle_preference_update_uint (GSettings *settings, - char *key) -{ - MetaUintPreference *cursor = preferences_uint; - unsigned int new_value; - - while (cursor->base.key && strcmp (key, cursor->base.key) != 0) - ++cursor; - - if (!cursor->base.key || !cursor->target) - return; - - new_value = g_settings_get_uint (SETTINGS (cursor->base.schema), key); - - if (*cursor->target != new_value) - { - *cursor->target = new_value; - queue_changed (cursor->base.pref); - } -} - - -/****************************************************************************/ -/* Listeners. */ -/****************************************************************************/ - -/** - * meta_prefs_add_listener: (skip) - * @func: a #MetaPrefsChangedFunc - * @user_data: data passed to the function - * - */ -void -meta_prefs_add_listener (MetaPrefsChangedFunc func, - gpointer user_data) -{ - MetaPrefsListener *l; - - l = g_new (MetaPrefsListener, 1); - l->func = func; - l->data = user_data; - - listeners = g_list_prepend (listeners, l); -} - -/** - * meta_prefs_remove_listener: (skip) - * @func: a #MetaPrefsChangedFunc - * @user_data: data passed to the function - * - */ -void -meta_prefs_remove_listener (MetaPrefsChangedFunc func, - gpointer user_data) -{ - GList *tmp; - - tmp = listeners; - while (tmp != NULL) - { - MetaPrefsListener *l = tmp->data; - - if (l->func == func && - l->data == user_data) - { - g_free (l); - listeners = g_list_delete_link (listeners, tmp); - - return; - } - - tmp = tmp->next; - } -} - -static void -emit_changed (MetaPreference pref) -{ - GList *tmp; - GList *copy; - - meta_topic (META_DEBUG_PREFS, "Notifying listeners that pref %s changed", - meta_preference_to_string (pref)); - - copy = g_list_copy (listeners); - - tmp = copy; - - while (tmp != NULL) - { - MetaPrefsListener *l = tmp->data; - - (* l->func) (pref, l->data); - - tmp = tmp->next; - } - - g_list_free (copy); -} - -static gboolean -changed_idle_handler (gpointer data) -{ - GList *tmp; - GList *copy; - - changed_idle = 0; - - copy = g_list_copy (changes); /* reentrancy paranoia */ - - g_list_free (changes); - changes = NULL; - - tmp = copy; - while (tmp != NULL) - { - MetaPreference pref = GPOINTER_TO_INT (tmp->data); - - emit_changed (pref); - - tmp = tmp->next; - } - - g_list_free (copy); - - return FALSE; -} - -static void -queue_changed (MetaPreference pref) -{ - meta_topic (META_DEBUG_PREFS, "Queueing change of pref %s", - meta_preference_to_string (pref)); - - if (g_list_find (changes, GINT_TO_POINTER (pref)) == NULL) - changes = g_list_prepend (changes, GINT_TO_POINTER (pref)); - else - meta_topic (META_DEBUG_PREFS, "Change of pref %s was already pending", - meta_preference_to_string (pref)); - - if (changed_idle == 0) - { - changed_idle = g_idle_add_full (META_PRIORITY_PREFS_NOTIFY, - changed_idle_handler, NULL, NULL); - g_source_set_name_by_id (changed_idle, "[mutter] changed_idle_handler"); - } -} - - -/****************************************************************************/ -/* Initialisation. */ -/****************************************************************************/ - -void -meta_prefs_init (void) -{ - GSettings *settings; - - settings_schemas = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, g_object_unref); - - settings = g_settings_new (SCHEMA_GENERAL); - g_signal_connect (settings, "changed", G_CALLBACK (settings_changed), NULL); - g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_GENERAL), settings); - - settings = g_settings_new (SCHEMA_MUTTER); - g_signal_connect (settings, "changed", G_CALLBACK (settings_changed), NULL); - g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_MUTTER), settings); - - settings = g_settings_new (SCHEMA_MOUSE); - g_signal_connect (settings, "changed", G_CALLBACK (settings_changed), NULL); - g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_MOUSE), settings); - - /* Individual keys we watch outside of our schemas */ - settings = g_settings_new (SCHEMA_INTERFACE); - g_signal_connect (settings, "changed::" KEY_GNOME_ACCESSIBILITY, - G_CALLBACK (settings_changed), NULL); - g_signal_connect (settings, "changed::" KEY_GNOME_ANIMATIONS, - G_CALLBACK (settings_changed), NULL); - g_signal_connect (settings, "changed::" KEY_GNOME_CURSOR_THEME, - G_CALLBACK (settings_changed), NULL); - g_signal_connect (settings, "changed::" KEY_GNOME_CURSOR_SIZE, - G_CALLBACK (settings_changed), NULL); - g_signal_connect (settings, "changed::" KEY_LOCATE_POINTER, - G_CALLBACK (settings_changed), NULL); - g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_INTERFACE), settings); - - settings = g_settings_new (SCHEMA_INPUT_SOURCES); - g_signal_connect (settings, "changed::" KEY_XKB_OPTIONS, - G_CALLBACK (settings_changed), NULL); - g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_INPUT_SOURCES), settings); - - /* Pick up initial values. */ - - handle_preference_init_enum (); - handle_preference_init_bool (); - handle_preference_init_string (); - handle_preference_init_string_array (); - handle_preference_init_int (); - handle_preference_init_uint (); - - init_bindings (); -} - -static gboolean -find_pref (void *prefs, - size_t pref_size, - const char *search_key, - MetaBasePreference **pref) -{ - void *p = prefs; - - while (TRUE) - { - char **key = p; - if (*key == NULL) - break; - - if (strcmp (*key, search_key) == 0) - { - *pref = p; - return TRUE; - } - - p = (guchar *)p + pref_size; - } - - return FALSE; -} - - -/****************************************************************************/ -/* Updates. */ -/****************************************************************************/ - - -static void -settings_changed (GSettings *settings, - gchar *key, - gpointer data) -{ - GVariant *value; - const GVariantType *type; - MetaEnumPreference *cursor; - gboolean found_enum; - - value = g_settings_get_value (settings, key); - type = g_variant_get_type (value); - - if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN)) - handle_preference_update_bool (settings, key); - else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32)) - handle_preference_update_int (settings, key); - else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32)) - handle_preference_update_uint (settings, key); - else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING_ARRAY)) - handle_preference_update_string_array (settings, key); - else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING)) - { - cursor = preferences_enum; - found_enum = FALSE; - - while (cursor->base.key != NULL) - { - - if (strcmp (key, cursor->base.key) == 0) - found_enum = TRUE; - - cursor++; - } - - if (found_enum) - handle_preference_update_enum (settings, key); - else - handle_preference_update_string (settings, key); - } - else - { - /* Unknown preference type. This quite likely simply isn't - * a preference we track changes to. */ - } - - g_variant_unref (value); -} - -static void -bindings_changed (GSettings *settings, - gchar *key, - gpointer data) -{ - gchar **strokes; - strokes = g_settings_get_strv (settings, key); - - if (update_key_binding (key, strokes)) - queue_changed (META_PREF_KEYBINDINGS); - - g_strfreev (strokes); -} - -/** - * maybe_give_disable_workaround_warning: - * - * Special case: give a warning the first time disable_workarounds - * is turned on. - */ -static void -maybe_give_disable_workarounds_warning (void) -{ - static gboolean first_disable = TRUE; - - if (first_disable && disable_workarounds) - { - first_disable = FALSE; - - meta_warning ("Workarounds for broken applications disabled. " - "Some applications may not behave properly."); - } -} - -MetaVirtualModifier -meta_prefs_get_mouse_button_mods (void) -{ - return mouse_button_mods; -} - -GDesktopFocusMode -meta_prefs_get_focus_mode (void) -{ - return focus_mode; -} - -GDesktopFocusNewWindows -meta_prefs_get_focus_new_windows (void) -{ - return focus_new_windows; -} - -gboolean -meta_prefs_get_center_new_windows (void) -{ - return center_new_windows; -} - -gboolean -meta_prefs_get_attach_modal_dialogs (void) -{ - return attach_modal_dialogs; -} - -gboolean -meta_prefs_get_raise_on_click (void) -{ - return raise_on_click; -} - -gboolean -meta_prefs_get_show_fallback_app_menu (void) -{ - return show_fallback_app_menu; -} - -void -meta_prefs_set_show_fallback_app_menu (gboolean whether) -{ - gboolean changed = FALSE; - - changed = (show_fallback_app_menu == !whether); - - show_fallback_app_menu = whether; - - if (changed) - queue_changed (META_PREF_BUTTON_LAYOUT); -} - -const char* -meta_prefs_get_cursor_theme (void) -{ - return cursor_theme; -} - -int -meta_prefs_get_cursor_size (void) -{ - return cursor_size; -} - - -/****************************************************************************/ -/* Handlers for string preferences. */ -/****************************************************************************/ - -static gboolean -titlebar_handler (GVariant *value, - gpointer *result, - gpointer data) -{ - PangoFontDescription *desc; - const gchar *string_value; - - *result = NULL; /* ignored */ - string_value = g_variant_get_string (value, NULL); - desc = pango_font_description_from_string (string_value); - - if (desc == NULL) - { - meta_warning ("Could not parse font description " - "\"%s\" from GSettings key %s", - string_value ? string_value : "(null)", - KEY_TITLEBAR_FONT); - return FALSE; - } - - /* Is the new description the same as the old? */ - if (titlebar_font && - pango_font_description_equal (desc, titlebar_font)) - { - pango_font_description_free (desc); - } - else - { - if (titlebar_font) - pango_font_description_free (titlebar_font); - - titlebar_font = desc; - queue_changed (META_PREF_TITLEBAR_FONT); - } - - return TRUE; -} - -static gboolean -mouse_button_mods_handler (GVariant *value, - gpointer *result, - gpointer data) -{ - MetaVirtualModifier mods; - const gchar *string_value; - - *result = NULL; /* ignored */ - string_value = g_variant_get_string (value, NULL); - - if (!string_value || !meta_parse_modifier (string_value, &mods)) - { - meta_topic (META_DEBUG_KEYBINDINGS, - "Failed to parse new GSettings value"); - - meta_warning ("\"%s\" found in configuration database is " - "not a valid value for mouse button modifier", - string_value); - - return FALSE; - } - - meta_topic (META_DEBUG_KEYBINDINGS, - "Mouse button modifier has new GSettings value \"%s\"", - string_value); - - if (mods != mouse_button_mods) - { - mouse_button_mods = mods; - queue_changed (META_PREF_MOUSE_BUTTON_MODS); - } - - return TRUE; -} - -static gboolean -button_layout_equal (const MetaButtonLayout *a, - const MetaButtonLayout *b) -{ - int i; - - i = 0; - while (i < MAX_BUTTONS_PER_CORNER) - { - if (a->left_buttons[i] != b->left_buttons[i]) - return FALSE; - if (a->right_buttons[i] != b->right_buttons[i]) - return FALSE; - if (a->left_buttons_has_spacer[i] != b->left_buttons_has_spacer[i]) - return FALSE; - if (a->right_buttons_has_spacer[i] != b->right_buttons_has_spacer[i]) - return FALSE; - ++i; - } - - return TRUE; -} - -/* - * This conversion cannot be handled by GSettings since - * several values are stored in the same key (as a string). - */ -static MetaButtonFunction -button_function_from_string (const char *str) -{ - if (strcmp (str, "menu") == 0) - return META_BUTTON_FUNCTION_MENU; - else if (strcmp (str, "minimize") == 0) - return META_BUTTON_FUNCTION_MINIMIZE; - else if (strcmp (str, "maximize") == 0) - return META_BUTTON_FUNCTION_MAXIMIZE; - else if (strcmp (str, "close") == 0) - return META_BUTTON_FUNCTION_CLOSE; - else - /* don't know; give up */ - return META_BUTTON_FUNCTION_LAST; -} - -static gboolean -button_layout_handler (GVariant *value, - gpointer *result, - gpointer data) -{ - MetaButtonLayout new_layout; - const gchar *string_value; - char **sides = NULL; - int i; - - /* We need to ignore unknown button functions, for - * compat with future versions - */ - - *result = NULL; /* ignored */ - string_value = g_variant_get_string (value, NULL); - - if (string_value) - sides = g_strsplit (string_value, ":", 2); - - i = 0; - if (sides != NULL && sides[0] != NULL) - { - char **buttons; - int b; - gboolean used[META_BUTTON_FUNCTION_LAST]; - - while (i < META_BUTTON_FUNCTION_LAST) - { - used[i] = FALSE; - new_layout.left_buttons_has_spacer[i] = FALSE; - ++i; - } - - buttons = g_strsplit (sides[0], ",", -1); - i = 0; - b = 0; - while (buttons[b] != NULL) - { - MetaButtonFunction f = button_function_from_string (buttons[b]); - if (i > 0 && strcmp("spacer", buttons[b]) == 0) - { - new_layout.left_buttons_has_spacer[i-1] = TRUE; - } - else - { - if (f != META_BUTTON_FUNCTION_LAST && !used[f]) - { - new_layout.left_buttons[i] = f; - used[f] = TRUE; - ++i; - } - else - { - meta_topic (META_DEBUG_PREFS, - "Ignoring unknown or already-used button name \"%s\"", - buttons[b]); - } - } - - ++b; - } - - g_strfreev (buttons); - } - - for (; i < MAX_BUTTONS_PER_CORNER; i++) - { - new_layout.left_buttons[i] = META_BUTTON_FUNCTION_LAST; - new_layout.left_buttons_has_spacer[i] = FALSE; - } - - i = 0; - if (sides != NULL && sides[0] != NULL && sides[1] != NULL) - { - char **buttons; - int b; - gboolean used[META_BUTTON_FUNCTION_LAST]; - - while (i < META_BUTTON_FUNCTION_LAST) - { - used[i] = FALSE; - new_layout.right_buttons_has_spacer[i] = FALSE; - ++i; - } - - buttons = g_strsplit (sides[1], ",", -1); - i = 0; - b = 0; - while (buttons[b] != NULL) - { - MetaButtonFunction f = button_function_from_string (buttons[b]); - if (i > 0 && strcmp("spacer", buttons[b]) == 0) - { - new_layout.right_buttons_has_spacer[i-1] = TRUE; - } - else - { - if (f != META_BUTTON_FUNCTION_LAST && !used[f]) - { - new_layout.right_buttons[i] = f; - used[f] = TRUE; - ++i; - } - else - { - meta_topic (META_DEBUG_PREFS, - "Ignoring unknown or already-used button name \"%s\"", - buttons[b]); - } - } - - ++b; - } - - g_strfreev (buttons); - } - - for (; i < MAX_BUTTONS_PER_CORNER; i++) - { - new_layout.right_buttons[i] = META_BUTTON_FUNCTION_LAST; - new_layout.right_buttons_has_spacer[i] = FALSE; - } - - g_strfreev (sides); - - /* Invert the button layout for RTL languages */ - if (meta_get_locale_direction() == META_LOCALE_DIRECTION_RTL) - { - MetaButtonLayout rtl_layout; - int j; - - for (i = 0; new_layout.left_buttons[i] != META_BUTTON_FUNCTION_LAST; i++); - for (j = 0; j < i; j++) - { - rtl_layout.right_buttons[j] = new_layout.left_buttons[i - j - 1]; - if (j == 0) - rtl_layout.right_buttons_has_spacer[i - 1] = new_layout.left_buttons_has_spacer[i - j - 1]; - else - rtl_layout.right_buttons_has_spacer[j - 1] = new_layout.left_buttons_has_spacer[i - j - 1]; - } - for (; j < MAX_BUTTONS_PER_CORNER; j++) - { - rtl_layout.right_buttons[j] = META_BUTTON_FUNCTION_LAST; - rtl_layout.right_buttons_has_spacer[j] = FALSE; - } - - for (i = 0; new_layout.right_buttons[i] != META_BUTTON_FUNCTION_LAST; i++); - for (j = 0; j < i; j++) - { - rtl_layout.left_buttons[j] = new_layout.right_buttons[i - j - 1]; - if (j == 0) - rtl_layout.left_buttons_has_spacer[i - 1] = new_layout.right_buttons_has_spacer[i - j - 1]; - else - rtl_layout.left_buttons_has_spacer[j - 1] = new_layout.right_buttons_has_spacer[i - j - 1]; - } - for (; j < MAX_BUTTONS_PER_CORNER; j++) - { - rtl_layout.left_buttons[j] = META_BUTTON_FUNCTION_LAST; - rtl_layout.left_buttons_has_spacer[j] = FALSE; - } - - new_layout = rtl_layout; - } - - if (!button_layout_equal (&button_layout, &new_layout)) - { - button_layout = new_layout; - emit_changed (META_PREF_BUTTON_LAYOUT); - } - - return TRUE; -} - -static gboolean -overlay_key_handler (GVariant *value, - gpointer *result, - gpointer data) -{ - MetaKeyCombo combo; - const gchar *string_value; - - *result = NULL; /* ignored */ - string_value = g_variant_get_string (value, NULL); - - if (string_value && meta_parse_accelerator (string_value, &combo)) - ; - else - { - meta_topic (META_DEBUG_KEYBINDINGS, - "Failed to parse value for overlay-key"); - return FALSE; - } - - combo.modifiers = 0; - - if (overlay_key_combo.keysym != combo.keysym || - overlay_key_combo.keycode != combo.keycode) - { - overlay_key_combo = combo; - queue_changed (META_PREF_KEYBINDINGS); - } - - return TRUE; -} - -static gboolean -locate_pointer_key_handler (GVariant *value, - gpointer *result, - gpointer data) -{ - MetaKeyCombo combo; - const gchar *string_value; - - *result = NULL; /* ignored */ - string_value = g_variant_get_string (value, NULL); - - if (!string_value || !meta_parse_accelerator (string_value, &combo)) - { - meta_topic (META_DEBUG_KEYBINDINGS, - "Failed to parse value for locate-pointer-key"); - return FALSE; - } - - combo.modifiers = 0; - - if (locate_pointer_key_combo.keysym != combo.keysym || - locate_pointer_key_combo.keycode != combo.keycode) - { - locate_pointer_key_combo = combo; - queue_changed (META_PREF_KEYBINDINGS); - } - - return TRUE; -} - -static gboolean -iso_next_group_handler (GVariant *value, - gpointer *result, - gpointer data) -{ - const char **xkb_options, **p; - const char *option = NULL; - gboolean changed = FALSE; - - *result = NULL; /* ignored */ - xkb_options = g_variant_get_strv (value, NULL); - - for (p = xkb_options; p && *p; ++p) - if (g_str_has_prefix (*p, "grp:")) - { - option = (*p + 4); - break; - } - - changed = (g_strcmp0 (option, iso_next_group_option) != 0); - - if (changed) - { - g_free (iso_next_group_option); - iso_next_group_option = g_strdup (option); - queue_changed (META_PREF_KEYBINDINGS); - } - - g_free (xkb_options); - - return TRUE; -} - -const PangoFontDescription* -meta_prefs_get_titlebar_font (void) -{ - if (use_system_font) - return NULL; - else - return titlebar_font; -} - -int -meta_prefs_get_num_workspaces (void) -{ - return num_workspaces; -} - -gboolean -meta_prefs_get_dynamic_workspaces (void) -{ - return dynamic_workspaces; -} - -gboolean -meta_prefs_get_disable_workarounds (void) -{ - return disable_workarounds; -} - -#ifdef WITH_VERBOSE_MODE -const char* -meta_preference_to_string (MetaPreference pref) -{ - /* TODO: better handled via GLib enum nicknames */ - switch (pref) - { - case META_PREF_MOUSE_BUTTON_MODS: - return "MOUSE_BUTTON_MODS"; - - case META_PREF_FOCUS_MODE: - return "FOCUS_MODE"; - - case META_PREF_FOCUS_NEW_WINDOWS: - return "FOCUS_NEW_WINDOWS"; - - case META_PREF_CENTER_NEW_WINDOWS: - return "CENTER_NEW_WINDOWS"; - - case META_PREF_ATTACH_MODAL_DIALOGS: - return "ATTACH_MODAL_DIALOGS"; - - case META_PREF_RAISE_ON_CLICK: - return "RAISE_ON_CLICK"; - - case META_PREF_TITLEBAR_FONT: - return "TITLEBAR_FONT"; - - case META_PREF_NUM_WORKSPACES: - return "NUM_WORKSPACES"; - - case META_PREF_KEYBINDINGS: - return "KEYBINDINGS"; - - case META_PREF_DISABLE_WORKAROUNDS: - return "DISABLE_WORKAROUNDS"; - - case META_PREF_ACTION_DOUBLE_CLICK_TITLEBAR: - return "ACTION_DOUBLE_CLICK_TITLEBAR"; - - case META_PREF_ACTION_MIDDLE_CLICK_TITLEBAR: - return "ACTION_MIDDLE_CLICK_TITLEBAR"; - - case META_PREF_ACTION_RIGHT_CLICK_TITLEBAR: - return "ACTION_RIGHT_CLICK_TITLEBAR"; - - case META_PREF_AUTO_RAISE: - return "AUTO_RAISE"; - - case META_PREF_AUTO_RAISE_DELAY: - return "AUTO_RAISE_DELAY"; - - case META_PREF_FOCUS_CHANGE_ON_POINTER_REST: - return "FOCUS_CHANGE_ON_POINTER_REST"; - - case META_PREF_BUTTON_LAYOUT: - return "BUTTON_LAYOUT"; - - case META_PREF_WORKSPACE_NAMES: - return "WORKSPACE_NAMES"; - - case META_PREF_VISUAL_BELL: - return "VISUAL_BELL"; - - case META_PREF_AUDIBLE_BELL: - return "AUDIBLE_BELL"; - - case META_PREF_VISUAL_BELL_TYPE: - return "VISUAL_BELL_TYPE"; - - case META_PREF_GNOME_ACCESSIBILITY: - return "GNOME_ACCESSIBILTY"; - - case META_PREF_GNOME_ANIMATIONS: - return "GNOME_ANIMATIONS"; - - case META_PREF_CURSOR_THEME: - return "CURSOR_THEME"; - - case META_PREF_CURSOR_SIZE: - return "CURSOR_SIZE"; - - case META_PREF_RESIZE_WITH_RIGHT_BUTTON: - return "RESIZE_WITH_RIGHT_BUTTON"; - - case META_PREF_EDGE_TILING: - return "EDGE_TILING"; - - case META_PREF_FORCE_FULLSCREEN: - return "FORCE_FULLSCREEN"; - - case META_PREF_WORKSPACES_ONLY_ON_PRIMARY: - return "WORKSPACES_ONLY_ON_PRIMARY"; - - case META_PREF_DRAGGABLE_BORDER_WIDTH: - return "DRAGGABLE_BORDER_WIDTH"; - - case META_PREF_DRAG_THRESHOLD: - return "DRAG_THRESHOLD"; - - case META_PREF_DYNAMIC_WORKSPACES: - return "DYNAMIC_WORKSPACES"; - - case META_PREF_AUTO_MAXIMIZE: - return "AUTO_MAXIMIZE"; - - case META_PREF_LOCATE_POINTER: - return "LOCATE_POINTER"; - - case META_PREF_CHECK_ALIVE_TIMEOUT: - return "CHECK_ALIVE_TIMEOUT"; - } - - return "(unknown)"; -} -#endif /* WITH_VERBOSE_MODE */ - -void -meta_prefs_set_num_workspaces (int n_workspaces) -{ - MetaBasePreference *pref; - - if (find_pref (preferences_int, sizeof(MetaIntPreference), - KEY_NUM_WORKSPACES, &pref)) - { - g_settings_set_int (SETTINGS (pref->schema), - KEY_NUM_WORKSPACES, - n_workspaces); - } -} - -static GHashTable *key_bindings; - -static void -meta_key_pref_free (MetaKeyPref *pref) -{ - update_binding (pref, NULL); - - g_free (pref->name); - g_object_unref (pref->settings); - - g_free (pref); -} - - -static void -init_bindings (void) -{ - MetaKeyPref *pref; - - key_bindings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, - (GDestroyNotify)meta_key_pref_free); - - pref = g_new0 (MetaKeyPref, 1); - pref->name = g_strdup ("overlay-key"); - pref->action = META_KEYBINDING_ACTION_OVERLAY_KEY; - pref->combos = g_slist_prepend (pref->combos, &overlay_key_combo); - pref->builtin = 1; - - g_hash_table_insert (key_bindings, g_strdup (pref->name), pref); - - pref = g_new0 (MetaKeyPref, 1); - pref->name = g_strdup ("locate-pointer-key"); - pref->action = META_KEYBINDING_ACTION_LOCATE_POINTER_KEY; - pref->combos = g_slist_prepend (pref->combos, &locate_pointer_key_combo); - pref->builtin = 1; - - g_hash_table_insert (key_bindings, g_strdup (pref->name), pref); -} - -static gboolean -update_binding (MetaKeyPref *binding, - gchar **strokes) -{ - GSList *old_combos, *a, *b; - gboolean changed; - int i; - - meta_topic (META_DEBUG_KEYBINDINGS, - "Binding \"%s\" has new GSettings value", - binding->name); - - old_combos = binding->combos; - binding->combos = NULL; - - for (i = 0; strokes && strokes[i]; i++) - { - MetaKeyCombo *combo; - - combo = g_malloc0 (sizeof (MetaKeyCombo)); - - if (!meta_parse_accelerator (strokes[i], combo)) - { - meta_topic (META_DEBUG_KEYBINDINGS, - "Failed to parse new GSettings value"); - meta_warning ("\"%s\" found in configuration database is not a valid value for keybinding \"%s\"", - strokes[i], binding->name); - - g_free (combo); - - /* Value is kept and will thus be removed next time we save the key. - * Changing the key in response to a modification could lead to cyclic calls. */ - continue; - } - - binding->combos = g_slist_prepend (binding->combos, combo); - } - - binding->combos = g_slist_reverse (binding->combos); - - a = old_combos; - b = binding->combos; - while (TRUE) - { - if ((!a && b) || (a && !b)) - { - changed = TRUE; - break; - } - else if (!a && !b) - { - changed = FALSE; - break; - } - else if (memcmp (a->data, b->data, sizeof (MetaKeyCombo)) != 0) - { - changed = TRUE; - break; - } - else - { - a = a->next; - b = b->next; - } - } - - g_slist_free_full (old_combos, g_free); - - return changed; -} - -static gboolean -update_key_binding (const char *key, - gchar **strokes) -{ - MetaKeyPref *pref = g_hash_table_lookup (key_bindings, key); - - if (pref) - return update_binding (pref, strokes); - else - return FALSE; -} - -const char* -meta_prefs_get_workspace_name (int i) -{ - const char *name; - - if (!workspace_names || - g_strv_length (workspace_names) < (guint)i + 1 || - !*workspace_names[i]) - { - char *generated_name = g_strdup_printf (_("Workspace %d"), i + 1); - name = g_intern_string (generated_name); - g_free (generated_name); - } - else - name = workspace_names[i]; - - meta_topic (META_DEBUG_PREFS, - "Getting name of workspace %d: \"%s\"", i, name); - - return name; -} - -void -meta_prefs_change_workspace_name (int num, - const char *name) -{ - GVariantBuilder builder; - int n_workspace_names, i; - - g_return_if_fail (num >= 0); - - meta_topic (META_DEBUG_PREFS, - "Changing name of workspace %d to %s", - num, name ? name : "none"); - - /* NULL and empty string both mean "default" here, - * and we also need to match the name against its default value - * to avoid saving it literally. */ - if (g_strcmp0 (name, meta_prefs_get_workspace_name (num)) == 0) - { - if (!name || !*name) - meta_topic (META_DEBUG_PREFS, - "Workspace %d already uses default name", num); - else - meta_topic (META_DEBUG_PREFS, - "Workspace %d already has name %s", num, name); - return; - } - - g_variant_builder_init (&builder, G_VARIANT_TYPE_STRING_ARRAY); - n_workspace_names = workspace_names ? g_strv_length (workspace_names) : 0; - - for (i = 0; i < MAX (num + 1, n_workspace_names); i++) - { - const char *value; - - if (i == num) - value = name ? name : ""; - else if (i < n_workspace_names) - value = workspace_names[i] ? workspace_names[i] : ""; - else - value = ""; - - g_variant_builder_add (&builder, "s", value); - } - - g_settings_set_value (SETTINGS (SCHEMA_GENERAL), KEY_WORKSPACE_NAMES, - g_variant_builder_end (&builder)); -} - -/** - * meta_prefs_get_button_layout: - * @button_layout: (out): - */ -void -meta_prefs_get_button_layout (MetaButtonLayout *button_layout_p) -{ - *button_layout_p = button_layout; -} - -gboolean -meta_prefs_get_visual_bell (void) -{ - return bell_is_visible; -} - -gboolean -meta_prefs_bell_is_audible (void) -{ - return bell_is_audible; -} - -GDesktopVisualBellType -meta_prefs_get_visual_bell_type (void) -{ - return visual_bell_type; -} - -gboolean -meta_prefs_add_keybinding (const char *name, - GSettings *settings, - MetaKeyBindingAction action, - MetaKeyBindingFlags flags) -{ - MetaKeyPref *pref; - char **strokes; - guint id; - - if (g_hash_table_lookup (key_bindings, name)) - { - meta_warning ("Trying to re-add keybinding \"%s\".", name); - return FALSE; - } - - pref = g_new0 (MetaKeyPref, 1); - pref->name = g_strdup (name); - pref->settings = g_object_ref (settings); - pref->action = action; - pref->combos = NULL; - pref->builtin = (flags & META_KEY_BINDING_BUILTIN) != 0; - - if (pref->builtin) - { - if (g_object_get_data (G_OBJECT (settings), "changed-signal") == NULL) - { - id = g_signal_connect (settings, "changed", - G_CALLBACK (bindings_changed), NULL); - g_object_set_data (G_OBJECT (settings), "changed-signal", GUINT_TO_POINTER (id)); - } - } - else - { - char *changed_signal = g_strdup_printf ("changed::%s", name); - id = g_signal_connect (settings, changed_signal, - G_CALLBACK (bindings_changed), NULL); - g_free (changed_signal); - - g_object_set_data (G_OBJECT (settings), name, GUINT_TO_POINTER (id)); - - queue_changed (META_PREF_KEYBINDINGS); - } - - strokes = g_settings_get_strv (settings, name); - update_binding (pref, strokes); - g_strfreev (strokes); - - g_hash_table_insert (key_bindings, g_strdup (name), pref); - - return TRUE; -} - -gboolean -meta_prefs_remove_keybinding (const char *name) -{ - MetaKeyPref *pref; - gulong id; - - pref = g_hash_table_lookup (key_bindings, name); - if (!pref) - { - meta_warning ("Trying to remove non-existent keybinding \"%s\".", name); - return FALSE; - } - - if (pref->builtin) - { - meta_warning ("Trying to remove builtin keybinding \"%s\".", name); - return FALSE; - } - - id = GPOINTER_TO_UINT (g_object_steal_data (G_OBJECT (pref->settings), name)); - g_clear_signal_handler (&id, pref->settings); - - g_hash_table_remove (key_bindings, name); - - queue_changed (META_PREF_KEYBINDINGS); - - return TRUE; -} - -GList * -meta_prefs_get_keybindings (void) -{ - return g_hash_table_get_values (key_bindings); -} - -void -meta_prefs_get_overlay_binding (MetaKeyCombo *combo) -{ - *combo = overlay_key_combo; -} - -void -meta_prefs_get_locate_pointer_binding (MetaKeyCombo *combo) -{ - *combo = locate_pointer_key_combo; -} - -gboolean -meta_prefs_is_locate_pointer_enabled (void) -{ - return locate_pointer_is_enabled; -} - -unsigned int -meta_prefs_get_check_alive_timeout (void) -{ - return check_alive_timeout; -} - -const char * -meta_prefs_get_iso_next_group_option (void) -{ - return iso_next_group_option; -} - -GDesktopTitlebarAction -meta_prefs_get_action_double_click_titlebar (void) -{ - return action_double_click_titlebar; -} - -GDesktopTitlebarAction -meta_prefs_get_action_middle_click_titlebar (void) -{ - return action_middle_click_titlebar; -} - -GDesktopTitlebarAction -meta_prefs_get_action_right_click_titlebar (void) -{ - return action_right_click_titlebar; -} - -gboolean -meta_prefs_get_auto_raise (void) -{ - return auto_raise; -} - -int -meta_prefs_get_auto_raise_delay (void) -{ - return auto_raise_delay; -} - -gboolean -meta_prefs_get_focus_change_on_pointer_rest (void) -{ - return focus_change_on_pointer_rest; -} - -gboolean -meta_prefs_get_gnome_accessibility (void) -{ - return gnome_accessibility; -} - -gboolean -meta_prefs_get_gnome_animations (void) -{ - return gnome_animations; -} - -gboolean -meta_prefs_get_edge_tiling (void) -{ - return edge_tiling; -} - -gboolean -meta_prefs_get_auto_maximize (void) -{ - return auto_maximize; -} - -MetaKeyBindingAction -meta_prefs_get_keybinding_action (const char *name) -{ - MetaKeyPref *pref = g_hash_table_lookup (key_bindings, name); - - return pref ? pref->action - : META_KEYBINDING_ACTION_NONE; -} - -gint -meta_prefs_get_mouse_button_resize (void) -{ - return resize_with_right_button ? 3: 2; -} - -gint -meta_prefs_get_mouse_button_menu (void) -{ - return resize_with_right_button ? 2: 3; -} - -gboolean -meta_prefs_get_force_fullscreen (void) -{ - return force_fullscreen; -} - -gboolean -meta_prefs_get_workspaces_only_on_primary (void) -{ - return workspaces_only_on_primary; -} - -int -meta_prefs_get_draggable_border_width (void) -{ - return draggable_border_width; -} - -int -meta_prefs_get_drag_threshold (void) -{ - return drag_threshold; -} - -void -meta_prefs_set_force_fullscreen (gboolean whether) -{ - force_fullscreen = whether; -} diff --git a/src/core/restart-helper.c b/src/core/restart-helper.c deleted file mode 100644 index 68ede2671..000000000 --- a/src/core/restart-helper.c +++ /dev/null @@ -1,84 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * SECTION:restart-helper - * @short_description: helper program during a restart - * - * To smoothly restart Mutter, we want to keep the composite - * overlay window enabled during the restart. This is done by - * spawning this program, which keeps a reference to the the composite - * overlay window until Mutter picks it back up. - */ - -/* - * Copyright (C) 2014 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "config.h" - -#include <stdlib.h> -#include <stdio.h> -#include <X11/Xlib.h> -#include <X11/extensions/Xcomposite.h> - -int -main (int argc, - char **argv) -{ - Display *display = XOpenDisplay (NULL); - Window selection_window; - XSetWindowAttributes xwa; - unsigned long mask = 0; - - xwa.override_redirect = True; - mask |= CWOverrideRedirect; - - - XCompositeGetOverlayWindow (display, DefaultRootWindow (display)); - - selection_window = XCreateWindow (display, - DefaultRootWindow (display), - -100, -100, 1, 1, 0, - 0, - InputOnly, - DefaultVisual (display, DefaultScreen (display)), - mask, &xwa); - - XSetSelectionOwner (display, - XInternAtom (display, "_MUTTER_RESTART_HELPER", False), - selection_window, - CurrentTime); - - /* Mutter looks for an (arbitrary) line printed to stdout to know that - * we have started and have a reference to the COW. XSync() so that - * everything is set on the X server before Mutter starts restarting. - */ - XSync (display, False); - - printf ("STARTED\n"); - fflush (stdout); - - while (True) - { - XEvent xev; - - XNextEvent (display, &xev); - /* Mutter restarted and unset the selection to indicate that - * it has a reference on the COW again */ - if (xev.xany.type == SelectionClear) - return 0; - } -} diff --git a/src/core/restart.c b/src/core/restart.c deleted file mode 100644 index e9a5bfd27..000000000 --- a/src/core/restart.c +++ /dev/null @@ -1,201 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2014 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -/* - * SECTION:restart - * @short_description: Smoothly restart the compositor - * - * There are some cases where we need to restart Mutter in order - * to deal with changes in state - the particular case inspiring - * this is enabling or disabling stereo output. To make this - * fairly smooth for the user, we need to do two things: - * - * - Display a message to the user and make sure that it is - * actually painted before we exit. - * - Use a helper program so that the Composite Overlay Window - * isn't unmapped and mapped. - * - * This handles both of these. - */ - -#include "config.h" - -#include <gio/gunixinputstream.h> - -#include "clutter/clutter.h" -#include "core/display-private.h" -#include "core/util-private.h" -#include "meta/main.h" -#include "ui/ui.h" -#include "x11/meta-x11-display-private.h" - -static gboolean restart_helper_started = FALSE; -static gboolean restart_message_shown = FALSE; -static gboolean is_restart = FALSE; - -void -meta_set_is_restart (gboolean whether) -{ - is_restart = whether; -} - -static void -restart_check_ready (void) -{ - if (restart_helper_started && restart_message_shown) - { - MetaDisplay *display = meta_get_display (); - - if (!meta_display_request_restart (display)) - meta_display_show_restart_message (display, NULL); - } -} - -static void -restart_helper_read_line_callback (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GError *error = NULL; - gsize length; - char *line = g_data_input_stream_read_line_finish_utf8 (G_DATA_INPUT_STREAM (source_object), - res, - &length, &error); - if (line == NULL) - { - meta_warning ("Failed to read output from restart helper%s%s", - error ? ": " : NULL, - error ? error->message : NULL); - } - else - g_free (line); /* We don't actually care what the restart helper outputs */ - - g_object_unref (source_object); - - restart_helper_started = TRUE; - restart_check_ready (); -} - -static gboolean -restart_message_painted (gpointer data) -{ - restart_message_shown = TRUE; - restart_check_ready (); - - return FALSE; -} - -/** - * meta_restart: - * @message: (allow-none): message to display to the user, or %NULL - * - * Starts the process of restarting the compositor. Note that Mutter's - * involvement here is to make the restart visually smooth for the - * user - it cannot itself safely reexec a program that embeds libmuttter. - * So in order for this to work, the compositor must handle two - * signals - MetaDisplay::show-restart-message, to display the - * message passed here on the Clutter stage, and ::restart to actually - * reexec the compositor. - */ -void -meta_restart (const char *message) -{ - MetaDisplay *display = meta_get_display(); - GInputStream *unix_stream; - GDataInputStream *data_stream; - GError *error = NULL; - int helper_out_fd; - - static const char * const helper_argv[] = { - MUTTER_LIBEXECDIR "/mutter-restart-helper", NULL - }; - - if (message && meta_display_show_restart_message (display, message)) - { - /* Wait until the stage was painted */ - clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT, - restart_message_painted, - NULL, NULL); - } - else - { - /* Can't show the message, show the message as soon as the - * restart helper starts - */ - restart_message_painted (NULL); - } - - /* We also need to wait for the restart helper to get its - * reference to the Composite Overlay Window. - */ - if (!g_spawn_async_with_pipes (NULL, /* working directory */ - (char **)helper_argv, - NULL, /* envp */ - G_SPAWN_DEFAULT, - NULL, NULL, /* child_setup */ - NULL, /* child_pid */ - NULL, /* standard_input */ - &helper_out_fd, - NULL, /* standard_error */ - &error)) - { - meta_warning ("Failed to start restart helper: %s", error->message); - goto error; - } - - unix_stream = g_unix_input_stream_new (helper_out_fd, TRUE); - data_stream = g_data_input_stream_new (unix_stream); - g_object_unref (unix_stream); - - g_data_input_stream_read_line_async (data_stream, G_PRIORITY_DEFAULT, - NULL, restart_helper_read_line_callback, - &error); - if (error != NULL) - { - meta_warning ("Failed to read from restart helper: %s", error->message); - g_object_unref (data_stream); - goto error; - } - - return; - - error: - /* If starting the restart helper fails, then we just go ahead and restart - * immediately. We won't get a smooth transition, since the overlay window - * will be destroyed and recreated, but otherwise it will work fine. - */ - restart_helper_started = TRUE; - restart_check_ready (); - - return; -} - -/** - * meta_is_restart: - * - * Returns %TRUE if this instance of Mutter comes from Mutter - * restarting itself (for example to enable/disable stereo.) - * See meta_restart(). If this is the case, any startup visuals - * or animations should be suppressed. - */ -gboolean -meta_is_restart (void) -{ - return is_restart; -} diff --git a/src/core/stack-tracker.c b/src/core/stack-tracker.c deleted file mode 100644 index fbfef6465..000000000 --- a/src/core/stack-tracker.c +++ /dev/null @@ -1,1293 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * SECTION:stack-tracker - * @short_description: Track stacking order for compositor - * - * #MetaStackTracker maintains the most accurate view we have at a - * given point of time of the ordering of the children of the root - * window (including override-redirect windows.) This is used to order - * the windows when the compositor draws them. - * - * By contrast, #MetaStack is responsible for keeping track of how we - * think that windows *should* be ordered. For windows we manage - * (non-override-redirect windows), the two stacking orders will be - * the same. - */ - -/* - * Copyright (C) 2009 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "config.h" - -#include "core/stack-tracker.h" - -#include <string.h> - -#include "core/display-private.h" -#include "core/frame.h" -#include "meta/compositor.h" -#include "meta/meta-x11-errors.h" -#include "meta/util.h" -#include "x11/meta-x11-display-private.h" - -/* The complexity here comes from resolving two competing factors: - * - * - We need to have a view of the stacking order that takes into - * account everything we have done without waiting for events - * back from the X server; we don't want to draw intermediate - * partially-stacked stack states just because we haven't received - * some notification yet. - * - * - Only the X server has an accurate view of the complete stacking; - * when we make a request to restack windows, we don't know how - * it will affect override-redirect windows, because at any point - * applications may restack these windows without our involvement. - * - * The technique we use is that we keep three sets of information: - * - * - The stacking order on the server as known from the last - * event we received. - * - A queue of stacking requests that *we* made subsequent to - * that last event. - * - A predicted stacking order, derived from applying the queued - * requests to the last state from the server. - * - * When we receive a new event: a) we compare the serial in the event to - * the serial of the queued requests and remove any that are now - * no longer pending b) if necessary, drop the predicted stacking - * order to recompute it at the next opportunity. - * - * Possible optimizations: - * Keep the stacks as an array + reverse-mapping hash table to avoid - * linear lookups. - * Keep the stacks as a GList + reverse-mapping hash table to avoid - * linear lookups and to make restacking constant-time. - */ - -typedef union _MetaStackOp MetaStackOp; - -typedef enum -{ - STACK_OP_ADD, - STACK_OP_REMOVE, - STACK_OP_RAISE_ABOVE, - STACK_OP_LOWER_BELOW -} MetaStackOpType; - -typedef enum -{ - APPLY_DEFAULT = 0, - /* Only do restacking that we can do locally without changing - * the order of X windows. After we've received any stack - * events from the X server, we apply the locally cached - * ops in this mode to handle the non-X parts */ - NO_RESTACK_X_WINDOWS = 1 << 0, - /* If the stacking operation wouldn't change the order of X - * windows, ignore it. We use this when applying events received - * from X so that a spontaneous ConfigureNotify (for a move, say) - * doesn't change the stacking of X windows with respect to - * Wayland windows. */ - IGNORE_NOOP_X_RESTACK = 1 << 1 -} ApplyFlags; - -/* MetaStackOp represents a "stacking operation" - a change to - * apply to a window stack. Depending on the context, it could - * either reflect a request we have sent to the server, or a - * notification event we received from the X server. - */ -union _MetaStackOp -{ - struct { - MetaStackOpType type; - gulong serial; - guint64 window; - } any; - struct { - MetaStackOpType type; - gulong serial; - guint64 window; - } add; - struct { - MetaStackOpType type; - gulong serial; - guint64 window; - } remove; - struct { - MetaStackOpType type; - gulong serial; - guint64 window; - guint64 sibling; - } raise_above; - struct { - MetaStackOpType type; - gulong serial; - guint64 window; - guint64 sibling; - } lower_below; -}; - -struct _MetaStackTracker -{ - MetaDisplay *display; - - /* This is the serial of the last request we made that was reflected - * in xserver_stack - */ - gulong xserver_serial; - - /* A combined stack containing X and Wayland windows but without - * any unverified operations applied. */ - GArray *verified_stack; - - /* This is a queue of requests we've made to change the stacking order, - * where we haven't yet gotten a reply back from the server. - */ - GQueue *unverified_predictions; - - /* This is how we think the stack is, based on verified_stack, and - * on the unverified_predictions we've made subsequent to - * verified_stack. - */ - GArray *predicted_stack; - - /* Idle function used to sync the compositor's view of the window - * stack up with our best guess before a frame is drawn. - */ - guint sync_stack_later; -}; - -static void -meta_stack_tracker_keep_override_redirect_on_top (MetaStackTracker *tracker); - -static inline const char * -get_window_desc (MetaStackTracker *tracker, - guint64 window) -{ - return meta_display_describe_stack_id (tracker->display, window); -} - -static void -meta_stack_op_dump (MetaStackTracker *tracker, - MetaStackOp *op, - const char *prefix, - const char *suffix) -{ -#ifdef WITH_VERBOSE_MODE - const char *window_desc = get_window_desc (tracker, op->any.window); -#endif - - switch (op->any.type) - { - case STACK_OP_ADD: - meta_topic (META_DEBUG_STACK, "%sADD(%s; %ld)%s", - prefix, window_desc, op->any.serial, suffix); - break; - case STACK_OP_REMOVE: - meta_topic (META_DEBUG_STACK, "%sREMOVE(%s; %ld)%s", - prefix, window_desc, op->any.serial, suffix); - break; - case STACK_OP_RAISE_ABOVE: - { - meta_topic (META_DEBUG_STACK, "%sRAISE_ABOVE(%s, %s; %ld)%s", - prefix, - window_desc, - get_window_desc (tracker, op->raise_above.sibling), - op->any.serial, - suffix); - break; - } - case STACK_OP_LOWER_BELOW: - { - meta_topic (META_DEBUG_STACK, "%sLOWER_BELOW(%s, %s; %ld)%s", - prefix, - window_desc, - get_window_desc (tracker, op->lower_below.sibling), - op->any.serial, - suffix); - break; - } - } -} - -#ifdef WITH_VERBOSE_MODE -static void -stack_dump (MetaStackTracker *tracker, - GArray *stack) -{ - guint i; - - meta_push_no_msg_prefix (); - for (i = 0; i < stack->len; i++) - { - guint64 window = g_array_index (stack, guint64, i); - meta_topic (META_DEBUG_STACK, " %s", get_window_desc (tracker, window)); - } - meta_pop_no_msg_prefix (); -} -#endif /* WITH_VERBOSE_MODE */ - -static void -meta_stack_tracker_dump (MetaStackTracker *tracker) -{ -#ifdef WITH_VERBOSE_MODE - GList *l; - - meta_topic (META_DEBUG_STACK, "MetaStackTracker state"); - meta_push_no_msg_prefix (); - meta_topic (META_DEBUG_STACK, " xserver_serial: %ld", tracker->xserver_serial); - meta_topic (META_DEBUG_STACK, " verified_stack: "); - stack_dump (tracker, tracker->verified_stack); - meta_topic (META_DEBUG_STACK, " unverified_predictions: ["); - for (l = tracker->unverified_predictions->head; l; l = l->next) - { - MetaStackOp *op = l->data; - meta_stack_op_dump (tracker, op, "", l->next ? ", " : ""); - } - meta_topic (META_DEBUG_STACK, "]"); - if (tracker->predicted_stack) - { - meta_topic (META_DEBUG_STACK, " predicted_stack: "); - stack_dump (tracker, tracker->predicted_stack); - } - meta_pop_no_msg_prefix (); -#endif /* WITH_VERBOSE_MODE */ -} - -static void -meta_stack_op_free (MetaStackOp *op) -{ - g_free (op); -} - -static int -find_window (GArray *window_stack, - guint64 window) -{ - guint i; - - for (i = 0; i < window_stack->len; i++) - { - guint64 current = g_array_index (window_stack, guint64, i); - if (current == window) - return i; - } - - return -1; -} - -/* Returns TRUE if stack was changed */ -static gboolean -move_window_above (GArray *stack, - guint64 window, - int old_pos, - int above_pos, - ApplyFlags apply_flags) -{ - int i; - gboolean can_restack_this_window = - (apply_flags & NO_RESTACK_X_WINDOWS) == 0 || !META_STACK_ID_IS_X11 (window); - - if (old_pos < above_pos) - { - if ((apply_flags & IGNORE_NOOP_X_RESTACK) != 0) - { - gboolean found_x_window = FALSE; - for (i = old_pos + 1; i <= above_pos; i++) - if (META_STACK_ID_IS_X11 (g_array_index (stack, guint64, i))) - found_x_window = TRUE; - - if (!found_x_window) - return FALSE; - } - - for (i = old_pos; i < above_pos; i++) - { - if (!can_restack_this_window && - META_STACK_ID_IS_X11 (g_array_index (stack, guint64, i + 1))) - break; - - g_array_index (stack, guint64, i) = - g_array_index (stack, guint64, i + 1); - } - - g_array_index (stack, guint64, i) = window; - - return i != old_pos; - } - else if (old_pos > above_pos + 1) - { - if ((apply_flags & IGNORE_NOOP_X_RESTACK) != 0) - { - gboolean found_x_window = FALSE; - for (i = above_pos + 1; i < old_pos; i++) - if (META_STACK_ID_IS_X11 (g_array_index (stack, guint64, i))) - found_x_window = TRUE; - - if (!found_x_window) - return FALSE; - } - - for (i = old_pos; i > above_pos + 1; i--) - { - if (!can_restack_this_window && - META_STACK_ID_IS_X11 (g_array_index (stack, guint64, i - 1))) - break; - - g_array_index (stack, guint64, i) = - g_array_index (stack, guint64, i - 1); - } - - g_array_index (stack, guint64, i) = window; - - return i != old_pos; - } - else - return FALSE; -} - -/* Returns TRUE if stack was changed */ -static gboolean -meta_stack_op_apply (MetaStackTracker *tracker, - MetaStackOp *op, - GArray *stack, - ApplyFlags apply_flags) -{ - switch (op->any.type) - { - case STACK_OP_ADD: - { - int old_pos; - - if (META_STACK_ID_IS_X11 (op->add.window) && - (apply_flags & NO_RESTACK_X_WINDOWS) != 0) - return FALSE; - - old_pos = find_window (stack, op->add.window); - if (old_pos >= 0) - { - meta_topic (META_DEBUG_STACK, - "STACK_OP_ADD: window %s already in stack", - get_window_desc (tracker, op->add.window)); - return FALSE; - } - - g_array_append_val (stack, op->add.window); - return TRUE; - } - case STACK_OP_REMOVE: - { - int old_pos; - - if (META_STACK_ID_IS_X11 (op->remove.window) && - (apply_flags & NO_RESTACK_X_WINDOWS) != 0) - return FALSE; - - old_pos = find_window (stack, op->remove.window); - if (old_pos < 0) - { - meta_topic (META_DEBUG_STACK, - "STACK_OP_REMOVE: window %s not in stack", - get_window_desc (tracker, op->remove.window)); - return FALSE; - } - - g_array_remove_index (stack, old_pos); - return TRUE; - } - case STACK_OP_RAISE_ABOVE: - { - int old_pos; - int above_pos; - - old_pos = find_window (stack, op->raise_above.window); - if (old_pos < 0) - { - meta_topic (META_DEBUG_STACK, - "STACK_OP_RAISE_ABOVE: window %s not in stack", - get_window_desc (tracker, op->raise_above.window)); - return FALSE; - } - - if (op->raise_above.sibling) - { - above_pos = find_window (stack, op->raise_above.sibling); - if (above_pos < 0) - { - meta_topic (META_DEBUG_STACK, - "STACK_OP_RAISE_ABOVE: sibling window %s not in stack", - get_window_desc (tracker, op->raise_above.sibling)); - return FALSE; - } - } - else - { - above_pos = -1; - } - - return move_window_above (stack, op->raise_above.window, old_pos, above_pos, - apply_flags); - } - case STACK_OP_LOWER_BELOW: - { - int old_pos; - int above_pos; - - old_pos = find_window (stack, op->raise_above.window); - if (old_pos < 0) - { - meta_topic (META_DEBUG_STACK, - "STACK_OP_LOWER_BELOW: window %s not in stack", - get_window_desc (tracker, op->lower_below.window)); - return FALSE; - } - - if (op->lower_below.sibling) - { - int below_pos; - - below_pos = find_window (stack, op->lower_below.sibling); - if (below_pos < 0) - { - meta_topic (META_DEBUG_STACK, - "STACK_OP_LOWER_BELOW: sibling window %s not in stack", - get_window_desc (tracker, op->lower_below.sibling)); - return FALSE; - } - - above_pos = below_pos - 1; - } - else - { - above_pos = stack->len - 1; - } - - return move_window_above (stack, op->lower_below.window, old_pos, above_pos, - apply_flags); - } - } - - g_assert_not_reached (); - return FALSE; -} - -static GArray * -copy_stack (GArray *stack) -{ - GArray *copy = g_array_sized_new (FALSE, FALSE, sizeof (guint64), stack->len); - - g_array_set_size (copy, stack->len); - - memcpy (copy->data, stack->data, sizeof (guint64) * stack->len); - - return copy; -} - -static void -query_xserver_stack (MetaDisplay *display, - MetaStackTracker *tracker) -{ - MetaX11Display *x11_display = display->x11_display; - Window ignored1, ignored2; - Window *children; - guint n_children; - guint i, old_len; - - tracker->xserver_serial = XNextRequest (x11_display->xdisplay); - - XQueryTree (x11_display->xdisplay, - x11_display->xroot, - &ignored1, &ignored2, &children, &n_children); - - old_len = tracker->verified_stack->len; - - g_array_set_size (tracker->verified_stack, old_len + n_children); - - for (i = 0; i < n_children; i++) - g_array_index (tracker->verified_stack, guint64, old_len + i) = children[i]; - - XFree (children); -} - -static void -drop_x11_windows (MetaDisplay *display, - MetaStackTracker *tracker) -{ - GArray *new_stack; - GList *l; - int i; - - tracker->xserver_serial = 0; - - new_stack = g_array_new (FALSE, FALSE, sizeof (guint64)); - - for (i = 0; i < tracker->verified_stack->len; i++) - { - guint64 window = g_array_index (tracker->verified_stack, guint64, i); - - if (!META_STACK_ID_IS_X11 (window)) - g_array_append_val (new_stack, window); - } - - g_array_unref (tracker->verified_stack); - tracker->verified_stack = new_stack; - l = tracker->unverified_predictions->head; - - while (l) - { - MetaStackOp *op = l->data; - GList *next = l->next; - - if (META_STACK_ID_IS_X11 (op->any.window)) - g_queue_remove (tracker->unverified_predictions, op); - - l = next; - } -} - -MetaStackTracker * -meta_stack_tracker_new (MetaDisplay *display) -{ - MetaStackTracker *tracker; - - tracker = g_new0 (MetaStackTracker, 1); - tracker->display = display; - - tracker->verified_stack = g_array_new (FALSE, FALSE, sizeof (guint64)); - tracker->unverified_predictions = g_queue_new (); - - g_signal_connect (display, - "x11-display-setup", - G_CALLBACK (query_xserver_stack), - tracker); - g_signal_connect (display, - "x11-display-closing", - G_CALLBACK (drop_x11_windows), - tracker); - - meta_stack_tracker_dump (tracker); - - return tracker; -} - -void -meta_stack_tracker_free (MetaStackTracker *tracker) -{ - if (tracker->sync_stack_later) - meta_later_remove (tracker->sync_stack_later); - - g_array_free (tracker->verified_stack, TRUE); - if (tracker->predicted_stack) - g_array_free (tracker->predicted_stack, TRUE); - - g_queue_foreach (tracker->unverified_predictions, (GFunc)meta_stack_op_free, NULL); - g_queue_free (tracker->unverified_predictions); - tracker->unverified_predictions = NULL; - - g_signal_handlers_disconnect_by_func (tracker->display, - (gpointer)query_xserver_stack, - tracker); - g_signal_handlers_disconnect_by_func (tracker->display, - drop_x11_windows, - tracker); - - g_free (tracker); -} - -static void -stack_tracker_apply_prediction (MetaStackTracker *tracker, - MetaStackOp *op) -{ - gboolean free_at_end = FALSE; - - /* If this operation doesn't involve restacking X windows then it's - * implicitly verified. We can apply it immediately unless there - * are outstanding X restacks that haven't yet been confirmed. - */ - if (op->any.serial == 0 && - tracker->unverified_predictions->length == 0) - { - if (meta_stack_op_apply (tracker, op, tracker->verified_stack, APPLY_DEFAULT)) - meta_stack_tracker_queue_sync_stack (tracker); - - free_at_end = TRUE; - } - else - { - meta_stack_op_dump (tracker, op, "Predicting: ", ""); - g_queue_push_tail (tracker->unverified_predictions, op); - } - - if (!tracker->predicted_stack || - meta_stack_op_apply (tracker, op, tracker->predicted_stack, APPLY_DEFAULT)) - meta_stack_tracker_queue_sync_stack (tracker); - - if (free_at_end) - meta_stack_op_free (op); - - meta_stack_tracker_dump (tracker); -} - -void -meta_stack_tracker_record_add (MetaStackTracker *tracker, - guint64 window, - gulong serial) -{ - MetaStackOp *op = g_new0 (MetaStackOp, 1); - - op->any.type = STACK_OP_ADD; - op->any.serial = serial; - op->any.window = window; - - stack_tracker_apply_prediction (tracker, op); -} - -void -meta_stack_tracker_record_remove (MetaStackTracker *tracker, - guint64 window, - gulong serial) -{ - MetaStackOp *op = g_new0 (MetaStackOp, 1); - - op->any.type = STACK_OP_REMOVE; - op->any.serial = serial; - op->any.window = window; - - stack_tracker_apply_prediction (tracker, op); -} - -static void -meta_stack_tracker_record_raise_above (MetaStackTracker *tracker, - guint64 window, - guint64 sibling, - gulong serial) -{ - MetaStackOp *op = g_new0 (MetaStackOp, 1); - - op->any.type = STACK_OP_RAISE_ABOVE; - op->any.serial = serial; - op->any.window = window; - op->raise_above.sibling = sibling; - - stack_tracker_apply_prediction (tracker, op); -} - -static void -meta_stack_tracker_record_lower_below (MetaStackTracker *tracker, - guint64 window, - guint64 sibling, - gulong serial) -{ - MetaStackOp *op = g_new0 (MetaStackOp, 1); - - op->any.type = STACK_OP_LOWER_BELOW; - op->any.serial = serial; - op->any.window = window; - op->lower_below.sibling = sibling; - - stack_tracker_apply_prediction (tracker, op); -} - -static void -stack_tracker_event_received (MetaStackTracker *tracker, - MetaStackOp *op) -{ - gboolean need_sync = FALSE; - - /* If the event is older than our initial query, then it's - * already included in our tree. Just ignore it. */ - if (op->any.serial < tracker->xserver_serial) - return; - - meta_stack_op_dump (tracker, op, "Stack op event received: ", ""); - - /* First we apply any operations that we have queued up that depended - * on X operations *older* than what we received .. those operations - * must have been ignored by the X server, so we just apply the - * operations we have as best as possible while not moving windows. - */ - while (tracker->unverified_predictions->head) - { - MetaStackOp *queued_op = tracker->unverified_predictions->head->data; - - if (queued_op->any.serial >= op->any.serial) - break; - - meta_stack_op_apply (tracker, queued_op, tracker->verified_stack, - NO_RESTACK_X_WINDOWS); - - g_queue_pop_head (tracker->unverified_predictions); - meta_stack_op_free (queued_op); - need_sync = TRUE; - } - - /* Then we apply the received event. If it's a spontaneous event - * based on stacking we didn't trigger, this is the only handling. If we - * triggered it, we do the X restacking here, and then any residual - * local-only Wayland stacking below. - */ - if (meta_stack_op_apply (tracker, op, tracker->verified_stack, - IGNORE_NOOP_X_RESTACK)) - need_sync = TRUE; - - /* What is left to process is the prediction corresponding to the event - * (if any), and then any subsequent Wayland-only events we can just - * go ahead and do now. - */ - while (tracker->unverified_predictions->head) - { - MetaStackOp *queued_op = tracker->unverified_predictions->head->data; - - if (queued_op->any.serial > op->any.serial) - break; - - meta_stack_op_apply (tracker, queued_op, tracker->verified_stack, - NO_RESTACK_X_WINDOWS); - - g_queue_pop_head (tracker->unverified_predictions); - meta_stack_op_free (queued_op); - need_sync = TRUE; - } - - if (need_sync) - { - if (tracker->predicted_stack) - { - g_array_free (tracker->predicted_stack, TRUE); - tracker->predicted_stack = NULL; - } - - meta_stack_tracker_queue_sync_stack (tracker); - } - - meta_stack_tracker_dump (tracker); -} - -void -meta_stack_tracker_create_event (MetaStackTracker *tracker, - XCreateWindowEvent *event) -{ - MetaStackOp op; - - op.any.type = STACK_OP_ADD; - op.any.serial = event->serial; - op.add.window = event->window; - - stack_tracker_event_received (tracker, &op); -} - -void -meta_stack_tracker_destroy_event (MetaStackTracker *tracker, - XDestroyWindowEvent *event) -{ - MetaStackOp op; - - op.any.type = STACK_OP_REMOVE; - op.any.serial = event->serial; - op.remove.window = event->window; - - stack_tracker_event_received (tracker, &op); -} - -void -meta_stack_tracker_reparent_event (MetaStackTracker *tracker, - XReparentEvent *event) -{ - if (event->parent == event->event) - { - MetaStackOp op; - - op.any.type = STACK_OP_ADD; - op.any.serial = event->serial; - op.add.window = event->window; - - stack_tracker_event_received (tracker, &op); - } - else - { - MetaStackOp op; - - op.any.type = STACK_OP_REMOVE; - op.any.serial = event->serial; - op.remove.window = event->window; - - stack_tracker_event_received (tracker, &op); - } -} - -void -meta_stack_tracker_configure_event (MetaStackTracker *tracker, - XConfigureEvent *event) -{ - MetaStackOp op; - - op.any.type = STACK_OP_RAISE_ABOVE; - op.any.serial = event->serial; - op.raise_above.window = event->window; - op.raise_above.sibling = event->above; - - stack_tracker_event_received (tracker, &op); -} - -static gboolean -meta_stack_tracker_is_guard_window (MetaStackTracker *tracker, - uint64_t stack_id) -{ - MetaX11Display *x11_display = tracker->display->x11_display; - - if (!x11_display) - return FALSE; - - return stack_id == x11_display->guard_window; -} - -/** - * meta_stack_tracker_get_stack: - * @tracker: a #MetaStackTracker - * @windows: location to store list of windows, or %NULL - * @n_windows: location to store count of windows, or %NULL - * - * @windows will contain the most current view we have of the stacking order - * of the children of the root window. The returned array contains - * everything: InputOnly windows, override-redirect windows, - * hidden windows, etc. Some of these will correspond to MetaWindow - * objects, others won't. - * - * Assuming that no other clients have made requests that change - * the stacking order since we last received a notification, the - * returned list of windows is exactly that you'd get as the - * children when calling XQueryTree() on the root window. - */ -void -meta_stack_tracker_get_stack (MetaStackTracker *tracker, - guint64 **windows, - int *n_windows) -{ - GArray *stack; - - if (tracker->unverified_predictions->length == 0) - { - stack = tracker->verified_stack; - } - else - { - if (tracker->predicted_stack == NULL) - { - GList *l; - - tracker->predicted_stack = copy_stack (tracker->verified_stack); - for (l = tracker->unverified_predictions->head; l; l = l->next) - { - MetaStackOp *op = l->data; - meta_stack_op_apply (tracker, op, tracker->predicted_stack, APPLY_DEFAULT); - } - } - - stack = tracker->predicted_stack; - } - - if (windows) - *windows = (guint64 *)stack->data; - if (n_windows) - *n_windows = stack->len; -} - -/** - * meta_stack_tracker_sync_stack: - * @tracker: a #MetaStackTracker - * - * Informs the compositor of the current stacking order of windows, - * based on the predicted view maintained by the #MetaStackTracker. - */ -void -meta_stack_tracker_sync_stack (MetaStackTracker *tracker) -{ - guint64 *windows; - GList *meta_windows; - int n_windows; - int i; - - if (tracker->sync_stack_later) - { - meta_later_remove (tracker->sync_stack_later); - tracker->sync_stack_later = 0; - } - - meta_stack_tracker_keep_override_redirect_on_top (tracker); - - meta_stack_tracker_get_stack (tracker, &windows, &n_windows); - - meta_windows = NULL; - for (i = 0; i < n_windows; i++) - { - guint64 window = windows[i]; - - if (META_STACK_ID_IS_X11 (window)) - { - MetaX11Display *x11_display = tracker->display->x11_display; - MetaWindow *meta_window = NULL; - - if (x11_display) - meta_window = meta_x11_display_lookup_x_window (x11_display, (Window) window); - - /* When mapping back from xwindow to MetaWindow we have to be a bit careful; - * children of the root could include unmapped windows created by toolkits - * for internal purposes, including ones that we have registered in our - * XID => window table. (Wine uses a toplevel for _NET_WM_USER_TIME_WINDOW; - * see window-prop.c:reload_net_wm_user_time_window() for registration.) - */ - if (meta_window && - ((Window)window == meta_window->xwindow || - (meta_window->frame && (Window)window == meta_window->frame->xwindow))) - meta_windows = g_list_prepend (meta_windows, meta_window); - } - else - meta_windows = g_list_prepend (meta_windows, - meta_display_lookup_stamp (tracker->display, window)); - } - - meta_compositor_sync_stack (tracker->display->compositor, - meta_windows); - g_list_free (meta_windows); - - meta_display_restacked (tracker->display); -} - -static gboolean -stack_tracker_sync_stack_later (gpointer data) -{ - meta_stack_tracker_sync_stack (data); - - return FALSE; -} - -/** - * meta_stack_tracker_queue_sync_stack: - * @tracker: a #MetaStackTracker - * - * Queue informing the compositor of the new stacking order before the - * next redraw. (See meta_stack_tracker_sync_stack()). This is called - * internally when the stack of X windows changes, but also needs be - * called directly when we an undecorated window is first shown or - * withdrawn since the compositor's stacking order (which contains only - * the windows that have a corresponding MetaWindow) will change without - * any change to the stacking order of the X windows, if we are creating - * or destroying MetaWindows. - */ -void -meta_stack_tracker_queue_sync_stack (MetaStackTracker *tracker) -{ - if (tracker->sync_stack_later == 0) - { - tracker->sync_stack_later = meta_later_add (META_LATER_SYNC_STACK, - stack_tracker_sync_stack_later, - tracker, NULL); - } -} - -/* When moving an X window we sometimes need an X based sibling. - * - * If the given sibling is X based this function returns it back - * otherwise it searches downwards looking for the nearest X window. - * - * If no X based sibling could be found return NULL. */ -static Window -find_x11_sibling_downwards (MetaStackTracker *tracker, - guint64 sibling) -{ - guint64 *windows; - int n_windows; - int i; - - if (META_STACK_ID_IS_X11 (sibling)) - return (Window)sibling; - - meta_stack_tracker_get_stack (tracker, - &windows, &n_windows); - - /* NB: Children are in order from bottom to top and we - * want to search downwards for the nearest X window. - */ - - for (i = n_windows - 1; i >= 0; i--) - if (windows[i] == sibling) - break; - - for (; i >= 0; i--) - { - if (META_STACK_ID_IS_X11 (windows[i])) - return (Window)windows[i]; - } - - return None; -} - -static Window -find_x11_sibling_upwards (MetaStackTracker *tracker, - guint64 sibling) -{ - guint64 *windows; - int n_windows; - int i; - - if (META_STACK_ID_IS_X11 (sibling)) - return (Window)sibling; - - meta_stack_tracker_get_stack (tracker, - &windows, &n_windows); - - for (i = 0; i < n_windows; i++) - if (windows[i] == sibling) - break; - - for (; i < n_windows; i++) - { - if (META_STACK_ID_IS_X11 (windows[i])) - return (Window)windows[i]; - } - - return None; -} - -static void -meta_stack_tracker_lower_below (MetaStackTracker *tracker, - guint64 window, - guint64 sibling) -{ - gulong serial = 0; - MetaX11Display *x11_display = tracker->display->x11_display; - - if (META_STACK_ID_IS_X11 (window)) - { - XWindowChanges changes; - changes.sibling = sibling ? find_x11_sibling_upwards (tracker, sibling) : None; - - if (changes.sibling != find_x11_sibling_upwards (tracker, window)) - { - serial = XNextRequest (x11_display->xdisplay); - - meta_x11_error_trap_push (x11_display); - - changes.stack_mode = changes.sibling ? Below : Above; - - XConfigureWindow (x11_display->xdisplay, - window, - (changes.sibling ? CWSibling : 0) | CWStackMode, - &changes); - - meta_x11_error_trap_pop (x11_display); - } - } - - meta_stack_tracker_record_lower_below (tracker, - window, sibling, - serial); -} - -static void -meta_stack_tracker_raise_above (MetaStackTracker *tracker, - guint64 window, - guint64 sibling) -{ - gulong serial = 0; - MetaX11Display *x11_display = tracker->display->x11_display; - - if (META_STACK_ID_IS_X11 (window)) - { - XWindowChanges changes; - changes.sibling = sibling ? find_x11_sibling_downwards (tracker, sibling) : None; - - if (changes.sibling != find_x11_sibling_downwards (tracker, window)) - { - serial = XNextRequest (x11_display->xdisplay); - - meta_x11_error_trap_push (x11_display); - - changes.stack_mode = changes.sibling ? Above : Below; - - XConfigureWindow (x11_display->xdisplay, - (Window)window, - (changes.sibling ? CWSibling : 0) | CWStackMode, - &changes); - - meta_x11_error_trap_pop (x11_display); - } - } - - meta_stack_tracker_record_raise_above (tracker, window, - sibling, serial); -} - -void -meta_stack_tracker_lower (MetaStackTracker *tracker, - guint64 window) -{ - meta_stack_tracker_raise_above (tracker, window, None); -} - -static void -meta_stack_tracker_keep_override_redirect_on_top (MetaStackTracker *tracker) -{ - guint64 *stack; - int n_windows, i; - int topmost_non_or; - - meta_stack_tracker_get_stack (tracker, &stack, &n_windows); - - for (i = n_windows - 1; i >= 0; i--) - { - MetaWindow *window; - - window = meta_display_lookup_stack_id (tracker->display, stack[i]); - if (window && window->layer != META_LAYER_OVERRIDE_REDIRECT) - break; - } - - topmost_non_or = i; - - for (i -= 1; i >= 0; i--) - { - MetaWindow *window; - - if (meta_stack_tracker_is_guard_window (tracker, stack[i])) - break; - - window = meta_display_lookup_stack_id (tracker->display, stack[i]); - if (window && window->layer == META_LAYER_OVERRIDE_REDIRECT) - { - meta_stack_tracker_raise_above (tracker, stack[i], stack[topmost_non_or]); - meta_stack_tracker_get_stack (tracker, &stack, &n_windows); - topmost_non_or -= 1; - } - } -} - -void -meta_stack_tracker_restack_managed (MetaStackTracker *tracker, - const guint64 *managed, - int n_managed) -{ - guint64 *windows; - int n_windows; - int old_pos, new_pos; - - COGL_TRACE_BEGIN_SCOPED (StackTrackerRestackManaged, - "StackTracker: Restack Managed"); - if (n_managed == 0) - return; - - COGL_TRACE_BEGIN (StackTrackerRestackManagedGet, - "StackTracker: Restack Managed (get)"); - meta_stack_tracker_get_stack (tracker, &windows, &n_windows); - - /* If the top window has to be restacked, we don't want to move it to the very - * top of the stack, since apps expect override-redirect windows to stay near - * the top of the X stack; we instead move it above all managed windows (or - * above the guard window if there are no non-hidden managed windows.) - */ - old_pos = n_windows - 1; - for (old_pos = n_windows - 1; old_pos >= 0; old_pos--) - { - MetaWindow *old_window = meta_display_lookup_stack_id (tracker->display, windows[old_pos]); - if ((old_window && !old_window->override_redirect && !old_window->unmanaging) || - meta_stack_tracker_is_guard_window (tracker, windows[old_pos])) - break; - } - g_assert (old_pos >= 0); - COGL_TRACE_END (StackTrackerRestackManagedGet); - - COGL_TRACE_BEGIN (StackTrackerRestackManagedRaise, - "StackTracker: Restack Managed (raise)"); - new_pos = n_managed - 1; - if (managed[new_pos] != windows[old_pos]) - { - /* Move the first managed window in the new stack above all managed windows */ - meta_stack_tracker_raise_above (tracker, managed[new_pos], windows[old_pos]); - meta_stack_tracker_get_stack (tracker, &windows, &n_windows); - /* Moving managed[new_pos] above windows[old_pos], moves the window at old_pos down by one */ - } - COGL_TRACE_END (StackTrackerRestackManagedRaise); - - old_pos--; - new_pos--; - - COGL_TRACE_BEGIN (StackTrackerRestackManagedRestack, - "StackTracker: Restack Managed (restack)"); - while (old_pos >= 0 && new_pos >= 0) - { - if (meta_stack_tracker_is_guard_window (tracker, windows[old_pos])) - break; - - if (windows[old_pos] == managed[new_pos]) - { - old_pos--; - new_pos--; - continue; - } - - MetaWindow *old_window = meta_display_lookup_stack_id (tracker->display, windows[old_pos]); - if (!old_window || old_window->override_redirect || old_window->unmanaging) - { - old_pos--; - continue; - } - - meta_stack_tracker_lower_below (tracker, managed[new_pos], managed[new_pos + 1]); - meta_stack_tracker_get_stack (tracker, &windows, &n_windows); - /* Moving managed[new_pos] above windows[old_pos] moves the window at old_pos down by one, - * we'll examine it again to see if it matches the next new window */ - old_pos--; - new_pos--; - } - COGL_TRACE_END (StackTrackerRestackManagedRestack); - - COGL_TRACE_BEGIN (StackTrackerRestackManagedLower, - "StackTracker: Restack Managed (lower)"); - while (new_pos > 0) - { - meta_stack_tracker_lower_below (tracker, managed[new_pos], managed[new_pos - 1]); - new_pos--; - } - COGL_TRACE_END (StackTrackerRestackManagedLower); -} - -void -meta_stack_tracker_restack_at_bottom (MetaStackTracker *tracker, - const guint64 *new_order, - int n_new_order) -{ - guint64 *windows; - int n_windows; - int pos; - - COGL_TRACE_BEGIN_SCOPED (StackTrackerRestackAtBottom, - "Stack tracker: Restack at bottom"); - meta_stack_tracker_get_stack (tracker, &windows, &n_windows); - - for (pos = 0; pos < n_new_order; pos++) - { - if (pos >= n_windows || windows[pos] != new_order[pos]) - { - if (pos == 0) - meta_stack_tracker_lower (tracker, new_order[pos]); - else - meta_stack_tracker_raise_above (tracker, new_order[pos], new_order[pos - 1]); - - meta_stack_tracker_get_stack (tracker, &windows, &n_windows); - } - } -} diff --git a/src/core/stack-tracker.h b/src/core/stack-tracker.h deleted file mode 100644 index dbffb0e5e..000000000 --- a/src/core/stack-tracker.h +++ /dev/null @@ -1,87 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/** - * \file stack-tracker.h Track stacking order for compositor - * - * MetaStackTracker maintains the most accurate view we have at a - * given point of time of the ordering of the children of the root - * window (including override-redirect windows.) This is used to order - * the windows when the compositor draws them. - * - * By contrast, MetaStack is responsible for keeping track of how we - * think that windows *should* be ordered. For windows we manage - * (non-override-redirect windows), the two stacking orders will be - * the same. - */ - -/* - * Copyright (C) 2009 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef META_STACK_TRACKER_H -#define META_STACK_TRACKER_H - -#include "core/util-private.h" -#include "meta/display.h" -#include "meta/window.h" - -typedef struct _MetaStackTracker MetaStackTracker; - -MetaStackTracker *meta_stack_tracker_new (MetaDisplay *display); -void meta_stack_tracker_free (MetaStackTracker *tracker); - -/* These functions are called when we make an X call that changes the - * stacking order; this allows MetaStackTracker to predict stacking - * order before it receives events back from the X server */ -void meta_stack_tracker_record_add (MetaStackTracker *tracker, - guint64 window, - gulong serial); -void meta_stack_tracker_record_remove (MetaStackTracker *tracker, - guint64 window, - gulong serial); - -/* We also have functions that also go ahead and do the work - */ -void meta_stack_tracker_lower (MetaStackTracker *tracker, - guint64 window); - -void meta_stack_tracker_restack_managed (MetaStackTracker *tracker, - const guint64 *windows, - int n_windows); -void meta_stack_tracker_restack_at_bottom (MetaStackTracker *tracker, - const guint64 *new_order, - int n_new_order); - -/* These functions are used to update the stack when we get events - * reflecting changes to the stacking order */ -void meta_stack_tracker_create_event (MetaStackTracker *tracker, - XCreateWindowEvent *event); -void meta_stack_tracker_destroy_event (MetaStackTracker *tracker, - XDestroyWindowEvent *event); -void meta_stack_tracker_reparent_event (MetaStackTracker *tracker, - XReparentEvent *event); -void meta_stack_tracker_configure_event (MetaStackTracker *tracker, - XConfigureEvent *event); - -META_EXPORT_TEST -void meta_stack_tracker_get_stack (MetaStackTracker *tracker, - guint64 **windows, - int *n_entries); - -void meta_stack_tracker_sync_stack (MetaStackTracker *tracker); -void meta_stack_tracker_queue_sync_stack (MetaStackTracker *tracker); - -#endif /* META_STACK_TRACKER_H */ diff --git a/src/core/stack.c b/src/core/stack.c deleted file mode 100644 index 62b8954e8..000000000 --- a/src/core/stack.c +++ /dev/null @@ -1,1340 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * SECTION:stack - * @short_description: Which windows cover which other windows - */ - -/* - * Copyright (C) 2001 Havoc Pennington - * Copyright (C) 2002, 2003 Red Hat, Inc. - * Copyright (C) 2004 Rob Adams - * Copyright (C) 2004, 2005 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "config.h" - -#include "core/stack.h" - -#include "backends/meta-logical-monitor.h" -#include "cogl/cogl.h" -#include "core/frame.h" -#include "core/meta-workspace-manager-private.h" -#include "core/window-private.h" -#include "meta/group.h" -#include "meta/prefs.h" -#include "meta/workspace.h" -#include "x11/meta-x11-display-private.h" - -#define WINDOW_TRANSIENT_FOR_WHOLE_GROUP(w) \ - (meta_window_has_transient_type (w) && w->transient_for == NULL) - -static void meta_window_set_stack_position_no_sync (MetaWindow *window, - int position); -static void stack_do_relayer (MetaStack *stack); -static void stack_do_constrain (MetaStack *stack); -static void stack_do_resort (MetaStack *stack); -static void stack_ensure_sorted (MetaStack *stack); - -enum -{ - PROP_DISPLAY = 1, - N_PROPS -}; - -enum -{ - CHANGED, - WINDOW_ADDED, - WINDOW_REMOVED, - N_SIGNALS -}; - -static GParamSpec *pspecs[N_PROPS] = { 0 }; -static guint signals[N_SIGNALS] = { 0 }; - -G_DEFINE_TYPE (MetaStack, meta_stack, G_TYPE_OBJECT) - -static void -on_stack_changed (MetaStack *stack) -{ - MetaDisplay *display = stack->display; - GArray *all_root_children_stacked; - GList *l; - GArray *hidden_stack_ids; - GList *sorted; - - COGL_TRACE_BEGIN_SCOPED (StackChanged, "Stack changed"); - - meta_topic (META_DEBUG_STACK, "Syncing window stack to server"); - - all_root_children_stacked = g_array_new (FALSE, FALSE, sizeof (uint64_t)); - hidden_stack_ids = g_array_new (FALSE, FALSE, sizeof (uint64_t)); - - meta_topic (META_DEBUG_STACK, "Bottom to top: "); - meta_push_no_msg_prefix (); - - sorted = meta_stack_list_windows (stack, NULL); - - for (l = sorted; l; l = l->next) - { - MetaWindow *w = l->data; - uint64_t top_level_window; - uint64_t stack_id; - - if (w->unmanaging) - continue; - - meta_topic (META_DEBUG_STACK, " %u:%d - %s ", - w->layer, w->stack_position, w->desc); - - if (w->frame) - top_level_window = w->frame->xwindow; - else - top_level_window = w->xwindow; - - if (w->client_type == META_WINDOW_CLIENT_TYPE_X11) - stack_id = top_level_window; - else - stack_id = w->stamp; - - /* We don't restack hidden windows along with the rest, though they are - * reflected in the _NET hints. Hidden windows all get pushed below - * the screens fullscreen guard_window. */ - if (w->hidden) - { - g_array_append_val (hidden_stack_ids, stack_id); - continue; - } - - g_array_append_val (all_root_children_stacked, stack_id); - } - - meta_pop_no_msg_prefix (); - - if (display->x11_display) - { - uint64_t guard_window_id; - - /* The screen guard window sits above all hidden windows and acts as - * a barrier to input reaching these windows. */ - guard_window_id = display->x11_display->guard_window; - g_array_append_val (hidden_stack_ids, guard_window_id); - } - - /* Sync to server */ - - meta_topic (META_DEBUG_STACK, "Restacking %u windows", - all_root_children_stacked->len); - - meta_stack_tracker_restack_managed (display->stack_tracker, - (uint64_t *)all_root_children_stacked->data, - all_root_children_stacked->len); - meta_stack_tracker_restack_at_bottom (display->stack_tracker, - (uint64_t *)hidden_stack_ids->data, - hidden_stack_ids->len); - - g_array_free (hidden_stack_ids, TRUE); - g_array_free (all_root_children_stacked, TRUE); - g_list_free (sorted); -} - -static void -meta_stack_init (MetaStack *stack) -{ - g_signal_connect (stack, "changed", - G_CALLBACK (on_stack_changed), NULL); -} - -static void -meta_stack_finalize (GObject *object) -{ - MetaStack *stack = META_STACK (object); - - g_list_free (stack->sorted); - - G_OBJECT_CLASS (meta_stack_parent_class)->finalize (object); -} - -static void -meta_stack_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - MetaStack *stack = META_STACK (object); - - switch (prop_id) - { - case PROP_DISPLAY: - stack->display = g_value_get_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -meta_stack_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - MetaStack *stack = META_STACK (object); - - switch (prop_id) - { - case PROP_DISPLAY: - g_value_set_object (value, stack->display); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -meta_stack_class_init (MetaStackClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->set_property = meta_stack_set_property; - object_class->get_property = meta_stack_get_property; - object_class->finalize = meta_stack_finalize; - - signals[CHANGED] = - g_signal_new ("changed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); - signals[WINDOW_ADDED] = - g_signal_new ("window-added", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, META_TYPE_WINDOW); - signals[WINDOW_REMOVED] = - g_signal_new ("window-removed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, META_TYPE_WINDOW); - - pspecs[PROP_DISPLAY] = - g_param_spec_object ("display", - "Display", - "Display", - META_TYPE_DISPLAY, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); - - g_object_class_install_properties (object_class, N_PROPS, pspecs); -} - -MetaStack * -meta_stack_new (MetaDisplay *display) -{ - return g_object_new (META_TYPE_STACK, - "display", display, - NULL); -} - -static void -meta_stack_changed (MetaStack *stack) -{ - /* Bail out if frozen */ - if (stack->freeze_count > 0) - return; - - COGL_TRACE_BEGIN_SCOPED (MetaStackChangedSort, "Stack: Changed"); - - stack_ensure_sorted (stack); - g_signal_emit (stack, signals[CHANGED], 0); -} - -void -meta_stack_add (MetaStack *stack, - MetaWindow *window) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - - COGL_TRACE_BEGIN_SCOPED (MetaStackAdd, - "Stack (add window)"); - - g_return_if_fail (meta_window_is_stackable (window)); - - meta_topic (META_DEBUG_STACK, "Adding window %s to the stack", window->desc); - - if (meta_window_is_in_stack (window)) - meta_bug ("Window %s had stack position already", window->desc); - - stack->sorted = g_list_prepend (stack->sorted, window); - stack->need_resort = TRUE; /* may not be needed as we add to top */ - stack->need_constrain = TRUE; - stack->need_relayer = TRUE; - - g_signal_emit (stack, signals[WINDOW_ADDED], 0, window); - - window->stack_position = stack->n_positions; - stack->n_positions += 1; - meta_topic (META_DEBUG_STACK, - "Window %s has stack_position initialized to %d", - window->desc, window->stack_position); - - meta_stack_changed (stack); - meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); -} - -void -meta_stack_remove (MetaStack *stack, - MetaWindow *window) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - - COGL_TRACE_BEGIN_SCOPED (MetaStackRemove, - "Stack (remove window)"); - - meta_topic (META_DEBUG_STACK, "Removing window %s from the stack", window->desc); - - /* Set window to top position, so removing it will not leave gaps - * in the set of positions - */ - meta_window_set_stack_position_no_sync (window, - stack->n_positions - 1); - window->stack_position = -1; - stack->n_positions -= 1; - - stack->sorted = g_list_remove (stack->sorted, window); - - g_signal_emit (stack, signals[WINDOW_REMOVED], 0, window); - - meta_stack_changed (stack); - meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); -} - -void -meta_stack_update_layer (MetaStack *stack, - MetaWindow *window) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - stack->need_relayer = TRUE; - - meta_stack_changed (stack); - meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); -} - -void -meta_stack_update_transient (MetaStack *stack, - MetaWindow *window) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - stack->need_constrain = TRUE; - - meta_stack_changed (stack); - meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); -} - -/* raise/lower within a layer */ -void -meta_stack_raise (MetaStack *stack, - MetaWindow *window) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - GList *l; - int max_stack_position = window->stack_position; - MetaWorkspace *workspace; - - stack_ensure_sorted (stack); - - workspace = meta_window_get_workspace (window); - for (l = stack->sorted; l; l = l->next) - { - MetaWindow *w = (MetaWindow *) l->data; - if (meta_window_located_on_workspace (w, workspace) && - w->stack_position > max_stack_position) - max_stack_position = w->stack_position; - } - - if (max_stack_position == window->stack_position) - return; - - meta_window_set_stack_position_no_sync (window, max_stack_position); - - meta_stack_changed (stack); - meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); -} - -void -meta_stack_lower (MetaStack *stack, - MetaWindow *window) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - GList *l; - int min_stack_position = window->stack_position; - MetaWorkspace *workspace; - - stack_ensure_sorted (stack); - - workspace = meta_window_get_workspace (window); - for (l = stack->sorted; l; l = l->next) - { - MetaWindow *w = (MetaWindow *) l->data; - if (meta_window_located_on_workspace (w, workspace) && - w->stack_position < min_stack_position) - min_stack_position = w->stack_position; - } - - if (min_stack_position == window->stack_position) - return; - - meta_window_set_stack_position_no_sync (window, min_stack_position); - - meta_stack_changed (stack); - meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); -} - -void -meta_stack_freeze (MetaStack *stack) -{ - stack->freeze_count += 1; -} - -void -meta_stack_thaw (MetaStack *stack) -{ - g_return_if_fail (stack->freeze_count > 0); - - stack->freeze_count -= 1; - meta_stack_changed (stack); - meta_stack_update_window_tile_matches (stack, NULL); -} - -void -meta_stack_update_window_tile_matches (MetaStack *stack, - MetaWorkspace *workspace) -{ - GList *windows, *tmp; - - if (stack->freeze_count > 0) - return; - - windows = meta_stack_list_windows (stack, workspace); - tmp = windows; - while (tmp) - { - meta_window_compute_tile_match ((MetaWindow *) tmp->data); - tmp = tmp->next; - } - - g_list_free (windows); -} - -/* Front of the layer list is the topmost window, - * so the lower stack position is later in the list - */ -static int -compare_window_position (void *a, - void *b) -{ - MetaWindow *window_a = a; - MetaWindow *window_b = b; - - /* Go by layer, then stack_position */ - if (window_a->layer < window_b->layer) - return 1; /* move window_a later in list */ - else if (window_a->layer > window_b->layer) - return -1; - else if (window_a->stack_position < window_b->stack_position) - return 1; /* move window_a later in list */ - else if (window_a->stack_position > window_b->stack_position) - return -1; - else - return 0; /* not reached */ -} - -/* - * Stacking constraints - * - * Assume constraints of the form "AB" meaning "window A must be - * below window B" - * - * If we have windows stacked from bottom to top - * "ABC" then raise A we get "BCA". Say C is - * transient for B is transient for A. So - * we have constraints AB and BC. - * - * After raising A, we need to reapply the constraints. - * If we do this by raising one window at a time - - * - * start: BCA - * apply AB: CAB - * apply BC: ABC - * - * but apply constraints in the wrong order and it breaks: - * - * start: BCA - * apply BC: BCA - * apply AB: CAB - * - * We make a directed graph of the constraints by linking - * from "above windows" to "below windows as follows: - * - * AB -> BC -> CD - * \ - * CE - * - * If we then walk that graph and apply the constraints in the order - * that they appear, we will apply them correctly. Note that the - * graph MAY have cycles, so we have to guard against that. - * - */ - -typedef struct Constraint Constraint; - -struct Constraint -{ - MetaWindow *above; - MetaWindow *below; - - /* used to keep the constraint in the - * list of constraints for window "below" - */ - Constraint *next; - - /* used to create the graph. */ - GSList *next_nodes; - - /* constraint has been applied, used - * to detect cycles. - */ - unsigned int applied : 1; - - /* constraint has a previous node in the graph, - * used to find places to start in the graph. - * (I think this also has the side effect - * of preventing cycles, since cycles will - * have no starting point - so maybe - * the "applied" flag isn't needed.) - */ - unsigned int has_prev : 1; -}; - -/* We index the array of constraints by window - * stack positions, just because the stack - * positions are a convenient index. - */ -static void -add_constraint (Constraint **constraints, - MetaWindow *above, - MetaWindow *below) -{ - Constraint *c; - - /* check if constraint is a duplicate */ - c = constraints[below->stack_position]; - while (c != NULL) - { - if (c->above == above) - return; - c = c->next; - } - - /* if not, add the constraint */ - c = g_new (Constraint, 1); - c->above = above; - c->below = below; - c->next = constraints[below->stack_position]; - c->next_nodes = NULL; - c->applied = FALSE; - c->has_prev = FALSE; - - constraints[below->stack_position] = c; -} - -static void -create_constraints (Constraint **constraints, - GList *windows) -{ - GList *tmp; - - tmp = windows; - while (tmp != NULL) - { - MetaWindow *w = tmp->data; - - if (!meta_window_is_in_stack (w)) - { - meta_topic (META_DEBUG_STACK, "Window %s not in the stack, not constraining it", - w->desc); - tmp = tmp->next; - continue; - } - - if (WINDOW_TRANSIENT_FOR_WHOLE_GROUP (w)) - { - GSList *group_windows; - GSList *tmp2; - MetaGroup *group; - - group = meta_window_get_group (w); - - if (group != NULL) - group_windows = meta_group_list_windows (group); - else - group_windows = NULL; - - tmp2 = group_windows; - - while (tmp2 != NULL) - { - MetaWindow *group_window = tmp2->data; - - if (!meta_window_is_in_stack (group_window) || - group_window->override_redirect) - { - tmp2 = tmp2->next; - continue; - } - -#if 0 - /* old way of doing it */ - if (!(meta_window_is_ancestor_of_transient (w, group_window)) && - !WINDOW_TRANSIENT_FOR_WHOLE_GROUP (group_window)) /* note */;/*note*/ -#else - /* better way I think, so transient-for-group are constrained - * only above non-transient-type windows in their group - */ - if (!meta_window_has_transient_type (group_window)) -#endif - { - meta_topic (META_DEBUG_STACK, - "Constraining %s above %s as it's transient for its group", - w->desc, group_window->desc); - add_constraint (constraints, w, group_window); - } - - tmp2 = tmp2->next; - } - - g_slist_free (group_windows); - } - else if (w->transient_for != NULL) - { - MetaWindow *parent; - - parent = w->transient_for; - - if (parent && meta_window_is_in_stack (parent)) - { - meta_topic (META_DEBUG_STACK, - "Constraining %s above %s due to transiency", - w->desc, parent->desc); - add_constraint (constraints, w, parent); - } - } - - tmp = tmp->next; - } -} - -static void -graph_constraints (Constraint **constraints, - int n_constraints) -{ - int i; - - i = 0; - while (i < n_constraints) - { - Constraint *c; - - /* If we have "A below B" and "B below C" then AB -> BC so we - * add BC to next_nodes in AB. - */ - - c = constraints[i]; - while (c != NULL) - { - Constraint *n; - - g_assert (c->below->stack_position == i); - - /* Constraints where ->above is below are our - * next_nodes and we are their previous - */ - n = constraints[c->above->stack_position]; - while (n != NULL) - { - c->next_nodes = g_slist_prepend (c->next_nodes, - n); - /* c is a previous node of n */ - n->has_prev = TRUE; - - n = n->next; - } - - c = c->next; - } - - ++i; - } -} - -static void -free_constraints (Constraint **constraints, - int n_constraints) -{ - int i; - - i = 0; - while (i < n_constraints) - { - Constraint *c; - - c = constraints[i]; - while (c != NULL) - { - Constraint *next = c->next; - - g_slist_free (c->next_nodes); - - g_free (c); - - c = next; - } - - ++i; - } -} - -static void -ensure_above (MetaWindow *above, - MetaWindow *below) -{ - gboolean is_transient; - - is_transient = meta_window_has_transient_type (above) || - above->transient_for == below; - if (is_transient && above->layer < below->layer) - { - meta_topic (META_DEBUG_STACK, - "Promoting window %s from layer %u to %u due to constraint", - above->desc, above->layer, below->layer); - above->layer = below->layer; - } - - if (above->stack_position < below->stack_position) - { - /* move above to below->stack_position bumping below down the stack */ - meta_window_set_stack_position_no_sync (above, below->stack_position); - g_assert (below->stack_position + 1 == above->stack_position); - } - meta_topic (META_DEBUG_STACK, "%s above at %d > %s below at %d", - above->desc, above->stack_position, - below->desc, below->stack_position); -} - -static void -traverse_constraint (Constraint *c) -{ - GSList *tmp; - - if (c->applied) - return; - - ensure_above (c->above, c->below); - c->applied = TRUE; - - tmp = c->next_nodes; - while (tmp != NULL) - { - traverse_constraint (tmp->data); - - tmp = tmp->next; - } -} - -static void -apply_constraints (Constraint **constraints, - int n_constraints) -{ - GSList *heads; - GSList *tmp; - int i; - - /* List all heads in an ordered constraint chain */ - heads = NULL; - i = 0; - while (i < n_constraints) - { - Constraint *c; - - c = constraints[i]; - while (c != NULL) - { - if (!c->has_prev) - heads = g_slist_prepend (heads, c); - - c = c->next; - } - - ++i; - } - - /* Now traverse the chain and apply constraints */ - tmp = heads; - while (tmp != NULL) - { - Constraint *c = tmp->data; - - traverse_constraint (c); - - tmp = tmp->next; - } - - g_slist_free (heads); -} - -/** - * stack_do_relayer: - * - * Update the layers that windows are in - */ -static void -stack_do_relayer (MetaStack *stack) -{ - GList *tmp; - - if (!stack->need_relayer) - return; - - meta_topic (META_DEBUG_STACK, - "Recomputing layers"); - - tmp = stack->sorted; - - while (tmp != NULL) - { - MetaWindow *w; - MetaStackLayer old_layer; - - w = tmp->data; - old_layer = w->layer; - - w->layer = meta_window_calculate_layer (w); - - if (w->layer != old_layer) - { - meta_topic (META_DEBUG_STACK, - "Window %s moved from layer %u to %u", - w->desc, old_layer, w->layer); - stack->need_resort = TRUE; - stack->need_constrain = TRUE; - /* don't need to constrain as constraining - * purely operates in terms of stack_position - * not layer - */ - } - - tmp = tmp->next; - } - - stack->need_relayer = FALSE; -} - -/** - * stack_do_constrain: - * - * Update stack_position and layer to reflect transiency - * constraints - */ -static void -stack_do_constrain (MetaStack *stack) -{ - Constraint **constraints; - - /* It'd be nice if this were all faster, probably */ - - if (!stack->need_constrain) - return; - - meta_topic (META_DEBUG_STACK, - "Reapplying constraints"); - - constraints = g_new0 (Constraint*, - stack->n_positions); - - create_constraints (constraints, stack->sorted); - - graph_constraints (constraints, stack->n_positions); - - apply_constraints (constraints, stack->n_positions); - - free_constraints (constraints, stack->n_positions); - g_free (constraints); - - stack->need_constrain = FALSE; -} - -/** - * stack_do_resort: - * - * Sort stack->sorted with layers having priority over stack_position. - */ -static void -stack_do_resort (MetaStack *stack) -{ - if (!stack->need_resort) - return; - - meta_topic (META_DEBUG_STACK, - "Sorting stack list"); - - stack->sorted = g_list_sort (stack->sorted, - (GCompareFunc) compare_window_position); - - meta_display_queue_check_fullscreen (stack->display); - - stack->need_resort = FALSE; -} - -/** - * stack_ensure_sorted: - * - * Puts the stack into canonical form. - * - * Honour the removed and added lists of the stack, and then recalculate - * all the layers (if the flag is set), re-run all the constraint calculations - * (if the flag is set), and finally re-sort the stack (if the flag is set, - * and if it wasn't already it might have become so during all the previous - * activity). - */ -static void -stack_ensure_sorted (MetaStack *stack) -{ - stack_do_relayer (stack); - stack_do_constrain (stack); - stack_do_resort (stack); -} - -MetaWindow * -meta_stack_get_top (MetaStack *stack) -{ - stack_ensure_sorted (stack); - - if (stack->sorted) - return stack->sorted->data; - else - return NULL; -} - -MetaWindow * -meta_stack_get_bottom (MetaStack *stack) -{ - GList *link; - - stack_ensure_sorted (stack); - - link = g_list_last (stack->sorted); - if (link != NULL) - return link->data; - else - return NULL; -} - -MetaWindow * -meta_stack_get_above (MetaStack *stack, - MetaWindow *window, - gboolean only_within_layer) -{ - GList *link; - MetaWindow *above; - - stack_ensure_sorted (stack); - - link = g_list_find (stack->sorted, window); - if (link == NULL) - return NULL; - if (link->prev == NULL) - return NULL; - - above = link->prev->data; - - if (only_within_layer && - above->layer != window->layer) - return NULL; - else - return above; -} - -MetaWindow * -meta_stack_get_below (MetaStack *stack, - MetaWindow *window, - gboolean only_within_layer) -{ - GList *link; - MetaWindow *below; - - stack_ensure_sorted (stack); - - link = g_list_find (stack->sorted, window); - - if (link == NULL) - return NULL; - if (link->next == NULL) - return NULL; - - below = link->next->data; - - if (only_within_layer && - below->layer != window->layer) - return NULL; - else - return below; -} - -static gboolean -window_contains_point (MetaWindow *window, - int root_x, - int root_y) -{ - MetaRectangle rect; - - meta_window_get_frame_rect (window, &rect); - - return META_POINT_IN_RECT (root_x, root_y, rect); -} - -static gboolean -window_can_get_default_focus (MetaWindow *window) -{ - if (window->unmaps_pending > 0) - return FALSE; - - if (window->unmanaging) - return FALSE; - - if (!meta_window_is_focusable (window)) - return FALSE; - - if (!meta_window_should_be_showing (window)) - return FALSE; - - if (window->type == META_WINDOW_DOCK) - return FALSE; - - return TRUE; -} - -static MetaWindow * -get_default_focus_window (MetaStack *stack, - MetaWorkspace *workspace, - MetaWindow *not_this_one, - gboolean must_be_at_point, - int root_x, - int root_y) -{ - /* Find the topmost, focusable, mapped, window. - * not_this_one is being unfocused or going away, so exclude it. - */ - - GList *l; - - stack_ensure_sorted (stack); - - /* top of this layer is at the front of the list */ - for (l = stack->sorted; l != NULL; l = l->next) - { - MetaWindow *window = l->data; - - if (!window) - continue; - - if (window == not_this_one) - continue; - - if (!window_can_get_default_focus (window)) - continue; - - if (must_be_at_point && !window_contains_point (window, root_x, root_y)) - continue; - - return window; - } - - return NULL; -} - -MetaWindow * -meta_stack_get_default_focus_window_at_point (MetaStack *stack, - MetaWorkspace *workspace, - MetaWindow *not_this_one, - int root_x, - int root_y) -{ - return get_default_focus_window (stack, workspace, not_this_one, - TRUE, root_x, root_y); -} - -MetaWindow * -meta_stack_get_default_focus_window (MetaStack *stack, - MetaWorkspace *workspace, - MetaWindow *not_this_one) -{ - return get_default_focus_window (stack, workspace, not_this_one, - FALSE, 0, 0); -} - -GList * -meta_stack_list_windows (MetaStack *stack, - MetaWorkspace *workspace) -{ - GList *workspace_windows = NULL; - GList *link; - - stack_ensure_sorted (stack); /* do adds/removes */ - - link = stack->sorted; - - while (link) - { - MetaWindow *window = link->data; - - if (window && - (workspace == NULL || meta_window_located_on_workspace (window, workspace))) - { - workspace_windows = g_list_prepend (workspace_windows, - window); - } - - link = link->next; - } - - return workspace_windows; -} - -GList * -meta_stack_get_default_focus_candidates (MetaStack *stack, - MetaWorkspace *workspace) -{ - GList *windows = meta_stack_list_windows (stack, workspace); - GList *l; - - for (l = windows; l;) - { - GList *next = l->next; - - if (!window_can_get_default_focus (l->data)) - windows = g_list_delete_link (windows, l); - - l = next; - } - - return windows; -} - -int -meta_stack_windows_cmp (MetaStack *stack, - MetaWindow *window_a, - MetaWindow *window_b) -{ - /* -1 means a below b */ - - stack_ensure_sorted (stack); /* update constraints, layers */ - - if (window_a->layer < window_b->layer) - return -1; - else if (window_a->layer > window_b->layer) - return 1; - else if (window_a->stack_position < window_b->stack_position) - return -1; - else if (window_a->stack_position > window_b->stack_position) - return 1; - else - return 0; /* not reached */ -} - -static int -compare_just_window_stack_position (void *a, - void *b) -{ - MetaWindow *window_a = a; - MetaWindow *window_b = b; - - if (window_a->stack_position < window_b->stack_position) - return -1; /* move window_a earlier in list */ - else if (window_a->stack_position > window_b->stack_position) - return 1; - else - return 0; /* not reached */ -} - -GList * -meta_stack_get_positions (MetaStack *stack) -{ - GList *tmp; - - /* Make sure to handle any adds or removes */ - stack_ensure_sorted (stack); - - tmp = g_list_copy (stack->sorted); - tmp = g_list_sort (tmp, (GCompareFunc) compare_just_window_stack_position); - - return tmp; -} - -static gint -compare_pointers (gconstpointer a, - gconstpointer b) -{ - if (a > b) - return 1; - else if (a < b) - return -1; - else - return 0; -} - -static gboolean -lists_contain_same_windows (GList *a, - GList *b) -{ - GList *copy1, *copy2; - GList *tmp1, *tmp2; - - if (g_list_length (a) != g_list_length (b)) - return FALSE; - - tmp1 = copy1 = g_list_sort (g_list_copy (a), compare_pointers); - tmp2 = copy2 = g_list_sort (g_list_copy (b), compare_pointers); - - while (tmp1 && tmp1->data == tmp2->data) /* tmp2 is non-NULL if tmp1 is */ - { - tmp1 = tmp1->next; - tmp2 = tmp2->next; - } - - g_list_free (copy1); - g_list_free (copy2); - - return (tmp1 == NULL); /* tmp2 is non-NULL if tmp1 is */ -} - -void -meta_stack_set_positions (MetaStack *stack, - GList *windows) -{ - int i; - GList *tmp; - - /* Make sure any adds or removes aren't in limbo -- is this needed? */ - stack_ensure_sorted (stack); - - if (!lists_contain_same_windows (windows, stack->sorted)) - { - meta_warning ("This list of windows has somehow changed; not resetting " - "positions of the windows."); - return; - } - - g_list_free (stack->sorted); - stack->sorted = g_list_copy (windows); - - stack->need_resort = TRUE; - stack->need_constrain = TRUE; - - i = 0; - tmp = windows; - while (tmp != NULL) - { - MetaWindow *w = tmp->data; - w->stack_position = i++; - tmp = tmp->next; - } - - meta_topic (META_DEBUG_STACK, - "Reset the stack positions of (nearly) all windows"); - - meta_stack_changed (stack); - meta_stack_update_window_tile_matches (stack, NULL); -} - -void -meta_window_set_stack_position_no_sync (MetaWindow *window, - int position) -{ - int low, high, delta; - GList *tmp; - - g_return_if_fail (window->display->stack != NULL); - g_return_if_fail (window->stack_position >= 0); - g_return_if_fail (position >= 0); - g_return_if_fail (position < window->display->stack->n_positions); - - if (position == window->stack_position) - { - meta_topic (META_DEBUG_STACK, "Window %s already has position %d", - window->desc, position); - return; - } - - window->display->stack->need_resort = TRUE; - window->display->stack->need_constrain = TRUE; - - if (position < window->stack_position) - { - low = position; - high = window->stack_position - 1; - delta = 1; - } - else - { - low = window->stack_position + 1; - high = position; - delta = -1; - } - - tmp = window->display->stack->sorted; - while (tmp != NULL) - { - MetaWindow *w = tmp->data; - - if (w->stack_position >= low && - w->stack_position <= high) - w->stack_position += delta; - - tmp = tmp->next; - } - - window->stack_position = position; - - meta_topic (META_DEBUG_STACK, - "Window %s had stack_position set to %d", - window->desc, window->stack_position); -} - -void -meta_window_set_stack_position (MetaWindow *window, - int position) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - - meta_window_set_stack_position_no_sync (window, position); - meta_stack_changed (window->display->stack); - meta_stack_update_window_tile_matches (window->display->stack, - workspace_manager->active_workspace); -} diff --git a/src/core/stack.h b/src/core/stack.h deleted file mode 100644 index ec5753fe7..000000000 --- a/src/core/stack.h +++ /dev/null @@ -1,406 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2001 Havoc Pennington - * Copyright (C) 2005 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef META_STACK_H -#define META_STACK_H - -/** - * SECTION:stack - * @short_description: Which windows cover which other windows - * - * There are two factors that determine window position. - * - * One is window->stack_position, which is a unique integer - * indicating how windows are ordered with respect to one - * another. The ordering here transcends layers; it isn't changed - * as the window is moved among layers. This allows us to move several - * windows from one layer to another, while preserving the relative - * order of the moved windows. Also, it allows us to restore - * the stacking order from a saved session. - * - * However when actually stacking windows on the screen, the - * layer overrides the stack_position; windows are first sorted - * by layer, then by stack_position within each layer. - */ - -#include "core/display-private.h" - -/** - * A sorted list of windows bearing some level of resemblance to the stack of - * windows on the X server. - * - * (This is only used as a field within a MetaScreen; we treat it as a separate - * class for simplicity.) - */ -struct _MetaStack -{ - GObject parent; - - /** The MetaDisplay containing this stack. */ - MetaDisplay *display; - - /** The MetaWindows of the windows we manage, sorted in order. */ - GList *sorted; - - /** - * If this is zero, the local stack oughtn't to be brought up to date with - * the X server's stack, because it is in the middle of being updated. - * If it is positive, the local stack is said to be "frozen", and will need - * to be thawed that many times before the stack can be brought up to date - * again. You may freeze the stack with meta_stack_freeze() and thaw it - * with meta_stack_thaw(). - */ - int freeze_count; - - /** - * The last-known stack of all windows, bottom to top. We cache it here - * so that subsequent times we'll be able to do incremental moves. - */ - GArray *last_all_root_children_stacked; - - /** - * Number of stack positions; same as the length of added, but - * kept for quick reference. - */ - gint n_positions; - - /** Is the stack in need of re-sorting? */ - unsigned int need_resort : 1; - - /** - * Are the windows in the stack in need of having their - * layers recalculated? - */ - unsigned int need_relayer : 1; - - /** - * Are the windows in the stack in need of having their positions - * recalculated with respect to transiency (parent and child windows)? - */ - unsigned int need_constrain : 1; -}; - -#define META_TYPE_STACK (meta_stack_get_type ()) -G_DECLARE_FINAL_TYPE (MetaStack, meta_stack, META, STACK, GObject) - -/** - * meta_stack_new: - * @display: The MetaDisplay which will be the parent of this stack. - * - * Creates and initialises a MetaStack. - * - * Returns: The new stack. - */ -MetaStack * meta_stack_new (MetaDisplay *display); - -/** - * meta_stack_add: - * @stack: The stack to add it to - * @window: The window to add - * - * Adds a window to the local stack. It is a fatal error to call this - * function on a window which already exists on the stack of any screen. - */ -void meta_stack_add (MetaStack *stack, - MetaWindow *window); - -/** - * meta_stack_remove: - * @stack: The stack to remove it from - * @window: The window to remove - * - * Removes a window from the local stack. It is a fatal error to call this - * function on a window which exists on the stack of any screen. - */ -void meta_stack_remove (MetaStack *stack, - MetaWindow *window); -/** - * meta_stack_update_layer: - * @stack: The stack to recalculate - * @window: Dummy parameter - * - * Recalculates the correct layer for all windows in the stack, - * and moves them about accordingly. - * - */ -void meta_stack_update_layer (MetaStack *stack, - MetaWindow *window); - -/** - * meta_stack_update_transient: - * @stack: The stack to recalculate - * @window: Dummy parameter - * - * Recalculates the correct stacking order for all windows in the stack - * according to their transience, and moves them about accordingly. - * - * FIXME: What's with the dummy parameter? - */ -void meta_stack_update_transient (MetaStack *stack, - MetaWindow *window); - -/** - * meta_stack_raise: - * @stack: The stack to modify. - * @window: The window that's making an ascension. - * (Amulet of Yendor not required.) - * - * Move a window to the top of its layer. - */ -void meta_stack_raise (MetaStack *stack, - MetaWindow *window); -/** - * meta_stack_lower: - * @stack: The stack to modify. - * @window: The window that's on the way downwards. - * - * Move a window to the bottom of its layer. - */ -void meta_stack_lower (MetaStack *stack, - MetaWindow *window); - -/** - * meta_stack_freeze: - * @stack: The stack to freeze. - * - * Prevent syncing to server until the next call of meta_stack_thaw(), - * so that we can carry out multiple operations in one go without having - * everything halfway reflected on the X server. - * - * (Calls to meta_stack_freeze() nest, so that multiple calls to - * meta_stack_freeze will require multiple calls to meta_stack_thaw().) - */ -void meta_stack_freeze (MetaStack *stack); - -/** - * meta_stack_thaw: - * @stack: The stack to thaw. - * - * Undoes a meta_stack_freeze(), and processes anything which has become - * necessary during the freeze. It is an error to call this function if - * the stack has not been frozen. - */ -void meta_stack_thaw (MetaStack *stack); - -/** - * meta_stack_get_top: - * @stack: The stack to examine. - * - * Finds the top window on the stack. - * - * Returns: The top window on the stack, or %NULL in the vanishingly unlikely - * event that you have no windows on your screen whatsoever. - */ -MetaWindow * meta_stack_get_top (MetaStack *stack); - -/** - * meta_stack_get_bottom: - * @stack: The stack to search - * - * Finds the window at the bottom of the stack. Since that's pretty much - * always the desktop, this isn't the most useful of functions, and nobody - * actually calls it. We should probably get rid of it. - */ -MetaWindow * meta_stack_get_bottom (MetaStack *stack); - -/** - * meta_stack_get_above: - * @stack: The stack to search. - * @window: The window to look above. - * @only_within_layer: If %TRUE, will return %NULL if @window is the - * top window in its layer. - * - * Finds the window above a given window in the stack. - * It is not an error to pass in a window which does not exist in - * the stack; the function will merely return %NULL. - * - * Returns: %NULL if there is no such window; - * the window above @window otherwise. - */ -MetaWindow * meta_stack_get_above (MetaStack *stack, - MetaWindow *window, - gboolean only_within_layer); - -/** - * meta_stack_get_below: - * @stack: The stack to search. - * @window: The window to look below. - * @only_within_layer: If %TRUE, will return %NULL if window is the - * bottom window in its layer. - * - * Finds the window below a given window in the stack. - * It is not an error to pass in a window which does not exist in - * the stack; the function will merely return %NULL. - * - * - * Returns: %NULL if there is no such window; - * the window below @window otherwise. - */ -MetaWindow * meta_stack_get_below (MetaStack *stack, - MetaWindow *window, - gboolean only_within_layer); - -/** - * meta_stack_get_default_focus_window: - * @stack: The stack to search. - * @workspace: %NULL to search all workspaces; otherwise only windows - * from that workspace will be returned. - * @not_this_one: Window to ignore because it's being unfocussed or - * going away. - * - * Find the topmost, focusable, mapped, window in a stack. If you supply - * a window as @not_this_one, we won't return that one (presumably - * because it's going to be going away). But if you do supply @not_this_one - * and we find its parent, we'll return that; and if @not_this_one is in - * a group, we'll return the top window of that group. - * - * Also, we are prejudiced against dock windows. Every kind of window, even - * the desktop, will be returned in preference to a dock window. - * - * Returns: The window matching all these constraints or %NULL if none does. - */ -MetaWindow * meta_stack_get_default_focus_window (MetaStack *stack, - MetaWorkspace *workspace, - MetaWindow *not_this_one); - -/** - * meta_stack_get_default_focus_window_at_point: - * @stack: The stack to search. - * @workspace: %NULL to search all workspaces; otherwise only windows - * from that workspace will be returned. - * @not_this_one: Window to ignore because it's being unfocussed or - * going away. - * @root_x: The returned window must contain this point, - * unless it's a dock. - * @root_y: See root_x. - * - * Find the topmost, focusable, mapped, window in a stack. If you supply - * a window as @not_this_one, we won't return that one (presumably - * because it's going to be going away). But if you do supply @not_this_one - * and we find its parent, we'll return that; and if @not_this_one is in - * a group, we'll return the top window of that group. - * - * Also, we are prejudiced against dock windows. Every kind of window, even - * the desktop, will be returned in preference to a dock window. - * - * Returns: The window matching all these constraints or %NULL if none does. - */ -MetaWindow * meta_stack_get_default_focus_window_at_point (MetaStack *stack, - MetaWorkspace *workspace, - MetaWindow *not_this_one, - int root_x, - int root_y); - -/** - * meta_stack_get_default_focus_candidates: - * @stack: The stack to examine. - * @workspace: If not %NULL, only windows on this workspace will be - * returned; otherwise all windows in the stack will be - * returned. - * - * Returns all the focus candidate windows in the stack, in order. - * - * Returns: (transfer container) (element-type Meta.Window): - * A #GList of #MetaWindow, in stacking order, honouring layers. - */ -GList * meta_stack_get_default_focus_candidates (MetaStack *stack, - MetaWorkspace *workspace); - -/** - * meta_stack_list_windows: - * @stack: The stack to examine. - * @workspace: If not %NULL, only windows on this workspace will be - * returned; otherwise all windows in the stack will be - * returned. - * - * Finds all the windows in the stack, in order. - * - * Returns: (transfer container) (element-type Meta.Window): - * A list of windows, in stacking order, honouring layers. - */ -GList * meta_stack_list_windows (MetaStack *stack, - MetaWorkspace *workspace); - -/** - * meta_stack_windows_cmp: - * @stack: A stack containing both window_a and window_b - * @window_a: A window - * @window_b Another window - * - * Comparison function for windows within a stack. This is not directly - * suitable for use within a standard comparison routine, because it takes - * an extra parameter; you will need to wrap it. - * - * (FIXME: We could remove the stack parameter and use the stack of - * the screen of window A, and complain if the stack of the screen of - * window B differed; then this would be a usable general comparison function.) - * - * (FIXME: Apparently identical to compare_window_position(). Merge them.) - * - * \return -1 if window_a is below window_b, honouring layers; 1 if it's - * above it; 0 if you passed in the same window twice! - */ -int meta_stack_windows_cmp (MetaStack *stack, - MetaWindow *window_a, - MetaWindow *window_b); - -/** - * meta_window_set_stack_position: - * @window: The window which is moving. - * @position: Where it should move to (0 is the bottom). - * - * Sets the position of a window within the stack. This will only move it - * up or down within its layer. It is an error to attempt to move this - * below position zero or above the last position in the stack (however, since - * we don't provide a simple way to tell the number of windows in the stack, - * this requirement may not be easy to fulfil). - */ -void meta_window_set_stack_position (MetaWindow *window, - int position); - -/** - * meta_stack_get_positions: - * @stack: The stack to examine. - * - * Returns the current stack state, allowing rudimentary transactions. - * - * Returns: (transfer container) (element-type Meta.Window): - * An opaque #GList representing the current stack sort order; - * it is the caller's responsibility to free it. - * Pass this to meta_stack_set_positions() later if you want to restore - * the state to where it was when you called this function. - */ -GList * meta_stack_get_positions (MetaStack *stack); - -/** - * meta_stack_set_positions: - * @stack: The stack to roll back. - * @windows: The list returned from meta_stack_get_positions(). - * - * Rolls back a transaction, given the list returned from - * meta_stack_get_positions(). - * - */ -void meta_stack_set_positions (MetaStack *stack, - GList *windows); - -void meta_stack_update_window_tile_matches (MetaStack *stack, - MetaWorkspace *workspace); -#endif diff --git a/src/core/startup-notification-private.h b/src/core/startup-notification-private.h deleted file mode 100644 index 5bdf2ac1a..000000000 --- a/src/core/startup-notification-private.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2001, 2002 Havoc Pennington - * Copyright (C) 2002, 2003 Red Hat Inc. - * Some ICCCM manager selection code derived from fvwm2, - * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team - * Copyright (C) 2003 Rob Adams - * Copyright (C) 2004-2006 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef META_STARTUP_NOTIFICATION_PRIVATE_H -#define META_STARTUP_NOTIFICATION_PRIVATE_H - -#include "core/display-private.h" -#include "meta/meta-startup-notification.h" - -struct _MetaStartupSequenceClass -{ - GObjectClass parent_class; - - void (* complete) (MetaStartupSequence *sequence); -}; - -MetaStartupNotification * - meta_startup_notification_new (MetaDisplay *display); - -gboolean meta_startup_notification_handle_xevent (MetaStartupNotification *sn, - XEvent *xevent); - -void meta_startup_notification_add_sequence (MetaStartupNotification *sn, - MetaStartupSequence *seq); -void meta_startup_notification_remove_sequence (MetaStartupNotification *sn, - MetaStartupSequence *seq); -MetaStartupSequence * - meta_startup_notification_lookup_sequence (MetaStartupNotification *sn, - const gchar *id); - -#endif /* META_STARTUP_NOTIFICATION_PRIVATE_H */ diff --git a/src/core/startup-notification.c b/src/core/startup-notification.c deleted file mode 100644 index 85a14e939..000000000 --- a/src/core/startup-notification.c +++ /dev/null @@ -1,696 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2001, 2002 Havoc Pennington - * Copyright (C) 2002, 2003 Red Hat Inc. - * Some ICCCM manager selection code derived from fvwm2, - * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team - * Copyright (C) 2003 Rob Adams - * Copyright (C) 2004-2006 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "config.h" - -#include <glib-object.h> - -#include "core/display-private.h" -#include "core/startup-notification-private.h" -#include "core/util-private.h" -#include "meta/meta-x11-errors.h" -#include "x11/meta-x11-display-private.h" - -/* This should be fairly long, as it should never be required unless - * apps or .desktop files are buggy, and it's confusing if - * OpenOffice or whatever seems to stop launching - people - * might decide they need to launch it again. - */ -#define STARTUP_TIMEOUT_MS 15000 - -enum -{ - PROP_0, - PROP_DISPLAY, - N_PROPS -}; - -enum -{ - PROP_SEQ_0, - PROP_SEQ_ID, - PROP_SEQ_TIMESTAMP, - PROP_SEQ_ICON_NAME, - PROP_SEQ_APPLICATION_ID, - PROP_SEQ_WMCLASS, - PROP_SEQ_WORKSPACE, - PROP_SEQ_NAME, - N_SEQ_PROPS -}; - -enum -{ - SEQ_COMPLETE, - SEQ_TIMEOUT, - N_SEQ_SIGNALS -}; - -enum -{ - CHANGED, - N_SIGNALS -}; - -static guint sn_signals[N_SIGNALS]; -static GParamSpec *sn_props[N_PROPS]; -static guint seq_signals[N_SEQ_SIGNALS]; -static GParamSpec *seq_props[N_SEQ_PROPS]; - -typedef struct -{ - GSList *list; - gint64 now; -} CollectTimedOutData; - -struct _MetaStartupNotification -{ - GObject parent_instance; - MetaDisplay *display; - - - GSList *startup_sequences; - guint startup_sequence_timeout; -}; - -typedef struct { - char *wmclass; - char *name; - char *application_id; - char *icon_name; - char *id; - uint64_t timestamp; - int workspace; - uint completed : 1; -} MetaStartupSequencePrivate; - -G_DEFINE_TYPE (MetaStartupNotification, - meta_startup_notification, - G_TYPE_OBJECT) -G_DEFINE_TYPE_WITH_PRIVATE (MetaStartupSequence, - meta_startup_sequence, - G_TYPE_OBJECT) - - -static void meta_startup_notification_ensure_timeout (MetaStartupNotification *sn); - -static gboolean -meta_startup_notification_has_pending_sequences (MetaStartupNotification *sn) -{ - GSList *l; - - for (l = sn->startup_sequences; l; l = l->next) - { - if (!meta_startup_sequence_get_completed (l->data)) - return TRUE; - } - - return FALSE; -} - -static void -meta_startup_notification_update_feedback (MetaStartupNotification *sn) -{ - MetaDisplay *display = sn->display; - - if (meta_startup_notification_has_pending_sequences (sn)) - { - meta_topic (META_DEBUG_STARTUP, - "Setting busy cursor"); - meta_display_set_cursor (display, META_CURSOR_BUSY); - } - else - { - meta_topic (META_DEBUG_STARTUP, - "Setting default cursor"); - meta_display_set_cursor (display, META_CURSOR_DEFAULT); - } -} - -static void -meta_startup_sequence_init (MetaStartupSequence *seq) -{ -} - -static void -meta_startup_sequence_finalize (GObject *object) -{ - MetaStartupSequence *seq; - MetaStartupSequencePrivate *priv; - - seq = META_STARTUP_SEQUENCE (object); - priv = meta_startup_sequence_get_instance_private (seq); - g_free (priv->id); - g_free (priv->wmclass); - g_free (priv->name); - g_free (priv->application_id); - g_free (priv->icon_name); - - G_OBJECT_CLASS (meta_startup_sequence_parent_class)->finalize (object); -} - -static void -meta_startup_sequence_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - MetaStartupSequence *seq; - MetaStartupSequencePrivate *priv; - - seq = META_STARTUP_SEQUENCE (object); - priv = meta_startup_sequence_get_instance_private (seq); - - switch (prop_id) - { - case PROP_SEQ_ID: - priv->id = g_value_dup_string (value); - break; - case PROP_SEQ_TIMESTAMP: - priv->timestamp = g_value_get_uint64 (value); - break; - case PROP_SEQ_ICON_NAME: - priv->icon_name = g_value_dup_string (value); - break; - case PROP_SEQ_APPLICATION_ID: - priv->application_id = g_value_dup_string (value); - break; - case PROP_SEQ_WMCLASS: - priv->wmclass = g_value_dup_string (value); - break; - case PROP_SEQ_WORKSPACE: - priv->workspace = g_value_get_int (value); - break; - case PROP_SEQ_NAME: - priv->name = g_value_dup_string (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -meta_startup_sequence_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - MetaStartupSequence *seq; - MetaStartupSequencePrivate *priv; - - seq = META_STARTUP_SEQUENCE (object); - priv = meta_startup_sequence_get_instance_private (seq); - - switch (prop_id) - { - case PROP_SEQ_ID: - g_value_set_string (value, priv->id); - break; - case PROP_SEQ_TIMESTAMP: - g_value_set_uint64 (value, priv->timestamp); - break; - case PROP_SEQ_ICON_NAME: - g_value_set_string (value, priv->icon_name); - break; - case PROP_SEQ_APPLICATION_ID: - g_value_set_string (value, priv->application_id); - break; - case PROP_SEQ_WMCLASS: - g_value_set_string (value, priv->wmclass); - break; - case PROP_SEQ_WORKSPACE: - g_value_set_int (value, priv->workspace); - break; - case PROP_SEQ_NAME: - g_value_set_string (value, priv->name); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -meta_startup_sequence_class_init (MetaStartupSequenceClass *klass) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->finalize = meta_startup_sequence_finalize; - object_class->set_property = meta_startup_sequence_set_property; - object_class->get_property = meta_startup_sequence_get_property; - - seq_signals[SEQ_COMPLETE] = - g_signal_new ("complete", - META_TYPE_STARTUP_SEQUENCE, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MetaStartupSequenceClass, complete), - NULL, NULL, NULL, - G_TYPE_NONE, 0); - seq_signals[SEQ_TIMEOUT] = - g_signal_new ("timeout", - META_TYPE_STARTUP_SEQUENCE, - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); - - seq_props[PROP_SEQ_ID] = - g_param_spec_string ("id", - "ID", - "ID", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY); - seq_props[PROP_SEQ_TIMESTAMP] = - g_param_spec_uint64 ("timestamp", - "Timestamp", - "Timestamp", - 0, G_MAXUINT64, 0, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY); - seq_props[PROP_SEQ_ICON_NAME] = - g_param_spec_string ("icon-name", - "Icon name", - "Icon name", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY); - seq_props[PROP_SEQ_APPLICATION_ID] = - g_param_spec_string ("application-id", - "Application ID", - "Application ID", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY); - seq_props[PROP_SEQ_WMCLASS] = - g_param_spec_string ("wmclass", - "WM class", - "WM class", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY); - seq_props[PROP_SEQ_WORKSPACE] = - g_param_spec_int ("workspace", - "Workspace", - "Workspace", - G_MININT, G_MAXINT, -1, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY); - seq_props[PROP_SEQ_NAME] = - g_param_spec_string ("name", - "Name", - "Name", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY); - - g_object_class_install_properties (object_class, N_SEQ_PROPS, seq_props); -} - -const char * -meta_startup_sequence_get_id (MetaStartupSequence *seq) -{ - MetaStartupSequencePrivate *priv; - - g_return_val_if_fail (META_IS_STARTUP_SEQUENCE (seq), NULL); - - priv = meta_startup_sequence_get_instance_private (seq); - return priv->id; -} - -uint64_t -meta_startup_sequence_get_timestamp (MetaStartupSequence *seq) -{ - MetaStartupSequencePrivate *priv; - - g_return_val_if_fail (META_IS_STARTUP_SEQUENCE (seq), 0); - - priv = meta_startup_sequence_get_instance_private (seq); - return priv->timestamp; -} - -void -meta_startup_sequence_complete (MetaStartupSequence *seq) -{ - MetaStartupSequencePrivate *priv; - - g_return_if_fail (META_IS_STARTUP_SEQUENCE (seq)); - - priv = meta_startup_sequence_get_instance_private (seq); - if (priv->completed) - return; - - priv->completed = TRUE; - g_signal_emit (seq, seq_signals[SEQ_COMPLETE], 0); -} - -gboolean -meta_startup_sequence_get_completed (MetaStartupSequence *seq) -{ - MetaStartupSequencePrivate *priv; - - g_return_val_if_fail (META_IS_STARTUP_SEQUENCE (seq), FALSE); - - priv = meta_startup_sequence_get_instance_private (seq); - return priv->completed; -} - -const char * -meta_startup_sequence_get_name (MetaStartupSequence *seq) -{ - MetaStartupSequencePrivate *priv; - - g_return_val_if_fail (META_IS_STARTUP_SEQUENCE (seq), NULL); - - priv = meta_startup_sequence_get_instance_private (seq); - return priv->name; -} - -int -meta_startup_sequence_get_workspace (MetaStartupSequence *seq) -{ - MetaStartupSequencePrivate *priv; - - g_return_val_if_fail (META_IS_STARTUP_SEQUENCE (seq), 0); - - priv = meta_startup_sequence_get_instance_private (seq); - return priv->workspace; -} - -const char * -meta_startup_sequence_get_icon_name (MetaStartupSequence *seq) -{ - MetaStartupSequencePrivate *priv; - - g_return_val_if_fail (META_IS_STARTUP_SEQUENCE (seq), NULL); - - priv = meta_startup_sequence_get_instance_private (seq); - return priv->icon_name; -} - -const char * -meta_startup_sequence_get_application_id (MetaStartupSequence *seq) -{ - MetaStartupSequencePrivate *priv; - - g_return_val_if_fail (META_IS_STARTUP_SEQUENCE (seq), NULL); - - priv = meta_startup_sequence_get_instance_private (seq); - return priv->application_id; -} - -const char * -meta_startup_sequence_get_wmclass (MetaStartupSequence *seq) -{ - MetaStartupSequencePrivate *priv; - - g_return_val_if_fail (META_IS_STARTUP_SEQUENCE (seq), NULL); - - priv = meta_startup_sequence_get_instance_private (seq); - return priv->wmclass; -} - -static void -on_sequence_completed (MetaStartupSequence *seq, - MetaStartupNotification *sn) -{ - meta_startup_notification_update_feedback (sn); - g_signal_emit (sn, sn_signals[CHANGED], 0, seq); -} - -void -meta_startup_notification_add_sequence (MetaStartupNotification *sn, - MetaStartupSequence *seq) -{ - sn->startup_sequences = g_slist_prepend (sn->startup_sequences, - g_object_ref (seq)); - g_signal_connect (seq, "complete", - G_CALLBACK (on_sequence_completed), sn); - - meta_startup_notification_ensure_timeout (sn); - meta_startup_notification_update_feedback (sn); - - g_signal_emit (sn, sn_signals[CHANGED], 0, seq); -} - -static void -collect_timed_out_foreach (void *element, - void *data) -{ - MetaStartupSequence *sequence = element; - CollectTimedOutData *ctod = data; - gint64 elapsed, timestamp; - - timestamp = meta_startup_sequence_get_timestamp (sequence); - elapsed = ctod->now - timestamp; - - meta_topic (META_DEBUG_STARTUP, - "Sequence used %" G_GINT64_FORMAT " ms vs. %d max: %s", - elapsed, STARTUP_TIMEOUT_MS, - meta_startup_sequence_get_id (sequence)); - - if (elapsed > STARTUP_TIMEOUT_MS) - ctod->list = g_slist_prepend (ctod->list, sequence); -} - -static gboolean -startup_sequence_timeout (void *data) -{ - MetaStartupNotification *sn = data; - CollectTimedOutData ctod; - GSList *l; - - ctod.list = NULL; - ctod.now = g_get_monotonic_time () / 1000; - g_slist_foreach (sn->startup_sequences, - collect_timed_out_foreach, - &ctod); - - for (l = ctod.list; l != NULL; l = l->next) - { - MetaStartupSequence *sequence = l->data; - - meta_topic (META_DEBUG_STARTUP, - "Timed out sequence %s", - meta_startup_sequence_get_id (sequence)); - - if (!meta_startup_sequence_get_completed (sequence)) - { - g_signal_emit (sequence, seq_signals[SEQ_TIMEOUT], 0, sequence); - meta_startup_sequence_complete (sequence); - } - - meta_startup_notification_remove_sequence (sn, sequence); - } - - g_slist_free (ctod.list); - - if (sn->startup_sequences != NULL) - { - return TRUE; - } - else - { - /* remove */ - sn->startup_sequence_timeout = 0; - return FALSE; - } -} - -static void -meta_startup_notification_ensure_timeout (MetaStartupNotification *sn) -{ - if (sn->startup_sequence_timeout != 0) - return; - - /* our timeout just polls every second, instead of bothering - * to compute exactly when we may next time out - */ - sn->startup_sequence_timeout = g_timeout_add_seconds (1, - startup_sequence_timeout, - sn); - g_source_set_name_by_id (sn->startup_sequence_timeout, - "[mutter] startup_sequence_timeout"); -} - -void -meta_startup_notification_remove_sequence (MetaStartupNotification *sn, - MetaStartupSequence *seq) -{ - sn->startup_sequences = g_slist_remove (sn->startup_sequences, seq); - meta_startup_notification_update_feedback (sn); - - g_signal_handlers_disconnect_by_func (seq, on_sequence_completed, sn); - - if (sn->startup_sequences == NULL) - g_clear_handle_id (&sn->startup_sequence_timeout, g_source_remove); - - g_signal_emit (sn, sn_signals[CHANGED], 0, seq); - g_object_unref (seq); -} - -MetaStartupSequence * -meta_startup_notification_lookup_sequence (MetaStartupNotification *sn, - const gchar *id) -{ - MetaStartupSequence *seq; - const gchar *seq_id; - GSList *l; - - for (l = sn->startup_sequences; l; l = l->next) - { - seq = l->data; - seq_id = meta_startup_sequence_get_id (seq); - - if (g_str_equal (seq_id, id)) - return l->data; - } - - return NULL; -} - -static void -meta_startup_notification_init (MetaStartupNotification *sn) -{ -} - -static void -meta_startup_notification_finalize (GObject *object) -{ - MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object); - - g_clear_handle_id (&sn->startup_sequence_timeout, g_source_remove); - - g_slist_free_full (sn->startup_sequences, g_object_unref); - sn->startup_sequences = NULL; - - G_OBJECT_CLASS (meta_startup_notification_parent_class)->finalize (object); -} - -static void -meta_startup_notification_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object); - - switch (prop_id) - { - case PROP_DISPLAY: - sn->display = g_value_get_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -meta_startup_notification_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object); - - switch (prop_id) - { - case PROP_DISPLAY: - g_value_set_object (value, sn->display); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -meta_startup_notification_constructed (GObject *object) -{ - MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object); - - g_assert (sn->display != NULL); - - sn->startup_sequences = NULL; - sn->startup_sequence_timeout = 0; -} - -static void -meta_startup_notification_class_init (MetaStartupNotificationClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->constructed = meta_startup_notification_constructed; - object_class->finalize = meta_startup_notification_finalize; - object_class->set_property = meta_startup_notification_set_property; - object_class->get_property = meta_startup_notification_get_property; - - sn_props[PROP_DISPLAY] = - g_param_spec_object ("display", - "Display", - "Display", - META_TYPE_DISPLAY, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - sn_signals[CHANGED] = - g_signal_new ("changed", - META_TYPE_STARTUP_NOTIFICATION, - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 1, G_TYPE_POINTER); - - g_object_class_install_properties (object_class, N_PROPS, sn_props); -} - -MetaStartupNotification * -meta_startup_notification_new (MetaDisplay *display) -{ - return g_object_new (META_TYPE_STARTUP_NOTIFICATION, - "display", display, - NULL); -} - -GSList * -meta_startup_notification_get_sequences (MetaStartupNotification *sn) -{ - return sn->startup_sequences; -} - -/** - * meta_startup_notification_create_launcher: - * @sn: a #MetaStartupNotification - * - * Creates an app launch context. - * - * Returns: (transfer full): a launch context. - **/ -MetaLaunchContext * -meta_startup_notification_create_launcher (MetaStartupNotification *sn) -{ - return g_object_new (META_TYPE_LAUNCH_CONTEXT, - "display", sn->display, - NULL); -} diff --git a/src/core/util-private.h b/src/core/util-private.h deleted file mode 100644 index 01b663966..000000000 --- a/src/core/util-private.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Mutter utilities */ - -/* - * Copyright (C) 2001 Havoc Pennington - * Copyright (C) 2005 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#ifndef META_UTIL_PRIVATE_H -#define META_UTIL_PRIVATE_H - -#include <glib/gi18n-lib.h> - -#include "meta/util.h" -#include "meta/common.h" - -/* META_EXPORT_TEST should be used to export symbols that are exported only - * for testability purposes */ -#define META_EXPORT_TEST META_EXPORT - -void meta_set_verbose (gboolean setting); -void meta_set_debugging (gboolean setting); - -META_EXPORT_TEST -void meta_set_syncing (gboolean setting); - -void meta_set_replace_current_wm (gboolean setting); -void meta_set_is_wayland_compositor (gboolean setting); - -char * meta_generate_random_id (GRand *rand, - int length); - -void meta_init_debug_utils (void); - -#define META_POINT_IN_RECT(xcoord, ycoord, rect) \ - ((xcoord) >= (rect).x && \ - (xcoord) < ((rect).x + (rect).width) && \ - (ycoord) >= (rect).y && \ - (ycoord) < ((rect).y + (rect).height)) - -void meta_get_clutter_debug_flags (ClutterDebugFlag *debug_flags, - ClutterDrawDebugFlag *draw_flags, - ClutterPickDebugFlag *pick_flags); - -#endif diff --git a/src/core/util.c b/src/core/util.c deleted file mode 100644 index 74a884c80..000000000 --- a/src/core/util.c +++ /dev/null @@ -1,796 +0,0 @@ -/* - * Copyright (C) 2001 Havoc Pennington - * Copyright (C) 2005 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -/** - * SECTION:util - * @title: Utility functions - * @short_description: Miscellaneous utility functions - */ - -#define _POSIX_C_SOURCE 200112L /* for fdopen() */ - -#include "config.h" - -#include "core/util-private.h" - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <errno.h> -#include <string.h> -#include <X11/Xlib.h> /* must explicitly be included for Solaris; #326746 */ -#include <X11/Xutil.h> /* Just for the definition of the various gravities */ - -#ifdef HAVE_SYS_PRCTL -#include <sys/prctl.h> -#endif - -#include "clutter/clutter-mutter.h" -#include "cogl/cogl.h" -#include "meta/common.h" -#include "meta/main.h" - -static const GDebugKey meta_debug_keys[] = { - { "focus", META_DEBUG_FOCUS }, - { "workarea", META_DEBUG_WORKAREA }, - { "stack", META_DEBUG_STACK }, - { "sm", META_DEBUG_SM }, - { "events", META_DEBUG_EVENTS }, - { "window-state", META_DEBUG_WINDOW_STATE }, - { "window-ops", META_DEBUG_WINDOW_OPS }, - { "geometry", META_DEBUG_GEOMETRY }, - { "placement", META_DEBUG_PLACEMENT }, - { "ping", META_DEBUG_PING }, - { "keybindings", META_DEBUG_KEYBINDINGS }, - { "sync", META_DEBUG_SYNC }, - { "startup", META_DEBUG_STARTUP }, - { "prefs", META_DEBUG_PREFS }, - { "groups", META_DEBUG_GROUPS }, - { "resizing", META_DEBUG_RESIZING }, - { "shapes", META_DEBUG_SHAPES }, - { "edge-resistance", META_DEBUG_EDGE_RESISTANCE }, - { "dbus", META_DEBUG_DBUS }, - { "input", META_DEBUG_INPUT }, - { "wayland", META_DEBUG_WAYLAND }, - { "kms", META_DEBUG_KMS }, - { "screen-cast", META_DEBUG_SCREEN_CAST }, - { "remote-desktop", META_DEBUG_REMOTE_DESKTOP }, - { "backend", META_DEBUG_BACKEND }, -}; - -#ifdef WITH_VERBOSE_MODE -static void -meta_topic_real_valist (MetaDebugTopic topic, - const char *format, - va_list args) G_GNUC_PRINTF(2, 0); -#endif - -static gint verbose_topics = 0; -static int no_prefix = 0; -static gboolean is_wayland_compositor = FALSE; -static int debug_paint_flags = 0; - -#ifdef WITH_VERBOSE_MODE -static FILE* logfile = NULL; - -static void -ensure_logfile (void) -{ - if (logfile == NULL && g_getenv ("MUTTER_USE_LOGFILE")) - { - char *filename = NULL; - char *tmpl; - int fd; - GError *err; - - tmpl = g_strdup_printf ("mutter-%d-debug-log-XXXXXX", - (int) getpid ()); - - err = NULL; - fd = g_file_open_tmp (tmpl, - &filename, - &err); - - g_free (tmpl); - - if (err != NULL) - { - meta_warning ("Failed to open debug log: %s", - err->message); - g_error_free (err); - return; - } - - logfile = fdopen (fd, "w"); - - if (logfile == NULL) - { - meta_warning ("Failed to fdopen() log file %s: %s", - filename, strerror (errno)); - close (fd); - } - else - { - g_printerr ("Opened log file %s", filename); - } - - g_free (filename); - } -} -#endif - -gboolean -meta_is_verbose (void) -{ - return verbose_topics != 0; -} - -void -meta_set_verbose (gboolean setting) -{ -#ifndef WITH_VERBOSE_MODE - if (setting) - meta_fatal (_("Mutter was compiled without support for verbose mode")); -#endif - - if (setting) - meta_add_verbose_topic (META_DEBUG_VERBOSE); - else - meta_remove_verbose_topic (META_DEBUG_VERBOSE); -} - -/** - * meta_add_verbose_topic: - * @topic: Topic for which logging will be started - * - * Ensure log messages for the given topic @topic - * will be printed. - */ -void -meta_add_verbose_topic (MetaDebugTopic topic) -{ - if (verbose_topics == META_DEBUG_VERBOSE) - return; - - ensure_logfile (); - - if (topic == META_DEBUG_VERBOSE) - verbose_topics = META_DEBUG_VERBOSE; - else - verbose_topics |= topic; -} - -/** - * meta_remove_verbose_topic: - * @topic: Topic for which logging will be stopped - * - * Stop printing log messages for the given topic @topic. Note - * that this method does not stack with meta_add_verbose_topic(); - * i.e. if two calls to meta_add_verbose_topic() for the same - * topic are made, one call to meta_remove_verbose_topic() will - * remove it. - */ -void -meta_remove_verbose_topic (MetaDebugTopic topic) -{ - if (topic == META_DEBUG_VERBOSE) - verbose_topics = 0; - else - verbose_topics &= ~topic; -} - -void -meta_init_debug_utils (void) -{ - const char *debug_env; - -#ifdef HAVE_SYS_PRCTL - prctl (PR_SET_DUMPABLE, 1); -#endif - - if (g_getenv ("MUTTER_VERBOSE")) - meta_set_verbose (TRUE); - - debug_env = g_getenv ("MUTTER_DEBUG"); - if (debug_env) - { - MetaDebugTopic topics; - - topics = g_parse_debug_string (debug_env, - meta_debug_keys, - G_N_ELEMENTS (meta_debug_keys)); - meta_add_verbose_topic (topics); - } -} - -gboolean -meta_is_wayland_compositor (void) -{ - return is_wayland_compositor; -} - -void -meta_set_is_wayland_compositor (gboolean value) -{ - is_wayland_compositor = value; -} - -char * -meta_g_utf8_strndup (const gchar *src, - gsize n) -{ - const gchar *s = src; - while (n && *s) - { - s = g_utf8_next_char (s); - n--; - } - - return g_strndup (src, s - src); -} - -static int -utf8_fputs (const char *str, - FILE *f) -{ - char *l; - int retval; - - l = g_locale_from_utf8 (str, -1, NULL, NULL, NULL); - - if (l == NULL) - retval = fputs (str, f); /* just print it anyway, better than nothing */ - else - retval = fputs (l, f); - - g_free (l); - - return retval; -} - -#ifdef WITH_VERBOSE_MODE -void -meta_verbose_real (const char *format, ...) -{ - va_list args; - - va_start (args, format); - meta_topic_real_valist (META_DEBUG_VERBOSE, format, args); - va_end (args); -} - -static const char* -topic_name (MetaDebugTopic topic) -{ - switch (topic) - { - case META_DEBUG_FOCUS: - return "FOCUS"; - case META_DEBUG_WORKAREA: - return "WORKAREA"; - case META_DEBUG_STACK: - return "STACK"; - case META_DEBUG_SM: - return "SM"; - case META_DEBUG_EVENTS: - return "EVENTS"; - case META_DEBUG_WINDOW_STATE: - return "WINDOW_STATE"; - case META_DEBUG_WINDOW_OPS: - return "WINDOW_OPS"; - case META_DEBUG_PLACEMENT: - return "PLACEMENT"; - case META_DEBUG_GEOMETRY: - return "GEOMETRY"; - case META_DEBUG_PING: - return "PING"; - case META_DEBUG_KEYBINDINGS: - return "KEYBINDINGS"; - case META_DEBUG_SYNC: - return "SYNC"; - case META_DEBUG_STARTUP: - return "STARTUP"; - case META_DEBUG_PREFS: - return "PREFS"; - case META_DEBUG_GROUPS: - return "GROUPS"; - case META_DEBUG_RESIZING: - return "RESIZING"; - case META_DEBUG_SHAPES: - return "SHAPES"; - case META_DEBUG_EDGE_RESISTANCE: - return "EDGE_RESISTANCE"; - case META_DEBUG_DBUS: - return "DBUS"; - case META_DEBUG_INPUT: - return "INPUT"; - case META_DEBUG_KMS: - return "KMS"; - case META_DEBUG_SCREEN_CAST: - return "SCREEN_CAST"; - case META_DEBUG_REMOTE_DESKTOP: - return "REMOTE_DESKTOP"; - case META_DEBUG_BACKEND: - return "BACKEND"; - case META_DEBUG_VERBOSE: - return "VERBOSE"; - case META_DEBUG_WAYLAND: - return "WAYLAND"; - } - - return "WM"; -} - -static int sync_count = 0; - -gboolean -meta_is_topic_enabled (MetaDebugTopic topic) -{ - if (verbose_topics == 0) - return FALSE; - - if (topic == META_DEBUG_VERBOSE && verbose_topics != META_DEBUG_VERBOSE) - return FALSE; - - return !!(verbose_topics & topic); -} - -static void -meta_topic_real_valist (MetaDebugTopic topic, - const char *format, - va_list args) -{ - gchar *str; - FILE *out; - - g_return_if_fail (format != NULL); - - if (!meta_is_topic_enabled (topic)) - return; - - str = g_strdup_vprintf (format, args); - - out = logfile ? logfile : stderr; - - if (no_prefix == 0) - fprintf (out, "%s: ", topic_name (topic)); - - if (topic == META_DEBUG_SYNC) - { - ++sync_count; - fprintf (out, "%d: ", sync_count); - } - - utf8_fputs (str, out); - utf8_fputs ("\n", out); - - fflush (out); - - g_free (str); -} - -void -meta_topic_real (MetaDebugTopic topic, - const char *format, - ...) -{ - va_list args; - - va_start (args, format); - meta_topic_real_valist (topic, format, args); - va_end (args); -} -#endif /* WITH_VERBOSE_MODE */ - -void -meta_bug (const char *format, ...) -{ - va_list args; - gchar *str; - FILE *out; - - g_return_if_fail (format != NULL); - - va_start (args, format); - str = g_strdup_vprintf (format, args); - va_end (args); - -#ifdef WITH_VERBOSE_MODE - out = logfile ? logfile : stderr; -#else - out = stderr; -#endif - - if (no_prefix == 0) - utf8_fputs ("Bug in window manager: ", out); - utf8_fputs (str, out); - utf8_fputs ("\n", out); - - fflush (out); - - g_free (str); - - /* stop us in a debugger */ - abort (); -} - -void -meta_warning (const char *format, ...) -{ - va_list args; - gchar *str; - FILE *out; - - g_return_if_fail (format != NULL); - - va_start (args, format); - str = g_strdup_vprintf (format, args); - va_end (args); - -#ifdef WITH_VERBOSE_MODE - out = logfile ? logfile : stderr; -#else - out = stderr; -#endif - - if (no_prefix == 0) - utf8_fputs ("Window manager warning: ", out); - utf8_fputs (str, out); - utf8_fputs ("\n", out); - - fflush (out); - - g_free (str); -} - -void -meta_fatal (const char *format, ...) -{ - va_list args; - gchar *str; - FILE *out; - - g_warn_if_fail (format); - if (!format) - meta_exit (META_EXIT_ERROR); - - va_start (args, format); - str = g_strdup_vprintf (format, args); - va_end (args); - -#ifdef WITH_VERBOSE_MODE - out = logfile ? logfile : stderr; -#else - out = stderr; -#endif - - if (no_prefix == 0) - utf8_fputs ("Window manager error: ", out); - utf8_fputs (str, out); - utf8_fputs ("\n", out); - - fflush (out); - - g_free (str); - - meta_exit (META_EXIT_ERROR); -} - -void -meta_push_no_msg_prefix (void) -{ - ++no_prefix; -} - -void -meta_pop_no_msg_prefix (void) -{ - g_return_if_fail (no_prefix > 0); - - --no_prefix; -} - -void -meta_exit (MetaExitCode code) -{ - - exit (code); -} - -gint -meta_unsigned_long_equal (gconstpointer v1, - gconstpointer v2) -{ - return *((const gulong*) v1) == *((const gulong*) v2); -} - -guint -meta_unsigned_long_hash (gconstpointer v) -{ - gulong val = * (const gulong *) v; - - /* I'm not sure this works so well. */ -#if GLIB_SIZEOF_LONG > 4 - return (guint) (val ^ (val >> 32)); -#else - return val; -#endif -} - -const char* -meta_gravity_to_string (MetaGravity gravity) -{ - switch (gravity) - { - case META_GRAVITY_NORTH_WEST: - return "META_GRAVITY_NORTH_WEST"; - break; - case META_GRAVITY_NORTH: - return "META_GRAVITY_NORTH"; - break; - case META_GRAVITY_NORTH_EAST: - return "META_GRAVITY_NORTH_EAST"; - break; - case META_GRAVITY_WEST: - return "META_GRAVITY_WEST"; - break; - case META_GRAVITY_CENTER: - return "META_GRAVITY_CENTER"; - break; - case META_GRAVITY_EAST: - return "META_GRAVITY_EAST"; - break; - case META_GRAVITY_SOUTH_WEST: - return "META_GRAVITY_SOUTH_WEST"; - break; - case META_GRAVITY_SOUTH: - return "META_GRAVITY_SOUTH"; - break; - case META_GRAVITY_SOUTH_EAST: - return "META_GRAVITY_SOUTH_EAST"; - break; - case META_GRAVITY_STATIC: - return "META_GRAVITY_STATIC"; - break; - default: - return "META_GRAVITY_NORTH_WEST"; - break; - } -} - -char* -meta_external_binding_name_for_action (guint keybinding_action) -{ - return g_strdup_printf ("external-grab-%u", keybinding_action); -} - -/* Command line arguments are passed in the locale encoding; in almost - * all cases, we'd hope that is UTF-8 and no conversion is necessary. - * If it's not UTF-8, then it's possible that the message isn't - * representable in the locale encoding. - */ -static void -append_argument (GPtrArray *args, - const char *arg) -{ - char *locale_arg = g_locale_from_utf8 (arg, -1, NULL, NULL, NULL); - - /* This is cheesy, but it's better to have a few ???'s in the dialog - * for an unresponsive application than no dialog at all appear */ - if (!locale_arg) - locale_arg = g_strdup ("???"); - - g_ptr_array_add (args, locale_arg); -} - -/** - * meta_show_dialog: (skip) - * @type: type of dialog - * @message: message - * @timeout: timeout - * @display: display - * @ok_text: text for Ok button - * @cancel_text: text for Cancel button - * @icon_name: icon name - * @transient_for: window XID of parent - * @columns: columns - * @entries: entries - * - */ -GPid -meta_show_dialog (const char *type, - const char *message, - const char *timeout, - const char *display, - const char *ok_text, - const char *cancel_text, - const char *icon_name, - const int transient_for, - GSList *columns, - GSList *entries) -{ - GError *error = NULL; - GSList *tmp; - GPid child_pid; - GPtrArray *args; - - args = g_ptr_array_new (); - - append_argument (args, "zenity"); - append_argument (args, type); - - if (display) - { - append_argument (args, "--display"); - append_argument (args, display); - } - - append_argument (args, "--class"); - append_argument (args, "mutter-dialog"); - append_argument (args, "--title"); - append_argument (args, ""); - append_argument (args, "--text"); - append_argument (args, message); - - if (timeout) - { - append_argument (args, "--timeout"); - append_argument (args, timeout); - } - - if (ok_text) - { - append_argument (args, "--ok-label"); - append_argument (args, ok_text); - } - - if (cancel_text) - { - append_argument (args, "--cancel-label"); - append_argument (args, cancel_text); - } - - if (icon_name) - { - append_argument (args, "--icon-name"); - append_argument (args, icon_name); - } - - tmp = columns; - while (tmp) - { - append_argument (args, "--column"); - append_argument (args, tmp->data); - tmp = tmp->next; - } - - tmp = entries; - while (tmp) - { - append_argument (args, tmp->data); - tmp = tmp->next; - } - - if (transient_for) - { - gchar *env = g_strdup_printf("%d", transient_for); - setenv ("WINDOWID", env, 1); - g_free (env); - - append_argument (args, "--modal"); - } - - g_ptr_array_add (args, NULL); /* NULL-terminate */ - - g_spawn_async ( - "/", - (gchar**) args->pdata, - NULL, - G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, - NULL, NULL, - &child_pid, - &error - ); - - if (transient_for) - unsetenv ("WINDOWID"); - - g_ptr_array_free (args, TRUE); - - if (error) - { - meta_warning ("%s", error->message); - g_error_free (error); - } - - return child_pid; -} - -MetaLocaleDirection -meta_get_locale_direction (void) -{ - switch (gtk_get_locale_direction ()) - { - case GTK_TEXT_DIR_LTR: - return META_LOCALE_DIRECTION_LTR; - case GTK_TEXT_DIR_RTL: - return META_LOCALE_DIRECTION_RTL; - default: - g_assert_not_reached (); - return 0; - } -} - -char * -meta_generate_random_id (GRand *rand, - int length) -{ - char *id; - int i; - - /* Generate a random string of printable ASCII characters. */ - - id = g_new0 (char, length + 1); - for (i = 0; i < length; i++) - id[i] = (char) g_rand_int_range (rand, 32, 127); - - return id; -} - - -void -meta_add_clutter_debug_flags (ClutterDebugFlag debug_flags, - ClutterDrawDebugFlag draw_flags, - ClutterPickDebugFlag pick_flags) -{ - clutter_add_debug_flags (debug_flags, draw_flags, pick_flags); -} - -void -meta_remove_clutter_debug_flags (ClutterDebugFlag debug_flags, - ClutterDrawDebugFlag draw_flags, - ClutterPickDebugFlag pick_flags) -{ - clutter_remove_debug_flags (debug_flags, draw_flags, pick_flags); -} - -void -meta_get_clutter_debug_flags (ClutterDebugFlag *debug_flags, - ClutterDrawDebugFlag *draw_flags, - ClutterPickDebugFlag *pick_flags) -{ - clutter_get_debug_flags (debug_flags, draw_flags, pick_flags); -} - -void -meta_add_debug_paint_flag (MetaDebugPaintFlag flag) -{ - debug_paint_flags |= flag; -} - -void -meta_remove_debug_paint_flag (MetaDebugPaintFlag flag) -{ - debug_paint_flags &= ~flag; -} - -MetaDebugPaintFlag -meta_get_debug_paint_flags (void) -{ - return debug_paint_flags; -} diff --git a/src/core/window-private.h b/src/core/window-private.h deleted file mode 100644 index d1730c988..000000000 --- a/src/core/window-private.h +++ /dev/null @@ -1,888 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/** - * \file window-private.h Windows which Mutter manages - * - * Managing X windows. - * This file contains methods on this class which are available to - * routines in core but not outside it. (See window.h for the routines - * which the rest of the world is allowed to use.) - */ - -/* - * Copyright (C) 2001 Havoc Pennington - * Copyright (C) 2002 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 <http://www.gnu.org/licenses/>. - */ - -#ifndef META_WINDOW_PRIVATE_H -#define META_WINDOW_PRIVATE_H - -#include <X11/Xutil.h> -#include <cairo.h> -#include <gdk-pixbuf/gdk-pixbuf.h> - -#include "backends/meta-logical-monitor.h" -#include "clutter/clutter.h" -#include "core/stack.h" -#include "meta/compositor.h" -#include "meta/meta-close-dialog.h" -#include "meta/util.h" -#include "meta/window.h" -#include "wayland/meta-wayland-types.h" -#include "x11/group-private.h" - -typedef struct _MetaWindowQueue MetaWindowQueue; - -typedef enum -{ - META_CLIENT_TYPE_UNKNOWN = 0, - META_CLIENT_TYPE_APPLICATION = 1, - META_CLIENT_TYPE_PAGER = 2, - META_CLIENT_TYPE_MAX_RECOGNIZED = 2 -} MetaClientType; - -typedef enum -{ - META_QUEUE_CALC_SHOWING = 1 << 0, - META_QUEUE_MOVE_RESIZE = 1 << 1, - META_QUEUE_UPDATE_ICON = 1 << 2, -} MetaQueueType; - -#define NUMBER_OF_QUEUES 3 - -typedef enum -{ - META_MOVE_RESIZE_CONFIGURE_REQUEST = 1 << 0, - META_MOVE_RESIZE_USER_ACTION = 1 << 1, - META_MOVE_RESIZE_MOVE_ACTION = 1 << 2, - META_MOVE_RESIZE_RESIZE_ACTION = 1 << 3, - META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE = 1 << 4, - META_MOVE_RESIZE_STATE_CHANGED = 1 << 5, - META_MOVE_RESIZE_UNMAXIMIZE = 1 << 6, - META_MOVE_RESIZE_UNFULLSCREEN = 1 << 7, - META_MOVE_RESIZE_FORCE_MOVE = 1 << 8, - META_MOVE_RESIZE_WAYLAND_STATE_CHANGED = 1 << 9, - META_MOVE_RESIZE_FORCE_UPDATE_MONITOR = 1 << 10, - META_MOVE_RESIZE_PLACEMENT_CHANGED = 1 << 11, -} MetaMoveResizeFlags; - -typedef enum -{ - META_MOVE_RESIZE_RESULT_MOVED = 1 << 0, - META_MOVE_RESIZE_RESULT_RESIZED = 1 << 1, - META_MOVE_RESIZE_RESULT_FRAME_SHAPE_CHANGED = 1 << 2, - META_MOVE_RESIZE_RESULT_STATE_CHANGED = 1 << 3, -} MetaMoveResizeResultFlags; - -typedef enum -{ - META_PLACEMENT_GRAVITY_NONE = 0, - META_PLACEMENT_GRAVITY_TOP = 1 << 0, - META_PLACEMENT_GRAVITY_BOTTOM = 1 << 1, - META_PLACEMENT_GRAVITY_LEFT = 1 << 2, - META_PLACEMENT_GRAVITY_RIGHT = 1 << 3, -} MetaPlacementGravity; - -typedef enum -{ - META_PLACEMENT_ANCHOR_NONE = 0, - META_PLACEMENT_ANCHOR_TOP = 1 << 0, - META_PLACEMENT_ANCHOR_BOTTOM = 1 << 1, - META_PLACEMENT_ANCHOR_LEFT = 1 << 2, - META_PLACEMENT_ANCHOR_RIGHT = 1 << 3, -} MetaPlacementAnchor; - -typedef enum -{ - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_NONE = 0, - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1 << 0, - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 1 << 1, - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X = 1 << 2, - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y = 1 << 3, - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_X = 1 << 4, - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 1 << 5, -} MetaPlacementConstraintAdjustment; - -typedef enum _MetaWindowUpdateMonitorFlags -{ - META_WINDOW_UPDATE_MONITOR_FLAGS_NONE = 0, - META_WINDOW_UPDATE_MONITOR_FLAGS_USER_OP = 1 << 0, - META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE = 1 << 1, -} MetaWindowUpdateMonitorFlags; - -typedef struct _MetaPlacementRule -{ - MetaRectangle anchor_rect; - MetaPlacementGravity gravity; - MetaPlacementAnchor anchor; - MetaPlacementConstraintAdjustment constraint_adjustment; - int offset_x; - int offset_y; - int width; - int height; - - gboolean is_reactive; - - MetaRectangle parent_rect; -} MetaPlacementRule; - -typedef enum _MetaPlacementState -{ - META_PLACEMENT_STATE_UNCONSTRAINED, - META_PLACEMENT_STATE_CONSTRAINED_PENDING, - META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED, - META_PLACEMENT_STATE_CONSTRAINED_FINISHED, - META_PLACEMENT_STATE_INVALIDATED, -} MetaPlacementState; - -typedef enum -{ - META_EDGE_CONSTRAINT_NONE = 0, - META_EDGE_CONSTRAINT_WINDOW = 1, - META_EDGE_CONSTRAINT_MONITOR = 2, -} MetaEdgeConstraint; - -typedef enum -{ - META_EDGE_RESISTANCE_DEFAULT = 0, - META_EDGE_RESISTANCE_SNAP = 1 << 0, - META_EDGE_RESISTANCE_KEYBOARD_OP = 1 << 1, - META_EDGE_RESISTANCE_WINDOWS = 1 << 2, -} MetaEdgeResistanceFlags; - -struct _MetaWindow -{ - GObject parent_instance; - - MetaDisplay *display; - uint64_t id; - guint64 stamp; - MetaLogicalMonitor *monitor; - MetaWorkspace *workspace; - MetaWindowClientType client_type; - MetaWaylandSurface *surface; - Window xwindow; - /* may be NULL! not all windows get decorated */ - MetaFrame *frame; - int depth; - Visual *xvisual; - char *desc; /* used in debug spew */ - char *title; - - cairo_surface_t *icon; - cairo_surface_t *mini_icon; - - MetaWindowType type; - - /* NOTE these five are not in UTF-8, we just treat them as random - * binary data - */ - char *res_class; - char *res_name; - char *role; - char *sm_client_id; - char *wm_client_machine; - - char *startup_id; - char *mutter_hints; - char *sandboxed_app_id; - char *gtk_theme_variant; - char *gtk_application_id; - char *gtk_unique_bus_name; - char *gtk_application_object_path; - char *gtk_window_object_path; - char *gtk_app_menu_object_path; - char *gtk_menubar_object_path; - - Window xtransient_for; - Window xgroup_leader; - Window xclient_leader; - MetaWindow *transient_for; - - /* Initial workspace property */ - int initial_workspace; - - /* Initial timestamp property */ - guint32 initial_timestamp; - - /* Whether this is an override redirect window or not */ - guint override_redirect : 1; - - /* Whether we're maximized */ - guint maximized_horizontally : 1; - guint maximized_vertically : 1; - - /* Whether we have to maximize/minimize after placement */ - guint maximize_horizontally_after_placement : 1; - guint maximize_vertically_after_placement : 1; - guint minimize_after_placement : 1; - - /* The current tile mode */ - MetaTileMode tile_mode; - - /* The last "full" maximized/unmaximized state. We need to keep track of - * that to toggle between normal/tiled or maximized/tiled states. */ - guint saved_maximize : 1; - int tile_monitor_number; - - struct { - MetaEdgeConstraint top; - MetaEdgeConstraint right; - MetaEdgeConstraint bottom; - MetaEdgeConstraint left; - } edge_constraints; - - double tile_hfraction; - - uint64_t preferred_output_winsys_id; - - /* Whether we're shaded */ - guint shaded : 1; - - /* Whether we're fullscreen */ - guint fullscreen : 1; - - /* Whether the window is marked as urgent */ - guint urgent : 1; - - /* Area to cover when in fullscreen mode. If _NET_WM_FULLSCREEN_MONITORS has - * been overridden (via a client message), the window will cover the union of - * these monitors. If not, this is the single monitor which the window's - * origin is on. */ - struct { - MetaLogicalMonitor *top; - MetaLogicalMonitor *bottom; - MetaLogicalMonitor *left; - MetaLogicalMonitor *right; - } fullscreen_monitors; - - /* Whether we're trying to constrain the window to be fully onscreen */ - guint require_fully_onscreen : 1; - - /* Whether we're trying to constrain the window to be on a single monitor */ - guint require_on_single_monitor : 1; - - /* Whether we're trying to constrain the window's titlebar to be onscreen */ - guint require_titlebar_visible : 1; - - /* Whether we're sticky in the multi-workspace sense - * (vs. the not-scroll-with-viewport sense, we don't - * have no stupid viewports) - */ - guint on_all_workspaces : 1; - - /* This is true if the client requested sticky, and implies on_all_workspaces == TRUE, - * however on_all_workspaces can be set TRUE for other internal reasons too, such as - * being override_redirect or being on the non-primary monitor. */ - guint on_all_workspaces_requested : 1; - - /* Minimize is the state controlled by the minimize button */ - guint minimized : 1; - guint tab_unminimized : 1; - - /* Whether the window is mapped; actual server-side state - * see also unmaps_pending - */ - guint mapped : 1; - - /* Whether window has been hidden from view by lowering it to the bottom - * of window stack. - */ - guint hidden : 1; - - /* Whether the compositor thinks the window is visible. - * This should match up with calls to meta_compositor_show_window / - * meta_compositor_hide_window. - */ - guint visible_to_compositor : 1; - - /* Whether the compositor knows about the window. - * This should match up with calls to meta_compositor_add_window / - * meta_compositor_remove_window. - */ - guint known_to_compositor : 1; - - /* When we next show or hide the window, what effect we should - * tell the compositor to perform. - */ - guint pending_compositor_effect : 4; /* MetaCompEffect */ - - /* Iconic is the state in WM_STATE; happens for workspaces/shading - * in addition to minimize - */ - guint iconic : 1; - /* initially_iconic is the WM_HINTS setting when we first manage - * the window. It's taken to mean initially minimized. - */ - guint initially_iconic : 1; - - /* whether an initial workspace was explicitly set */ - guint initial_workspace_set : 1; - - /* whether an initial timestamp was explicitly set */ - guint initial_timestamp_set : 1; - - /* whether net_wm_user_time has been set yet */ - guint net_wm_user_time_set : 1; - - /* whether net_wm_icon_geometry has been set */ - guint icon_geometry_set : 1; - - /* Globally active / No input */ - guint input : 1; - - /* MWM hints about features of window */ - guint mwm_decorated : 1; - guint mwm_border_only : 1; - guint mwm_has_close_func : 1; - guint mwm_has_minimize_func : 1; - guint mwm_has_maximize_func : 1; - guint mwm_has_move_func : 1; - guint mwm_has_resize_func : 1; - - /* Computed features of window */ - guint decorated : 1; - guint border_only : 1; - guint always_sticky : 1; - guint has_close_func : 1; - guint has_minimize_func : 1; - guint has_maximize_func : 1; - guint has_shade_func : 1; - guint has_move_func : 1; - guint has_resize_func : 1; - guint has_fullscreen_func : 1; - - /* Computed whether to skip taskbar or not */ - guint skip_taskbar : 1; - guint skip_pager : 1; - guint skip_from_window_list : 1; - - /* TRUE if client set these */ - guint wm_state_above : 1; - guint wm_state_below : 1; - - /* EWHH demands attention flag */ - guint wm_state_demands_attention : 1; - - /* TRUE iff window == window->display->focus_window */ - guint has_focus : 1; - - /* Have we placed this window? */ - guint placed : 1; - - /* Is this not a transient of the focus window which is being denied focus? */ - guint denied_focus_and_not_transient : 1; - - /* Has this window not ever been shown yet? */ - guint showing_for_first_time : 1; - - /* Are we in meta_window_unmanage()? */ - guint unmanaging : 1; - - /* Are we in meta_window_new()? */ - guint constructing : 1; - - /* Are we in the various queues? (Bitfield: see META_WINDOW_IS_IN_QUEUE) */ - guint is_in_queues : NUMBER_OF_QUEUES; - - /* Used by keybindings.c */ - guint keys_grabbed : 1; /* normal keybindings grabbed */ - guint grab_on_frame : 1; /* grabs are on the frame */ - guint all_keys_grabbed : 1; /* AnyKey grabbed */ - - /* Set if the reason for unmanaging the window is that - * it was withdrawn - */ - guint withdrawn : 1; - - /* TRUE if constrain_position should calc placement. - * only relevant if !window->placed - */ - guint calc_placement : 1; - - /* if TRUE, window was maximized at start of current grab op */ - guint shaken_loose : 1; - - /* if TRUE we have a grab on the focus click buttons */ - guint have_focus_click_grab : 1; - - /* if TRUE, application is buggy and SYNC resizing is turned off */ - guint disable_sync : 1; - - /* if TRUE, window is attached to its parent */ - guint attached : 1; - - /* whether or not the window is from a program running on another machine */ - guint is_remote : 1; - - /* whether focus should be restored on map */ - guint restore_focus_on_map : 1; - - /* if non-NULL, the bounds of the window frame */ - cairo_region_t *frame_bounds; - - /* if non-NULL, the bounding shape region of the window. Relative to - * the server-side client window. */ - cairo_region_t *shape_region; - - /* if non-NULL, the opaque region _NET_WM_OPAQUE_REGION */ - cairo_region_t *opaque_region; - - /* the input shape region for picking */ - cairo_region_t *input_region; - - /* _NET_WM_WINDOW_OPACITY rescaled to 0xFF */ - guint8 opacity; - - /* if TRUE, the we have the new form of sync request counter which - * also handles application frames */ - guint extended_sync_request_counter : 1; - - /* Note: can be NULL */ - GSList *struts; - - /* XSync update counter */ - XSyncCounter sync_request_counter; - gint64 sync_request_serial; - gint64 sync_request_wait_serial; - guint sync_request_timeout_id; - /* alarm monitoring client's _NET_WM_SYNC_REQUEST_COUNTER */ - XSyncAlarm sync_request_alarm; - - /* Number of UnmapNotify that are caused by us, if - * we get UnmapNotify with none pending then the client - * is withdrawing the window. - */ - int unmaps_pending; - - /* Number of XReparentWindow requests that we have queued. - */ - int reparents_pending; - - /* See docs for meta_window_get_stable_sequence() */ - guint32 stable_sequence; - - /* set to the most recent user-interaction event timestamp that we - know about for this window */ - guint32 net_wm_user_time; - - /* window that gets updated net_wm_user_time values */ - Window user_time_window; - - gboolean has_custom_frame_extents; - GtkBorder custom_frame_extents; - - /* The rectangles here are in "frame rect" coordinates. See the - * comment at the top of meta_window_move_resize_internal() for more - * information. */ - - /* The current window geometry of the window. */ - MetaRectangle rect; - - /* The geometry to restore when we unmaximize. */ - MetaRectangle saved_rect; - - /* The geometry to restore when we unfullscreen. */ - MetaRectangle saved_rect_fullscreen; - - /* This is the geometry the window will have if no constraints have - * applied. We use this whenever we are moving implicitly (for example, - * if we move to avoid a panel, we can snap back to this position if - * the panel moves again). - */ - MetaRectangle unconstrained_rect; - - /* The rectangle of the "server-side" geometry of the buffer, - * in root coordinates. - * - * For X11 windows, this matches XGetGeometry of the toplevel. - * - * For Wayland windows, the position matches the position of the - * surface associated with shell surface (wl_shell_surface, xdg_surface - * etc). The size matches the size surface size as displayed in the stage. - */ - MetaRectangle buffer_rect; - - /* Cached net_wm_icon_geometry */ - MetaRectangle icon_geometry; - - /* x/y/w/h here get filled with ConfigureRequest values */ - XSizeHints size_hints; - - /* Managed by stack.c */ - MetaStackLayer layer; - int stack_position; /* see comment in stack.h */ - - /* Managed by delete.c */ - MetaCloseDialog *close_dialog; - - /* maintained by group.c */ - MetaGroup *group; - - GObject *compositor_private; - - /* Focused window that is (directly or indirectly) attached to this one */ - MetaWindow *attached_focus_window; - - /* The currently complementary tiled window, if any */ - MetaWindow *tile_match; - - struct { - MetaPlacementRule *rule; - MetaPlacementState state; - - struct { - int x; - int y; - int rel_x; - int rel_y; - } pending; - - struct { - int rel_x; - int rel_y; - } current; - } placement; - - guint unmanage_idle_id; - - pid_t client_pid; -}; - -struct _MetaWindowClass -{ - GObjectClass parent_class; - - void (*manage) (MetaWindow *window); - void (*unmanage) (MetaWindow *window); - void (*ping) (MetaWindow *window, - guint32 serial); - void (*delete) (MetaWindow *window, - guint32 timestamp); - void (*kill) (MetaWindow *window); - void (*focus) (MetaWindow *window, - guint32 timestamp); - void (*grab_op_began) (MetaWindow *window, - MetaGrabOp op); - void (*grab_op_ended) (MetaWindow *window, - MetaGrabOp op); - void (*current_workspace_changed) (MetaWindow *window); - void (*move_resize_internal) (MetaWindow *window, - MetaGravity gravity, - MetaRectangle unconstrained_rect, - MetaRectangle constrained_rect, - MetaRectangle temporary_rect, - int rel_x, - int rel_y, - MetaMoveResizeFlags flags, - MetaMoveResizeResultFlags *result); - gboolean (*update_struts) (MetaWindow *window); - void (*get_default_skip_hints) (MetaWindow *window, - gboolean *skip_taskbar_out, - gboolean *skip_pager_out); - gboolean (*update_icon) (MetaWindow *window, - cairo_surface_t **icon, - cairo_surface_t **mini_icon); - pid_t (*get_client_pid) (MetaWindow *window); - void (*update_main_monitor) (MetaWindow *window, - MetaWindowUpdateMonitorFlags flags); - void (*main_monitor_changed) (MetaWindow *window, - const MetaLogicalMonitor *old); - void (*adjust_fullscreen_monitor_rect) (MetaWindow *window, - MetaRectangle *monitor_rect); - void (*force_restore_shortcuts) (MetaWindow *window, - ClutterInputDevice *source); - gboolean (*shortcuts_inhibited) (MetaWindow *window, - ClutterInputDevice *source); - gboolean (*is_focusable) (MetaWindow *window); - gboolean (*is_stackable) (MetaWindow *window); - gboolean (*can_ping) (MetaWindow *window); - gboolean (*are_updates_frozen) (MetaWindow *window); - gboolean (*is_focus_async) (MetaWindow *window); - - MetaStackLayer (*calculate_layer) (MetaWindow *window); - - void (* map) (MetaWindow *window); - void (* unmap) (MetaWindow *window); -}; - -/* These differ from window->has_foo_func in that they consider - * the dynamic window state such as "maximized", not just the - * window's type - */ -#define META_WINDOW_MAXIMIZED(w) ((w)->maximized_horizontally && \ - (w)->maximized_vertically) -#define META_WINDOW_MAXIMIZED_VERTICALLY(w) ((w)->maximized_vertically) -#define META_WINDOW_MAXIMIZED_HORIZONTALLY(w) ((w)->maximized_horizontally) -#define META_WINDOW_TILED_SIDE_BY_SIDE(w) ((w)->maximized_vertically && \ - !(w)->maximized_horizontally && \ - (w)->tile_mode != META_TILE_NONE) -#define META_WINDOW_TILED_LEFT(w) (META_WINDOW_TILED_SIDE_BY_SIDE(w) && \ - (w)->tile_mode == META_TILE_LEFT) -#define META_WINDOW_TILED_RIGHT(w) (META_WINDOW_TILED_SIDE_BY_SIDE(w) && \ - (w)->tile_mode == META_TILE_RIGHT) -#define META_WINDOW_TILED_MAXIMIZED(w)(META_WINDOW_MAXIMIZED(w) && \ - (w)->tile_mode == META_TILE_MAXIMIZED) -#define META_WINDOW_ALLOWS_MOVE(w) ((w)->has_move_func && !(w)->fullscreen) -#define META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS(w) ((w)->has_resize_func && !META_WINDOW_MAXIMIZED (w) && !(w)->fullscreen && !(w)->shaded) -#define META_WINDOW_ALLOWS_RESIZE(w) (META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS (w) && \ - (((w)->size_hints.min_width < (w)->size_hints.max_width) || \ - ((w)->size_hints.min_height < (w)->size_hints.max_height))) -#define META_WINDOW_ALLOWS_HORIZONTAL_RESIZE(w) (META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS (w) && (w)->size_hints.min_width < (w)->size_hints.max_width) -#define META_WINDOW_ALLOWS_VERTICAL_RESIZE(w) (META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS (w) && (w)->size_hints.min_height < (w)->size_hints.max_height) - -MetaWindow * _meta_window_shared_new (MetaDisplay *display, - MetaWindowClientType client_type, - MetaWaylandSurface *surface, - Window xwindow, - gulong existing_wm_state, - MetaCompEffect effect, - XWindowAttributes *attrs); - -void meta_window_unmanage (MetaWindow *window, - guint32 timestamp); -void meta_window_unmanage_on_idle (MetaWindow *window); -void meta_window_queue (MetaWindow *window, - guint queuebits); -META_EXPORT_TEST -void meta_window_untile (MetaWindow *window); - -META_EXPORT_TEST -void meta_window_tile (MetaWindow *window, - MetaTileMode mode); -MetaTileMode meta_window_get_tile_mode (MetaWindow *window); -void meta_window_restore_tile (MetaWindow *window, - MetaTileMode mode, - int width, - int height); -void meta_window_maximize_internal (MetaWindow *window, - MetaMaximizeFlags directions, - MetaRectangle *saved_rect); - -void meta_window_make_fullscreen_internal (MetaWindow *window); -void meta_window_update_fullscreen_monitors (MetaWindow *window, - MetaLogicalMonitor *top, - MetaLogicalMonitor *bottom, - MetaLogicalMonitor *left, - MetaLogicalMonitor *right); - -gboolean meta_window_has_fullscreen_monitors (MetaWindow *window); - -void meta_window_adjust_fullscreen_monitor_rect (MetaWindow *window, - MetaRectangle *monitor_rect); - -void meta_window_resize_frame_with_gravity (MetaWindow *window, - gboolean user_op, - int w, - int h, - MetaGravity gravity); - -/* Return whether the window should be currently mapped */ -gboolean meta_window_should_be_showing (MetaWindow *window); - -void meta_window_update_struts (MetaWindow *window); - -/* gets position we need to set to stay in current position, - * assuming position will be gravity-compensated. i.e. - * this is the position a client would send in a configure - * request. - */ -void meta_window_get_gravity_position (MetaWindow *window, - MetaGravity gravity, - int *x, - int *y); -/* Get geometry for saving in the session; x/y are gravity - * position, and w/h are in resize inc above the base size. - */ -void meta_window_get_session_geometry (MetaWindow *window, - int *x, - int *y, - int *width, - int *height); - -void meta_window_update_unfocused_button_grabs (MetaWindow *window); - -void meta_window_set_focused_internal (MetaWindow *window, - gboolean focused); - -gboolean meta_window_is_focusable (MetaWindow *window); - -gboolean meta_window_can_ping (MetaWindow *window); - -MetaStackLayer meta_window_calculate_layer (MetaWindow *window); - -void meta_window_current_workspace_changed (MetaWindow *window); - -void meta_window_show_menu (MetaWindow *window, - MetaWindowMenuType menu, - int x, - int y); - -void meta_window_show_menu_for_rect (MetaWindow *window, - MetaWindowMenuType menu, - MetaRectangle *rect); - -gboolean meta_window_handle_mouse_grab_op_event (MetaWindow *window, - const ClutterEvent *event); - -GList* meta_window_get_workspaces (MetaWindow *window); - -void meta_window_get_work_area_for_logical_monitor (MetaWindow *window, - MetaLogicalMonitor *logical_monitor, - MetaRectangle *area); - -int meta_window_get_current_tile_monitor_number (MetaWindow *window); -void meta_window_get_tile_area (MetaWindow *window, - MetaTileMode mode, - MetaRectangle *tile_area); - - -gboolean meta_window_same_application (MetaWindow *window, - MetaWindow *other_window); - -#define META_WINDOW_IN_NORMAL_TAB_CHAIN_TYPE(w) \ - ((w)->type != META_WINDOW_DOCK && (w)->type != META_WINDOW_DESKTOP) -#define META_WINDOW_IN_NORMAL_TAB_CHAIN(w) \ - (meta_window_is_focusable (w) && META_WINDOW_IN_NORMAL_TAB_CHAIN_TYPE (w) && (!(w)->skip_taskbar)) -#define META_WINDOW_IN_DOCK_TAB_CHAIN(w) \ - (meta_window_is_focusable (w) && (! META_WINDOW_IN_NORMAL_TAB_CHAIN_TYPE (w) || (w)->skip_taskbar)) -#define META_WINDOW_IN_GROUP_TAB_CHAIN(w, g) \ - (meta_window_is_focusable (w) && (!g || meta_window_get_group(w)==g)) - -void meta_window_free_delete_dialog (MetaWindow *window); - -void meta_window_update_keyboard_resize (MetaWindow *window, - gboolean update_cursor); -void meta_window_update_keyboard_move (MetaWindow *window); - -MetaStackLayer meta_window_get_default_layer (MetaWindow *window); -void meta_window_update_layer (MetaWindow *window); - -void meta_window_recalc_features (MetaWindow *window); - -void meta_window_set_type (MetaWindow *window, - MetaWindowType type); - -void meta_window_frame_size_changed (MetaWindow *window); - -gboolean meta_window_is_in_stack (MetaWindow *window); - -void meta_window_stack_just_below (MetaWindow *window, - MetaWindow *below_this_one); - -void meta_window_stack_just_above (MetaWindow *window, - MetaWindow *above_this_one); - -void meta_window_set_user_time (MetaWindow *window, - guint32 timestamp); - -void meta_window_update_for_monitors_changed (MetaWindow *window); -void meta_window_on_all_workspaces_changed (MetaWindow *window); - -gboolean meta_window_should_attach_to_parent (MetaWindow *window); -gboolean meta_window_can_tile_side_by_side (MetaWindow *window); - -void meta_window_compute_tile_match (MetaWindow *window); - -gboolean meta_window_updates_are_frozen (MetaWindow *window); - -void meta_window_set_title (MetaWindow *window, - const char *title); -void meta_window_set_wm_class (MetaWindow *window, - const char *wm_class, - const char *wm_instance); -void meta_window_set_gtk_dbus_properties (MetaWindow *window, - const char *application_id, - const char *unique_bus_name, - const char *appmenu_path, - const char *menubar_path, - const char *application_object_path, - const char *window_object_path); - -gboolean meta_window_has_transient_type (MetaWindow *window); - -void meta_window_set_transient_for (MetaWindow *window, - MetaWindow *parent); - -void meta_window_set_opacity (MetaWindow *window, - guint8 opacity); - -void meta_window_handle_enter (MetaWindow *window, - guint32 timestamp, - guint root_x, - guint root_y); -void meta_window_handle_leave (MetaWindow *window); - -gboolean meta_window_handle_ui_frame_event (MetaWindow *window, - const ClutterEvent *event); - -void meta_window_handle_ungrabbed_event (MetaWindow *window, - const ClutterEvent *event); - -void meta_window_get_client_area_rect (const MetaWindow *window, - cairo_rectangle_int_t *rect); -void meta_window_get_titlebar_rect (MetaWindow *window, - MetaRectangle *titlebar_rect); - -void meta_window_activate_full (MetaWindow *window, - guint32 timestamp, - MetaClientType source_indication, - MetaWorkspace *workspace); - -META_EXPORT_TEST -MetaLogicalMonitor * meta_window_calculate_main_logical_monitor (MetaWindow *window); - -MetaLogicalMonitor * meta_window_get_main_logical_monitor (MetaWindow *window); -void meta_window_update_monitor (MetaWindow *window, - MetaWindowUpdateMonitorFlags flags); - -void meta_window_set_urgent (MetaWindow *window, - gboolean urgent); - -void meta_window_update_resize (MetaWindow *window, - MetaEdgeResistanceFlags flags, - int x, int y, - gboolean force); - -void meta_window_move_resize_internal (MetaWindow *window, - MetaMoveResizeFlags flags, - MetaGravity gravity, - MetaRectangle frame_rect); - -void meta_window_grab_op_began (MetaWindow *window, MetaGrabOp op); -void meta_window_grab_op_ended (MetaWindow *window, MetaGrabOp op); - -void meta_window_set_alive (MetaWindow *window, gboolean is_alive); - -gboolean meta_window_has_pointer (MetaWindow *window); - -void meta_window_emit_size_changed (MetaWindow *window); - -MetaPlacementRule *meta_window_get_placement_rule (MetaWindow *window); - -void meta_window_force_placement (MetaWindow *window, - gboolean force_move); - -void meta_window_force_restore_shortcuts (MetaWindow *window, - ClutterInputDevice *source); - -gboolean meta_window_shortcuts_inhibited (MetaWindow *window, - ClutterInputDevice *source); -gboolean meta_window_is_stackable (MetaWindow *window); -gboolean meta_window_is_focus_async (MetaWindow *window); -#endif diff --git a/src/core/window.c b/src/core/window.c deleted file mode 100644 index ea56f3328..000000000 --- a/src/core/window.c +++ /dev/null @@ -1,8665 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2001 Havoc Pennington, Anders Carlsson - * Copyright (C) 2002, 2003 Red Hat, Inc. - * Copyright (C) 2003 Rob Adams - * Copyright (C) 2004-2006 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -/** - * SECTION:meta-window - * @title: MetaWindow - * @short_description: A display-agnostic abstraction for a window. - * - * #MetaWindow is the core abstraction in Mutter of a window. It has the - * properties you'd expect, such as a title, an icon, whether it's fullscreen, - * has decorations, etc. - * - * Since a lot of different kinds of windows exist, each window also a - * #MetaWindowType which denotes which kind of window we're exactly dealing - * with. For example, one expects slightly different behaviour from a dialog - * than a "normal" window. The type of a window can be queried with - * meta_window_get_type(). - * - * Common API for windows include: - * - Minimizing: meta_window_minimize() / meta_window_unminimize() - * - Maximizing: meta_window_maximize() / meta_window_unmaximize() - * - Fullscreen: meta_window_make_fullscreen() / meta_window_unmake_fullscreen() - * / meta_window_is_fullscreen() - * - * Each #MetaWindow is part of either one or all #MetaWorkspace<!-- -->s of the - * desktop. You can activate a window on a certain workspace using - * meta_window_activate_with_workspace(), and query on which workspace it is - * located using meta_window_located_on_workspace(). The workspace it is part - * of can be obtained using meta_window_get_workspace(). - * - * Each display protocol should make a subclass to be compatible with that - * protocols' specifics, for example #MetaWindowX11 and #MetaWindowWayland. - * This is independent of the protocol that the client uses, which is modeled - * using the #MetaWindowClientType enum. - * - * To integrate within the Clutter scene graph, which deals with the actual - * rendering, each #MetaWindow will be part of a #MetaWindowActor. - */ - -#include "config.h" - -#include "core/window-private.h" - -#include <math.h> -#include <stdlib.h> -#include <string.h> -#include <X11/Xatom.h> - -#include "backends/meta-backend-private.h" -#include "backends/meta-logical-monitor.h" -#include "cogl/cogl.h" -#include "core/boxes-private.h" -#include "core/constraints.h" -#include "core/edge-resistance.h" -#include "core/frame.h" -#include "core/keybindings-private.h" -#include "core/meta-workspace-manager-private.h" -#include "core/place.h" -#include "core/stack.h" -#include "core/util-private.h" -#include "core/workspace-private.h" -#include "meta/compositor-mutter.h" -#include "meta/group.h" -#include "meta/meta-cursor-tracker.h" -#include "meta/meta-enum-types.h" -#include "meta/meta-x11-errors.h" -#include "meta/prefs.h" -#include "ui/ui.h" -#include "x11/meta-x11-display-private.h" -#include "x11/window-props.h" -#include "x11/window-x11.h" -#include "x11/xprops.h" - -#ifdef HAVE_WAYLAND -#include "wayland/meta-wayland-private.h" -#include "wayland/meta-wayland-surface.h" -#include "wayland/meta-window-wayland.h" -#include "wayland/meta-window-xwayland.h" -#endif - -/* Windows that unmaximize to a size bigger than that fraction of the workarea - * will be scaled down to that size (while maintaining aspect ratio). - * Windows that cover an area greater then this size are automaximized on map. - */ -#define MAX_UNMAXIMIZED_WINDOW_AREA .8 - -#define SNAP_SECURITY_LABEL_PREFIX "snap." - -static int destroying_windows_disallowed = 0; - -/* Each window has a "stamp" which is a non-recycled 64-bit ID. They - * start after the end of the XID space so that, for stacking - * we can keep a guint64 that represents one or the other - */ -static guint64 next_window_stamp = G_GUINT64_CONSTANT(0x100000000); - -static void invalidate_work_areas (MetaWindow *window); -static void set_wm_state (MetaWindow *window); -static void set_net_wm_state (MetaWindow *window); -static void meta_window_set_above (MetaWindow *window, - gboolean new_value); - -static void meta_window_show (MetaWindow *window); -static void meta_window_hide (MetaWindow *window); - -static void meta_window_save_rect (MetaWindow *window); - -static void ensure_mru_position_after (MetaWindow *window, - MetaWindow *after_this_one); - -static void meta_window_move_resize_now (MetaWindow *window); - -static void meta_window_unqueue (MetaWindow *window, guint queuebits); - -static void update_move (MetaWindow *window, - MetaEdgeResistanceFlags flags, - int x, - int y); -static gboolean update_move_timeout (gpointer data); -static void update_resize (MetaWindow *window, - MetaEdgeResistanceFlags flags, - int x, - int y, - gboolean force); -static gboolean update_resize_timeout (gpointer data); -static gboolean should_be_on_all_workspaces (MetaWindow *window); - -static void meta_window_flush_calc_showing (MetaWindow *window); - -static gboolean queue_calc_showing_func (MetaWindow *window, - void *data); - -static void meta_window_move_between_rects (MetaWindow *window, - MetaMoveResizeFlags move_resize_flags, - const MetaRectangle *old_area, - const MetaRectangle *new_area); - -static void unmaximize_window_before_freeing (MetaWindow *window); -static void unminimize_window_and_all_transient_parents (MetaWindow *window); - -static void meta_window_propagate_focus_appearance (MetaWindow *window, - gboolean focused); -static void meta_window_update_icon_now (MetaWindow *window, - gboolean force); - -static void set_workspace_state (MetaWindow *window, - gboolean on_all_workspaces, - MetaWorkspace *workspace); - -static MetaWindow * meta_window_find_tile_match (MetaWindow *window, - MetaTileMode mode); -static void update_edge_constraints (MetaWindow *window); - -/* Idle handlers for the three queues (run with meta_later_add()). The - * "data" parameter in each case will be a GINT_TO_POINTER of the - * index into the queue arrays to use. - * - * TODO: Possibly there is still some code duplication among these, which we - * need to sort out at some point. - */ -static gboolean idle_calc_showing (gpointer data); -static gboolean idle_move_resize (gpointer data); -static gboolean idle_update_icon (gpointer data); - -G_DEFINE_ABSTRACT_TYPE (MetaWindow, meta_window, G_TYPE_OBJECT); - -enum -{ - PROP_0, - - PROP_TITLE, - PROP_ICON, - PROP_MINI_ICON, - PROP_DECORATED, - PROP_FULLSCREEN, - PROP_MAXIMIZED_HORIZONTALLY, - PROP_MAXIMIZED_VERTICALLY, - PROP_MINIMIZED, - PROP_WINDOW_TYPE, - PROP_USER_TIME, - PROP_DEMANDS_ATTENTION, - PROP_URGENT, - PROP_SKIP_TASKBAR, - PROP_MUTTER_HINTS, - PROP_APPEARS_FOCUSED, - PROP_RESIZEABLE, - PROP_ABOVE, - PROP_WM_CLASS, - PROP_GTK_APPLICATION_ID, - PROP_GTK_UNIQUE_BUS_NAME, - PROP_GTK_APPLICATION_OBJECT_PATH, - PROP_GTK_WINDOW_OBJECT_PATH, - PROP_GTK_APP_MENU_OBJECT_PATH, - PROP_GTK_MENUBAR_OBJECT_PATH, - PROP_ON_ALL_WORKSPACES, - - PROP_LAST, -}; - -static GParamSpec *obj_props[PROP_LAST]; - -enum -{ - WORKSPACE_CHANGED, - FOCUS, - RAISED, - UNMANAGING, - UNMANAGED, - SIZE_CHANGED, - POSITION_CHANGED, - SHOWN, - - LAST_SIGNAL -}; - -static guint window_signals[LAST_SIGNAL] = { 0 }; - -static void -prefs_changed_callback (MetaPreference pref, - gpointer data) -{ - MetaWindow *window = data; - - if (pref == META_PREF_WORKSPACES_ONLY_ON_PRIMARY) - { - meta_window_on_all_workspaces_changed (window); - } - else if (pref == META_PREF_ATTACH_MODAL_DIALOGS && - window->type == META_WINDOW_MODAL_DIALOG) - { - window->attached = meta_window_should_attach_to_parent (window); - meta_window_recalc_features (window); - meta_window_queue (window, META_QUEUE_MOVE_RESIZE); - } -} - -static void -meta_window_real_grab_op_began (MetaWindow *window, - MetaGrabOp op) -{ -} - -static void -meta_window_real_grab_op_ended (MetaWindow *window, - MetaGrabOp op) -{ - window->shaken_loose = FALSE; -} - -static void -meta_window_real_current_workspace_changed (MetaWindow *window) -{ -} - -static gboolean -meta_window_real_update_struts (MetaWindow *window) -{ - return FALSE; -} - -static void -meta_window_real_get_default_skip_hints (MetaWindow *window, - gboolean *skip_taskbar_out, - gboolean *skip_pager_out) -{ - *skip_taskbar_out = FALSE; - *skip_pager_out = FALSE; -} - -static gboolean -meta_window_real_update_icon (MetaWindow *window, - cairo_surface_t **icon, - cairo_surface_t **mini_icon) -{ - *icon = NULL; - *mini_icon = NULL; - return FALSE; -} - -static pid_t -meta_window_real_get_client_pid (MetaWindow *window) -{ - return 0; -} - -static void -meta_window_finalize (GObject *object) -{ - MetaWindow *window = META_WINDOW (object); - - if (window->icon) - cairo_surface_destroy (window->icon); - - if (window->mini_icon) - cairo_surface_destroy (window->mini_icon); - - if (window->frame_bounds) - cairo_region_destroy (window->frame_bounds); - - if (window->shape_region) - cairo_region_destroy (window->shape_region); - - if (window->opaque_region) - cairo_region_destroy (window->opaque_region); - - if (window->input_region) - cairo_region_destroy (window->input_region); - - if (window->transient_for) - g_object_unref (window->transient_for); - - g_free (window->sm_client_id); - g_free (window->wm_client_machine); - g_free (window->startup_id); - g_free (window->role); - g_free (window->res_class); - g_free (window->res_name); - g_free (window->title); - g_free (window->desc); - g_free (window->sandboxed_app_id); - g_free (window->gtk_theme_variant); - g_free (window->gtk_application_id); - g_free (window->gtk_unique_bus_name); - g_free (window->gtk_application_object_path); - g_free (window->gtk_window_object_path); - g_free (window->gtk_app_menu_object_path); - g_free (window->gtk_menubar_object_path); - g_free (window->placement.rule); - - G_OBJECT_CLASS (meta_window_parent_class)->finalize (object); -} - -static void -meta_window_get_property(GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - MetaWindow *win = META_WINDOW (object); - - switch (prop_id) - { - case PROP_TITLE: - g_value_set_string (value, win->title); - break; - case PROP_ICON: - g_value_set_pointer (value, win->icon); - break; - case PROP_MINI_ICON: - g_value_set_pointer (value, win->mini_icon); - break; - case PROP_DECORATED: - g_value_set_boolean (value, win->decorated); - break; - case PROP_FULLSCREEN: - g_value_set_boolean (value, win->fullscreen); - break; - case PROP_MAXIMIZED_HORIZONTALLY: - g_value_set_boolean (value, win->maximized_horizontally); - break; - case PROP_MAXIMIZED_VERTICALLY: - g_value_set_boolean (value, win->maximized_vertically); - break; - case PROP_MINIMIZED: - g_value_set_boolean (value, win->minimized); - break; - case PROP_WINDOW_TYPE: - g_value_set_enum (value, win->type); - break; - case PROP_USER_TIME: - g_value_set_uint (value, win->net_wm_user_time); - break; - case PROP_DEMANDS_ATTENTION: - g_value_set_boolean (value, win->wm_state_demands_attention); - break; - case PROP_URGENT: - g_value_set_boolean (value, win->urgent); - break; - case PROP_SKIP_TASKBAR: - g_value_set_boolean (value, win->skip_taskbar); - break; - case PROP_MUTTER_HINTS: - g_value_set_string (value, win->mutter_hints); - break; - case PROP_APPEARS_FOCUSED: - g_value_set_boolean (value, meta_window_appears_focused (win)); - break; - case PROP_WM_CLASS: - g_value_set_string (value, win->res_class); - break; - case PROP_RESIZEABLE: - g_value_set_boolean (value, win->has_resize_func); - break; - case PROP_ABOVE: - g_value_set_boolean (value, win->wm_state_above); - break; - case PROP_GTK_APPLICATION_ID: - g_value_set_string (value, win->gtk_application_id); - break; - case PROP_GTK_UNIQUE_BUS_NAME: - g_value_set_string (value, win->gtk_unique_bus_name); - break; - case PROP_GTK_APPLICATION_OBJECT_PATH: - g_value_set_string (value, win->gtk_application_object_path); - break; - case PROP_GTK_WINDOW_OBJECT_PATH: - g_value_set_string (value, win->gtk_window_object_path); - break; - case PROP_GTK_APP_MENU_OBJECT_PATH: - g_value_set_string (value, win->gtk_app_menu_object_path); - break; - case PROP_GTK_MENUBAR_OBJECT_PATH: - g_value_set_string (value, win->gtk_menubar_object_path); - break; - case PROP_ON_ALL_WORKSPACES: - g_value_set_boolean (value, win->on_all_workspaces); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -meta_window_set_property(GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (prop_id) - { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -meta_window_class_init (MetaWindowClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = meta_window_finalize; - - object_class->get_property = meta_window_get_property; - object_class->set_property = meta_window_set_property; - - klass->grab_op_began = meta_window_real_grab_op_began; - klass->grab_op_ended = meta_window_real_grab_op_ended; - klass->current_workspace_changed = meta_window_real_current_workspace_changed; - klass->update_struts = meta_window_real_update_struts; - klass->get_default_skip_hints = meta_window_real_get_default_skip_hints; - klass->update_icon = meta_window_real_update_icon; - klass->get_client_pid = meta_window_real_get_client_pid; - - obj_props[PROP_TITLE] = - g_param_spec_string ("title", - "Title", - "The title of the window", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_ICON] = - g_param_spec_pointer ("icon", - "Icon", - "Normal icon, usually 96x96 pixels", - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_MINI_ICON] = - g_param_spec_pointer ("mini-icon", - "Mini Icon", - "Mini icon, usually 16x16 pixels", - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_DECORATED] = - g_param_spec_boolean ("decorated", - "Decorated", - "Whether window is decorated", - TRUE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_FULLSCREEN] = - g_param_spec_boolean ("fullscreen", - "Fullscreen", - "Whether window is fullscreened", - FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_MAXIMIZED_HORIZONTALLY] = - g_param_spec_boolean ("maximized-horizontally", - "Maximized horizontally", - "Whether window is maximized horizontally", - FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_MAXIMIZED_VERTICALLY] = - g_param_spec_boolean ("maximized-vertically", - "Maximizing vertically", - "Whether window is maximized vertically", - FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_MINIMIZED] = - g_param_spec_boolean ("minimized", - "Minimizing", - "Whether window is minimized", - FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_WINDOW_TYPE] = - g_param_spec_enum ("window-type", - "Window Type", - "The type of the window", - META_TYPE_WINDOW_TYPE, - META_WINDOW_NORMAL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_USER_TIME] = - g_param_spec_uint ("user-time", - "User time", - "Timestamp of last user interaction", - 0, - G_MAXUINT, - 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_DEMANDS_ATTENTION] = - g_param_spec_boolean ("demands-attention", - "Demands Attention", - "Whether the window has _NET_WM_STATE_DEMANDS_ATTENTION set", - FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_URGENT] = - g_param_spec_boolean ("urgent", - "Urgent", - "Whether the urgent flag of WM_HINTS is set", - FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_SKIP_TASKBAR] = - g_param_spec_boolean ("skip-taskbar", - "Skip taskbar", - "Whether the skip-taskbar flag of WM_HINTS is set", - FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_MUTTER_HINTS] = - g_param_spec_string ("mutter-hints", - "_MUTTER_HINTS", - "Contents of the _MUTTER_HINTS property of this window", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_APPEARS_FOCUSED] = - g_param_spec_boolean ("appears-focused", - "Appears focused", - "Whether the window is drawn as being focused", - FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_RESIZEABLE] = - g_param_spec_boolean ("resizeable", - "Resizeable", - "Whether the window can be resized", - FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_ABOVE] = - g_param_spec_boolean ("above", - "Above", - "Whether the window is shown as always-on-top", - FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_WM_CLASS] = - g_param_spec_string ("wm-class", - "WM_CLASS", - "Contents of the WM_CLASS property of this window", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_GTK_APPLICATION_ID] = - g_param_spec_string ("gtk-application-id", - "_GTK_APPLICATION_ID", - "Contents of the _GTK_APPLICATION_ID property of this window", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_GTK_UNIQUE_BUS_NAME] = - g_param_spec_string ("gtk-unique-bus-name", - "_GTK_UNIQUE_BUS_NAME", - "Contents of the _GTK_UNIQUE_BUS_NAME property of this window", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_GTK_APPLICATION_OBJECT_PATH] = - g_param_spec_string ("gtk-application-object-path", - "_GTK_APPLICATION_OBJECT_PATH", - "Contents of the _GTK_APPLICATION_OBJECT_PATH property of this window", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_GTK_WINDOW_OBJECT_PATH] = - g_param_spec_string ("gtk-window-object-path", - "_GTK_WINDOW_OBJECT_PATH", - "Contents of the _GTK_WINDOW_OBJECT_PATH property of this window", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_GTK_APP_MENU_OBJECT_PATH] = - g_param_spec_string ("gtk-app-menu-object-path", - "_GTK_APP_MENU_OBJECT_PATH", - "Contents of the _GTK_APP_MENU_OBJECT_PATH property of this window", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_GTK_MENUBAR_OBJECT_PATH] = - g_param_spec_string ("gtk-menubar-object-path", - "_GTK_MENUBAR_OBJECT_PATH", - "Contents of the _GTK_MENUBAR_OBJECT_PATH property of this window", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_ON_ALL_WORKSPACES] = - g_param_spec_boolean ("on-all-workspaces", - "On all workspaces", - "Whether the window is set to appear on all workspaces", - FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, PROP_LAST, obj_props); - - window_signals[WORKSPACE_CHANGED] = - g_signal_new ("workspace-changed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 0); - - window_signals[FOCUS] = - g_signal_new ("focus", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 0); - - window_signals[RAISED] = - g_signal_new ("raised", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 0); - - window_signals[UNMANAGING] = - g_signal_new ("unmanaging", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 0); - - window_signals[UNMANAGED] = - g_signal_new ("unmanaged", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 0); - - /** - * MetaWindow::position-changed: - * @window: a #MetaWindow - * - * This is emitted when the position of a window might - * have changed. Specifically, this is emitted when the - * position of the toplevel window has changed, or when - * the position of the client window has changed. - */ - window_signals[POSITION_CHANGED] = - g_signal_new ("position-changed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 0); - - /** - * MetaWindow::shown: - * @window: a #MetaWindow - * - * This is emitted after a window has been shown. - */ - window_signals[SHOWN] = - g_signal_new ("shown", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 0); - - /** - * MetaWindow::size-changed: - * @window: a #MetaWindow - * - * This is emitted when the size of a window might - * have changed. Specifically, this is emitted when the - * size of the toplevel window has changed, or when the - * size of the client window has changed. - */ - window_signals[SIZE_CHANGED] = - g_signal_new ("size-changed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 0); -} - -static void -meta_window_init (MetaWindow *self) -{ - self->stamp = next_window_stamp++; - meta_prefs_add_listener (prefs_changed_callback, self); -} - -static gboolean -is_desktop_or_dock_foreach (MetaWindow *window, - void *data) -{ - gboolean *result = data; - - *result = - window->type == META_WINDOW_DESKTOP || - window->type == META_WINDOW_DOCK || - window->skip_from_window_list; - if (*result) - return FALSE; /* stop as soon as we find one */ - else - return TRUE; -} - -/* window is the window that's newly mapped provoking - * the possible change - */ -static void -maybe_leave_show_desktop_mode (MetaWindow *window) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - gboolean is_desktop_or_dock; - - if (!workspace_manager->active_workspace->showing_desktop) - return; - - /* If the window is a transient for the dock or desktop, don't - * leave show desktop mode when the window opens. That's - * so you can e.g. hide all windows, manipulate a file on - * the desktop via a dialog, then unshow windows again. - */ - is_desktop_or_dock = FALSE; - is_desktop_or_dock_foreach (window, - &is_desktop_or_dock); - - meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach, - &is_desktop_or_dock); - - if (!is_desktop_or_dock) - { - meta_workspace_manager_minimize_all_on_active_workspace_except (workspace_manager, - window); - meta_workspace_manager_unshow_desktop (workspace_manager); - } -} - -gboolean -meta_window_should_attach_to_parent (MetaWindow *window) -{ - MetaWindow *parent; - - if (!meta_prefs_get_attach_modal_dialogs () || - window->type != META_WINDOW_MODAL_DIALOG) - return FALSE; - - parent = meta_window_get_transient_for (window); - if (!parent) - return FALSE; - - switch (parent->type) - { - case META_WINDOW_NORMAL: - case META_WINDOW_DIALOG: - case META_WINDOW_MODAL_DIALOG: - return TRUE; - - default: - return FALSE; - } -} - -static gboolean -client_window_should_be_mapped (MetaWindow *window) -{ -#ifdef HAVE_WAYLAND - if (window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND && - !meta_wayland_surface_get_buffer (window->surface)) - return FALSE; -#endif - - return !window->shaded; -} - -static void -sync_client_window_mapped (MetaWindow *window) -{ - gboolean should_be_mapped = client_window_should_be_mapped (window); - - g_return_if_fail (!window->override_redirect); - - if (window->mapped == should_be_mapped) - return; - - window->mapped = should_be_mapped; - - if (window->mapped) - META_WINDOW_GET_CLASS (window)->map (window); - else - META_WINDOW_GET_CLASS (window)->unmap (window); -} - -static gboolean -meta_window_update_flatpak_id (MetaWindow *window, - uint32_t pid) -{ - g_autoptr(GKeyFile) key_file = NULL; - g_autofree char *info_filename = NULL; - - g_return_val_if_fail (pid != 0, FALSE); - g_return_val_if_fail (window->sandboxed_app_id == NULL, FALSE); - - key_file = g_key_file_new (); - info_filename = g_strdup_printf ("/proc/%u/root/.flatpak-info", pid); - - if (!g_key_file_load_from_file (key_file, info_filename, G_KEY_FILE_NONE, NULL)) - return FALSE; - - window->sandboxed_app_id = g_key_file_get_string (key_file, "Application", "name", NULL); - - return TRUE; -} - -static gboolean -meta_window_update_snap_id (MetaWindow *window, - uint32_t pid) -{ - g_autofree char *security_label_filename = NULL; - g_autofree char *security_label_contents = NULL; - gsize i, security_label_contents_size = 0; - char *contents_start; - char *contents_end; - char *sandboxed_app_id; - - g_return_val_if_fail (pid != 0, FALSE); - g_return_val_if_fail (window->sandboxed_app_id == NULL, FALSE); - - security_label_filename = g_strdup_printf ("/proc/%u/attr/current", pid); - - if (!g_file_get_contents (security_label_filename, - &security_label_contents, - &security_label_contents_size, - NULL)) - return FALSE; - - if (!g_str_has_prefix (security_label_contents, SNAP_SECURITY_LABEL_PREFIX)) - return FALSE; - - /* We need to translate the security profile into the desktop-id. - * The profile is in the form of 'snap.name-space.binary-name (current)' - * while the desktop id will be name-space_binary-name. - */ - security_label_contents_size -= sizeof (SNAP_SECURITY_LABEL_PREFIX) - 1; - contents_start = security_label_contents + sizeof (SNAP_SECURITY_LABEL_PREFIX) - 1; - contents_end = strchr (contents_start, ' '); - - if (contents_end) - security_label_contents_size = contents_end - contents_start; - - for (i = 0; i < security_label_contents_size; ++i) - { - if (contents_start[i] == '.') - contents_start[i] = '_'; - } - - sandboxed_app_id = g_malloc0 (security_label_contents_size + 1); - memcpy (sandboxed_app_id, contents_start, security_label_contents_size); - - window->sandboxed_app_id = sandboxed_app_id; - - return TRUE; -} - -static void -meta_window_update_sandboxed_app_id (MetaWindow *window) -{ - pid_t pid; - - g_clear_pointer (&window->sandboxed_app_id, g_free); - - pid = meta_window_get_pid (window); - - if (pid < 1) - return; - - if (meta_window_update_flatpak_id (window, pid)) - return; - - if (meta_window_update_snap_id (window, pid)) - return; -} - -static void -meta_window_update_desc (MetaWindow *window) -{ - g_clear_pointer (&window->desc, g_free); - - if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) - window->desc = g_strdup_printf ("0x%lx", window->xwindow); - else - { - guint64 small_stamp = window->stamp - G_GUINT64_CONSTANT(0x100000000); - - window->desc = g_strdup_printf ("W%" G_GUINT64_FORMAT , small_stamp); - } -} - -static void -meta_window_main_monitor_changed (MetaWindow *window, - const MetaLogicalMonitor *old) -{ - META_WINDOW_GET_CLASS (window)->main_monitor_changed (window, old); - - if (old) - g_signal_emit_by_name (window->display, "window-left-monitor", - old->number, window); - if (window->monitor) - g_signal_emit_by_name (window->display, "window-entered-monitor", - window->monitor->number, window); -} - -MetaLogicalMonitor * -meta_window_calculate_main_logical_monitor (MetaWindow *window) -{ - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - MetaRectangle window_rect; - - meta_window_get_frame_rect (window, &window_rect); - return meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager, - &window_rect); -} - -static void -meta_window_manage (MetaWindow *window) -{ - COGL_TRACE_BEGIN_SCOPED (MetaWindowManage, - "Window (manage)"); - - META_WINDOW_GET_CLASS (window)->manage (window); -} - -MetaWindow * -_meta_window_shared_new (MetaDisplay *display, - MetaWindowClientType client_type, - MetaWaylandSurface *surface, - Window xwindow, - gulong existing_wm_state, - MetaCompEffect effect, - XWindowAttributes *attrs) -{ - MetaWorkspaceManager *workspace_manager = display->workspace_manager; - MetaWindow *window; - - COGL_TRACE_BEGIN_SCOPED (MetaWindowSharedNew, - "Window (new)"); - - g_assert (attrs != NULL); - - meta_verbose ("attrs->map_state = %d (%s)", - attrs->map_state, - (attrs->map_state == IsUnmapped) ? - "IsUnmapped" : - (attrs->map_state == IsViewable) ? - "IsViewable" : - (attrs->map_state == IsUnviewable) ? - "IsUnviewable" : - "(unknown)"); - - if (client_type == META_WINDOW_CLIENT_TYPE_X11 && !meta_is_wayland_compositor ()) - window = g_object_new (META_TYPE_WINDOW_X11, NULL); -#ifdef HAVE_WAYLAND - else if (client_type == META_WINDOW_CLIENT_TYPE_X11) - window = g_object_new (META_TYPE_WINDOW_XWAYLAND, NULL); - else if (client_type == META_WINDOW_CLIENT_TYPE_WAYLAND) - window = g_object_new (META_TYPE_WINDOW_WAYLAND, NULL); -#endif - else - g_assert_not_reached (); - - window->constructing = TRUE; - - window->client_type = client_type; - window->surface = surface; - window->xwindow = xwindow; - - window->display = display; - meta_display_register_stamp (window->display, &window->stamp, window); - - window->workspace = NULL; - - window->sync_request_counter = None; - window->sync_request_serial = 0; - window->sync_request_timeout_id = 0; - window->sync_request_alarm = None; - - meta_window_update_sandboxed_app_id (window); - meta_window_update_desc (window); - - window->override_redirect = attrs->override_redirect; - - /* avoid tons of stack updates */ - meta_stack_freeze (window->display->stack); - - window->rect.x = attrs->x; - window->rect.y = attrs->y; - window->rect.width = attrs->width; - window->rect.height = attrs->height; - - /* size_hints are the "request" */ - window->size_hints.x = attrs->x; - window->size_hints.y = attrs->y; - window->size_hints.width = attrs->width; - window->size_hints.height = attrs->height; - /* initialize the remaining size_hints as if size_hints.flags were zero */ - meta_set_normal_hints (window, NULL); - - /* And this is our unmaximized size */ - window->saved_rect = window->rect; - window->unconstrained_rect = window->rect; - - window->depth = attrs->depth; - window->xvisual = attrs->visual; - - window->title = NULL; - window->icon = NULL; - window->mini_icon = NULL; - - window->frame = NULL; - window->has_focus = FALSE; - window->attached_focus_window = NULL; - - window->maximized_horizontally = FALSE; - window->maximized_vertically = FALSE; - window->maximize_horizontally_after_placement = FALSE; - window->maximize_vertically_after_placement = FALSE; - window->minimize_after_placement = FALSE; - window->fullscreen = FALSE; - window->require_fully_onscreen = TRUE; - window->require_on_single_monitor = TRUE; - window->require_titlebar_visible = TRUE; - window->on_all_workspaces = FALSE; - window->on_all_workspaces_requested = FALSE; - window->tile_mode = META_TILE_NONE; - window->tile_monitor_number = -1; - window->tile_hfraction = -1.; - window->shaded = FALSE; - window->initially_iconic = FALSE; - window->minimized = FALSE; - window->tab_unminimized = FALSE; - window->iconic = FALSE; - window->mapped = attrs->map_state != IsUnmapped; - window->known_to_compositor = FALSE; - window->visible_to_compositor = FALSE; - window->pending_compositor_effect = effect; - /* if already mapped, no need to worry about focus-on-first-time-showing */ - window->showing_for_first_time = !window->mapped; - /* if already mapped we don't want to do the placement thing; - * override-redirect windows are placed by the app */ - window->placed = ((window->mapped && !window->hidden) || window->override_redirect); - window->denied_focus_and_not_transient = FALSE; - window->unmanaging = FALSE; - window->is_in_queues = 0; - window->keys_grabbed = FALSE; - window->grab_on_frame = FALSE; - window->all_keys_grabbed = FALSE; - window->withdrawn = FALSE; - window->initial_workspace_set = FALSE; - window->initial_timestamp_set = FALSE; - window->net_wm_user_time_set = FALSE; - window->user_time_window = None; - window->input = TRUE; - window->calc_placement = FALSE; - window->shaken_loose = FALSE; - window->have_focus_click_grab = FALSE; - window->disable_sync = FALSE; - - window->unmaps_pending = 0; - window->reparents_pending = 0; - - window->mwm_decorated = TRUE; - window->mwm_border_only = FALSE; - window->mwm_has_close_func = TRUE; - window->mwm_has_minimize_func = TRUE; - window->mwm_has_maximize_func = TRUE; - window->mwm_has_move_func = TRUE; - window->mwm_has_resize_func = TRUE; - - switch (client_type) - { - case META_WINDOW_CLIENT_TYPE_X11: - window->decorated = TRUE; - window->hidden = FALSE; - break; - case META_WINDOW_CLIENT_TYPE_WAYLAND: - window->decorated = FALSE; - window->hidden = TRUE; - break; - } - - window->has_close_func = TRUE; - window->has_minimize_func = TRUE; - window->has_maximize_func = TRUE; - window->has_move_func = TRUE; - window->has_resize_func = TRUE; - - window->has_shade_func = TRUE; - - window->has_fullscreen_func = TRUE; - - window->always_sticky = FALSE; - - window->skip_taskbar = FALSE; - window->skip_pager = FALSE; - window->skip_from_window_list = FALSE; - window->wm_state_above = FALSE; - window->wm_state_below = FALSE; - window->wm_state_demands_attention = FALSE; - - window->res_class = NULL; - window->res_name = NULL; - window->role = NULL; - window->sm_client_id = NULL; - window->wm_client_machine = NULL; - window->is_remote = FALSE; - window->startup_id = NULL; - - window->client_pid = 0; - - window->xtransient_for = None; - window->xclient_leader = None; - - window->type = META_WINDOW_NORMAL; - - window->struts = NULL; - - window->layer = META_LAYER_LAST; /* invalid value */ - window->stack_position = -1; - window->initial_workspace = 0; /* not used */ - window->initial_timestamp = 0; /* not used */ - - window->compositor_private = NULL; - - window->monitor = meta_window_calculate_main_logical_monitor (window); - if (window->monitor) - window->preferred_output_winsys_id = window->monitor->winsys_id; - else - window->preferred_output_winsys_id = UINT_MAX; - - window->tile_match = NULL; - - /* Assign this #MetaWindow a sequence number which can be used - * for sorting. - */ - window->stable_sequence = ++display->window_sequence_counter; - - window->opacity = 0xFF; - - if (window->override_redirect) - { - window->decorated = FALSE; - window->always_sticky = TRUE; - window->has_close_func = FALSE; - window->has_shade_func = FALSE; - window->has_move_func = FALSE; - window->has_resize_func = FALSE; - } - - window->id = meta_display_generate_window_id (display); - - meta_window_manage (window); - - if (!window->override_redirect) - meta_window_update_icon_now (window, TRUE); - - if (window->initially_iconic) - { - /* WM_HINTS said minimized */ - window->minimized = TRUE; - meta_verbose ("Window %s asked to start out minimized", window->desc); - } - - if (existing_wm_state == IconicState) - { - /* WM_STATE said minimized */ - window->minimized = TRUE; - meta_verbose ("Window %s had preexisting WM_STATE = IconicState, minimizing", - window->desc); - - /* Assume window was previously placed, though perhaps it's - * been iconic its whole life, we have no way of knowing. - */ - window->placed = TRUE; - } - - /* Apply any window attributes such as initial workspace - * based on startup notification - */ - meta_display_apply_startup_properties (window->display, window); - - /* Try to get a "launch timestamp" for the window. If the window is - * a transient, we'd like to be able to get a last-usage timestamp - * from the parent window. If the window has no parent, there isn't - * much we can do...except record the current time so that any children - * can use this time as a fallback. - */ - if (!window->override_redirect && !window->net_wm_user_time_set) { - /* First, maybe the app was launched with startup notification using an - * obsolete version of the spec; use that timestamp if it exists. - */ - if (window->initial_timestamp_set) - /* NOTE: Do NOT toggle net_wm_user_time_set to true; this is just - * being recorded as a fallback for potential transients - */ - window->net_wm_user_time = window->initial_timestamp; - else if (window->transient_for != NULL) - meta_window_set_user_time (window, window->transient_for->net_wm_user_time); - else - /* NOTE: Do NOT toggle net_wm_user_time_set to true; this is just - * being recorded as a fallback for potential transients - */ - window->net_wm_user_time = - meta_display_get_current_time_roundtrip (window->display); - } - - window->attached = meta_window_should_attach_to_parent (window); - if (window->attached) - meta_window_recalc_features (window); - - if (window->type == META_WINDOW_DESKTOP || - window->type == META_WINDOW_DOCK) - { - /* Change the default, but don't enforce this if the user - * focuses the dock/desktop and unsticks it using key shortcuts. - * Need to set this before adding to the workspaces so the MRU - * lists will be updated. - */ - window->on_all_workspaces_requested = TRUE; - } - - window->on_all_workspaces = should_be_on_all_workspaces (window); - - /* For the workspace, first honor hints, - * if that fails put transients with parents, - * otherwise put window on active space - */ - - if (window->initial_workspace_set) - { - gboolean on_all_workspaces = window->on_all_workspaces; - MetaWorkspace *workspace = NULL; - - if (window->initial_workspace == (int) 0xFFFFFFFF) - { - meta_topic (META_DEBUG_PLACEMENT, - "Window %s is initially on all spaces", - window->desc); - - /* need to set on_all_workspaces first so that it will be - * added to all the MRU lists - */ - window->on_all_workspaces_requested = TRUE; - - on_all_workspaces = TRUE; - } - else if (!on_all_workspaces) - { - meta_topic (META_DEBUG_PLACEMENT, - "Window %s is initially on space %d", - window->desc, window->initial_workspace); - - workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, - window->initial_workspace); - } - - /* Ignore when a window requests to be placed on a non-existent workspace - */ - if (on_all_workspaces || workspace != NULL) - set_workspace_state (window, on_all_workspaces, workspace); - } - - /* override-redirect windows are subtly different from other windows - * with window->on_all_workspaces == TRUE. Other windows are part of - * some workspace (so they can return to that if the flag is turned off), - * but appear on other workspaces. override-redirect windows are part - * of no workspace. - */ - if (!window->override_redirect && window->workspace == NULL) - { - if (window->transient_for != NULL) - { - meta_topic (META_DEBUG_PLACEMENT, - "Putting window %s on same workspace as parent %s", - window->desc, window->transient_for->desc); - - g_warn_if_fail (!window->transient_for->override_redirect); - set_workspace_state (window, - window->transient_for->on_all_workspaces, - window->transient_for->workspace); - } - else if (window->on_all_workspaces) - { - meta_topic (META_DEBUG_PLACEMENT, - "Putting window %s on all workspaces", - window->desc); - - set_workspace_state (window, TRUE, NULL); - } - else - { - meta_topic (META_DEBUG_PLACEMENT, - "Putting window %s on active workspace", - window->desc); - - set_workspace_state (window, FALSE, workspace_manager->active_workspace); - } - - meta_window_update_struts (window); - } - - meta_window_main_monitor_changed (window, NULL); - - /* Must add window to stack before doing move/resize, since the - * window might have fullscreen size (i.e. should have been - * fullscreen'd; acrobat is one such braindead case; it withdraws - * and remaps its window whenever trying to become fullscreen...) - * and thus constraints may try to auto-fullscreen it which also - * means restacking it. - */ - if (meta_window_is_stackable (window)) - meta_stack_add (window->display->stack, - window); - else if (window->override_redirect) - window->layer = META_LAYER_OVERRIDE_REDIRECT; /* otherwise set by MetaStack */ - - if (!window->override_redirect) - { - /* FIXME we have a tendency to set this then immediately - * change it again. - */ - set_wm_state (window); - set_net_wm_state (window); - } - - meta_compositor_add_window (window->display->compositor, window); - window->known_to_compositor = TRUE; - - /* Sync stack changes */ - meta_stack_thaw (window->display->stack); - - /* Usually the we'll have queued a stack sync anyways, because we've - * added a new frame window or restacked. But if an undecorated - * window is mapped, already stacked in the right place, then we - * might need to do this explicitly. - */ - meta_stack_tracker_queue_sync_stack (window->display->stack_tracker); - - /* disable show desktop mode unless we're a desktop component */ - maybe_leave_show_desktop_mode (window); - - meta_window_queue (window, META_QUEUE_CALC_SHOWING); - /* See bug 303284; a transient of the given window can already exist, in which - * case we think it should probably be shown. - */ - meta_window_foreach_transient (window, - queue_calc_showing_func, - NULL); - /* See bug 334899; the window may have minimized ancestors - * which need to be shown. - * - * However, we shouldn't unminimize windows here when opening - * a new display because that breaks passing _NET_WM_STATE_HIDDEN - * between window managers when replacing them; see bug 358042. - * - * And we shouldn't unminimize windows if they were initially - * iconic. - */ - if (!window->override_redirect && - !display->display_opening && - !window->initially_iconic) - unminimize_window_and_all_transient_parents (window); - - window->constructing = FALSE; - - meta_display_notify_window_created (display, window); - - if (window->wm_state_demands_attention) - g_signal_emit_by_name (window->display, "window-demands-attention", window); - - return window; -} - -static gboolean -detach_foreach_func (MetaWindow *window, - void *data) -{ - GList **children = data; - MetaWindow *parent; - - if (window->attached) - { - /* Only return the immediate children of the window being unmanaged */ - parent = meta_window_get_transient_for (window); - if (parent->unmanaging) - *children = g_list_prepend (*children, window); - } - - return TRUE; -} - -void -meta_window_unmanage (MetaWindow *window, - guint32 timestamp) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - GList *tmp; - - meta_verbose ("Unmanaging %s", window->desc); - window->unmanaging = TRUE; - - g_clear_handle_id (&window->unmanage_idle_id, g_source_remove); - - g_signal_emit (window, window_signals[UNMANAGING], 0); - - meta_window_free_delete_dialog (window); - - if (window->visible_to_compositor) - { - window->visible_to_compositor = FALSE; - meta_compositor_hide_window (window->display->compositor, window, - META_COMP_EFFECT_DESTROY); - } - - meta_compositor_remove_window (window->display->compositor, window); - window->known_to_compositor = FALSE; - - if (destroying_windows_disallowed > 0) - meta_bug ("Tried to destroy window %s while destruction was not allowed", - window->desc); - - meta_display_unregister_stamp (window->display, window->stamp); - - if (meta_prefs_get_attach_modal_dialogs ()) - { - GList *attached_children = NULL, *iter; - - /* Detach any attached dialogs by unmapping and letting them - * be remapped after @window is destroyed. - */ - meta_window_foreach_transient (window, - detach_foreach_func, - &attached_children); - for (iter = attached_children; iter; iter = iter->next) - meta_window_unmanage (iter->data, timestamp); - g_list_free (attached_children); - } - - /* Make sure to only show window on all workspaces if requested, to - * not confuse other window managers that may take over - */ - if (meta_prefs_get_workspaces_only_on_primary ()) - meta_window_on_all_workspaces_changed (window); - - if (window->fullscreen) - { - MetaGroup *group; - - /* If the window is fullscreen, it may be forcing - * other windows in its group to a higher layer - */ - - meta_stack_freeze (window->display->stack); - group = meta_window_get_group (window); - if (group) - meta_group_update_layers (group); - meta_stack_thaw (window->display->stack); - } - - meta_display_remove_pending_pings_for_window (window->display, window); - - /* safe to do this early as group.c won't re-add to the - * group if window->unmanaging */ - meta_window_shutdown_group (window); - - /* If we have the focus, focus some other window. - * This is done first, so that if the unmap causes - * an EnterNotify the EnterNotify will have final say - * on what gets focused, maintaining sloppy focus - * invariants. - */ - if (meta_window_appears_focused (window)) - meta_window_propagate_focus_appearance (window, FALSE); - if (window->has_focus) - { - meta_topic (META_DEBUG_FOCUS, - "Focusing default window since we're unmanaging %s", - window->desc); - meta_workspace_focus_default_window (workspace_manager->active_workspace, - window, - timestamp); - } - else - { - meta_topic (META_DEBUG_FOCUS, - "Unmanaging window %s which doesn't currently have focus", - window->desc); - } - - g_assert (window->display->focus_window != window); - - if (window->struts) - { - g_slist_free_full (window->struts, g_free); - window->struts = NULL; - - meta_topic (META_DEBUG_WORKAREA, - "Unmanaging window %s which has struts, so invalidating work areas", - window->desc); - invalidate_work_areas (window); - } - - g_clear_handle_id (&window->sync_request_timeout_id, g_source_remove); - - if (window->display->grab_window == window) - meta_display_end_grab_op (window->display, timestamp); - - g_assert (window->display->grab_window != window); - - if (window->maximized_horizontally || window->maximized_vertically) - unmaximize_window_before_freeing (window); - - meta_window_unqueue (window, META_QUEUE_CALC_SHOWING | - META_QUEUE_MOVE_RESIZE | - META_QUEUE_UPDATE_ICON); - - set_workspace_state (window, FALSE, NULL); - - g_assert (window->workspace == NULL); - -#ifndef G_DISABLE_CHECKS - tmp = workspace_manager->workspaces; - while (tmp != NULL) - { - MetaWorkspace *workspace = tmp->data; - - g_assert (g_list_find (workspace->windows, window) == NULL); - g_assert (g_list_find (workspace->mru_list, window) == NULL); - - tmp = tmp->next; - } -#endif - - if (window->monitor) - { - const MetaLogicalMonitor *old = window->monitor; - - window->monitor = NULL; - meta_window_main_monitor_changed (window, old); - } - - if (meta_window_is_in_stack (window)) - meta_stack_remove (window->display->stack, window); - - /* If an undecorated window is being withdrawn, that will change the - * stack as presented to the compositing manager, without actually - * changing the stacking order of X windows. - */ - meta_stack_tracker_queue_sync_stack (window->display->stack_tracker); - - if (window->display->autoraise_window == window) - meta_display_remove_autoraise_callback (window->display); - - META_WINDOW_GET_CLASS (window)->unmanage (window); - - meta_prefs_remove_listener (prefs_changed_callback, window); - meta_display_queue_check_fullscreen (window->display); - - g_signal_emit (window, window_signals[UNMANAGED], 0); - - g_object_unref (window); -} - -static void -set_wm_state (MetaWindow *window) -{ - if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) - meta_window_x11_set_wm_state (window); -} - -static void -set_net_wm_state (MetaWindow *window) -{ - if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) - meta_window_x11_set_net_wm_state (window); -} - -static void -set_allowed_actions_hint (MetaWindow *window) -{ - if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) - meta_window_x11_set_allowed_actions_hint (window); -} - -/** - * meta_window_located_on_workspace: - * @window: a #MetaWindow - * @workspace: a #MetaWorkspace - * - * Returns: whether @window is displayed on @workspace, or whether it - * will be displayed on all workspaces. - */ -gboolean -meta_window_located_on_workspace (MetaWindow *window, - MetaWorkspace *workspace) -{ - return (window->on_all_workspaces) || (window->workspace == workspace); -} - -static gboolean -is_minimized_foreach (MetaWindow *window, - void *data) -{ - gboolean *result = data; - - *result = window->minimized; - if (*result) - return FALSE; /* stop as soon as we find one */ - else - return TRUE; -} - -static gboolean -ancestor_is_minimized (MetaWindow *window) -{ - gboolean is_minimized; - - is_minimized = FALSE; - - meta_window_foreach_ancestor (window, is_minimized_foreach, &is_minimized); - - return is_minimized; -} - -/** - * meta_window_showing_on_its_workspace: - * @window: A #MetaWindow - * - * Returns: %TRUE if window would be visible, if its workspace was current - */ -gboolean -meta_window_showing_on_its_workspace (MetaWindow *window) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - gboolean showing; - gboolean is_desktop_or_dock; - MetaWorkspace* workspace_of_window; - - showing = TRUE; - - /* 1. See if we're minimized */ - if (window->minimized) - showing = FALSE; - - /* 2. See if we're in "show desktop" mode */ - is_desktop_or_dock = FALSE; - is_desktop_or_dock_foreach (window, - &is_desktop_or_dock); - - meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach, - &is_desktop_or_dock); - - if (window->on_all_workspaces) - workspace_of_window = workspace_manager->active_workspace; - else if (window->workspace) - workspace_of_window = window->workspace; - else /* This only seems to be needed for startup */ - workspace_of_window = NULL; - - if (showing && - workspace_of_window && workspace_of_window->showing_desktop && - !is_desktop_or_dock) - { - meta_verbose ("We're showing the desktop on the workspace(s) that window %s is on", - window->desc); - showing = FALSE; - } - - /* 3. See if an ancestor is minimized (note that - * ancestor's "mapped" field may not be up to date - * since it's being computed in this same idle queue) - */ - - if (showing) - { - if (ancestor_is_minimized (window)) - showing = FALSE; - } - - return showing; -} - -gboolean -meta_window_should_be_showing (MetaWindow *window) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - -#ifdef HAVE_WAYLAND - if (window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND && - !meta_wayland_surface_get_buffer (window->surface)) - return FALSE; -#endif - - /* Windows should be showing if they're located on the - * active workspace and they're showing on their own workspace. */ - return (meta_window_located_on_workspace (window, workspace_manager->active_workspace) && - meta_window_showing_on_its_workspace (window)); -} - -static void -implement_showing (MetaWindow *window, - gboolean showing) -{ - /* Actually show/hide the window */ - meta_verbose ("Implement showing = %d for window %s", - showing, window->desc); - - /* Some windows are not stackable until being showed, so add those now. */ - if (meta_window_is_stackable (window) && !meta_window_is_in_stack (window)) - meta_stack_add (window->display->stack, window); - - if (!showing) - { - /* When we manage a new window, we normally delay placing it - * until it is is first shown, but if we're previewing hidden - * windows we might want to know where they are on the screen, - * so we should place the window even if we're hiding it rather - * than showing it. - * Force placing windows only when they should be already mapped, - * see #751887 - */ - if (!window->placed && client_window_should_be_mapped (window)) - meta_window_force_placement (window, FALSE); - - meta_window_hide (window); - } - else - meta_window_show (window); - - if (!window->override_redirect) - sync_client_window_mapped (window); -} - -static void -meta_window_calc_showing (MetaWindow *window) -{ - implement_showing (window, meta_window_should_be_showing (window)); -} - -static guint queue_later[NUMBER_OF_QUEUES] = {0, 0, 0}; -static GSList *queue_pending[NUMBER_OF_QUEUES] = {NULL, NULL, NULL}; - -static int -stackcmp (gconstpointer a, gconstpointer b) -{ - MetaWindow *aw = (gpointer) a; - MetaWindow *bw = (gpointer) b; - - return meta_stack_windows_cmp (aw->display->stack, - aw, bw); -} - -static gboolean -idle_calc_showing (gpointer data) -{ - MetaDisplay *display = meta_get_display (); - GSList *tmp; - GSList *copy; - GSList *should_show; - GSList *should_hide; - GSList *unplaced; - GSList *displays; - guint queue_index = GPOINTER_TO_INT (data); - - COGL_TRACE_BEGIN_SCOPED (MetaWindowCalcShowing, "Window: Calc showing"); - - g_return_val_if_fail (queue_pending[queue_index] != NULL, FALSE); - - meta_topic (META_DEBUG_WINDOW_STATE, - "Clearing the calc_showing queue"); - - /* Work with a copy, for reentrancy. The allowed reentrancy isn't - * complete; destroying a window while we're in here would result in - * badness. But it's OK to queue/unqueue calc_showings. - */ - copy = g_slist_copy (queue_pending[queue_index]); - g_slist_free (queue_pending[queue_index]); - queue_pending[queue_index] = NULL; - queue_later[queue_index] = 0; - - destroying_windows_disallowed += 1; - - /* We map windows from top to bottom and unmap from bottom to - * top, to avoid extra expose events. The exception is - * for unplaced windows, which have to be mapped from bottom to - * top so placement works. - */ - should_show = NULL; - should_hide = NULL; - unplaced = NULL; - displays = NULL; - - COGL_TRACE_BEGIN (MetaWindowCalcShowingCalc, "Window: Calc showing (calc)"); - - tmp = copy; - while (tmp != NULL) - { - MetaWindow *window; - - window = tmp->data; - - if (!window->placed) - unplaced = g_slist_prepend (unplaced, window); - else if (meta_window_should_be_showing (window)) - should_show = g_slist_prepend (should_show, window); - else - should_hide = g_slist_prepend (should_hide, window); - - tmp = tmp->next; - } - - /* bottom to top */ - unplaced = g_slist_sort (unplaced, stackcmp); - should_hide = g_slist_sort (should_hide, stackcmp); - /* top to bottom */ - should_show = g_slist_sort (should_show, stackcmp); - should_show = g_slist_reverse (should_show); - - COGL_TRACE_END (MetaWindowCalcShowingCalc); - - COGL_TRACE_BEGIN (MetaWindowCalcShowingUnplaced, - "Window: Calc showing (calc unplaced)"); - - tmp = unplaced; - while (tmp != NULL) - { - MetaWindow *window; - - window = tmp->data; - - meta_window_calc_showing (window); - - tmp = tmp->next; - } - - COGL_TRACE_END (MetaWindowCalcShowingUnplaced); - - meta_stack_freeze (display->stack); - - COGL_TRACE_BEGIN (MetaWindowCalcShowingShow, "Window: Calc showing (show)"); - tmp = should_show; - while (tmp != NULL) - { - MetaWindow *window; - - window = tmp->data; - - implement_showing (window, TRUE); - - tmp = tmp->next; - } - COGL_TRACE_END (MetaWindowCalcShowingShow); - - COGL_TRACE_BEGIN (MetaWindowCalcShowingHide, "Window: Calc showing (hide)"); - tmp = should_hide; - while (tmp != NULL) - { - MetaWindow *window; - - window = tmp->data; - - implement_showing (window, FALSE); - - tmp = tmp->next; - } - COGL_TRACE_END (MetaWindowCalcShowingHide); - - COGL_TRACE_BEGIN (MetaWindowCalcShowingSync, - "Window: Calc showing (sync stack)"); - meta_stack_thaw (display->stack); - COGL_TRACE_END (MetaWindowCalcShowingSync); - - tmp = copy; - while (tmp != NULL) - { - MetaWindow *window; - - window = tmp->data; - - /* important to set this here for reentrancy - - * if we queue a window again while it's in "copy", - * then queue_calc_showing will just return since - * we are still in the calc_showing queue - */ - window->is_in_queues &= ~META_QUEUE_CALC_SHOWING; - - tmp = tmp->next; - } - - if (meta_prefs_get_focus_mode () != G_DESKTOP_FOCUS_MODE_CLICK) - { - /* When display->mouse_mode is false, we want to ignore - * EnterNotify events unless they come from mouse motion. To do - * that, we set a sentinel property on the root window if we're - * not in mouse_mode. - */ - tmp = should_show; - while (tmp != NULL) - { - MetaWindow *window = tmp->data; - MetaDisplay *display = window->display; - - if (display->x11_display && !display->mouse_mode) - meta_x11_display_increment_focus_sentinel (display->x11_display); - - tmp = tmp->next; - } - } - - g_slist_free (copy); - - g_slist_free (unplaced); - g_slist_free (should_show); - g_slist_free (should_hide); - g_slist_free (displays); - - destroying_windows_disallowed -= 1; - - return FALSE; -} - -#ifdef WITH_VERBOSE_MODE -static const gchar* meta_window_queue_names[NUMBER_OF_QUEUES] = - {"calc_showing", "move_resize", "update_icon"}; -#endif - -static void -meta_window_unqueue (MetaWindow *window, guint queuebits) -{ - gint queuenum; - - for (queuenum=0; queuenum<NUMBER_OF_QUEUES; queuenum++) - { - if ((queuebits & 1<<queuenum) /* they have asked to unqueue */ - && - (window->is_in_queues & 1<<queuenum)) /* it's in the queue */ - { - - meta_topic (META_DEBUG_WINDOW_STATE, - "Removing %s from the %s queue", - window->desc, - meta_window_queue_names[queuenum]); - - /* Note that window may not actually be in the queue - * because it may have been in "copy" inside the idle handler - */ - queue_pending[queuenum] = g_slist_remove (queue_pending[queuenum], window); - window->is_in_queues &= ~(1<<queuenum); - - /* Okay, so maybe we've used up all the entries in the queue. - * In that case, we should kill the function that deals with - * the queue, because there's nothing left for it to do. - */ - if (queue_pending[queuenum] == NULL && queue_later[queuenum] != 0) - { - meta_later_remove (queue_later[queuenum]); - queue_later[queuenum] = 0; - } - } - } -} - -static void -meta_window_flush_calc_showing (MetaWindow *window) -{ - if (window->is_in_queues & META_QUEUE_CALC_SHOWING) - { - meta_window_unqueue (window, META_QUEUE_CALC_SHOWING); - meta_window_calc_showing (window); - } -} - -void -meta_window_queue (MetaWindow *window, guint queuebits) -{ - guint queuenum; - - /* Easier to debug by checking here rather than in the idle */ - g_return_if_fail (!window->override_redirect || (queuebits & META_QUEUE_MOVE_RESIZE) == 0); - - for (queuenum=0; queuenum<NUMBER_OF_QUEUES; queuenum++) - { - if (queuebits & 1<<queuenum) - { - /* Data which varies between queues. - * Yes, these do look a lot like associative arrays: - * I seem to be turning into a Perl programmer. - */ - - const MetaLaterType window_queue_later_when[NUMBER_OF_QUEUES] = - { - META_LATER_CALC_SHOWING, /* CALC_SHOWING */ - META_LATER_RESIZE, /* MOVE_RESIZE */ - META_LATER_BEFORE_REDRAW /* UPDATE_ICON */ - }; - - const GSourceFunc window_queue_later_handler[NUMBER_OF_QUEUES] = - { - idle_calc_showing, - idle_move_resize, - idle_update_icon, - }; - - /* If we're about to drop the window, there's no point in putting - * it on a queue. - */ - if (window->unmanaging) - break; - - /* If the window already claims to be in that queue, there's no - * point putting it in the queue. - */ - if (window->is_in_queues & 1<<queuenum) - break; - - meta_topic (META_DEBUG_WINDOW_STATE, - "Putting %s in the %s queue", - window->desc, - meta_window_queue_names[queuenum]); - - /* So, mark it as being in this queue. */ - window->is_in_queues |= 1<<queuenum; - - /* There's not a lot of point putting things into a queue if - * nobody's on the other end pulling them out. Therefore, - * let's check to see whether an idle handler exists to do - * that. If not, we'll create one. - */ - - if (queue_later[queuenum] == 0) - queue_later[queuenum] = meta_later_add - ( - window_queue_later_when[queuenum], - window_queue_later_handler[queuenum], - GUINT_TO_POINTER(queuenum), - NULL - ); - - /* And now we actually put it on the queue. */ - queue_pending[queuenum] = g_slist_prepend (queue_pending[queuenum], - window); - } - } -} - -static gboolean -intervening_user_event_occurred (MetaWindow *window) -{ - guint32 compare; - MetaWindow *focus_window; - - focus_window = window->display->focus_window; - - meta_topic (META_DEBUG_STARTUP, - "COMPARISON:\n" - " net_wm_user_time_set : %d\n" - " net_wm_user_time : %u\n" - " initial_timestamp_set: %d\n" - " initial_timestamp : %u", - window->net_wm_user_time_set, - window->net_wm_user_time, - window->initial_timestamp_set, - window->initial_timestamp); - if (focus_window != NULL) - { - meta_topic (META_DEBUG_STARTUP, - "COMPARISON (continued):\n" - " focus_window : %s\n" - " fw->net_wm_user_time_set : %d\n" - " fw->net_wm_user_time : %u", - focus_window->desc, - focus_window->net_wm_user_time_set, - focus_window->net_wm_user_time); - } - - /* We expect the most common case for not focusing a new window - * to be when a hint to not focus it has been set. Since we can - * deal with that case rapidly, we use special case it--this is - * merely a preliminary optimization. :) - */ - if ( ((window->net_wm_user_time_set == TRUE) && - (window->net_wm_user_time == 0)) - || - ((window->initial_timestamp_set == TRUE) && - (window->initial_timestamp == 0))) - { - meta_topic (META_DEBUG_STARTUP, - "window %s explicitly requested no focus", - window->desc); - return TRUE; - } - - if (!(window->net_wm_user_time_set) && !(window->initial_timestamp_set)) - { - meta_topic (META_DEBUG_STARTUP, - "no information about window %s found", - window->desc); - return FALSE; - } - - if (focus_window != NULL && - !focus_window->net_wm_user_time_set) - { - meta_topic (META_DEBUG_STARTUP, - "focus window, %s, doesn't have a user time set yet!", - window->desc); - return FALSE; - } - - /* To determine the "launch" time of an application, - * startup-notification can set the TIMESTAMP and the - * application (usually via its toolkit such as gtk or qt) can - * set the _NET_WM_USER_TIME. If both are set, we need to be - * using the newer of the two values. - * - * See http://bugzilla.gnome.org/show_bug.cgi?id=573922 - */ - compare = 0; - if (window->net_wm_user_time_set && - window->initial_timestamp_set) - compare = - XSERVER_TIME_IS_BEFORE (window->net_wm_user_time, - window->initial_timestamp) ? - window->initial_timestamp : window->net_wm_user_time; - else if (window->net_wm_user_time_set) - compare = window->net_wm_user_time; - else if (window->initial_timestamp_set) - compare = window->initial_timestamp; - - if ((focus_window != NULL) && - XSERVER_TIME_IS_BEFORE (compare, focus_window->net_wm_user_time)) - { - meta_topic (META_DEBUG_STARTUP, - "window %s focus prevented by other activity; %u < %u", - window->desc, - compare, - focus_window->net_wm_user_time); - return TRUE; - } - else - { - meta_topic (META_DEBUG_STARTUP, - "new window %s with no intervening events", - window->desc); - return FALSE; - } -} - -/* This function is an ugly hack. It's experimental in nature and ought to be - * replaced by a real hint from the app to the WM if we decide the experimental - * behavior is worthwhile. The basic idea is to get more feedback about how - * usage scenarios of "strict" focus users and what they expect. See #326159. - */ -static gboolean -window_is_terminal (MetaWindow *window) -{ - if (window == NULL || window->res_class == NULL) - return FALSE; - - /* - * Compare res_class, which is not user-settable, and thus theoretically - * a more-reliable indication of term-ness. - */ - - /* gnome-terminal -- if you couldn't guess */ - if (strcmp (window->res_class, "Gnome-terminal") == 0) - return TRUE; - /* xterm, rxvt, aterm */ - else if (strcmp (window->res_class, "XTerm") == 0) - return TRUE; - /* konsole, KDE's terminal program */ - else if (strcmp (window->res_class, "Konsole") == 0) - return TRUE; - /* rxvt-unicode */ - else if (strcmp (window->res_class, "URxvt") == 0) - return TRUE; - /* eterm */ - else if (strcmp (window->res_class, "Eterm") == 0) - return TRUE; - /* KTerm -- some terminal not KDE based; so not like Konsole */ - else if (strcmp (window->res_class, "KTerm") == 0) - return TRUE; - /* Multi-gnome-terminal */ - else if (strcmp (window->res_class, "Multi-gnome-terminal") == 0) - return TRUE; - /* mlterm ("multi lingual terminal emulator on X") */ - else if (strcmp (window->res_class, "mlterm") == 0) - return TRUE; - /* Terminal -- XFCE Terminal */ - else if (strcmp (window->res_class, "Terminal") == 0) - return TRUE; - - return FALSE; -} - -/* This function determines what state the window should have assuming that it - * and the focus_window have no relation - */ -static void -window_state_on_map (MetaWindow *window, - gboolean *takes_focus, - gboolean *places_on_top) -{ - gboolean intervening_events; - - intervening_events = intervening_user_event_occurred (window); - - *takes_focus = !intervening_events; - *places_on_top = *takes_focus; - - /* don't initially focus windows that are intended to not accept - * focus - */ - if (!meta_window_is_focusable (window)) - { - *takes_focus = FALSE; - return; - } - - /* Terminal usage may be different; some users intend to launch - * many apps in quick succession or to just view things in the new - * window while still interacting with the terminal. In that case, - * apps launched from the terminal should not take focus. This - * isn't quite the same as not allowing focus to transfer from - * terminals due to new window map, but the latter is a much easier - * approximation to enforce so we do that. - */ - if (*takes_focus && - meta_prefs_get_focus_new_windows () == G_DESKTOP_FOCUS_NEW_WINDOWS_STRICT && - !window->display->allow_terminal_deactivation && - window_is_terminal (window->display->focus_window) && - !meta_window_is_ancestor_of_transient (window->display->focus_window, - window)) - { - meta_topic (META_DEBUG_FOCUS, - "focus_window is terminal; not focusing new window."); - *takes_focus = FALSE; - *places_on_top = FALSE; - } - - switch (window->type) - { - case META_WINDOW_UTILITY: - case META_WINDOW_TOOLBAR: - *takes_focus = FALSE; - *places_on_top = FALSE; - break; - case META_WINDOW_DOCK: - case META_WINDOW_DESKTOP: - case META_WINDOW_SPLASHSCREEN: - case META_WINDOW_MENU: - /* override redirect types: */ - case META_WINDOW_DROPDOWN_MENU: - case META_WINDOW_POPUP_MENU: - case META_WINDOW_TOOLTIP: - case META_WINDOW_NOTIFICATION: - case META_WINDOW_COMBO: - case META_WINDOW_DND: - case META_WINDOW_OVERRIDE_OTHER: - /* don't focus any of these; places_on_top may be irrelevant for some of - * these (e.g. dock)--but you never know--the focus window might also be - * of the same type in some weird situation... - */ - *takes_focus = FALSE; - break; - case META_WINDOW_NORMAL: - case META_WINDOW_DIALOG: - case META_WINDOW_MODAL_DIALOG: - /* The default is correct for these */ - break; - } -} - -static gboolean -windows_overlap (const MetaWindow *w1, const MetaWindow *w2) -{ - MetaRectangle w1rect, w2rect; - meta_window_get_frame_rect (w1, &w1rect); - meta_window_get_frame_rect (w2, &w2rect); - return meta_rectangle_overlap (&w1rect, &w2rect); -} - -/* Returns whether a new window would be covered by any - * existing window on the same workspace that is set - * to be "above" ("always on top"). A window that is not - * set "above" would be underneath the new window anyway. - * - * We take "covered" to mean even partially covered, but - * some people might prefer entirely covered. I think it - * is more useful to behave this way if any part of the - * window is covered, because a partial coverage could be - * (say) ninety per cent and almost indistinguishable from total. - */ -static gboolean -window_would_be_covered (const MetaWindow *newbie) -{ - MetaWorkspace *workspace = meta_window_get_workspace ((MetaWindow *)newbie); - GList *tmp, *windows; - - windows = meta_workspace_list_windows (workspace); - - tmp = windows; - while (tmp != NULL) - { - MetaWindow *w = tmp->data; - - if (w->wm_state_above && w != newbie) - { - /* We have found a window that is "above". Perhaps it overlaps. */ - if (windows_overlap (w, newbie)) - { - g_list_free (windows); /* clean up... */ - return TRUE; /* yes, it does */ - } - } - - tmp = tmp->next; - } - - g_list_free (windows); - return FALSE; /* none found */ -} - -void -meta_window_force_placement (MetaWindow *window, - gboolean force_move) -{ - MetaMoveResizeFlags flags; - - if (window->placed) - return; - - /* We have to recalc the placement here since other windows may - * have been mapped/placed since we last did constrain_position - */ - - /* calc_placement is an efficiency hack to avoid - * multiple placement calculations before we finally - * show the window. - */ - window->calc_placement = TRUE; - - flags = META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION; - if (force_move) - flags |= META_MOVE_RESIZE_FORCE_MOVE; - - meta_window_move_resize_internal (window, - flags, - META_GRAVITY_NORTH_WEST, - window->unconstrained_rect); - window->calc_placement = FALSE; - - /* don't ever do the initial position constraint thing again. - * This is toggled here so that initially-iconified windows - * still get placed when they are ultimately shown. - */ - window->placed = TRUE; - - /* Don't want to accidentally reuse the fact that we had been denied - * focus in any future constraints unless we're denied focus again. - */ - window->denied_focus_and_not_transient = FALSE; -} - -static void -meta_window_show (MetaWindow *window) -{ - gboolean did_show; - gboolean takes_focus_on_map; - gboolean place_on_top_on_map; - gboolean needs_stacking_adjustment; - MetaWindow *focus_window; - gboolean notify_demands_attention = FALSE; - MetaDisplay *display = window->display; - - meta_topic (META_DEBUG_WINDOW_STATE, - "Showing window %s, shaded: %d iconic: %d placed: %d", - window->desc, window->shaded, window->iconic, window->placed); - - focus_window = window->display->focus_window; /* May be NULL! */ - did_show = FALSE; - window_state_on_map (window, &takes_focus_on_map, &place_on_top_on_map); - needs_stacking_adjustment = FALSE; - - meta_topic (META_DEBUG_WINDOW_STATE, - "Window %s %s focus on map, and %s place on top on map.", - window->desc, - takes_focus_on_map ? "does" : "does not", - place_on_top_on_map ? "does" : "does not"); - - /* Now, in some rare cases we should *not* put a new window on top. - * These cases include certain types of windows showing for the first - * time, and any window which would be covered because of another window - * being set "above" ("always on top"). - * - * FIXME: Although "place_on_top_on_map" and "takes_focus_on_map" are - * generally based on the window type, there is a special case when the - * focus window is a terminal for them both to be false; this should - * probably rather be a term in the "if" condition below. - */ - - if ( focus_window != NULL && window->showing_for_first_time && - ( (!place_on_top_on_map && !takes_focus_on_map) || - window_would_be_covered (window) ) - ) { - if (!meta_window_is_ancestor_of_transient (focus_window, window)) - { - needs_stacking_adjustment = TRUE; - if (!window->placed) - window->denied_focus_and_not_transient = TRUE; - } - } - - if (!window->placed) - { - if (window->monitor && - meta_prefs_get_auto_maximize() && - window->showing_for_first_time && - window->has_maximize_func) - { - MetaRectangle work_area; - meta_window_get_work_area_for_monitor (window, window->monitor->number, &work_area); - /* Automaximize windows that map with a size > MAX_UNMAXIMIZED_WINDOW_AREA of the work area */ - if (window->rect.width * window->rect.height > work_area.width * work_area.height * MAX_UNMAXIMIZED_WINDOW_AREA) - { - window->maximize_horizontally_after_placement = TRUE; - window->maximize_vertically_after_placement = TRUE; - } - } - meta_window_force_placement (window, FALSE); - } - - if (needs_stacking_adjustment) - { - gboolean overlap; - - /* This window isn't getting focus on map. We may need to do some - * special handing with it in regards to - * - the stacking of the window - * - the MRU position of the window - * - the demands attention setting of the window - * - * Firstly, set the flag so we don't give the window focus anyway - * and confuse people. - */ - - takes_focus_on_map = FALSE; - - overlap = windows_overlap (window, focus_window); - - /* We want alt tab to go to the denied-focus window */ - ensure_mru_position_after (window, focus_window); - - /* We don't want the denied-focus window to obscure the focus - * window, and if we're in both click-to-focus mode and - * raise-on-click mode then we want to maintain the invariant - * that MRU order == stacking order. The need for this if - * comes from the fact that in sloppy/mouse focus the focus - * window may not overlap other windows and also can be - * considered "below" them; this combination means that - * placing the denied-focus window "below" the focus window - * in the stack when it doesn't overlap it confusingly places - * that new window below a lot of other windows. - */ - if (overlap || - (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK && - meta_prefs_get_raise_on_click ())) - meta_window_stack_just_below (window, focus_window); - - /* If the window will be obscured by the focus window, then the - * user might not notice the window appearing so set the - * demands attention hint. - * - * We set the hint ourselves rather than calling - * meta_window_set_demands_attention() because that would cause - * a recalculation of overlap, and a call to set_net_wm_state() - * which we are going to call ourselves here a few lines down. - */ - if (overlap) - { - if (!window->wm_state_demands_attention) - { - window->wm_state_demands_attention = TRUE; - notify_demands_attention = TRUE; - } - } - } - - if (window->hidden) - { - meta_stack_freeze (window->display->stack); - window->hidden = FALSE; - meta_stack_thaw (window->display->stack); - did_show = TRUE; - } - - if (window->iconic) - { - window->iconic = FALSE; - set_wm_state (window); - } - - if (!window->visible_to_compositor) - { - MetaCompEffect effect = META_COMP_EFFECT_NONE; - - window->visible_to_compositor = TRUE; - - switch (window->pending_compositor_effect) - { - case META_COMP_EFFECT_CREATE: - case META_COMP_EFFECT_UNMINIMIZE: - effect = window->pending_compositor_effect; - break; - case META_COMP_EFFECT_NONE: - case META_COMP_EFFECT_DESTROY: - case META_COMP_EFFECT_MINIMIZE: - break; - } - - meta_compositor_show_window (window->display->compositor, - window, effect); - window->pending_compositor_effect = META_COMP_EFFECT_NONE; - } - - /* We don't want to worry about all cases from inside - * implement_showing(); we only want to worry about focus if this - * window has not been shown before. - */ - if (window->showing_for_first_time) - { - window->showing_for_first_time = FALSE; - if (takes_focus_on_map) - { - guint32 timestamp; - - timestamp = meta_display_get_current_time_roundtrip (window->display); - - meta_window_focus (window, timestamp); - } - else if (display->x11_display) - { - /* Prevent EnterNotify events in sloppy/mouse focus from - * erroneously focusing the window that had been denied - * focus. FIXME: This introduces a race; I have a couple - * ideas for a better way to accomplish the same thing, but - * they're more involved so do it this way for now. - */ - meta_x11_display_increment_focus_sentinel (display->x11_display); - } - } - - set_net_wm_state (window); - - if (did_show && window->struts) - { - meta_topic (META_DEBUG_WORKAREA, - "Mapped window %s with struts, so invalidating work areas", - window->desc); - invalidate_work_areas (window); - } - - if (did_show) - meta_display_queue_check_fullscreen (window->display); - - /* - * Now that we have shown the window, we no longer want to consider the - * initial timestamp in any subsequent deliberations whether to focus this - * window or not, so clear the flag. - * - * See http://bugzilla.gnome.org/show_bug.cgi?id=573922 - */ - window->initial_timestamp_set = FALSE; - - if (notify_demands_attention) - { - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_DEMANDS_ATTENTION]); - g_signal_emit_by_name (window->display, "window-demands-attention", - window); - } - - if (did_show) - g_signal_emit (window, window_signals[SHOWN], 0); -} - -static void -meta_window_hide (MetaWindow *window) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - gboolean did_hide; - - meta_topic (META_DEBUG_WINDOW_STATE, - "Hiding window %s", window->desc); - - if (window->visible_to_compositor) - { - MetaCompEffect effect = META_COMP_EFFECT_NONE; - - window->visible_to_compositor = FALSE; - - switch (window->pending_compositor_effect) - { - case META_COMP_EFFECT_CREATE: - case META_COMP_EFFECT_UNMINIMIZE: - case META_COMP_EFFECT_NONE: - break; - case META_COMP_EFFECT_DESTROY: - case META_COMP_EFFECT_MINIMIZE: - effect = window->pending_compositor_effect; - break; - } - - meta_compositor_hide_window (window->display->compositor, window, effect); - window->pending_compositor_effect = META_COMP_EFFECT_NONE; - } - - did_hide = FALSE; - - if (!window->hidden) - { - meta_stack_freeze (window->display->stack); - window->hidden = TRUE; - meta_stack_thaw (window->display->stack); - - did_hide = TRUE; - } - - if (!window->iconic) - { - window->iconic = TRUE; - set_wm_state (window); - } - - set_net_wm_state (window); - - if (did_hide && window->struts) - { - meta_topic (META_DEBUG_WORKAREA, - "Unmapped window %s with struts, so invalidating work areas", - window->desc); - invalidate_work_areas (window); - } - - if (window->has_focus) - { - MetaWindow *not_this_one = NULL; - MetaWorkspace *my_workspace = meta_window_get_workspace (window); - guint32 timestamp = meta_display_get_current_time_roundtrip (window->display); - - /* - * If this window is modal, passing the not_this_one window to - * _focus_default_window() makes the focus to be given to this window's - * ancestor. This can only be the case if the window is on the currently - * active workspace; when it is not, we need to pass in NULL, so as to - * focus the default window for the active workspace (this scenario - * arises when we are switching workspaces). - * We also pass in NULL if we are in the process of hiding all non-desktop - * windows to avoid unexpected changes to the stacking order. - */ - if (my_workspace == workspace_manager->active_workspace && - !my_workspace->showing_desktop) - not_this_one = window; - - meta_workspace_focus_default_window (workspace_manager->active_workspace, - not_this_one, - timestamp); - } - - if (did_hide) - meta_display_queue_check_fullscreen (window->display); -} - -static gboolean -queue_calc_showing_func (MetaWindow *window, - void *data) -{ - meta_window_queue(window, META_QUEUE_CALC_SHOWING); - return TRUE; -} - -void -meta_window_minimize (MetaWindow *window) -{ - g_return_if_fail (!window->override_redirect); - - if (!window->minimized) - { - window->minimized = TRUE; - window->pending_compositor_effect = META_COMP_EFFECT_MINIMIZE; - meta_window_queue(window, META_QUEUE_CALC_SHOWING); - - meta_window_foreach_transient (window, - queue_calc_showing_func, - NULL); - - if (window->has_focus) - { - meta_topic (META_DEBUG_FOCUS, - "Focusing default window due to minimization of focus window %s", - window->desc); - } - else - { - meta_topic (META_DEBUG_FOCUS, - "Minimizing window %s which doesn't have the focus", - window->desc); - } - - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_MINIMIZED]); - } -} - -void -meta_window_unminimize (MetaWindow *window) -{ - g_return_if_fail (!window->override_redirect); - - if (window->minimized) - { - window->minimized = FALSE; - window->pending_compositor_effect = META_COMP_EFFECT_UNMINIMIZE; - meta_window_queue(window, META_QUEUE_CALC_SHOWING); - - meta_window_foreach_transient (window, - queue_calc_showing_func, - NULL); - - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_MINIMIZED]); - } -} - -static void -ensure_size_hints_satisfied (MetaRectangle *rect, - const XSizeHints *size_hints) -{ - int minw, minh, maxw, maxh; /* min/max width/height */ - int basew, baseh, winc, hinc; /* base width/height, width/height increment */ - int extra_width, extra_height; - - minw = size_hints->min_width; minh = size_hints->min_height; - maxw = size_hints->max_width; maxh = size_hints->max_height; - basew = size_hints->base_width; baseh = size_hints->base_height; - winc = size_hints->width_inc; hinc = size_hints->height_inc; - - /* First, enforce min/max size constraints */ - rect->width = CLAMP (rect->width, minw, maxw); - rect->height = CLAMP (rect->height, minh, maxh); - - /* Now, verify size increment constraints are satisfied, or make them be */ - extra_width = (rect->width - basew) % winc; - extra_height = (rect->height - baseh) % hinc; - - rect->width -= extra_width; - rect->height -= extra_height; - - /* Adjusting width/height down, as done above, may violate minimum size - * constraints, so one last fix. - */ - if (rect->width < minw) - rect->width += ((minw - rect->width)/winc + 1)*winc; - if (rect->height < minh) - rect->height += ((minh - rect->height)/hinc + 1)*hinc; -} - -static void -meta_window_save_rect (MetaWindow *window) -{ - if (!(META_WINDOW_MAXIMIZED (window) || META_WINDOW_TILED_SIDE_BY_SIDE (window) || window->fullscreen)) - { - /* save size/pos as appropriate args for move_resize */ - if (!window->maximized_horizontally) - { - window->saved_rect.x = window->rect.x; - window->saved_rect.width = window->rect.width; - } - if (!window->maximized_vertically) - { - window->saved_rect.y = window->rect.y; - window->saved_rect.height = window->rect.height; - } - } -} - -void -meta_window_maximize_internal (MetaWindow *window, - MetaMaximizeFlags directions, - MetaRectangle *saved_rect) -{ - /* At least one of the two directions ought to be set */ - gboolean maximize_horizontally, maximize_vertically; - maximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL; - maximize_vertically = directions & META_MAXIMIZE_VERTICAL; - g_assert (maximize_horizontally || maximize_vertically); - - meta_topic (META_DEBUG_WINDOW_OPS, - "Maximizing %s%s", - window->desc, - maximize_horizontally && maximize_vertically ? "" : - maximize_horizontally ? " horizontally" : - maximize_vertically ? " vertically" : "BUGGGGG"); - - if (saved_rect != NULL) - window->saved_rect = *saved_rect; - else - meta_window_save_rect (window); - - if (maximize_horizontally && maximize_vertically) - window->saved_maximize = TRUE; - - window->maximized_horizontally = - window->maximized_horizontally || maximize_horizontally; - window->maximized_vertically = - window->maximized_vertically || maximize_vertically; - - /* Update the edge constraints */ - update_edge_constraints (window); - - meta_window_recalc_features (window); - set_net_wm_state (window); - - if (window->monitor && window->monitor->in_fullscreen) - meta_display_queue_check_fullscreen (window->display); - - g_object_freeze_notify (G_OBJECT (window)); - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_MAXIMIZED_HORIZONTALLY]); - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_MAXIMIZED_VERTICALLY]); - g_object_thaw_notify (G_OBJECT (window)); -} - -void -meta_window_maximize (MetaWindow *window, - MetaMaximizeFlags directions) -{ - MetaRectangle *saved_rect = NULL; - gboolean maximize_horizontally, maximize_vertically; - - g_return_if_fail (!window->override_redirect); - - /* At least one of the two directions ought to be set */ - maximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL; - maximize_vertically = directions & META_MAXIMIZE_VERTICAL; - g_assert (maximize_horizontally || maximize_vertically); - - /* Only do something if the window isn't already maximized in the - * given direction(s). - */ - if ((maximize_horizontally && !window->maximized_horizontally) || - (maximize_vertically && !window->maximized_vertically)) - { - if (window->shaded && maximize_vertically) - { - /* Shading sucks anyway; I'm not adding a timestamp argument - * to this function just for this niche usage & corner case. - */ - guint32 timestamp = - meta_display_get_current_time_roundtrip (window->display); - meta_window_unshade (window, timestamp); - } - - /* if the window hasn't been placed yet, we'll maximize it then - */ - if (!window->placed) - { - window->maximize_horizontally_after_placement = - window->maximize_horizontally_after_placement || - maximize_horizontally; - window->maximize_vertically_after_placement = - window->maximize_vertically_after_placement || - maximize_vertically; - return; - } - - if (window->tile_mode != META_TILE_NONE) - { - saved_rect = &window->saved_rect; - - window->maximized_vertically = FALSE; - window->tile_mode = META_TILE_NONE; - } - - meta_window_maximize_internal (window, - directions, - saved_rect); - - MetaRectangle old_frame_rect, old_buffer_rect; - - meta_window_get_frame_rect (window, &old_frame_rect); - meta_window_get_buffer_rect (window, &old_buffer_rect); - - meta_compositor_size_change_window (window->display->compositor, window, - META_SIZE_CHANGE_MAXIMIZE, - &old_frame_rect, &old_buffer_rect); - - meta_window_move_resize_internal (window, - (META_MOVE_RESIZE_MOVE_ACTION | - META_MOVE_RESIZE_RESIZE_ACTION | - META_MOVE_RESIZE_STATE_CHANGED), - META_GRAVITY_NORTH_WEST, - window->unconstrained_rect); - } -} - -/** - * meta_window_get_maximized: - * @window: a #MetaWindow - * - * Gets the current maximization state of the window, as combination - * of the %META_MAXIMIZE_HORIZONTAL and %META_MAXIMIZE_VERTICAL flags; - * - * Return value: current maximization state - */ -MetaMaximizeFlags -meta_window_get_maximized (MetaWindow *window) -{ - return ((window->maximized_horizontally ? META_MAXIMIZE_HORIZONTAL : 0) | - (window->maximized_vertically ? META_MAXIMIZE_VERTICAL : 0)); -} - -/** - * meta_window_is_fullscreen: - * @window: a #MetaWindow - * - * Return value: %TRUE if the window is currently fullscreen - */ -gboolean -meta_window_is_fullscreen (MetaWindow *window) -{ - return window->fullscreen; -} - -/** - * meta_window_is_screen_sized: - * @window: A #MetaWindow - * - * Return value: %TRUE if the window is occupies the - * the whole screen (all monitors). - */ -gboolean -meta_window_is_screen_sized (MetaWindow *window) -{ - MetaRectangle window_rect; - int screen_width, screen_height; - - meta_display_get_size (window->display, &screen_width, &screen_height); - meta_window_get_frame_rect (window, &window_rect); - - if (window_rect.x == 0 && window_rect.y == 0 && - window_rect.width == screen_width && window_rect.height == screen_height) - return TRUE; - - return FALSE; -} - -/** - * meta_window_is_monitor_sized: - * @window: a #MetaWindow - * - * Return value: %TRUE if the window is occupies an entire monitor or - * the whole screen. - */ -gboolean -meta_window_is_monitor_sized (MetaWindow *window) -{ - if (!window->monitor) - return FALSE; - - if (window->fullscreen) - return TRUE; - - if (meta_window_is_screen_sized (window)) - return TRUE; - - if (window->override_redirect) - { - MetaRectangle window_rect, monitor_rect; - - meta_window_get_frame_rect (window, &window_rect); - meta_display_get_monitor_geometry (window->display, window->monitor->number, &monitor_rect); - - if (meta_rectangle_equal (&window_rect, &monitor_rect)) - return TRUE; - } - - return FALSE; -} - -/** - * meta_window_is_on_primary_monitor: - * @window: a #MetaWindow - * - * Return value: %TRUE if the window is on the primary monitor - */ -gboolean -meta_window_is_on_primary_monitor (MetaWindow *window) -{ - g_return_val_if_fail (window->monitor, FALSE); - - return window->monitor->is_primary; -} - -static void -meta_window_get_tile_fraction (MetaWindow *window, - MetaTileMode tile_mode, - double *fraction) -{ - MetaWindow *tile_match; - - /* Make sure the tile match is up-to-date and matches the - * passed in mode rather than the current state - */ - tile_match = meta_window_find_tile_match (window, tile_mode); - - if (tile_mode == META_TILE_NONE) - *fraction = -1.; - else if (tile_mode == META_TILE_MAXIMIZED) - *fraction = 1.; - else if (tile_match) - *fraction = 1. - tile_match->tile_hfraction; - else if (META_WINDOW_TILED_SIDE_BY_SIDE (window)) - { - if (window->tile_mode != tile_mode) - *fraction = 1. - window->tile_hfraction; - else - *fraction = window->tile_hfraction; - } - else - *fraction = .5; -} - -static void -meta_window_update_tile_fraction (MetaWindow *window, - int new_w, - int new_h) -{ - MetaWindow *tile_match = window->tile_match; - MetaRectangle work_area; - - if (!META_WINDOW_TILED_SIDE_BY_SIDE (window)) - return; - - meta_window_get_work_area_for_monitor (window, - window->tile_monitor_number, - &work_area); - window->tile_hfraction = (double)new_w / work_area.width; - - if (tile_match && window->display->grab_window == window) - meta_window_tile (tile_match, tile_match->tile_mode); -} - -static void -update_edge_constraints (MetaWindow *window) -{ - switch (window->tile_mode) - { - case META_TILE_NONE: - window->edge_constraints.top = META_EDGE_CONSTRAINT_NONE; - window->edge_constraints.right = META_EDGE_CONSTRAINT_NONE; - window->edge_constraints.bottom = META_EDGE_CONSTRAINT_NONE; - window->edge_constraints.left = META_EDGE_CONSTRAINT_NONE; - break; - - case META_TILE_MAXIMIZED: - window->edge_constraints.top = META_EDGE_CONSTRAINT_MONITOR; - window->edge_constraints.right = META_EDGE_CONSTRAINT_MONITOR; - window->edge_constraints.bottom = META_EDGE_CONSTRAINT_MONITOR; - window->edge_constraints.left = META_EDGE_CONSTRAINT_MONITOR; - break; - - case META_TILE_LEFT: - window->edge_constraints.top = META_EDGE_CONSTRAINT_MONITOR; - - if (window->tile_match) - window->edge_constraints.right = META_EDGE_CONSTRAINT_WINDOW; - else - window->edge_constraints.right = META_EDGE_CONSTRAINT_NONE; - - window->edge_constraints.bottom = META_EDGE_CONSTRAINT_MONITOR; - window->edge_constraints.left = META_EDGE_CONSTRAINT_MONITOR; - break; - - case META_TILE_RIGHT: - window->edge_constraints.top = META_EDGE_CONSTRAINT_MONITOR; - window->edge_constraints.right = META_EDGE_CONSTRAINT_MONITOR; - window->edge_constraints.bottom = META_EDGE_CONSTRAINT_MONITOR; - - if (window->tile_match) - window->edge_constraints.left = META_EDGE_CONSTRAINT_WINDOW; - else - window->edge_constraints.left = META_EDGE_CONSTRAINT_NONE; - break; - } - - /* h/vmaximize also modify the edge constraints */ - if (window->maximized_vertically) - { - window->edge_constraints.top = META_EDGE_CONSTRAINT_MONITOR; - window->edge_constraints.bottom = META_EDGE_CONSTRAINT_MONITOR; - } - - if (window->maximized_horizontally) - { - window->edge_constraints.right = META_EDGE_CONSTRAINT_MONITOR; - window->edge_constraints.left = META_EDGE_CONSTRAINT_MONITOR; - } -} - -void -meta_window_untile (MetaWindow *window) -{ - window->tile_monitor_number = - window->saved_maximize ? window->monitor->number - : -1; - window->tile_mode = - window->saved_maximize ? META_TILE_MAXIMIZED - : META_TILE_NONE; - - if (window->saved_maximize) - meta_window_maximize (window, META_MAXIMIZE_BOTH); - else - meta_window_unmaximize (window, META_MAXIMIZE_BOTH); -} - -void -meta_window_tile (MetaWindow *window, - MetaTileMode tile_mode) -{ - MetaMaximizeFlags directions; - MetaRectangle old_frame_rect, old_buffer_rect; - - meta_window_get_tile_fraction (window, tile_mode, &window->tile_hfraction); - window->tile_mode = tile_mode; - - /* Don't do anything if no tiling is requested */ - if (window->tile_mode == META_TILE_NONE) - { - window->tile_monitor_number = -1; - return; - } - else if (window->tile_monitor_number < 0) - { - window->tile_monitor_number = window->monitor->number; - } - - if (window->tile_mode == META_TILE_MAXIMIZED) - directions = META_MAXIMIZE_BOTH; - else - directions = META_MAXIMIZE_VERTICAL; - - meta_window_maximize_internal (window, directions, NULL); - meta_display_update_tile_preview (window->display, FALSE); - - /* Setup the edge constraints */ - update_edge_constraints (window); - - meta_window_get_frame_rect (window, &old_frame_rect); - meta_window_get_buffer_rect (window, &old_buffer_rect); - - meta_compositor_size_change_window (window->display->compositor, window, - META_SIZE_CHANGE_MAXIMIZE, - &old_frame_rect, &old_buffer_rect); - - meta_window_move_resize_internal (window, - (META_MOVE_RESIZE_MOVE_ACTION | - META_MOVE_RESIZE_RESIZE_ACTION | - META_MOVE_RESIZE_STATE_CHANGED), - META_GRAVITY_NORTH_WEST, - window->unconstrained_rect); - - if (window->frame) - meta_frame_queue_draw (window->frame); -} - -MetaTileMode -meta_window_get_tile_mode (MetaWindow *window) -{ - return window->tile_mode; -} - -void -meta_window_restore_tile (MetaWindow *window, - MetaTileMode mode, - int width, - int height) -{ - meta_window_update_tile_fraction (window, width, height); - meta_window_tile (window, mode); -} - -static gboolean -meta_window_can_tile_maximized (MetaWindow *window) -{ - return window->has_maximize_func; -} - -gboolean -meta_window_can_tile_side_by_side (MetaWindow *window) -{ - int monitor; - MetaRectangle tile_area; - MetaRectangle client_rect; - - if (!meta_window_can_tile_maximized (window)) - return FALSE; - - monitor = meta_display_get_current_monitor (window->display); - meta_window_get_work_area_for_monitor (window, monitor, &tile_area); - - /* Do not allow tiling in portrait orientation */ - if (tile_area.height > tile_area.width) - return FALSE; - - tile_area.width /= 2; - - meta_window_frame_rect_to_client_rect (window, &tile_area, &client_rect); - - return client_rect.width >= window->size_hints.min_width && - client_rect.height >= window->size_hints.min_height; -} - -static void -unmaximize_window_before_freeing (MetaWindow *window) -{ - meta_topic (META_DEBUG_WINDOW_OPS, - "Unmaximizing %s just before freeing", - window->desc); - - window->maximized_horizontally = FALSE; - window->maximized_vertically = FALSE; - - if (window->withdrawn) /* See bug #137185 */ - { - window->rect = window->saved_rect; - set_net_wm_state (window); - } -#ifdef HAVE_WAYLAND - else if (!meta_is_wayland_compositor ()) - { - /* Do NOT update net_wm_state: this screen is closing, - * it likely will be managed by another window manager - * that will need the current _NET_WM_STATE atoms. - * Moreover, it will need to know the unmaximized geometry, - * therefore move_resize the window to saved_rect here - * before closing it. */ - meta_window_move_resize_frame (window, - FALSE, - window->saved_rect.x, - window->saved_rect.y, - window->saved_rect.width, - window->saved_rect.height); - } -#endif -} - -static void -meta_window_maybe_apply_size_hints (MetaWindow *window, - MetaRectangle *target_rect) -{ - meta_window_frame_rect_to_client_rect (window, target_rect, target_rect); - ensure_size_hints_satisfied (target_rect, &window->size_hints); - meta_window_client_rect_to_frame_rect (window, target_rect, target_rect); -} - -void -meta_window_unmaximize (MetaWindow *window, - MetaMaximizeFlags directions) -{ - gboolean unmaximize_horizontally, unmaximize_vertically; - - g_return_if_fail (!window->override_redirect); - - /* At least one of the two directions ought to be set */ - unmaximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL; - unmaximize_vertically = directions & META_MAXIMIZE_VERTICAL; - g_assert (unmaximize_horizontally || unmaximize_vertically); - - if (unmaximize_horizontally && unmaximize_vertically) - window->saved_maximize = FALSE; - - /* Only do something if the window isn't already maximized in the - * given direction(s). - */ - if ((unmaximize_horizontally && window->maximized_horizontally) || - (unmaximize_vertically && window->maximized_vertically)) - { - MetaRectangle *desired_rect; - MetaRectangle target_rect; - MetaRectangle work_area; - MetaRectangle old_frame_rect, old_buffer_rect; - gboolean has_target_size; - - meta_window_get_work_area_for_monitor (window, window->monitor->number, &work_area); - meta_window_get_frame_rect (window, &old_frame_rect); - meta_window_get_buffer_rect (window, &old_buffer_rect); - - if (unmaximize_vertically) - window->tile_mode = META_TILE_NONE; - - meta_topic (META_DEBUG_WINDOW_OPS, - "Unmaximizing %s%s", - window->desc, - unmaximize_horizontally && unmaximize_vertically ? "" : - unmaximize_horizontally ? " horizontally" : - unmaximize_vertically ? " vertically" : "BUGGGGG"); - - window->maximized_horizontally = - window->maximized_horizontally && !unmaximize_horizontally; - window->maximized_vertically = - window->maximized_vertically && !unmaximize_vertically; - - /* Update the edge constraints */ - update_edge_constraints (window); - - /* recalc_features() will eventually clear the cached frame - * extents, but we need the correct frame extents in the code below, - * so invalidate the old frame extents manually up front. - */ - meta_window_frame_size_changed (window); - - desired_rect = &window->saved_rect; - - /* Unmaximize to the saved_rect position in the direction(s) - * being unmaximized. - */ - target_rect = old_frame_rect; - - /* Avoid unmaximizing to "almost maximized" size when the previous size - * is greater then 80% of the work area use MAX_UNMAXIMIZED_WINDOW_AREA of the work area as upper limit - * while maintaining the aspect ratio. - */ - if (unmaximize_horizontally && unmaximize_vertically && - desired_rect->width * desired_rect->height > work_area.width * work_area.height * MAX_UNMAXIMIZED_WINDOW_AREA) - { - if (desired_rect->width > desired_rect->height) - { - float aspect = (float)desired_rect->height / (float)desired_rect->width; - desired_rect->width = MAX (work_area.width * sqrt (MAX_UNMAXIMIZED_WINDOW_AREA), window->size_hints.min_width); - desired_rect->height = MAX (desired_rect->width * aspect, window->size_hints.min_height); - } - else - { - float aspect = (float)desired_rect->width / (float)desired_rect->height; - desired_rect->height = MAX (work_area.height * sqrt (MAX_UNMAXIMIZED_WINDOW_AREA), window->size_hints.min_height); - desired_rect->width = MAX (desired_rect->height * aspect, window->size_hints.min_width); - } - } - - if (unmaximize_horizontally) - { - target_rect.x = desired_rect->x; - target_rect.width = desired_rect->width; - } - if (unmaximize_vertically) - { - target_rect.y = desired_rect->y; - target_rect.height = desired_rect->height; - } - - /* Window's size hints may have changed while maximized, making - * saved_rect invalid. #329152 - * Do not enforce limits, if no previous 'saved_rect' has been stored. - */ - has_target_size = (target_rect.width > 0 && target_rect.height > 0); - if (has_target_size) - meta_window_maybe_apply_size_hints (window, &target_rect); - - meta_compositor_size_change_window (window->display->compositor, window, - META_SIZE_CHANGE_UNMAXIMIZE, - &old_frame_rect, &old_buffer_rect); - - meta_window_move_resize_internal (window, - (META_MOVE_RESIZE_MOVE_ACTION | - META_MOVE_RESIZE_RESIZE_ACTION | - META_MOVE_RESIZE_STATE_CHANGED | - META_MOVE_RESIZE_UNMAXIMIZE), - META_GRAVITY_NORTH_WEST, - target_rect); - - /* When we unmaximize, if we're doing a mouse move also we could - * get the window suddenly jumping to the upper left corner of - * the workspace, since that's where it was when the grab op - * started. So we need to update the grab anchor position. - */ - if (meta_grab_op_is_moving (window->display->grab_op) && - window->display->grab_window == window) - { - window->display->grab_anchor_window_pos = target_rect; - } - - meta_window_recalc_features (window); - set_net_wm_state (window); - if (!window->monitor->in_fullscreen) - meta_display_queue_check_fullscreen (window->display); - } - - g_object_freeze_notify (G_OBJECT (window)); - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_MAXIMIZED_HORIZONTALLY]); - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_MAXIMIZED_VERTICALLY]); - g_object_thaw_notify (G_OBJECT (window)); -} - -void -meta_window_make_above (MetaWindow *window) -{ - g_return_if_fail (!window->override_redirect); - - meta_window_set_above (window, TRUE); - meta_window_raise (window); -} - -void -meta_window_unmake_above (MetaWindow *window) -{ - g_return_if_fail (!window->override_redirect); - - meta_window_set_above (window, FALSE); - meta_window_raise (window); -} - -static void -meta_window_set_above (MetaWindow *window, - gboolean new_value) -{ - new_value = new_value != FALSE; - if (new_value == window->wm_state_above) - return; - - window->wm_state_above = new_value; - meta_window_update_layer (window); - set_net_wm_state (window); - meta_window_frame_size_changed (window); - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_ABOVE]); -} - -void -meta_window_make_fullscreen_internal (MetaWindow *window) -{ - if (!window->fullscreen) - { - meta_topic (META_DEBUG_WINDOW_OPS, - "Fullscreening %s", window->desc); - - if (window->shaded) - { - /* Shading sucks anyway; I'm not adding a timestamp argument - * to this function just for this niche usage & corner case. - */ - guint32 timestamp = - meta_display_get_current_time_roundtrip (window->display); - meta_window_unshade (window, timestamp); - } - - window->saved_rect_fullscreen = window->rect; - - window->fullscreen = TRUE; - - meta_stack_freeze (window->display->stack); - - meta_window_raise (window); - meta_stack_thaw (window->display->stack); - - meta_window_recalc_features (window); - set_net_wm_state (window); - - /* For the auto-minimize feature, if we fail to get focus */ - meta_display_queue_check_fullscreen (window->display); - - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_FULLSCREEN]); - } -} - -void -meta_window_make_fullscreen (MetaWindow *window) -{ - g_return_if_fail (!window->override_redirect); - - if (!window->fullscreen) - { - MetaRectangle old_frame_rect, old_buffer_rect; - - meta_window_get_frame_rect (window, &old_frame_rect); - meta_window_get_buffer_rect (window, &old_buffer_rect); - - meta_compositor_size_change_window (window->display->compositor, - window, META_SIZE_CHANGE_FULLSCREEN, - &old_frame_rect, &old_buffer_rect); - - meta_window_make_fullscreen_internal (window); - meta_window_move_resize_internal (window, - (META_MOVE_RESIZE_MOVE_ACTION | - META_MOVE_RESIZE_RESIZE_ACTION | - META_MOVE_RESIZE_STATE_CHANGED), - META_GRAVITY_NORTH_WEST, - window->unconstrained_rect); - } -} - -void -meta_window_unmake_fullscreen (MetaWindow *window) -{ - g_return_if_fail (!window->override_redirect); - - if (window->fullscreen) - { - MetaRectangle old_frame_rect, old_buffer_rect, target_rect; - gboolean has_target_size; - - meta_topic (META_DEBUG_WINDOW_OPS, - "Unfullscreening %s", window->desc); - - window->fullscreen = FALSE; - target_rect = window->saved_rect_fullscreen; - - meta_window_frame_size_changed (window); - meta_window_get_frame_rect (window, &old_frame_rect); - meta_window_get_buffer_rect (window, &old_buffer_rect); - - /* Window's size hints may have changed while maximized, making - * saved_rect invalid. #329152 - * Do not enforce limits, if no previous 'saved_rect' has been stored. - */ - has_target_size = (target_rect.width > 0 && target_rect.height > 0); - if (has_target_size) - meta_window_maybe_apply_size_hints (window, &target_rect); - - /* Need to update window->has_resize_func before we move_resize() - */ - meta_window_recalc_features (window); - set_net_wm_state (window); - - meta_compositor_size_change_window (window->display->compositor, - window, META_SIZE_CHANGE_UNFULLSCREEN, - &old_frame_rect, &old_buffer_rect); - - meta_window_move_resize_internal (window, - (META_MOVE_RESIZE_MOVE_ACTION | - META_MOVE_RESIZE_RESIZE_ACTION | - META_MOVE_RESIZE_STATE_CHANGED | - META_MOVE_RESIZE_UNFULLSCREEN), - META_GRAVITY_NORTH_WEST, - target_rect); - - meta_display_queue_check_fullscreen (window->display); - - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_FULLSCREEN]); - } -} - -static void -meta_window_clear_fullscreen_monitors (MetaWindow *window) -{ - window->fullscreen_monitors.top = NULL; - window->fullscreen_monitors.bottom = NULL; - window->fullscreen_monitors.left = NULL; - window->fullscreen_monitors.right = NULL; -} - -void -meta_window_update_fullscreen_monitors (MetaWindow *window, - MetaLogicalMonitor *top, - MetaLogicalMonitor *bottom, - MetaLogicalMonitor *left, - MetaLogicalMonitor *right) -{ - if (top && bottom && left && right) - { - window->fullscreen_monitors.top = top; - window->fullscreen_monitors.bottom = bottom; - window->fullscreen_monitors.left = left; - window->fullscreen_monitors.right = right; - } - else - { - meta_window_clear_fullscreen_monitors (window); - } - - if (window->fullscreen) - { - meta_window_queue(window, META_QUEUE_MOVE_RESIZE); - } -} - -gboolean -meta_window_has_fullscreen_monitors (MetaWindow *window) -{ - return window->fullscreen_monitors.top != NULL; -} - -void -meta_window_adjust_fullscreen_monitor_rect (MetaWindow *window, - MetaRectangle *monitor_rect) -{ - MetaWindowClass *window_class = META_WINDOW_GET_CLASS (window); - - if (window_class->adjust_fullscreen_monitor_rect) - window_class->adjust_fullscreen_monitor_rect (window, monitor_rect); -} - -void -meta_window_shade (MetaWindow *window, - guint32 timestamp) -{ - g_return_if_fail (!window->override_redirect); - - meta_topic (META_DEBUG_WINDOW_OPS, - "Shading %s", window->desc); - if (!window->shaded) - { - window->shaded = TRUE; - - meta_window_queue(window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING); - meta_window_frame_size_changed (window); - - /* After queuing the calc showing, since _focus flushes it, - * and we need to focus the frame - */ - meta_topic (META_DEBUG_FOCUS, - "Re-focusing window %s after shading it", - window->desc); - meta_window_focus (window, timestamp); - - set_net_wm_state (window); - } -} - -void -meta_window_unshade (MetaWindow *window, - guint32 timestamp) -{ - g_return_if_fail (!window->override_redirect); - - meta_topic (META_DEBUG_WINDOW_OPS, - "Unshading %s", window->desc); - if (window->shaded) - { - window->shaded = FALSE; - meta_window_queue(window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING); - meta_window_frame_size_changed (window); - - /* focus the window */ - meta_topic (META_DEBUG_FOCUS, - "Focusing window %s after unshading it", - window->desc); - meta_window_focus (window, timestamp); - - set_net_wm_state (window); - } -} - -static gboolean -unminimize_func (MetaWindow *window, - void *data) -{ - meta_window_unminimize (window); - return TRUE; -} - -static void -unminimize_window_and_all_transient_parents (MetaWindow *window) -{ - meta_window_unminimize (window); - meta_window_foreach_ancestor (window, unminimize_func, NULL); -} - -void -meta_window_activate_full (MetaWindow *window, - guint32 timestamp, - MetaClientType source_indication, - MetaWorkspace *workspace) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - gboolean allow_workspace_switch; - - if (window->unmanaging) - { - g_warning ("Trying to activate unmanaged window '%s'", window->desc); - return; - } - - meta_topic (META_DEBUG_FOCUS, - "_NET_ACTIVE_WINDOW message sent for %s at time %u " - "by client type %u.", - window->desc, timestamp, source_indication); - - allow_workspace_switch = (timestamp != 0); - if (timestamp != 0 && - XSERVER_TIME_IS_BEFORE (timestamp, window->display->last_user_time)) - { - meta_topic (META_DEBUG_FOCUS, - "last_user_time (%u) is more recent; ignoring " - " _NET_ACTIVE_WINDOW message.", - window->display->last_user_time); - meta_window_set_demands_attention(window); - return; - } - - if (timestamp == 0) - timestamp = meta_display_get_current_time_roundtrip (window->display); - - meta_window_set_user_time (window, timestamp); - - /* disable show desktop mode unless we're a desktop component */ - maybe_leave_show_desktop_mode (window); - - /* Get window on current or given workspace */ - if (workspace == NULL) - workspace = workspace_manager->active_workspace; - - /* For non-transient windows, we just set up a pulsing indicator, - rather than move windows or workspaces. - See http://bugzilla.gnome.org/show_bug.cgi?id=482354 */ - if (window->transient_for == NULL && - !allow_workspace_switch && - !meta_window_located_on_workspace (window, workspace)) - { - meta_window_set_demands_attention (window); - /* We've marked it as demanding, don't need to do anything else. */ - return; - } - else if (window->transient_for != NULL) - { - /* Move transients to current workspace - preference dialogs should appear over - the source window. */ - meta_window_change_workspace (window, workspace); - } - - if (window->shaded) - meta_window_unshade (window, timestamp); - - unminimize_window_and_all_transient_parents (window); - - if (meta_prefs_get_raise_on_click () || - source_indication == META_CLIENT_TYPE_PAGER) - meta_window_raise (window); - - meta_topic (META_DEBUG_FOCUS, - "Focusing window %s due to activation", - window->desc); - - if (meta_window_located_on_workspace (window, workspace)) - meta_window_focus (window, timestamp); - else - meta_workspace_activate_with_focus (window->workspace, window, timestamp); - - meta_window_check_alive (window, timestamp); -} - -/* This function exists since most of the functionality in window_activate - * is useful for Mutter, but Mutter shouldn't need to specify a client - * type for itself. ;-) - */ -void -meta_window_activate (MetaWindow *window, - guint32 timestamp) -{ - g_return_if_fail (!window->override_redirect); - - /* We're not really a pager, but the behavior we want is the same as if - * we were such. If we change the pager behavior later, we could revisit - * this and just add extra flags to window_activate. - */ - meta_window_activate_full (window, timestamp, META_CLIENT_TYPE_PAGER, NULL); -} - -void -meta_window_activate_with_workspace (MetaWindow *window, - guint32 timestamp, - MetaWorkspace *workspace) -{ - g_return_if_fail (!window->override_redirect); - - meta_window_activate_full (window, timestamp, META_CLIENT_TYPE_APPLICATION, workspace); -} - -/** - * meta_window_updates_are_frozen: - * @window: a #MetaWindow - * - * Gets whether the compositor should be updating the window contents; - * window content updates may be frozen at client request by setting - * an odd value in the extended _NET_WM_SYNC_REQUEST_COUNTER counter - * by the window manager during a resize operation while waiting for - * the client to redraw. - * - * Return value: %TRUE if updates are currently frozen - */ -gboolean -meta_window_updates_are_frozen (MetaWindow *window) -{ - return META_WINDOW_GET_CLASS (window)->are_updates_frozen (window); -} - -static void -meta_window_reposition (MetaWindow *window) -{ - meta_window_move_resize_internal (window, - (META_MOVE_RESIZE_MOVE_ACTION | - META_MOVE_RESIZE_RESIZE_ACTION), - META_GRAVITY_NORTH_WEST, - window->rect); -} - -static gboolean -maybe_move_attached_window (MetaWindow *window, - void *data) -{ - if (window->hidden) - return G_SOURCE_CONTINUE; - - if (meta_window_is_attached_dialog (window) || - meta_window_get_placement_rule (window)) - meta_window_reposition (window); - - return G_SOURCE_CONTINUE; -} - -/** - * meta_window_get_monitor: - * @window: a #MetaWindow - * - * Gets index of the monitor that this window is on. - * - * Return Value: The index of the monitor in the screens monitor list, or -1 - * if the window has been recently unmanaged and does not have a monitor. - */ -int -meta_window_get_monitor (MetaWindow *window) -{ - if (!window->monitor) - return -1; - - return window->monitor->number; -} - -MetaLogicalMonitor * -meta_window_get_main_logical_monitor (MetaWindow *window) -{ - return window->monitor; -} - -static MetaLogicalMonitor * -find_monitor_by_winsys_id (MetaWindow *window, - uint64_t winsys_id) -{ - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - GList *logical_monitors, *l; - - logical_monitors = - meta_monitor_manager_get_logical_monitors (monitor_manager); - - for (l = logical_monitors; l; l = l->next) - { - MetaLogicalMonitor *logical_monitor = l->data; - - if (logical_monitor->winsys_id == winsys_id) - return logical_monitor; - } - - return NULL; -} - -/* This is called when the monitor setup has changed. The window->monitor - * reference is still "valid", but refer to the previous monitor setup */ -void -meta_window_update_for_monitors_changed (MetaWindow *window) -{ - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - const MetaLogicalMonitor *old, *new; - - if (meta_window_has_fullscreen_monitors (window)) - meta_window_clear_fullscreen_monitors (window); - - if (window->override_redirect || window->type == META_WINDOW_DESKTOP) - { - meta_window_update_monitor (window, - META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE); - goto out; - } - - old = window->monitor; - - /* Try the preferred output first */ - new = find_monitor_by_winsys_id (window, window->preferred_output_winsys_id); - - /* Otherwise, try to find the old output on a new monitor */ - if (old && !new) - new = find_monitor_by_winsys_id (window, old->winsys_id); - - /* Fall back to primary if everything else failed */ - if (!new) - new = meta_monitor_manager_get_primary_logical_monitor (monitor_manager); - - if (window->tile_mode != META_TILE_NONE) - { - if (new) - window->tile_monitor_number = new->number; - else - window->tile_monitor_number = -1; - } - - if (new && old) - { - /* This will eventually reach meta_window_update_monitor that - * will send leave/enter-monitor events. The old != new monitor - * check will always fail (due to the new logical_monitors set) so - * we will always send the events, even if the new and old monitor - * index is the same. That is right, since the enumeration of the - * monitors changed and the same index could be refereing - * to a different monitor. */ - meta_window_move_between_rects (window, - META_MOVE_RESIZE_FORCE_UPDATE_MONITOR, - &old->rect, - &new->rect); - } - else - { - meta_window_update_monitor (window, - META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE); - } - -out: - g_assert (!window->monitor || - g_list_find (meta_monitor_manager_get_logical_monitors (monitor_manager), - window->monitor)); -} - -void -meta_window_update_monitor (MetaWindow *window, - MetaWindowUpdateMonitorFlags flags) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - const MetaLogicalMonitor *old; - - old = window->monitor; - META_WINDOW_GET_CLASS (window)->update_main_monitor (window, flags); - if (old != window->monitor) - { - meta_window_on_all_workspaces_changed (window); - - /* If workspaces only on primary and we moved back to primary due to a user action, - * ensure that the window is now in that workspace. We do this because while - * the window is on a non-primary monitor it is always visible, so it would be - * very jarring if it disappeared when it crossed the monitor border. - * The one time we want it to both change to the primary monitor and a non-active - * workspace is when dropping the window on some other workspace thumbnail directly. - * That should be handled by explicitly moving the window before changing the - * workspace. - */ - if (meta_prefs_get_workspaces_only_on_primary () && - flags & META_WINDOW_UPDATE_MONITOR_FLAGS_USER_OP && - meta_window_is_on_primary_monitor (window) && - workspace_manager->active_workspace != window->workspace) - meta_window_change_workspace (window, workspace_manager->active_workspace); - - meta_window_main_monitor_changed (window, old); - - /* If we're changing monitors, we need to update the has_maximize_func flag, - * as the working area has changed. */ - meta_window_recalc_features (window); - } -} - -void -meta_window_move_resize_internal (MetaWindow *window, - MetaMoveResizeFlags flags, - MetaGravity gravity, - MetaRectangle frame_rect) -{ - /* The rectangle here that's passed in *always* in "frame rect" - * coordinates. That means the position of the frame's visible bounds, - * with x and y being absolute (root window) coordinates. - * - * For an X11 framed window, the client window's server rectangle is - * inset from this rectangle by the frame's visible borders, and the - * frame window's server rectangle is outset by the invisible borders. - * - * For an X11 unframed window, the rectangle here directly matches - * the server's rectangle, since the visible and invisible borders - * are both 0. - * - * For an X11 CSD window, the client window's server rectangle is - * outset from this rectagle by the client-specified frame extents. - * - * For a Wayland window, this rectangle can simply be sent directly - * to the client. - */ - - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - gboolean did_placement; - MetaRectangle unconstrained_rect; - MetaRectangle constrained_rect; - MetaRectangle temporary_rect; - int rel_x = 0; - int rel_y = 0; - MetaMoveResizeResultFlags result = 0; - gboolean moved_or_resized = FALSE; - MetaWindowUpdateMonitorFlags update_monitor_flags; - - g_return_if_fail (!window->override_redirect); - - /* The action has to be a move, a resize or the wayland client - * acking our choice of size. - */ - g_assert (flags & (META_MOVE_RESIZE_MOVE_ACTION | - META_MOVE_RESIZE_RESIZE_ACTION | - META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE)); - - did_placement = !window->placed && window->calc_placement; - - /* We don't need it in the idle queue anymore. */ - meta_window_unqueue (window, META_QUEUE_MOVE_RESIZE); - - if ((flags & META_MOVE_RESIZE_RESIZE_ACTION) && (flags & META_MOVE_RESIZE_MOVE_ACTION)) - { - /* We're both moving and resizing. Just use the passed in rect. */ - unconstrained_rect = frame_rect; - } - else if ((flags & META_MOVE_RESIZE_RESIZE_ACTION)) - { - /* If this is only a resize, then ignore the position given in - * the parameters and instead calculate the new position from - * resizing the old rectangle with the given gravity. */ - meta_rectangle_resize_with_gravity (&window->rect, - &unconstrained_rect, - gravity, - frame_rect.width, - frame_rect.height); - } - else if ((flags & META_MOVE_RESIZE_MOVE_ACTION)) - { - /* If this is only a move, then ignore the passed in size and - * just use the existing size of the window. */ - unconstrained_rect.x = frame_rect.x; - unconstrained_rect.y = frame_rect.y; - unconstrained_rect.width = window->rect.width; - unconstrained_rect.height = window->rect.height; - } - else if ((flags & META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE)) - { - /* This is a Wayland buffer acking our size. The new rect is - * just the existing one we have. Ignore the passed-in rect - * completely. */ - unconstrained_rect = window->rect; - } - else - g_assert_not_reached (); - - constrained_rect = unconstrained_rect; - temporary_rect = window->rect; - if (flags & (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION) && - !(flags & META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE) && - !(flags & (META_MOVE_RESIZE_UNMAXIMIZE | META_MOVE_RESIZE_UNFULLSCREEN)) && - window->monitor) - { - MetaRectangle old_rect; - meta_window_get_frame_rect (window, &old_rect); - - meta_window_constrain (window, - flags, - gravity, - &old_rect, - &constrained_rect, - &temporary_rect, - &rel_x, - &rel_y); - } - else if (window->placement.rule) - { - rel_x = window->placement.pending.rel_x; - rel_y = window->placement.pending.rel_y; - } - - /* If we did placement, then we need to save the position that the window - * was placed at to make sure that meta_window_move_resize_now places the - * window correctly. - */ - if (did_placement) - { - unconstrained_rect.x = constrained_rect.x; - unconstrained_rect.y = constrained_rect.y; - } - - /* Do the protocol-specific move/resize logic */ - META_WINDOW_GET_CLASS (window)->move_resize_internal (window, - gravity, - unconstrained_rect, - constrained_rect, - temporary_rect, - rel_x, - rel_y, - flags, &result); - - if (result & META_MOVE_RESIZE_RESULT_MOVED) - { - moved_or_resized = TRUE; - g_signal_emit (window, window_signals[POSITION_CHANGED], 0); - } - - if (result & META_MOVE_RESIZE_RESULT_RESIZED) - { - moved_or_resized = TRUE; - g_signal_emit (window, window_signals[SIZE_CHANGED], 0); - } - - if (moved_or_resized || did_placement) - window->unconstrained_rect = unconstrained_rect; - - if ((moved_or_resized || - did_placement || - (result & META_MOVE_RESIZE_RESULT_STATE_CHANGED) != 0) && - window->known_to_compositor) - { - meta_compositor_sync_window_geometry (window->display->compositor, - window, - did_placement); - } - - update_monitor_flags = META_WINDOW_UPDATE_MONITOR_FLAGS_NONE; - if (flags & META_MOVE_RESIZE_USER_ACTION) - update_monitor_flags |= META_WINDOW_UPDATE_MONITOR_FLAGS_USER_OP; - if (flags & META_MOVE_RESIZE_FORCE_UPDATE_MONITOR) - update_monitor_flags |= META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE; - - if (window->monitor) - { - uint64_t old_output_winsys_id; - - old_output_winsys_id = window->monitor->winsys_id; - - meta_window_update_monitor (window, update_monitor_flags); - - if (old_output_winsys_id != window->monitor->winsys_id && - flags & META_MOVE_RESIZE_MOVE_ACTION && flags & META_MOVE_RESIZE_USER_ACTION) - window->preferred_output_winsys_id = window->monitor->winsys_id; - } - else - { - meta_window_update_monitor (window, update_monitor_flags); - } - - if ((result & META_MOVE_RESIZE_RESULT_FRAME_SHAPE_CHANGED) && window->frame_bounds) - { - cairo_region_destroy (window->frame_bounds); - window->frame_bounds = NULL; - } - - meta_window_foreach_transient (window, maybe_move_attached_window, NULL); - - meta_stack_update_window_tile_matches (window->display->stack, - workspace_manager->active_workspace); -} - -/** - * meta_window_move_frame: - * @window: a #MetaWindow - * @user_op: bool to indicate whether or not this is a user operation - * @root_x_nw: desired x pos - * @root_y_nw: desired y pos - * - * Moves the window to the desired location on window's assigned - * workspace, using the northwest edge of the frame as the reference, - * instead of the actual window's origin, but only if a frame is present. - * Otherwise, acts identically to meta_window_move(). - */ -void -meta_window_move_frame (MetaWindow *window, - gboolean user_op, - int root_x_nw, - int root_y_nw) -{ - MetaMoveResizeFlags flags; - MetaRectangle rect = { root_x_nw, root_y_nw, 0, 0 }; - - g_return_if_fail (!window->override_redirect); - - flags = (user_op ? META_MOVE_RESIZE_USER_ACTION : 0) | META_MOVE_RESIZE_MOVE_ACTION; - meta_window_move_resize_internal (window, flags, META_GRAVITY_NORTH_WEST, rect); -} - -static void -meta_window_move_between_rects (MetaWindow *window, - MetaMoveResizeFlags move_resize_flags, - const MetaRectangle *old_area, - const MetaRectangle *new_area) -{ - int rel_x, rel_y; - double scale_x, scale_y; - - if (old_area) - { - rel_x = window->unconstrained_rect.x - old_area->x; - rel_y = window->unconstrained_rect.y - old_area->y; - scale_x = (double)new_area->width / old_area->width; - scale_y = (double)new_area->height / old_area->height; - } - else - { - rel_x = rel_y = scale_x = scale_y = 0; - } - - window->unconstrained_rect.x = new_area->x + rel_x * scale_x; - window->unconstrained_rect.y = new_area->y + rel_y * scale_y; - window->saved_rect.x = window->unconstrained_rect.x; - window->saved_rect.y = window->unconstrained_rect.y; - - meta_window_move_resize_internal (window, - move_resize_flags | - META_MOVE_RESIZE_MOVE_ACTION | - META_MOVE_RESIZE_RESIZE_ACTION, - META_GRAVITY_NORTH_WEST, - window->unconstrained_rect); -} - -/** - * meta_window_move_resize_frame: - * @window: a #MetaWindow - * @user_op: bool to indicate whether or not this is a user operation - * @root_x_nw: new x - * @root_y_nw: new y - * @w: desired width - * @h: desired height - * - * Resizes the window so that its outer bounds (including frame) - * fit within the given rect - */ -void -meta_window_move_resize_frame (MetaWindow *window, - gboolean user_op, - int root_x_nw, - int root_y_nw, - int w, - int h) -{ - MetaMoveResizeFlags flags; - MetaRectangle rect = { root_x_nw, root_y_nw, w, h }; - - g_return_if_fail (!window->override_redirect); - - flags = (user_op ? META_MOVE_RESIZE_USER_ACTION : 0) | META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION; - - meta_window_move_resize_internal (window, flags, META_GRAVITY_NORTH_WEST, rect); -} - -/** - * meta_window_move_to_monitor: - * @window: a #MetaWindow - * @monitor: desired monitor index - * - * Moves the window to the monitor with index @monitor, keeping - * the relative position of the window's top left corner. - */ -void -meta_window_move_to_monitor (MetaWindow *window, - int monitor) -{ - MetaRectangle old_area, new_area; - - if (window->tile_mode != META_TILE_NONE) - window->tile_monitor_number = monitor; - - meta_window_get_work_area_for_monitor (window, - window->monitor->number, - &old_area); - meta_window_get_work_area_for_monitor (window, - monitor, - &new_area); - - if (window->unconstrained_rect.width == 0 || - window->unconstrained_rect.height == 0 || - !meta_rectangle_overlap (&window->unconstrained_rect, &old_area)) - { - meta_window_move_between_rects (window, 0, NULL, &new_area); - } - else - { - if (monitor == window->monitor->number) - return; - - meta_window_move_between_rects (window, 0, &old_area, &new_area); - } - - window->preferred_output_winsys_id = window->monitor->winsys_id; - - if (window->fullscreen || window->override_redirect) - meta_display_queue_check_fullscreen (window->display); -} - -static void -adjust_size_for_tile_match (MetaWindow *window, - int *new_w, - int *new_h) -{ - MetaRectangle work_area, rect; - MetaWindow *tile_match = window->tile_match; - - if (!META_WINDOW_TILED_SIDE_BY_SIDE (window) || !tile_match) - return; - - meta_window_get_work_area_for_monitor (window, window->tile_monitor_number, &work_area); - - /* Make sure the resize does not break minimum sizes */ - rect = work_area; - rect.width = *new_w; - - meta_window_frame_rect_to_client_rect (window, &rect, &rect); - *new_w += MAX(0, window->size_hints.min_width - rect.width); - - /* Make sure we're not resizing the tile match below its min width */ - rect = work_area; - rect.width = work_area.width - *new_w; - - meta_window_frame_rect_to_client_rect (tile_match, &rect, &rect); - *new_w -= MAX(0, tile_match->size_hints.min_width - rect.width); -} - -void -meta_window_resize_frame_with_gravity (MetaWindow *window, - gboolean user_op, - int w, - int h, - MetaGravity gravity) -{ - MetaMoveResizeFlags flags; - MetaRectangle rect; - - rect.width = w; - rect.height = h; - - if (user_op) - { - /* When resizing in-tandem with a tile match, we need to respect - * its minimum width - */ - if (window->display->grab_window == window) - adjust_size_for_tile_match (window, &w, &h); - meta_window_update_tile_fraction (window, w, h); - } - - flags = (user_op ? META_MOVE_RESIZE_USER_ACTION : 0) | META_MOVE_RESIZE_RESIZE_ACTION; - meta_window_move_resize_internal (window, flags, gravity, rect); -} - -static void -meta_window_move_resize_now (MetaWindow *window) -{ - meta_window_move_resize_frame (window, FALSE, - window->unconstrained_rect.x, - window->unconstrained_rect.y, - window->unconstrained_rect.width, - window->unconstrained_rect.height); -} - -static gboolean -idle_move_resize (gpointer data) -{ - GSList *tmp; - GSList *copy; - guint queue_index = GPOINTER_TO_INT (data); - - meta_topic (META_DEBUG_GEOMETRY, "Clearing the move_resize queue"); - - /* Work with a copy, for reentrancy. The allowed reentrancy isn't - * complete; destroying a window while we're in here would result in - * badness. But it's OK to queue/unqueue move_resizes. - */ - copy = g_slist_copy (queue_pending[queue_index]); - g_slist_free (queue_pending[queue_index]); - queue_pending[queue_index] = NULL; - queue_later[queue_index] = 0; - - destroying_windows_disallowed += 1; - - tmp = copy; - while (tmp != NULL) - { - MetaWindow *window; - - window = tmp->data; - - /* As a side effect, sets window->move_resize_queued = FALSE */ - meta_window_move_resize_now (window); - - tmp = tmp->next; - } - - g_slist_free (copy); - - destroying_windows_disallowed -= 1; - - return FALSE; -} - -void -meta_window_get_gravity_position (MetaWindow *window, - MetaGravity gravity, - int *root_x, - int *root_y) -{ - MetaRectangle frame_extents; - int w, h; - int x, y; - - w = window->rect.width; - h = window->rect.height; - - if (gravity == META_GRAVITY_STATIC) - { - frame_extents = window->rect; - if (window->frame) - { - frame_extents.x = window->frame->rect.x + window->frame->child_x; - frame_extents.y = window->frame->rect.y + window->frame->child_y; - } - } - else - { - if (window->frame == NULL) - frame_extents = window->rect; - else - frame_extents = window->frame->rect; - } - - x = frame_extents.x; - y = frame_extents.y; - - switch (gravity) - { - case META_GRAVITY_NORTH: - case META_GRAVITY_CENTER: - case META_GRAVITY_SOUTH: - /* Find center of frame. */ - x += frame_extents.width / 2; - /* Center client window on that point. */ - x -= w / 2; - break; - - case META_GRAVITY_SOUTH_EAST: - case META_GRAVITY_EAST: - case META_GRAVITY_NORTH_EAST: - /* Find right edge of frame */ - x += frame_extents.width; - /* Align left edge of client at that point. */ - x -= w; - break; - default: - break; - } - - switch (gravity) - { - case META_GRAVITY_WEST: - case META_GRAVITY_CENTER: - case META_GRAVITY_EAST: - /* Find center of frame. */ - y += frame_extents.height / 2; - /* Center client window there. */ - y -= h / 2; - break; - case META_GRAVITY_SOUTH_WEST: - case META_GRAVITY_SOUTH: - case META_GRAVITY_SOUTH_EAST: - /* Find south edge of frame */ - y += frame_extents.height; - /* Place bottom edge of client there */ - y -= h; - break; - default: - break; - } - - if (root_x) - *root_x = x; - if (root_y) - *root_y = y; -} - -void -meta_window_get_session_geometry (MetaWindow *window, - int *x, - int *y, - int *width, - int *height) -{ - meta_window_get_gravity_position (window, - window->size_hints.win_gravity, - x, y); - - *width = (window->rect.width - window->size_hints.base_width) / - window->size_hints.width_inc; - *height = (window->rect.height - window->size_hints.base_height) / - window->size_hints.height_inc; -} - -/** - * meta_window_get_buffer_rect: - * @window: a #MetaWindow - * @rect: (out): pointer to an allocated #MetaRectangle - * - * Gets the rectangle that the pixmap or buffer of @window occupies. - * - * For X11 windows, this is the server-side geometry of the toplevel - * window. - * - * For Wayland windows, this is the bounding rectangle of the attached - * buffer. - */ -void -meta_window_get_buffer_rect (const MetaWindow *window, - MetaRectangle *rect) -{ - *rect = window->buffer_rect; -} - -/** - * meta_window_client_rect_to_frame_rect: - * @window: a #MetaWindow - * @client_rect: client rectangle in root coordinates - * @frame_rect: (out): location to store the computed corresponding frame bounds. - * - * Converts a desired bounds of the client window into the corresponding bounds - * of the window frame (excluding invisible borders and client side shadows.) - */ -void -meta_window_client_rect_to_frame_rect (MetaWindow *window, - MetaRectangle *client_rect, - MetaRectangle *frame_rect) -{ - if (!frame_rect) - return; - - *frame_rect = *client_rect; - - /* The support for G_MAXINT here to mean infinity is a convenience for - * constraints.c:get_size_limits() and not something that we provide - * in other locations or document. - */ - if (window->frame) - { - MetaFrameBorders borders; - meta_frame_calc_borders (window->frame, &borders); - - frame_rect->x -= borders.visible.left; - frame_rect->y -= borders.visible.top; - if (frame_rect->width != G_MAXINT) - frame_rect->width += borders.visible.left + borders.visible.right; - if (frame_rect->height != G_MAXINT) - frame_rect->height += borders.visible.top + borders.visible.bottom; - } - else - { - const GtkBorder *extents = &window->custom_frame_extents; - frame_rect->x += extents->left; - frame_rect->y += extents->top; - if (frame_rect->width != G_MAXINT) - frame_rect->width -= extents->left + extents->right; - if (frame_rect->height != G_MAXINT) - frame_rect->height -= extents->top + extents->bottom; - } -} - -/** - * meta_window_frame_rect_to_client_rect: - * @window: a #MetaWindow - * @frame_rect: desired frame bounds for the window - * @client_rect: (out): location to store the computed corresponding client rectangle. - * - * Converts a desired frame bounds for a window into the bounds of the client - * window. - */ -void -meta_window_frame_rect_to_client_rect (MetaWindow *window, - MetaRectangle *frame_rect, - MetaRectangle *client_rect) -{ - if (!client_rect) - return; - - *client_rect = *frame_rect; - - if (window->frame) - { - MetaFrameBorders borders; - meta_frame_calc_borders (window->frame, &borders); - - client_rect->x += borders.visible.left; - client_rect->y += borders.visible.top; - client_rect->width -= borders.visible.left + borders.visible.right; - client_rect->height -= borders.visible.top + borders.visible.bottom; - } - else - { - const GtkBorder *extents = &window->custom_frame_extents; - client_rect->x -= extents->left; - client_rect->y -= extents->top; - client_rect->width += extents->left + extents->right; - client_rect->height += extents->top + extents->bottom; - } -} - -/** - * meta_window_get_frame_rect: - * @window: a #MetaWindow - * @rect: (out): pointer to an allocated #MetaRectangle - * - * Gets the rectangle that bounds @window that is what the user thinks of - * as the edge of the window. This doesn't include any extra reactive - * area that we or the client adds to the window, or any area that the - * client adds to draw a client-side shadow. - */ -void -meta_window_get_frame_rect (const MetaWindow *window, - MetaRectangle *rect) -{ - *rect = window->rect; -} - -/** - * meta_window_get_client_area_rect: - * @window: a #MetaWindow - * @rect: (out): pointer to a cairo rectangle - * - * Gets the rectangle for the boundaries of the client area, relative - * to the buffer rect. If the window is shaded, the height of the - * rectangle is 0. - */ -void -meta_window_get_client_area_rect (const MetaWindow *window, - cairo_rectangle_int_t *rect) -{ - MetaFrameBorders borders; - - meta_frame_calc_borders (window->frame, &borders); - - rect->x = borders.total.left; - rect->y = borders.total.top; - - rect->width = window->buffer_rect.width - borders.total.left - borders.total.right; - if (window->shaded) - rect->height = 0; - else - rect->height = window->buffer_rect.height - borders.total.top - borders.total.bottom; -} - -void -meta_window_get_titlebar_rect (MetaWindow *window, - MetaRectangle *rect) -{ - meta_window_get_frame_rect (window, rect); - - /* The returned rectangle is relative to the frame rect. */ - rect->x = 0; - rect->y = 0; - - if (window->frame) - { - rect->height = window->frame->child_y; - } - else - { - /* Pick an arbitrary height for a titlebar. We might want to - * eventually have CSD windows expose their borders to us. */ - rect->height = 50; - } -} - -const char* -meta_window_get_startup_id (MetaWindow *window) -{ - if (window->startup_id == NULL) - { - MetaGroup *group; - - group = meta_window_get_group (window); - - if (group != NULL) - return meta_group_get_startup_id (group); - } - - return window->startup_id; -} - -static MetaWindow* -get_modal_transient (MetaWindow *window) -{ - GSList *windows; - GSList *tmp; - MetaWindow *modal_transient; - - /* A window can't be the transient of itself, but this is just for - * convenience in the loop below; we manually fix things up at the - * end if no real modal transient was found. - */ - modal_transient = window; - - windows = meta_display_list_windows (window->display, META_LIST_DEFAULT); - tmp = windows; - while (tmp != NULL) - { - MetaWindow *transient = tmp->data; - - if (transient->transient_for == modal_transient && - transient->type == META_WINDOW_MODAL_DIALOG) - { - modal_transient = transient; - tmp = windows; - continue; - } - - tmp = tmp->next; - } - - g_slist_free (windows); - - if (window == modal_transient) - modal_transient = NULL; - - return modal_transient; -} - -static gboolean -meta_window_transient_can_focus (MetaWindow *window) -{ -#ifdef HAVE_WAYLAND - if (window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND) - return meta_wayland_surface_get_buffer (window->surface) != NULL; -#endif - - return TRUE; -} - -/* XXX META_EFFECT_FOCUS */ -void -meta_window_focus (MetaWindow *window, - guint32 timestamp) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - MetaWindow *modal_transient; - - g_return_if_fail (!window->override_redirect); - - /* This is a oneshot flag */ - window->restore_focus_on_map = FALSE; - - meta_topic (META_DEBUG_FOCUS, - "Setting input focus to window %s, input: %d focusable: %d", - window->desc, window->input, meta_window_is_focusable (window)); - - if (window->display->grab_window && - window->display->grab_window != window && - window->display->grab_window->all_keys_grabbed && - !window->display->grab_window->unmanaging) - { - meta_topic (META_DEBUG_FOCUS, - "Current focus window %s has global keygrab, not focusing window %s after all", - window->display->grab_window->desc, window->desc); - return; - } - - modal_transient = get_modal_transient (window); - if (modal_transient != NULL && - !modal_transient->unmanaging && - meta_window_transient_can_focus (modal_transient)) - { - meta_topic (META_DEBUG_FOCUS, - "%s has %s as a modal transient, so focusing it instead.", - window->desc, modal_transient->desc); - if (!meta_window_located_on_workspace (modal_transient, workspace_manager->active_workspace)) - meta_window_change_workspace (modal_transient, workspace_manager->active_workspace); - window = modal_transient; - } - - meta_window_flush_calc_showing (window); - - if ((!window->mapped || window->hidden) && !window->shaded) - { - meta_topic (META_DEBUG_FOCUS, - "Window %s is not showing, not focusing after all", - window->desc); - return; - } - - META_WINDOW_GET_CLASS (window)->focus (window, timestamp); - - if (window->display->event_route == META_EVENT_ROUTE_NORMAL) - { - MetaBackend *backend = meta_get_backend (); - ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); - clutter_stage_set_key_focus (stage, NULL); - } - - if (window->close_dialog && - meta_close_dialog_is_visible (window->close_dialog)) - meta_close_dialog_focus (window->close_dialog); - - if (window->wm_state_demands_attention) - meta_window_unset_demands_attention(window); - -/* meta_effect_run_focus(window, NULL, NULL); */ -} - -/* Workspace management. Invariants: - * - * - window->workspace describes the workspace the window is on. - * - * - workspace->windows is a list of windows that is located on - * that workspace. - * - * - If the window is on_all_workspaces, then - * window->workspace == NULL, but workspace->windows contains - * the window. - */ - -static void -set_workspace_state (MetaWindow *window, - gboolean on_all_workspaces, - MetaWorkspace *workspace) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - - /* If we're on all workspaces, then our new workspace must be NULL, - * otherwise it must be set, unless we're unmanaging. */ - if (on_all_workspaces) - g_assert_null (workspace); - else - g_assert_true (window->unmanaging || workspace != NULL); - - /* If this is an override-redirect window, ensure that the only - * times we're setting the workspace state is either during construction - * to mark as on_all_workspaces, or when unmanaging to remove all the - * workspaces. */ - if (window->override_redirect) - g_return_if_fail ((window->constructing && on_all_workspaces) || window->unmanaging); - - if (on_all_workspaces == window->on_all_workspaces && - workspace == window->workspace && - !window->constructing) - return; - - if (window->workspace) - meta_workspace_remove_window (window->workspace, window); - else if (window->on_all_workspaces) - { - GList *l; - for (l = workspace_manager->workspaces; l != NULL; l = l->next) - { - MetaWorkspace *ws = l->data; - meta_workspace_remove_window (ws, window); - } - } - - window->on_all_workspaces = on_all_workspaces; - window->workspace = workspace; - - if (window->workspace) - meta_workspace_add_window (window->workspace, window); - else if (window->on_all_workspaces) - { - GList *l; - for (l = workspace_manager->workspaces; l != NULL; l = l->next) - { - MetaWorkspace *ws = l->data; - meta_workspace_add_window (ws, window); - } - } - - /* queue a move_resize since changing workspaces may change - * the relevant struts - */ - if (!window->override_redirect) - meta_window_queue (window, META_QUEUE_MOVE_RESIZE); - meta_window_queue (window, META_QUEUE_CALC_SHOWING); - meta_window_current_workspace_changed (window); - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_ON_ALL_WORKSPACES]); - g_signal_emit (window, window_signals[WORKSPACE_CHANGED], 0); -} - -static gboolean -should_be_on_all_workspaces (MetaWindow *window) -{ - if (window->always_sticky) - return TRUE; - - if (window->on_all_workspaces_requested) - return TRUE; - - if (window->override_redirect) - return TRUE; - - if (meta_prefs_get_workspaces_only_on_primary () && - !window->unmanaging && - window->monitor && - !meta_window_is_on_primary_monitor (window)) - return TRUE; - - return FALSE; -} - -void -meta_window_on_all_workspaces_changed (MetaWindow *window) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - gboolean on_all_workspaces = should_be_on_all_workspaces (window); - - if (window->on_all_workspaces == on_all_workspaces) - return; - - MetaWorkspace *workspace; - - if (on_all_workspaces) - { - workspace = NULL; - } - else - { - /* We're coming out of the sticky state. Put the window on - * the currently active workspace. */ - workspace = workspace_manager->active_workspace; - } - - set_workspace_state (window, on_all_workspaces, workspace); -} - -static void -meta_window_change_workspace_without_transients (MetaWindow *window, - MetaWorkspace *workspace) -{ - /* Try to unstick the window if it's stuck. This doesn't - * have any guarantee that we'll actually unstick the - * window, since it could be stuck for other reasons. */ - if (window->on_all_workspaces_requested) - meta_window_unstick (window); - - /* We failed to unstick the window. */ - if (window->on_all_workspaces) - return; - - if (window->workspace == workspace) - return; - - set_workspace_state (window, FALSE, workspace); -} - -static gboolean -change_workspace_foreach (MetaWindow *window, - void *data) -{ - meta_window_change_workspace_without_transients (window, data); - return TRUE; -} - -void -meta_window_change_workspace (MetaWindow *window, - MetaWorkspace *workspace) -{ - g_return_if_fail (!window->override_redirect); - - meta_window_change_workspace_without_transients (window, workspace); - - meta_window_foreach_transient (window, change_workspace_foreach, - workspace); - meta_window_foreach_ancestor (window, change_workspace_foreach, - workspace); -} - -static void -window_stick_impl (MetaWindow *window) -{ - meta_verbose ("Sticking window %s current on_all_workspaces = %d", - window->desc, window->on_all_workspaces); - - if (window->on_all_workspaces_requested) - return; - - /* We don't change window->workspaces, because we revert - * to that original workspace list if on_all_workspaces is - * toggled back off. - */ - window->on_all_workspaces_requested = TRUE; - meta_window_on_all_workspaces_changed (window); -} - -static void -window_unstick_impl (MetaWindow *window) -{ - if (!window->on_all_workspaces_requested) - return; - - /* Revert to window->workspaces */ - - window->on_all_workspaces_requested = FALSE; - meta_window_on_all_workspaces_changed (window); -} - -static gboolean -stick_foreach_func (MetaWindow *window, - void *data) -{ - gboolean stick; - - stick = *(gboolean*)data; - if (stick) - window_stick_impl (window); - else - window_unstick_impl (window); - return TRUE; -} - -void -meta_window_stick (MetaWindow *window) -{ - gboolean stick = TRUE; - - g_return_if_fail (!window->override_redirect); - - window_stick_impl (window); - meta_window_foreach_transient (window, - stick_foreach_func, - &stick); -} - -void -meta_window_unstick (MetaWindow *window) -{ - gboolean stick = FALSE; - - g_return_if_fail (!window->override_redirect); - - window_unstick_impl (window); - meta_window_foreach_transient (window, - stick_foreach_func, - &stick); -} - -void -meta_window_current_workspace_changed (MetaWindow *window) -{ - META_WINDOW_GET_CLASS (window)->current_workspace_changed (window); -} - -static gboolean -find_root_ancestor (MetaWindow *window, - void *data) -{ - MetaWindow **ancestor = data; - - /* Overwrite the previously "most-root" ancestor with the new one found */ - *ancestor = window; - - /* We want this to continue until meta_window_foreach_ancestor quits because - * there are no more valid ancestors. - */ - return TRUE; -} - -/** - * meta_window_find_root_ancestor: - * @window: a #MetaWindow - * - * Follow the chain of parents of @window, skipping transient windows, - * and return the "root" window which has no non-transient parent. - * - * Returns: (transfer none): The root ancestor window - */ -MetaWindow * -meta_window_find_root_ancestor (MetaWindow *window) -{ - MetaWindow *ancestor; - ancestor = window; - meta_window_foreach_ancestor (window, find_root_ancestor, &ancestor); - return ancestor; -} - -void -meta_window_raise (MetaWindow *window) -{ - MetaWindow *ancestor; - - g_return_if_fail (!window->override_redirect); - - ancestor = meta_window_find_root_ancestor (window); - - meta_topic (META_DEBUG_WINDOW_OPS, - "Raising window %s, ancestor of %s", - ancestor->desc, window->desc); - - /* Raise the ancestor of the window (if the window has no ancestor, - * then ancestor will be set to the window itself); do this because - * it's weird to see windows from other apps stacked between a child - * and parent window of the currently active app. The stacking - * constraints in stack.c then magically take care of raising all - * the child windows appropriately. - */ - if (window->display->stack == ancestor->display->stack) - { - meta_stack_raise (window->display->stack, ancestor); - } - else - { - meta_warning ( - "Either stacks aren't per screen or some window has a weird " - "transient_for hint; window->display->stack != " - "ancestor->screen->stack. window = %s, ancestor = %s.", - window->desc, ancestor->desc); - /* We could raise the window here, but don't want to do that twice and - * so we let the case below handle that. - */ - } - - /* Okay, so stacking constraints misses one case: If a window has - * two children and we want to raise one of those children, then - * raising the ancestor isn't enough; we need to also raise the - * correct child. See bug 307875. - */ - if (window != ancestor) - meta_stack_raise (window->display->stack, window); - - g_signal_emit (window, window_signals[RAISED], 0); -} - -void -meta_window_lower (MetaWindow *window) -{ - g_return_if_fail (!window->override_redirect); - - meta_topic (META_DEBUG_WINDOW_OPS, - "Lowering window %s", window->desc); - - meta_stack_lower (window->display->stack, window); -} - -/* - * Move window to the requested workspace; append controls whether new WS - * should be created if one does not exist. - */ -void -meta_window_change_workspace_by_index (MetaWindow *window, - gint space_index, - gboolean append) -{ - MetaWorkspaceManager *workspace_manager; - MetaWorkspace *workspace; - MetaDisplay *display; - - g_return_if_fail (!window->override_redirect); - - if (space_index == -1) - { - meta_window_stick (window); - return; - } - - display = window->display; - workspace_manager = display->workspace_manager; - - workspace = - meta_workspace_manager_get_workspace_by_index (workspace_manager, space_index); - - if (!workspace && append) - workspace = meta_workspace_manager_append_new_workspace (workspace_manager, FALSE, META_CURRENT_TIME); - - if (workspace) - meta_window_change_workspace (window, workspace); -} - -static void -meta_window_appears_focused_changed (MetaWindow *window) -{ - set_net_wm_state (window); - meta_window_frame_size_changed (window); - - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_APPEARS_FOCUSED]); - - if (window->frame) - meta_frame_queue_draw (window->frame); -} - -static gboolean -should_propagate_focus_appearance (MetaWindow *window) -{ - /* Parents of attached modal dialogs should appear focused. */ - if (meta_window_is_attached_dialog (window)) - return TRUE; - - /* Parents of these sorts of override-redirect windows should - * appear focused. */ - switch (window->type) - { - case META_WINDOW_DROPDOWN_MENU: - case META_WINDOW_POPUP_MENU: - case META_WINDOW_COMBO: - case META_WINDOW_TOOLTIP: - case META_WINDOW_NOTIFICATION: - case META_WINDOW_DND: - case META_WINDOW_OVERRIDE_OTHER: - return TRUE; - default: - break; - } - - return FALSE; -} - -/** - * meta_window_propagate_focus_appearance: - * @window: the window to start propagating from - * @focused: %TRUE if @window's ancestors should appear focused, - * %FALSE if they should not. - * - * Adjusts the value of #MetaWindow:appears-focused on @window's - * ancestors (but not @window itself). If @focused is %TRUE, each of - * @window's ancestors will have its %attached_focus_window field set - * to the current %focus_window. If @focused if %FALSE, each of - * @window's ancestors will have its %attached_focus_window field - * cleared if it is currently %focus_window. - */ -static void -meta_window_propagate_focus_appearance (MetaWindow *window, - gboolean focused) -{ - MetaWindow *child, *parent, *focus_window; - - focus_window = window->display->focus_window; - - child = window; - parent = meta_window_get_transient_for (child); - while (parent && (!focused || should_propagate_focus_appearance (child))) - { - gboolean child_focus_state_changed; - - if (focused) - { - if (parent->attached_focus_window == focus_window) - break; - child_focus_state_changed = (parent->attached_focus_window == NULL); - parent->attached_focus_window = focus_window; - } - else - { - if (parent->attached_focus_window != focus_window) - break; - child_focus_state_changed = (parent->attached_focus_window != NULL); - parent->attached_focus_window = NULL; - } - - if (child_focus_state_changed && !parent->has_focus) - { - meta_window_appears_focused_changed (parent); - } - - child = parent; - parent = meta_window_get_transient_for (child); - } -} - -void -meta_window_set_focused_internal (MetaWindow *window, - gboolean focused) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - - if (focused) - { - window->has_focus = TRUE; - if (window->override_redirect) - return; - - /* Move to the front of the focusing workspace's MRU list. - * We should only be "removing" it from the MRU list if it's - * not already there. Note that it's possible that we might - * be processing this FocusIn after we've changed to a - * different workspace; we should therefore update the MRU - * list only if the window is actually on the active - * workspace. - */ - if (workspace_manager->active_workspace && - meta_window_located_on_workspace (window, - workspace_manager->active_workspace)) - { - GList* link; - link = g_list_find (workspace_manager->active_workspace->mru_list, - window); - g_assert (link); - - workspace_manager->active_workspace->mru_list = - g_list_remove_link (workspace_manager->active_workspace->mru_list, - link); - g_list_free (link); - - workspace_manager->active_workspace->mru_list = - g_list_prepend (workspace_manager->active_workspace->mru_list, - window); - } - - if (window->frame) - meta_frame_queue_draw (window->frame); - - /* Ungrab click to focus button since the sync grab can interfere - * with some things you might do inside the focused window, by - * causing the client to get funky enter/leave events. - * - * The reason we usually have a passive grab on the window is - * so that we can intercept clicks and raise the window in - * response. For click-to-focus we don't need that since the - * focused window is already raised. When raise_on_click is - * FALSE we also don't need that since we don't do anything - * when the window is clicked. - * - * There is dicussion in bugs 102209, 115072, and 461577 - */ - if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK || - !meta_prefs_get_raise_on_click()) - { - meta_display_ungrab_focus_window_button (window->display, window); - /* Since we ungrab with XIAnyModifier above, all button - grabs go way so we need to re-grab the window buttons. */ - meta_display_grab_window_buttons (window->display, window->xwindow); - } - - g_signal_emit (window, window_signals[FOCUS], 0); - - if (!window->attached_focus_window) - meta_window_appears_focused_changed (window); - - meta_window_propagate_focus_appearance (window, TRUE); - } - else - { - window->has_focus = FALSE; - if (window->override_redirect) - return; - - meta_window_propagate_focus_appearance (window, FALSE); - - if (!window->attached_focus_window) - meta_window_appears_focused_changed (window); - - /* Re-grab for click to focus and raise-on-click, if necessary */ - if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK || - !meta_prefs_get_raise_on_click ()) - meta_display_grab_focus_window_button (window->display, window); - } -} - -/** - * meta_window_get_icon_geometry: - * @window: a #MetaWindow - * @rect: (out): rectangle into which to store the returned geometry. - * - * Gets the location of the icon corresponding to the window. The location - * will be provided set by the task bar or other user interface element - * displaying the icon, and is relative to the root window. - * - * Return value: %TRUE if the icon geometry was successfully retrieved. - */ -gboolean -meta_window_get_icon_geometry (MetaWindow *window, - MetaRectangle *rect) -{ - g_return_val_if_fail (!window->override_redirect, FALSE); - - if (window->icon_geometry_set) - { - if (rect) - *rect = window->icon_geometry; - - return TRUE; - } - - return FALSE; -} - -/** - * meta_window_set_icon_geometry: - * @window: a #MetaWindow - * @rect: (nullable): rectangle with the desired geometry or %NULL. - * - * Sets or unsets the location of the icon corresponding to the window. If - * set, the location should correspond to a dock, task bar or other user - * interface element displaying the icon, and is relative to the root window. - */ -void -meta_window_set_icon_geometry (MetaWindow *window, - MetaRectangle *rect) -{ - if (rect) - { - window->icon_geometry = *rect; - window->icon_geometry_set = TRUE; - } - else - { - window->icon_geometry_set = FALSE; - } -} - -static void -redraw_icon (MetaWindow *window) -{ - /* We could probably be smart and just redraw the icon here, - * instead of the whole frame. - */ - if (window->frame) - meta_frame_queue_draw (window->frame); -} - -static void -meta_window_update_icon_now (MetaWindow *window, - gboolean force) -{ - gboolean changed; - cairo_surface_t *icon = NULL; - cairo_surface_t *mini_icon; - - g_return_if_fail (!window->override_redirect); - - changed = META_WINDOW_GET_CLASS (window)->update_icon (window, &icon, &mini_icon); - - if (changed || force) - { - if (window->icon) - cairo_surface_destroy (window->icon); - window->icon = icon; - - if (window->mini_icon) - cairo_surface_destroy (window->mini_icon); - window->mini_icon = mini_icon; - - g_object_freeze_notify (G_OBJECT (window)); - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_ICON]); - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_MINI_ICON]); - g_object_thaw_notify (G_OBJECT (window)); - - redraw_icon (window); - } -} - -static gboolean -idle_update_icon (gpointer data) -{ - GSList *tmp; - GSList *copy; - guint queue_index = GPOINTER_TO_INT (data); - - meta_topic (META_DEBUG_GEOMETRY, "Clearing the update_icon queue"); - - /* Work with a copy, for reentrancy. The allowed reentrancy isn't - * complete; destroying a window while we're in here would result in - * badness. But it's OK to queue/unqueue update_icons. - */ - copy = g_slist_copy (queue_pending[queue_index]); - g_slist_free (queue_pending[queue_index]); - queue_pending[queue_index] = NULL; - queue_later[queue_index] = 0; - - destroying_windows_disallowed += 1; - - tmp = copy; - while (tmp != NULL) - { - MetaWindow *window; - - window = tmp->data; - - meta_window_update_icon_now (window, FALSE); - window->is_in_queues &= ~META_QUEUE_UPDATE_ICON; - - tmp = tmp->next; - } - - g_slist_free (copy); - - destroying_windows_disallowed -= 1; - - return FALSE; -} - -GList* -meta_window_get_workspaces (MetaWindow *window) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - - if (window->on_all_workspaces) - return workspace_manager->workspaces; - else if (window->workspace != NULL) - return window->workspace->list_containing_self; - else if (window->constructing) - return NULL; - else - g_assert_not_reached (); - return NULL; -} - -static void -invalidate_work_areas (MetaWindow *window) -{ - GList *tmp; - - tmp = meta_window_get_workspaces (window); - - while (tmp != NULL) - { - meta_workspace_invalidate_work_area (tmp->data); - tmp = tmp->next; - } -} - -void -meta_window_update_struts (MetaWindow *window) -{ - if (META_WINDOW_GET_CLASS (window)->update_struts (window)) - invalidate_work_areas (window); -} - -static void -meta_window_type_changed (MetaWindow *window) -{ - gboolean old_decorated = window->decorated; - GObject *object = G_OBJECT (window); - - window->attached = meta_window_should_attach_to_parent (window); - meta_window_recalc_features (window); - - if (!window->override_redirect) - set_net_wm_state (window); - - /* Update frame */ - if (window->decorated) - meta_window_ensure_frame (window); - else - meta_window_destroy_frame (window); - - /* update stacking constraints */ - meta_window_update_layer (window); - - meta_window_grab_keys (window); - - g_object_freeze_notify (object); - - if (old_decorated != window->decorated) - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_DECORATED]); - - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_WINDOW_TYPE]); - - g_object_thaw_notify (object); -} - -void -meta_window_set_type (MetaWindow *window, - MetaWindowType type) -{ - if (window->type == type) - return; - - window->type = type; - meta_window_type_changed (window); -} - -void -meta_window_frame_size_changed (MetaWindow *window) -{ - if (window->frame) - meta_frame_clear_cached_borders (window->frame); -} - -static void -meta_window_get_default_skip_hints (MetaWindow *window, - gboolean *skip_taskbar_out, - gboolean *skip_pager_out) -{ - META_WINDOW_GET_CLASS (window)->get_default_skip_hints (window, skip_taskbar_out, skip_pager_out); -} - -static void -meta_window_recalc_skip_features (MetaWindow *window) -{ - switch (window->type) - { - /* Force skip taskbar/pager on these window types */ - case META_WINDOW_DESKTOP: - case META_WINDOW_DOCK: - case META_WINDOW_TOOLBAR: - case META_WINDOW_MENU: - case META_WINDOW_UTILITY: - case META_WINDOW_SPLASHSCREEN: - case META_WINDOW_DROPDOWN_MENU: - case META_WINDOW_POPUP_MENU: - case META_WINDOW_TOOLTIP: - case META_WINDOW_NOTIFICATION: - case META_WINDOW_COMBO: - case META_WINDOW_DND: - case META_WINDOW_OVERRIDE_OTHER: - window->skip_taskbar = TRUE; - window->skip_pager = TRUE; - break; - - case META_WINDOW_DIALOG: - case META_WINDOW_MODAL_DIALOG: - /* only skip taskbar if we have a real transient parent - (and ignore the application hints) */ - if (window->transient_for != NULL) - window->skip_taskbar = TRUE; - else - window->skip_taskbar = window->skip_from_window_list; - break; - - case META_WINDOW_NORMAL: - { - gboolean skip_taskbar_hint, skip_pager_hint; - meta_window_get_default_skip_hints (window, &skip_taskbar_hint, &skip_pager_hint); - window->skip_taskbar = skip_taskbar_hint | window->skip_from_window_list; - window->skip_pager = skip_pager_hint | window->skip_from_window_list; - } - break; - } -} - -void -meta_window_recalc_features (MetaWindow *window) -{ - gboolean old_has_close_func; - gboolean old_has_minimize_func; - gboolean old_has_move_func; - gboolean old_has_resize_func; - gboolean old_has_shade_func; - gboolean old_always_sticky; - gboolean old_skip_taskbar; - - old_has_close_func = window->has_close_func; - old_has_minimize_func = window->has_minimize_func; - old_has_move_func = window->has_move_func; - old_has_resize_func = window->has_resize_func; - old_has_shade_func = window->has_shade_func; - old_always_sticky = window->always_sticky; - old_skip_taskbar = window->skip_taskbar; - - /* Use MWM hints initially */ - if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) - window->decorated = window->mwm_decorated; - else - window->decorated = FALSE; - window->border_only = window->mwm_border_only; - window->has_close_func = window->mwm_has_close_func; - window->has_minimize_func = window->mwm_has_minimize_func; - window->has_maximize_func = window->mwm_has_maximize_func; - window->has_move_func = window->mwm_has_move_func; - - window->has_resize_func = TRUE; - - /* If min_size == max_size, then don't allow resize */ - if (window->size_hints.min_width == window->size_hints.max_width && - window->size_hints.min_height == window->size_hints.max_height) - window->has_resize_func = FALSE; - else if (!window->mwm_has_resize_func) - { - /* We ignore mwm_has_resize_func because WM_NORMAL_HINTS is the - * authoritative source for that info. Some apps such as mplayer or - * xine disable resize via MWM but not WM_NORMAL_HINTS, but that - * leads to e.g. us not fullscreening their windows. Apps that set - * MWM but not WM_NORMAL_HINTS are basically broken. We complain - * about these apps but make them work. - */ - - meta_warning ("Window %s sets an MWM hint indicating it isn't resizable, but sets min size %d x %d and max size %d x %d; this doesn't make much sense.", - window->desc, - window->size_hints.min_width, - window->size_hints.min_height, - window->size_hints.max_width, - window->size_hints.max_height); - } - - window->has_shade_func = TRUE; - window->has_fullscreen_func = TRUE; - - window->always_sticky = FALSE; - - /* Semantic category overrides the MWM hints */ - if (window->type == META_WINDOW_TOOLBAR) - window->decorated = FALSE; - - if (window->type == META_WINDOW_DESKTOP || - window->type == META_WINDOW_DOCK || - window->override_redirect) - window->always_sticky = TRUE; - - if (window->override_redirect || - meta_window_get_frame_type (window) == META_FRAME_TYPE_LAST) - { - window->decorated = FALSE; - window->has_close_func = FALSE; - window->has_shade_func = FALSE; - - /* FIXME this keeps panels and things from using - * NET_WM_MOVERESIZE; the problem is that some - * panels (edge panels) have fixed possible locations, - * and others ("floating panels") do not. - * - * Perhaps we should require edge panels to explicitly - * disable movement? - */ - window->has_move_func = FALSE; - window->has_resize_func = FALSE; - } - - if (window->type != META_WINDOW_NORMAL) - { - window->has_minimize_func = FALSE; - window->has_maximize_func = FALSE; - window->has_fullscreen_func = FALSE; - } - - if (!window->has_resize_func) - { - window->has_maximize_func = FALSE; - MetaRectangle display_rect = { 0 }; - - meta_display_get_size (window->display, &display_rect.width, - &display_rect.height); - - /* don't allow fullscreen if we can't resize, unless the size - * is entire screen size (kind of broken, because we - * actually fullscreen to monitor size not screen size) - */ - if (window->size_hints.min_width == display_rect.width && - window->size_hints.min_height == display_rect.height) - ; /* leave fullscreen available */ - else - window->has_fullscreen_func = FALSE; - } - - /* We leave fullscreen windows decorated, just push the frame outside - * the screen. This avoids flickering to unparent them. - * - * Note that setting has_resize_func = FALSE here must come after the - * above code that may disable fullscreen, because if the window - * is not resizable purely due to fullscreen, we don't want to - * disable fullscreen mode. - */ - if (window->fullscreen) - { - window->has_shade_func = FALSE; - window->has_move_func = FALSE; - window->has_resize_func = FALSE; - window->has_maximize_func = FALSE; - } - - if (window->has_maximize_func && window->monitor) - { - MetaRectangle work_area, client_rect; - - meta_window_get_work_area_current_monitor (window, &work_area); - meta_window_frame_rect_to_client_rect (window, &work_area, &client_rect); - - if (window->size_hints.min_width >= client_rect.width || - window->size_hints.min_height >= client_rect.height) - window->has_maximize_func = FALSE; - } - - meta_topic (META_DEBUG_WINDOW_OPS, - "Window %s fullscreen = %d not resizable, maximizable = %d fullscreenable = %d min size %dx%d max size %dx%d", - window->desc, - window->fullscreen, - window->has_maximize_func, window->has_fullscreen_func, - window->size_hints.min_width, - window->size_hints.min_height, - window->size_hints.max_width, - window->size_hints.max_height); - - /* no shading if not decorated */ - if (!window->decorated || window->border_only) - window->has_shade_func = FALSE; - - meta_window_recalc_skip_features (window); - - /* To prevent users from losing windows, let's prevent users from - * minimizing skip-taskbar windows through the window decorations. */ - if (window->skip_taskbar) - window->has_minimize_func = FALSE; - - meta_topic (META_DEBUG_WINDOW_OPS, - "Window %s decorated = %d border_only = %d has_close = %d has_minimize = %d has_maximize = %d has_move = %d has_shade = %d skip_taskbar = %d skip_pager = %d", - window->desc, - window->decorated, - window->border_only, - window->has_close_func, - window->has_minimize_func, - window->has_maximize_func, - window->has_move_func, - window->has_shade_func, - window->skip_taskbar, - window->skip_pager); - - if (old_skip_taskbar != window->skip_taskbar) - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_SKIP_TASKBAR]); - - /* FIXME: - * Lame workaround for recalc_features being used overzealously. - * The fix is to only recalc_features when something has - * actually changed. - */ - if (window->constructing || - old_has_close_func != window->has_close_func || - old_has_minimize_func != window->has_minimize_func || - old_has_move_func != window->has_move_func || - old_has_resize_func != window->has_resize_func || - old_has_shade_func != window->has_shade_func || - old_always_sticky != window->always_sticky) - set_allowed_actions_hint (window); - - if (window->has_resize_func != old_has_resize_func) - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_RESIZEABLE]); - - meta_window_frame_size_changed (window); - - /* FIXME perhaps should ensure if we don't have a shade func, - * we aren't shaded, etc. - */ -} - -void -meta_window_show_menu (MetaWindow *window, - MetaWindowMenuType menu, - int x, - int y) -{ - g_return_if_fail (!window->override_redirect); - meta_compositor_show_window_menu (window->display->compositor, window, menu, x, y); -} - -void -meta_window_show_menu_for_rect (MetaWindow *window, - MetaWindowMenuType menu, - MetaRectangle *rect) -{ - g_return_if_fail (!window->override_redirect); - meta_compositor_show_window_menu_for_rect (window->display->compositor, window, menu, rect); -} - -void -meta_window_shove_titlebar_onscreen (MetaWindow *window) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - MetaRectangle frame_rect; - GList *onscreen_region; - int horiz_amount, vert_amount; - - g_return_if_fail (!window->override_redirect); - - /* If there's no titlebar, don't bother */ - if (!window->frame) - return; - - /* Get the basic info we need */ - meta_window_get_frame_rect (window, &frame_rect); - onscreen_region = workspace_manager->active_workspace->screen_region; - - /* Extend the region (just in case the window is too big to fit on the - * screen), then shove the window on screen, then return the region to - * normal. - */ - horiz_amount = frame_rect.width; - vert_amount = frame_rect.height; - meta_rectangle_expand_region (onscreen_region, - horiz_amount, - horiz_amount, - 0, - vert_amount); - meta_rectangle_shove_into_region(onscreen_region, - FIXED_DIRECTION_X, - &frame_rect); - meta_rectangle_expand_region (onscreen_region, - -horiz_amount, - -horiz_amount, - 0, - -vert_amount); - - meta_window_move_frame (window, FALSE, frame_rect.x, frame_rect.y); -} - -gboolean -meta_window_titlebar_is_onscreen (MetaWindow *window) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - MetaRectangle titlebar_rect, frame_rect; - GList *onscreen_region; - gboolean is_onscreen; - - const int min_height_needed = 8; - const float min_width_percent = 0.5; - const int min_width_absolute = 50; - - /* Titlebar can't be offscreen if there is no titlebar... */ - if (!window->frame) - return TRUE; - - /* Get the rectangle corresponding to the titlebar */ - meta_window_get_titlebar_rect (window, &titlebar_rect); - - /* Translate into screen coordinates */ - meta_window_get_frame_rect (window, &frame_rect); - titlebar_rect.x = frame_rect.x; - titlebar_rect.y = frame_rect.y; - - /* Run through the spanning rectangles for the screen and see if one of - * them overlaps with the titlebar sufficiently to consider it onscreen. - */ - is_onscreen = FALSE; - onscreen_region = workspace_manager->active_workspace->screen_region; - while (onscreen_region) - { - MetaRectangle *spanning_rect = onscreen_region->data; - MetaRectangle overlap; - - meta_rectangle_intersect (&titlebar_rect, spanning_rect, &overlap); - if (overlap.height > MIN (titlebar_rect.height, min_height_needed) && - overlap.width > MIN (titlebar_rect.width * min_width_percent, - min_width_absolute)) - { - is_onscreen = TRUE; - break; - } - - onscreen_region = onscreen_region->next; - } - - return is_onscreen; -} - -static gboolean -check_moveresize_frequency (MetaWindow *window, - gdouble *remaining) -{ - int64_t current_time; - const double max_resizes_per_second = 25.0; - const double ms_between_resizes = 1000.0 / max_resizes_per_second; - double elapsed; - - current_time = g_get_real_time (); - - /* If we are throttling via _NET_WM_SYNC_REQUEST, we don't need - * an artificial timeout-based throttled */ - if (!window->disable_sync && - window->sync_request_alarm != None) - return TRUE; - - elapsed = (current_time - window->display->grab_last_moveresize_time) / 1000; - - if (elapsed >= 0.0 && elapsed < ms_between_resizes) - { - meta_topic (META_DEBUG_RESIZING, - "Delaying move/resize as only %g of %g ms elapsed", - elapsed, ms_between_resizes); - - if (remaining) - *remaining = (ms_between_resizes - elapsed); - - return FALSE; - } - - meta_topic (META_DEBUG_RESIZING, - " Checked moveresize freq, allowing move/resize now (%g of %g seconds elapsed)", - elapsed / 1000.0, 1.0 / max_resizes_per_second); - - return TRUE; -} - -static gboolean -update_move_timeout (gpointer data) -{ - MetaWindow *window = data; - - update_move (window, - window->display->grab_last_edge_resistance_flags, - window->display->grab_latest_motion_x, - window->display->grab_latest_motion_y); - - return FALSE; -} - -static void -update_move_maybe_tile (MetaWindow *window, - int shake_threshold, - int x, - int y) -{ - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - MetaLogicalMonitor *logical_monitor; - MetaDisplay *display = window->display; - MetaRectangle work_area; - - /* For side-by-side tiling we are interested in the inside vertical - * edges of the work area of the monitor where the pointer is located, - * and in the outside top edge for maximized tiling. - * - * For maximized tiling we use the outside edge instead of the - * inside edge, because we don't want to force users to maximize - * windows they are placing near the top of their screens. - * - * The "current" idea of meta_window_get_work_area_current_monitor() and - * meta_screen_get_current_monitor() is slightly different: the former - * refers to the monitor which contains the largest part of the window, - * the latter to the one where the pointer is located. - */ - logical_monitor = - meta_monitor_manager_get_logical_monitor_at (monitor_manager, x, y); - if (!logical_monitor) - return; - - meta_window_get_work_area_for_monitor (window, - logical_monitor->number, - &work_area); - - /* Check if the cursor is in a position which triggers tiling - * and set tile_mode accordingly. - */ - if (meta_window_can_tile_side_by_side (window) && - x >= logical_monitor->rect.x && x < (work_area.x + shake_threshold)) - display->preview_tile_mode = META_TILE_LEFT; - else if (meta_window_can_tile_side_by_side (window) && - x >= work_area.x + work_area.width - shake_threshold && - x < (logical_monitor->rect.x + logical_monitor->rect.width)) - display->preview_tile_mode = META_TILE_RIGHT; - else if (meta_window_can_tile_maximized (window) && - y >= logical_monitor->rect.y && y <= work_area.y) - display->preview_tile_mode = META_TILE_MAXIMIZED; - else - display->preview_tile_mode = META_TILE_NONE; - - if (display->preview_tile_mode != META_TILE_NONE) - window->tile_monitor_number = logical_monitor->number; -} - -static void -update_move (MetaWindow *window, - MetaEdgeResistanceFlags flags, - int x, - int y) -{ - int dx, dy; - int new_x, new_y; - MetaRectangle old; - int shake_threshold; - MetaDisplay *display = window->display; - - display->grab_latest_motion_x = x; - display->grab_latest_motion_y = y; - - dx = x - display->grab_anchor_root_x; - dy = y - display->grab_anchor_root_y; - - new_x = display->grab_anchor_window_pos.x + dx; - new_y = display->grab_anchor_window_pos.y + dy; - - meta_verbose ("x,y = %d,%d anchor ptr %d,%d anchor pos %d,%d dx,dy %d,%d", - x, y, - display->grab_anchor_root_x, - display->grab_anchor_root_y, - display->grab_anchor_window_pos.x, - display->grab_anchor_window_pos.y, - dx, dy); - - /* Don't bother doing anything if no move has been specified. (This - * happens often, even in keyboard moving, due to the warping of the - * pointer. - */ - if (dx == 0 && dy == 0) - return; - - /* Originally for detaching maximized windows, but we use this - * for the zones at the sides of the monitor where trigger tiling - * because it's about the right size - */ -#define DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR 6 - shake_threshold = meta_prefs_get_drag_threshold () * - DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR; - - if (flags & META_EDGE_RESISTANCE_SNAP) - { - /* We don't want to tile while snapping. Also, clear any previous tile - request. */ - display->preview_tile_mode = META_TILE_NONE; - window->tile_monitor_number = -1; - } - else if (meta_prefs_get_edge_tiling () && - !META_WINDOW_MAXIMIZED (window) && - !META_WINDOW_TILED_SIDE_BY_SIDE (window)) - { - update_move_maybe_tile (window, shake_threshold, x, y); - } - - /* shake loose (unmaximize) maximized or tiled window if dragged beyond - * the threshold in the Y direction. Tiled windows can also be pulled - * loose via X motion. - */ - - if ((META_WINDOW_MAXIMIZED (window) && ABS (dy) >= shake_threshold) || - (META_WINDOW_TILED_SIDE_BY_SIDE (window) && (MAX (ABS (dx), ABS (dy)) >= shake_threshold))) - { - double prop; - - /* Shake loose, so that the window snaps back to maximized - * when dragged near the top; do not snap back if tiling - * is enabled, as top edge tiling can be used in that case - */ - window->shaken_loose = !meta_prefs_get_edge_tiling (); - window->tile_mode = META_TILE_NONE; - - /* move the unmaximized window to the cursor */ - prop = - ((double)(x - display->grab_initial_window_pos.x)) / - ((double)display->grab_initial_window_pos.width); - - display->grab_initial_window_pos.x = x - window->saved_rect.width * prop; - - /* If we started dragging the window from above the top of the window, - * pretend like we started dragging from the middle of the titlebar - * instead, as the "correct" anchoring looks wrong. */ - if (display->grab_anchor_root_y < display->grab_initial_window_pos.y) - { - MetaRectangle titlebar_rect; - meta_window_get_titlebar_rect (window, &titlebar_rect); - display->grab_anchor_root_y = display->grab_initial_window_pos.y + titlebar_rect.height / 2; - } - - window->saved_rect.x = display->grab_initial_window_pos.x; - window->saved_rect.y = display->grab_initial_window_pos.y; - - meta_window_unmaximize (window, META_MAXIMIZE_BOTH); - return; - } - - /* remaximize window on another monitor if window has been shaken - * loose or it is still maximized (then move straight) - */ - else if ((window->shaken_loose || META_WINDOW_MAXIMIZED (window)) && - window->tile_mode != META_TILE_LEFT && window->tile_mode != META_TILE_RIGHT) - { - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - int n_logical_monitors; - const MetaLogicalMonitor *wmonitor; - MetaRectangle work_area; - int monitor; - - window->tile_mode = META_TILE_NONE; - wmonitor = window->monitor; - n_logical_monitors = - meta_monitor_manager_get_num_logical_monitors (monitor_manager); - - for (monitor = 0; monitor < n_logical_monitors; monitor++) - { - meta_window_get_work_area_for_monitor (window, monitor, &work_area); - - /* check if cursor is near the top of a monitor work area */ - if (x >= work_area.x && - x < (work_area.x + work_area.width) && - y >= work_area.y && - y < (work_area.y + shake_threshold)) - { - /* move the saved rect if window will become maximized on an - * other monitor so user isn't surprised on a later unmaximize - */ - if (wmonitor->number != monitor) - { - window->saved_rect.x = work_area.x; - window->saved_rect.y = work_area.y; - - if (window->frame) - { - window->saved_rect.x += window->frame->child_x; - window->saved_rect.y += window->frame->child_y; - } - - window->unconstrained_rect.x = window->saved_rect.x; - window->unconstrained_rect.y = window->saved_rect.y; - - meta_window_unmaximize (window, META_MAXIMIZE_BOTH); - - display->grab_initial_window_pos = work_area; - display->grab_anchor_root_x = x; - display->grab_anchor_root_y = y; - window->shaken_loose = FALSE; - - meta_window_maximize (window, META_MAXIMIZE_BOTH); - } - - return; - } - } - } - - /* Delay showing the tile preview slightly to make it more unlikely to - * trigger it unwittingly, e.g. when shaking loose the window or moving - * it to another monitor. - */ - meta_display_update_tile_preview (window->display, - window->tile_mode != META_TILE_NONE); - - meta_window_get_frame_rect (window, &old); - - /* Don't allow movement in the maximized directions or while tiled */ - if (window->maximized_horizontally || META_WINDOW_TILED_SIDE_BY_SIDE (window)) - new_x = old.x; - if (window->maximized_vertically) - new_y = old.y; - - /* Do any edge resistance/snapping */ - meta_window_edge_resistance_for_move (window, - &new_x, - &new_y, - update_move_timeout, - flags); - - meta_window_move_frame (window, TRUE, new_x, new_y); -} - -static gboolean -update_resize_timeout (gpointer data) -{ - MetaWindow *window = data; - - update_resize (window, - window->display->grab_last_edge_resistance_flags, - window->display->grab_latest_motion_x, - window->display->grab_latest_motion_y, - TRUE); - return FALSE; -} - -static void -update_resize (MetaWindow *window, - MetaEdgeResistanceFlags flags, - int x, - int y, - gboolean force) -{ - int dx, dy; - MetaGravity gravity; - MetaRectangle new_rect; - MetaRectangle old_rect; - double remaining = 0; - - window->display->grab_latest_motion_x = x; - window->display->grab_latest_motion_y = y; - - dx = x - window->display->grab_anchor_root_x; - dy = y - window->display->grab_anchor_root_y; - - /* Attached modal dialogs are special in that size - * changes apply to both sides, so that the dialog - * remains centered to the parent. - */ - if (meta_window_is_attached_dialog (window)) - { - dx *= 2; - dy *= 2; - } - - new_rect.width = window->display->grab_anchor_window_pos.width; - new_rect.height = window->display->grab_anchor_window_pos.height; - - /* Don't bother doing anything if no move has been specified. (This - * happens often, even in keyboard resizing, due to the warping of the - * pointer. - */ - if (dx == 0 && dy == 0) - return; - - if (window->display->grab_op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN) - { - MetaGrabOp op = META_GRAB_OP_WINDOW_BASE | META_GRAB_OP_WINDOW_FLAG_KEYBOARD; - - if (dx > 0) - op |= META_GRAB_OP_WINDOW_DIR_EAST; - else if (dx < 0) - op |= META_GRAB_OP_WINDOW_DIR_WEST; - - if (dy > 0) - op |= META_GRAB_OP_WINDOW_DIR_SOUTH; - else if (dy < 0) - op |= META_GRAB_OP_WINDOW_DIR_NORTH; - - window->display->grab_op = op; - - meta_window_update_keyboard_resize (window, TRUE); - } - - if (window->display->grab_op & META_GRAB_OP_WINDOW_DIR_EAST) - new_rect.width += dx; - else if (window->display->grab_op & META_GRAB_OP_WINDOW_DIR_WEST) - new_rect.width -= dx; - - if (window->display->grab_op & META_GRAB_OP_WINDOW_DIR_SOUTH) - new_rect.height += dy; - else if (window->display->grab_op & META_GRAB_OP_WINDOW_DIR_NORTH) - new_rect.height -= dy; - - meta_window_maybe_apply_size_hints (window, &new_rect); - - /* If we're waiting for a request for _NET_WM_SYNC_REQUEST, we'll - * resize the window when the window responds, or when we time - * the response out. - */ - if (window->sync_request_timeout_id != 0) - return; - - if (!check_moveresize_frequency (window, &remaining) && !force) - { - /* we are ignoring an event here, so we schedule a - * compensation event when we would otherwise not ignore - * an event. Otherwise we can become stuck if the user never - * generates another event. - */ - if (!window->display->grab_resize_timeout_id) - { - window->display->grab_resize_timeout_id = - g_timeout_add ((int)remaining, update_resize_timeout, window); - g_source_set_name_by_id (window->display->grab_resize_timeout_id, - "[mutter] update_resize_timeout"); - } - - return; - } - - /* Remove any scheduled compensation events */ - g_clear_handle_id (&window->display->grab_resize_timeout_id, g_source_remove); - - meta_window_get_frame_rect (window, &old_rect); - - /* One sided resizing ought to actually be one-sided, despite the fact that - * aspect ratio windows don't interact nicely with the above stuff. So, - * to avoid some nasty flicker, we enforce that. - */ - - if ((window->display->grab_op & (META_GRAB_OP_WINDOW_DIR_WEST | META_GRAB_OP_WINDOW_DIR_EAST)) == 0) - new_rect.width = old_rect.width; - - if ((window->display->grab_op & (META_GRAB_OP_WINDOW_DIR_NORTH | META_GRAB_OP_WINDOW_DIR_SOUTH)) == 0) - new_rect.height = old_rect.height; - - /* compute gravity of client during operation */ - gravity = meta_resize_gravity_from_grab_op (window->display->grab_op); - g_assert (gravity >= 0); - - /* Do any edge resistance/snapping */ - meta_window_edge_resistance_for_resize (window, - &new_rect.width, - &new_rect.height, - gravity, - update_resize_timeout, - flags); - - meta_window_resize_frame_with_gravity (window, TRUE, - new_rect.width, new_rect.height, - gravity); - - /* Store the latest resize time, if we actually resized. */ - if (window->rect.width != old_rect.width || - window->rect.height != old_rect.height) - window->display->grab_last_moveresize_time = g_get_real_time (); -} - -static void -maybe_maximize_tiled_window (MetaWindow *window) -{ - MetaRectangle work_area; - gint shake_threshold; - - if (!META_WINDOW_TILED_SIDE_BY_SIDE (window)) - return; - - shake_threshold = meta_prefs_get_drag_threshold (); - - meta_window_get_work_area_for_monitor (window, - window->tile_monitor_number, - &work_area); - if (window->rect.width >= work_area.width - shake_threshold) - meta_window_maximize (window, META_MAXIMIZE_BOTH); -} - -void -meta_window_update_resize (MetaWindow *window, - MetaEdgeResistanceFlags flags, - int x, int y, - gboolean force) -{ - update_resize (window, flags, x, y, force); -} - -static void -end_grab_op (MetaWindow *window, - const ClutterEvent *event) -{ - ClutterModifierType modifiers; - MetaEdgeResistanceFlags last_flags; - gfloat x, y; - - clutter_event_get_coords (event, &x, &y); - modifiers = clutter_event_get_state (event); - meta_display_check_threshold_reached (window->display, x, y); - - /* If the user was snap moving then ignore the button - * release because they may have let go of shift before - * releasing the mouse button and they almost certainly do - * not want a non-snapped movement to occur from the button - * release. - */ - last_flags = window->display->grab_last_edge_resistance_flags; - if ((last_flags & META_EDGE_RESISTANCE_SNAP) == 0) - { - MetaEdgeResistanceFlags flags = META_EDGE_RESISTANCE_DEFAULT; - - if (modifiers & CLUTTER_SHIFT_MASK) - flags |= META_EDGE_RESISTANCE_SNAP; - - if (modifiers & CLUTTER_CONTROL_MASK) - flags |= META_EDGE_RESISTANCE_WINDOWS; - - if (meta_grab_op_is_moving (window->display->grab_op)) - { - if (window->display->preview_tile_mode != META_TILE_NONE) - meta_window_tile (window, window->display->preview_tile_mode); - else - update_move (window, flags, x, y); - } - else if (meta_grab_op_is_resizing (window->display->grab_op)) - { - if (window->tile_match != NULL) - flags |= (META_EDGE_RESISTANCE_SNAP | META_EDGE_RESISTANCE_WINDOWS); - - update_resize (window, flags, x, y, TRUE); - maybe_maximize_tiled_window (window); - } - } - window->display->preview_tile_mode = META_TILE_NONE; - meta_display_end_grab_op (window->display, clutter_event_get_time (event)); -} - -gboolean -meta_window_handle_mouse_grab_op_event (MetaWindow *window, - const ClutterEvent *event) -{ - ClutterEventSequence *sequence = clutter_event_get_event_sequence (event); - ClutterModifierType modifier_state; - MetaEdgeResistanceFlags flags; - gfloat x, y; - - switch (event->type) - { - case CLUTTER_TOUCH_BEGIN: - if (!meta_display_is_pointer_emulating_sequence (window->display, sequence)) - return FALSE; - - return TRUE; - - case CLUTTER_BUTTON_PRESS: - { - ClutterModifierType grab_mods = meta_display_get_compositor_modifiers (window->display); - - /* This is the keybinding or menu case where we've - * been dragging around the window without the button - * pressed. */ - - if ((meta_grab_op_is_mouse (window->display->grab_op) && - (event->button.modifier_state & grab_mods) == grab_mods && - window->display->grab_button != (int) event->button.button) || - meta_grab_op_is_keyboard (window->display->grab_op)) - { - end_grab_op (window, event); - return FALSE; - } - return TRUE; - } - - case CLUTTER_TOUCH_END: - if (!meta_display_is_pointer_emulating_sequence (window->display, sequence)) - return FALSE; - - end_grab_op (window, event); - return TRUE; - - case CLUTTER_BUTTON_RELEASE: - if (event->button.button == 1 || - event->button.button == (unsigned int) meta_prefs_get_mouse_button_resize ()) - end_grab_op (window, event); - - return TRUE; - - case CLUTTER_TOUCH_UPDATE: - if (!meta_display_is_pointer_emulating_sequence (window->display, sequence)) - return FALSE; - - /* Fall through */ - case CLUTTER_MOTION: - modifier_state = clutter_event_get_state (event); - clutter_event_get_coords (event, &x, &y); - flags = META_EDGE_RESISTANCE_DEFAULT; - - if (modifier_state & CLUTTER_SHIFT_MASK) - flags |= META_EDGE_RESISTANCE_SNAP; - - if (modifier_state & CLUTTER_CONTROL_MASK) - flags |= META_EDGE_RESISTANCE_WINDOWS; - - meta_display_check_threshold_reached (window->display, x, y); - if (meta_grab_op_is_moving (window->display->grab_op)) - { - update_move (window, flags, x, y); - } - else if (meta_grab_op_is_resizing (window->display->grab_op)) - { - if (window->tile_match != NULL) - flags |= (META_EDGE_RESISTANCE_SNAP | META_EDGE_RESISTANCE_WINDOWS); - - update_resize (window, flags, x, y, FALSE); - } - return TRUE; - - case CLUTTER_TOUCH_CANCEL: - end_grab_op (window, event); - return FALSE; - - default: - return FALSE; - } -} - -void -meta_window_get_work_area_for_logical_monitor (MetaWindow *window, - MetaLogicalMonitor *logical_monitor, - MetaRectangle *area) -{ - GList *tmp; - - g_assert (logical_monitor); - - /* Initialize to the whole monitor */ - *area = logical_monitor->rect; - - tmp = meta_window_get_workspaces (window); - while (tmp != NULL) - { - MetaRectangle workspace_work_area; - meta_workspace_get_work_area_for_logical_monitor (tmp->data, - logical_monitor, - &workspace_work_area); - meta_rectangle_intersect (area, - &workspace_work_area, - area); - tmp = tmp->next; - } - - meta_topic (META_DEBUG_WORKAREA, - "Window %s monitor %d has work area %d,%d %d x %d", - window->desc, logical_monitor->number, - area->x, area->y, area->width, area->height); -} - -/** - * meta_window_get_work_area_current_monitor: - * @window: a #MetaWindow - * @area: (out): a location to store the work area - * - * Get the work area for the monitor @window is currently on. - */ -void -meta_window_get_work_area_current_monitor (MetaWindow *window, - MetaRectangle *area) -{ - meta_window_get_work_area_for_monitor (window, - window->monitor->number, - area); -} - -/** - * meta_window_get_work_area_for_monitor: - * @window: a #MetaWindow - * @which_monitor: a moniotr to get the work area for - * @area: (out): a location to store the work area - * - * Get the work area for @window, given the monitor index - * @which_monitor. - */ -void -meta_window_get_work_area_for_monitor (MetaWindow *window, - int which_monitor, - MetaRectangle *area) -{ - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); - MetaLogicalMonitor *logical_monitor; - - g_return_if_fail (which_monitor >= 0); - - logical_monitor = - meta_monitor_manager_get_logical_monitor_from_number (monitor_manager, - which_monitor); - - meta_window_get_work_area_for_logical_monitor (window, logical_monitor, area); -} - -/** - * meta_window_get_work_area_all_monitors: - * @window: a #MetaWindow - * @area: (out): a location to store the work area - * - * Get the work area for all monitors for @window. - */ -void -meta_window_get_work_area_all_monitors (MetaWindow *window, - MetaRectangle *area) -{ - GList *tmp; - MetaRectangle display_rect = { 0 }; - - meta_display_get_size (window->display, - &display_rect.width, - &display_rect.height); - - /* Initialize to the whole display */ - *area = display_rect; - - tmp = meta_window_get_workspaces (window); - while (tmp != NULL) - { - MetaRectangle workspace_work_area; - meta_workspace_get_work_area_all_monitors (tmp->data, - &workspace_work_area); - meta_rectangle_intersect (area, - &workspace_work_area, - area); - tmp = tmp->next; - } - - meta_topic (META_DEBUG_WORKAREA, - "Window %s has whole-screen work area %d,%d %d x %d", - window->desc, area->x, area->y, area->width, area->height); -} - -int -meta_window_get_current_tile_monitor_number (MetaWindow *window) -{ - int tile_monitor_number = window->tile_monitor_number; - - if (tile_monitor_number < 0) - { - meta_warning ("%s called with an invalid monitor number; using 0 instead", G_STRFUNC); - tile_monitor_number = 0; - } - - return tile_monitor_number; -} - -void -meta_window_get_tile_area (MetaWindow *window, - MetaTileMode tile_mode, - MetaRectangle *tile_area) -{ - MetaRectangle work_area; - int tile_monitor_number; - double fraction; - - g_return_if_fail (tile_mode != META_TILE_NONE); - - tile_monitor_number = meta_window_get_current_tile_monitor_number (window); - - meta_window_get_work_area_for_monitor (window, tile_monitor_number, &work_area); - meta_window_get_tile_fraction (window, tile_mode, &fraction); - - *tile_area = work_area; - tile_area->width = round (tile_area->width * fraction); - - if (tile_mode == META_TILE_RIGHT) - tile_area->x += work_area.width - tile_area->width; -} - -gboolean -meta_window_same_application (MetaWindow *window, - MetaWindow *other_window) -{ - MetaGroup *group = meta_window_get_group (window); - MetaGroup *other_group = meta_window_get_group (other_window); - - return - group!=NULL && - other_group!=NULL && - group==other_group; -} - -/** - * meta_window_is_client_decorated: - * - * Check if if the window has decorations drawn by the client. - * (window->decorated refers only to whether we should add decorations) - */ -gboolean -meta_window_is_client_decorated (MetaWindow *window) -{ - if (window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND) - { - /* Assume all Wayland clients draw decorations - not strictly - * true but good enough for current purposes. - */ - return TRUE; - } - else - { - /* Currently the implementation here is hackish - - * has_custom_frame_extents() is set if _GTK_FRAME_EXTENTS is set - * to any value even 0. GTK+ always sets _GTK_FRAME_EXTENTS for - * client-side-decorated window, even if the value is 0 because - * the window is maxized and has no invisible borders or shadows. - */ - return window->has_custom_frame_extents; - } -} - -/** - * meta_window_foreach_transient: - * @window: a #MetaWindow - * @func: (scope call) (closure user_data): Called for each window which is a transient of @window (transitively) - * @user_data: User data - * - * Call @func for every window which is either transient for @window, or is - * a transient of a window which is in turn transient for @window. - * The order of window enumeration is not defined. - * - * Iteration will stop if @func at any point returns %FALSE. - */ -void -meta_window_foreach_transient (MetaWindow *window, - MetaWindowForeachFunc func, - void *user_data) -{ - GSList *windows; - GSList *tmp; - - windows = meta_display_list_windows (window->display, META_LIST_DEFAULT); - - tmp = windows; - while (tmp != NULL) - { - MetaWindow *transient = tmp->data; - - if (meta_window_is_ancestor_of_transient (window, transient)) - { - if (!(* func) (transient, user_data)) - break; - } - - tmp = tmp->next; - } - - g_slist_free (windows); -} - -/** - * meta_window_foreach_ancestor: - * @window: a #MetaWindow - * @func: (scope call) (closure user_data): Called for each window which is a transient parent of @window - * @user_data: User data - * - * If @window is transient, call @func with the window for which it's transient, - * repeatedly until either we find a non-transient window, or @func returns %FALSE. - */ -void -meta_window_foreach_ancestor (MetaWindow *window, - MetaWindowForeachFunc func, - void *user_data) -{ - MetaWindow *w; - - w = window; - do - { - if (w->transient_for == NULL) - break; - - w = w->transient_for; - } - while (w && (* func) (w, user_data)); -} - -typedef struct -{ - MetaWindow *ancestor; - gboolean found; -} FindAncestorData; - -static gboolean -find_ancestor_func (MetaWindow *window, - void *data) -{ - FindAncestorData *d = data; - - if (window == d->ancestor) - { - d->found = TRUE; - return FALSE; - } - - return TRUE; -} - -/** - * meta_window_is_ancestor_of_transient: - * @window: a #MetaWindow - * @transient: a #MetaWindow - * - * The function determines whether @window is an ancestor of @transient; it does - * so by traversing the @transient's ancestors until it either locates @window - * or reaches an ancestor that is not transient. - * - * Return Value: %TRUE if window is an ancestor of transient. - */ -gboolean -meta_window_is_ancestor_of_transient (MetaWindow *window, - MetaWindow *transient) -{ - FindAncestorData d; - - d.ancestor = window; - d.found = FALSE; - - meta_window_foreach_ancestor (transient, find_ancestor_func, &d); - - return d.found; -} - -/* Warp pointer to location appropriate for grab, - * return root coordinates where pointer ended up. - */ -static gboolean -warp_grab_pointer (MetaWindow *window, - MetaGrabOp grab_op, - int *x, - int *y) -{ - MetaRectangle rect; - MetaRectangle display_rect = { 0 }; - MetaDisplay *display; - ClutterSeat *seat; - - display = window->display; - meta_display_get_size (display, - &display_rect.width, - &display_rect.height); - - /* We may not have done begin_grab_op yet, i.e. may not be in a grab - */ - - meta_window_get_frame_rect (window, &rect); - - if (grab_op & META_GRAB_OP_WINDOW_DIR_WEST) - *x = 0; - else if (grab_op & META_GRAB_OP_WINDOW_DIR_EAST) - *x = rect.width - 1; - else - *x = rect.width / 2; - - if (grab_op & META_GRAB_OP_WINDOW_DIR_NORTH) - *y = 0; - else if (grab_op & META_GRAB_OP_WINDOW_DIR_SOUTH) - *y = rect.height - 1; - else - *y = rect.height / 2; - - *x += rect.x; - *y += rect.y; - - /* Avoid weird bouncing at the screen edge; see bug 154706 */ - *x = CLAMP (*x, 0, display_rect.width - 1); - *y = CLAMP (*y, 0, display_rect.height - 1); - - meta_topic (META_DEBUG_WINDOW_OPS, - "Warping pointer to %d,%d with window at %d,%d", - *x, *y, rect.x, rect.y); - - /* Need to update the grab positions so that the MotionNotify and other - * events generated by the XWarpPointer() call below don't cause complete - * funkiness. See bug 124582 and bug 122670. - */ - display->grab_anchor_root_x = *x; - display->grab_anchor_root_y = *y; - display->grab_latest_motion_x = *x; - display->grab_latest_motion_y = *y; - meta_window_get_frame_rect (window, - &display->grab_anchor_window_pos); - - seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); - clutter_seat_warp_pointer (seat, *x, *y); - - return TRUE; -} - -void -meta_window_begin_grab_op (MetaWindow *window, - MetaGrabOp op, - gboolean frame_action, - guint32 timestamp) -{ - int x, y; - - warp_grab_pointer (window, - op, &x, &y); - - meta_display_begin_grab_op (window->display, - window, - op, - FALSE, - frame_action, - 0 /* button */, - 0, - timestamp, - x, y); -} - -void -meta_window_update_keyboard_resize (MetaWindow *window, - gboolean update_cursor) -{ - int x, y; - - warp_grab_pointer (window, - window->display->grab_op, - &x, &y); - - if (update_cursor) - meta_display_update_cursor (window->display); -} - -void -meta_window_update_keyboard_move (MetaWindow *window) -{ - int x, y; - - warp_grab_pointer (window, - window->display->grab_op, - &x, &y); -} - -MetaStackLayer -meta_window_get_default_layer (MetaWindow *window) -{ - if (window->wm_state_below) - return META_LAYER_BOTTOM; - else if (window->wm_state_above && !META_WINDOW_MAXIMIZED (window)) - return META_LAYER_TOP; - else - return META_LAYER_NORMAL; -} - -void -meta_window_update_layer (MetaWindow *window) -{ - MetaGroup *group; - - meta_stack_freeze (window->display->stack); - group = meta_window_get_group (window); - if (group) - meta_group_update_layers (group); - else - meta_stack_update_layer (window->display->stack, window); - meta_stack_thaw (window->display->stack); -} - -/* ensure_mru_position_after ensures that window appears after - * below_this_one in the active_workspace's mru_list (i.e. it treats - * window as having been less recently used than below_this_one) - */ -static void -ensure_mru_position_after (MetaWindow *window, - MetaWindow *after_this_one) -{ - /* This is sort of slow since it runs through the entire list more - * than once (especially considering the fact that we expect the - * windows of interest to be the first two elements in the list), - * but it doesn't matter while we're only using it on new window - * map. - */ - - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - GList* active_mru_list; - GList* window_position; - GList* after_this_one_position; - - active_mru_list = workspace_manager->active_workspace->mru_list; - window_position = g_list_find (active_mru_list, window); - after_this_one_position = g_list_find (active_mru_list, after_this_one); - - /* after_this_one_position is NULL when we switch workspaces, but in - * that case we don't need to do any MRU shuffling so we can simply - * return. - */ - if (after_this_one_position == NULL) - return; - - if (g_list_length (window_position) > g_list_length (after_this_one_position)) - { - workspace_manager->active_workspace->mru_list = - g_list_delete_link (workspace_manager->active_workspace->mru_list, - window_position); - - workspace_manager->active_workspace->mru_list = - g_list_insert_before (workspace_manager->active_workspace->mru_list, - after_this_one_position->next, - window); - } -} - -gboolean -meta_window_is_in_stack (MetaWindow *window) -{ - return window->stack_position >= 0; -} - -void -meta_window_stack_just_below (MetaWindow *window, - MetaWindow *below_this_one) -{ - g_return_if_fail (window != NULL); - g_return_if_fail (below_this_one != NULL); - - if (window->stack_position > below_this_one->stack_position) - { - meta_topic (META_DEBUG_STACK, - "Setting stack position of window %s to %d (making it below window %s).", - window->desc, - below_this_one->stack_position, - below_this_one->desc); - meta_window_set_stack_position (window, below_this_one->stack_position); - } - else - { - meta_topic (META_DEBUG_STACK, - "Window %s was already below window %s.", - window->desc, below_this_one->desc); - } -} - -void -meta_window_stack_just_above (MetaWindow *window, - MetaWindow *above_this_one) -{ - g_return_if_fail (window != NULL); - g_return_if_fail (above_this_one != NULL); - - if (window->stack_position < above_this_one->stack_position) - { - meta_topic (META_DEBUG_STACK, - "Setting stack position of window %s to %d (making it above window %s).", - window->desc, - above_this_one->stack_position, - above_this_one->desc); - meta_window_set_stack_position (window, above_this_one->stack_position); - } - else - { - meta_topic (META_DEBUG_STACK, - "Window %s was already above window %s.", - window->desc, above_this_one->desc); - } -} - -/** - * meta_window_get_user_time: - * @window: a #MetaWindow - * - * The user time represents a timestamp for the last time the user - * interacted with this window. Note this property is only available - * for non-override-redirect windows. - * - * The property is set by Mutter initially upon window creation, - * and updated thereafter on input events (key and button presses) seen by Mutter, - * client updates to the _NET_WM_USER_TIME property (if later than the current time) - * and when focusing the window. - * - * Returns: The last time the user interacted with this window. - */ -guint32 -meta_window_get_user_time (MetaWindow *window) -{ - return window->net_wm_user_time; -} - -void -meta_window_set_user_time (MetaWindow *window, - 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. - */ - - g_return_if_fail (!window->override_redirect); - - /* Only update the time if this timestamp is newer... */ - if (window->net_wm_user_time_set && - XSERVER_TIME_IS_BEFORE (timestamp, window->net_wm_user_time)) - { - meta_topic (META_DEBUG_STARTUP, - "Window %s _NET_WM_USER_TIME not updated to %u, because it " - "is less than %u", - window->desc, timestamp, window->net_wm_user_time); - } - else - { - meta_topic (META_DEBUG_STARTUP, - "Window %s has _NET_WM_USER_TIME of %u", - window->desc, timestamp); - window->net_wm_user_time_set = TRUE; - window->net_wm_user_time = timestamp; - if (XSERVER_TIME_IS_BEFORE (window->display->last_user_time, timestamp)) - window->display->last_user_time = timestamp; - - /* If this is a terminal, user interaction with it means the user likely - * doesn't want to have focus transferred for now due to new windows. - */ - if (meta_prefs_get_focus_new_windows () == G_DESKTOP_FOCUS_NEW_WINDOWS_STRICT && - window_is_terminal (window)) - window->display->allow_terminal_deactivation = FALSE; - - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_USER_TIME]); - } -} - -/** - * meta_window_get_stable_sequence: - * @window: A #MetaWindow - * - * The stable sequence number is a monotonicially increasing - * unique integer assigned to each #MetaWindow upon creation. - * - * This number can be useful for sorting windows in a stable - * fashion. - * - * Returns: Internal sequence number for this window - */ -guint32 -meta_window_get_stable_sequence (MetaWindow *window) -{ - g_return_val_if_fail (META_IS_WINDOW (window), 0); - - return window->stable_sequence; -} - -/* Sets the demands_attention hint on a window, but only - * if it's at least partially obscured (see #305882). - */ -void -meta_window_set_demands_attention (MetaWindow *window) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - MetaRectangle candidate_rect, other_rect; - GList *stack = window->display->stack->sorted; - MetaWindow *other_window; - gboolean obscured = FALSE; - - MetaWorkspace *workspace = workspace_manager->active_workspace; - - if (window->wm_state_demands_attention) - return; - - if (!meta_window_located_on_workspace (window, workspace)) - { - /* windows on other workspaces are necessarily obscured */ - obscured = TRUE; - } - else if (window->minimized) - { - obscured = TRUE; - } - else - { - meta_window_get_frame_rect (window, &candidate_rect); - - /* The stack is sorted with the top windows first. */ - - while (stack != NULL && stack->data != window) - { - other_window = stack->data; - stack = stack->next; - - if (meta_window_located_on_workspace (other_window, workspace)) - { - meta_window_get_frame_rect (other_window, &other_rect); - - if (meta_rectangle_overlap (&candidate_rect, &other_rect)) - { - obscured = TRUE; - break; - } - } - } - } - - if (obscured) - { - meta_topic (META_DEBUG_WINDOW_OPS, - "Marking %s as needing attention", - window->desc); - - window->wm_state_demands_attention = TRUE; - set_net_wm_state (window); - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_DEMANDS_ATTENTION]); - g_signal_emit_by_name (window->display, "window-demands-attention", - window); - } - else - { - /* If the window's in full view, there's no point setting the flag. */ - - meta_topic (META_DEBUG_WINDOW_OPS, - "Not marking %s as needing attention because " - "it's in full view", - window->desc); - } -} - -void -meta_window_unset_demands_attention (MetaWindow *window) -{ - meta_topic (META_DEBUG_WINDOW_OPS, - "Marking %s as not needing attention", window->desc); - - if (window->wm_state_demands_attention) - { - window->wm_state_demands_attention = FALSE; - set_net_wm_state (window); - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_DEMANDS_ATTENTION]); - } -} - -/** - * meta_window_get_frame: (skip) - * @window: a #MetaWindow - * - */ -MetaFrame * -meta_window_get_frame (MetaWindow *window) -{ - return window->frame; -} - -/** - * meta_window_appears_focused: - * @window: a #MetaWindow - * - * Determines if the window should be drawn with a focused appearance. This is - * true for focused windows but also true for windows with a focused modal - * dialog attached. - * - * Return value: %TRUE if the window should be drawn with a focused frame - */ -gboolean -meta_window_appears_focused (MetaWindow *window) -{ - return window->has_focus || (window->attached_focus_window != NULL); -} - -gboolean -meta_window_has_focus (MetaWindow *window) -{ - return window->has_focus; -} - -gboolean -meta_window_is_shaded (MetaWindow *window) -{ - return window->shaded; -} - -/** - * meta_window_is_override_redirect: - * @window: A #MetaWindow - * - * Returns: %TRUE if this window isn't managed by mutter; it will - * control its own positioning and mutter won't draw decorations - * among other things. In X terminology this is "override redirect". - */ -gboolean -meta_window_is_override_redirect (MetaWindow *window) -{ - return window->override_redirect; -} - -/** - * meta_window_is_skip_taskbar: - * @window: A #MetaWindow - * - * Gets whether this window should be ignored by task lists. - * - * Return value: %TRUE if the skip bar hint is set. - */ -gboolean -meta_window_is_skip_taskbar (MetaWindow *window) -{ - g_return_val_if_fail (META_IS_WINDOW (window), FALSE); - - return window->skip_taskbar; -} - -/** - * meta_window_get_display: - * @window: A #MetaWindow - * - * Returns: (transfer none): The display for @window - */ -MetaDisplay * -meta_window_get_display (MetaWindow *window) -{ - return window->display; -} - -/** - * meta_window_get_xwindow: (skip) - * @window: a #MetaWindow - * - */ -Window -meta_window_get_xwindow (MetaWindow *window) -{ - return window->xwindow; -} - -MetaWindowType -meta_window_get_window_type (MetaWindow *window) -{ - return window->type; -} - -/** - * meta_window_get_workspace: - * @window: a #MetaWindow - * - * Gets the #MetaWorkspace that the window is currently displayed on. - * If the window is on all workspaces, returns the currently active - * workspace. - * - * Return value: (transfer none): the #MetaWorkspace for the window - */ -MetaWorkspace * -meta_window_get_workspace (MetaWindow *window) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - - if (window->on_all_workspaces) - return workspace_manager->active_workspace; - else - return window->workspace; -} - -gboolean -meta_window_is_on_all_workspaces (MetaWindow *window) -{ - return window->on_all_workspaces; -} - -gboolean -meta_window_is_hidden (MetaWindow *window) -{ - return window->hidden; -} - -const char * -meta_window_get_description (MetaWindow *window) -{ - if (!window) - return NULL; - - return window->desc; -} - -/** - * meta_window_get_wm_class: - * @window: a #MetaWindow - * - * Return the current value of the name part of WM_CLASS X property. - */ -const char * -meta_window_get_wm_class (MetaWindow *window) -{ - if (!window) - return NULL; - - return window->res_class; -} - -/** - * meta_window_get_wm_class_instance: - * @window: a #MetaWindow - * - * Return the current value of the instance part of WM_CLASS X property. - */ -const char * -meta_window_get_wm_class_instance (MetaWindow *window) -{ - if (!window) - return NULL; - - return window->res_name; -} - -/** - * meta_window_get_sandboxed_app_id: - * @window: a #MetaWindow - * - * Gets an unique id for a sandboxed app (currently flatpaks and snaps are - * supported). - * - * Return value: (transfer none): the sandboxed application ID or %NULL - **/ -const char * -meta_window_get_sandboxed_app_id (MetaWindow *window) -{ - /* We're abusing this API here not to break the gnome shell assumptions - * or adding a new function, to be renamed to generic names in new versions */ - return window->sandboxed_app_id; -} - -/** - * meta_window_get_gtk_theme_variant: - * @window: a #MetaWindow - * - * Return value: (transfer none): the theme variant or %NULL - **/ -const char * -meta_window_get_gtk_theme_variant (MetaWindow *window) -{ - return window->gtk_theme_variant; -} - -/** - * meta_window_get_gtk_application_id: - * @window: a #MetaWindow - * - * Return value: (transfer none): the application ID - **/ -const char * -meta_window_get_gtk_application_id (MetaWindow *window) -{ - return window->gtk_application_id; -} - -/** - * meta_window_get_gtk_unique_bus_name: - * @window: a #MetaWindow - * - * Return value: (transfer none): the unique name - **/ -const char * -meta_window_get_gtk_unique_bus_name (MetaWindow *window) -{ - return window->gtk_unique_bus_name; -} - -/** - * meta_window_get_gtk_application_object_path: - * @window: a #MetaWindow - * - * Return value: (transfer none): the object path - **/ -const char * -meta_window_get_gtk_application_object_path (MetaWindow *window) -{ - return window->gtk_application_object_path; -} - -/** - * meta_window_get_gtk_window_object_path: - * @window: a #MetaWindow - * - * Return value: (transfer none): the object path - **/ -const char * -meta_window_get_gtk_window_object_path (MetaWindow *window) -{ - return window->gtk_window_object_path; -} - -/** - * meta_window_get_gtk_app_menu_object_path: - * @window: a #MetaWindow - * - * Return value: (transfer none): the object path - **/ -const char * -meta_window_get_gtk_app_menu_object_path (MetaWindow *window) -{ - return window->gtk_app_menu_object_path; -} - -/** - * meta_window_get_gtk_menubar_object_path: - * @window: a #MetaWindow - * - * Return value: (transfer none): the object path - **/ -const char * -meta_window_get_gtk_menubar_object_path (MetaWindow *window) -{ - return window->gtk_menubar_object_path; -} - -/** - * meta_window_get_compositor_private: - * @window: a #MetaWindow - * - * Gets the compositor's wrapper object for @window. - * - * Return value: (transfer none): the wrapper object. - **/ -GObject * -meta_window_get_compositor_private (MetaWindow *window) -{ - if (!window) - return NULL; - return window->compositor_private; -} - -void -meta_window_set_compositor_private (MetaWindow *window, GObject *priv) -{ - if (!window) - return; - window->compositor_private = priv; -} - -const char * -meta_window_get_role (MetaWindow *window) -{ - if (!window) - return NULL; - - return window->role; -} - -/** - * meta_window_get_title: - * @window: a #MetaWindow - * - * Returns: the current title of the window. - */ -const char * -meta_window_get_title (MetaWindow *window) -{ - g_return_val_if_fail (META_IS_WINDOW (window), NULL); - - return window->title; -} - -MetaStackLayer -meta_window_get_layer (MetaWindow *window) -{ - return window->layer; -} - -/** - * meta_window_get_transient_for: - * @window: a #MetaWindow - * - * Returns the #MetaWindow for the window that is pointed to by the - * WM_TRANSIENT_FOR hint on this window (see XGetTransientForHint() - * or XSetTransientForHint()). Metacity keeps transient windows above their - * parents. A typical usage of this hint is for a dialog that wants to stay - * above its associated window. - * - * Return value: (transfer none): the window this window is transient for, or - * %NULL if the WM_TRANSIENT_FOR hint is unset or does not point to a toplevel - * window that Metacity knows about. - */ -MetaWindow * -meta_window_get_transient_for (MetaWindow *window) -{ - g_return_val_if_fail (META_IS_WINDOW (window), NULL); - - if (window->transient_for) - return window->transient_for; - else if (window->xtransient_for) - return meta_x11_display_lookup_x_window (window->display->x11_display, - window->xtransient_for); - else - return NULL; -} - -/** - * meta_window_get_pid: - * @window: a #MetaWindow - * - * Returns the pid of the process that created this window, if available - * to the windowing system. - * - * Note that the value returned by this is vulnerable to spoofing attacks - * by the client. - * - * Return value: the pid, or 0 if not known. - */ -pid_t -meta_window_get_pid (MetaWindow *window) -{ - g_return_val_if_fail (META_IS_WINDOW (window), 0); - - if (window->client_pid == 0) - window->client_pid = META_WINDOW_GET_CLASS (window)->get_client_pid (window); - - return window->client_pid; -} - -/** - * meta_window_get_client_machine: - * @window: a #MetaWindow - * - * Returns name of the client machine from which this windows was created, - * if known (obtained from the WM_CLIENT_MACHINE property). - * - * Return value: (transfer none): the machine name, or NULL; the string is - * owned by the window manager and should not be freed or modified by the - * caller. - */ -const char * -meta_window_get_client_machine (MetaWindow *window) -{ - g_return_val_if_fail (META_IS_WINDOW (window), NULL); - - return window->wm_client_machine; -} - -/** - * meta_window_is_remote: - * @window: a #MetaWindow - * - * Returns: %TRUE if this window originates from a host - * different from the one running mutter. - */ -gboolean -meta_window_is_remote (MetaWindow *window) -{ - return window->is_remote; -} - -/** - * meta_window_get_mutter_hints: - * @window: a #MetaWindow - * - * Gets the current value of the _MUTTER_HINTS property. - * - * The purpose of the hints is to allow fine-tuning of the Window Manager and - * Compositor behaviour on per-window basis, and is intended primarily for - * hints that are plugin-specific. - * - * The property is a list of colon-separated key=value pairs. The key names for - * any plugin-specific hints must be suitably namespaced to allow for shared - * use; 'mutter-' key prefix is reserved for internal use, and must not be used - * by plugins. - * - * Return value: (transfer none): the _MUTTER_HINTS string, or %NULL if no hints - * are set. - */ -const char * -meta_window_get_mutter_hints (MetaWindow *window) -{ - g_return_val_if_fail (META_IS_WINDOW (window), NULL); - - return window->mutter_hints; -} - -/** - * meta_window_get_frame_type: - * @window: a #MetaWindow - * - * Gets the type of window decorations that should be used for this window. - * - * Return value: the frame type - */ -MetaFrameType -meta_window_get_frame_type (MetaWindow *window) -{ - MetaFrameType base_type = META_FRAME_TYPE_LAST; - - switch (window->type) - { - case META_WINDOW_NORMAL: - base_type = META_FRAME_TYPE_NORMAL; - break; - - case META_WINDOW_DIALOG: - base_type = META_FRAME_TYPE_DIALOG; - break; - - case META_WINDOW_MODAL_DIALOG: - if (meta_window_is_attached_dialog (window)) - base_type = META_FRAME_TYPE_ATTACHED; - else - base_type = META_FRAME_TYPE_MODAL_DIALOG; - break; - - case META_WINDOW_MENU: - base_type = META_FRAME_TYPE_MENU; - break; - - case META_WINDOW_UTILITY: - base_type = META_FRAME_TYPE_UTILITY; - break; - - case META_WINDOW_DESKTOP: - case META_WINDOW_DOCK: - case META_WINDOW_TOOLBAR: - case META_WINDOW_SPLASHSCREEN: - case META_WINDOW_DROPDOWN_MENU: - case META_WINDOW_POPUP_MENU: - case META_WINDOW_TOOLTIP: - case META_WINDOW_NOTIFICATION: - case META_WINDOW_COMBO: - case META_WINDOW_DND: - case META_WINDOW_OVERRIDE_OTHER: - /* No frame */ - base_type = META_FRAME_TYPE_LAST; - break; - } - - if (base_type == META_FRAME_TYPE_LAST) - { - /* can't add border if undecorated */ - return META_FRAME_TYPE_LAST; - } - else if (window->border_only) - { - /* override base frame type */ - return META_FRAME_TYPE_BORDER; - } - else - { - return base_type; - } -} - -/** - * meta_window_get_frame_bounds: - * @window: a #MetaWindow - * - * Gets a region representing the outer bounds of the window's frame. - * - * Return value: (transfer none) (nullable): a #cairo_region_t - * holding the outer bounds of the window, or %NULL if the window - * doesn't have a frame. - */ -cairo_region_t * -meta_window_get_frame_bounds (MetaWindow *window) -{ - if (!window->frame_bounds) - { - if (window->frame) - window->frame_bounds = meta_frame_get_frame_bounds (window->frame); - } - - return window->frame_bounds; -} - -/** - * meta_window_is_attached_dialog: - * @window: a #MetaWindow - * - * Tests if @window is should be attached to its parent window. - * (If the "attach_modal_dialogs" option is not enabled, this will - * always return %FALSE.) - * - * Return value: whether @window should be attached to its parent - */ -gboolean -meta_window_is_attached_dialog (MetaWindow *window) -{ - return window->attached; -} - -/** - * meta_window_get_tile_match: - * @window: a #MetaWindow - * - * Returns the matching tiled window on the same monitor as @window. This is - * the topmost tiled window in a complementary tile mode that is: - * - * - on the same monitor; - * - on the same workspace; - * - spanning the remaining monitor width; - * - there is no 3rd window stacked between both tiled windows that's - * partially visible in the common edge. - * - * Return value: (transfer none) (nullable): the matching tiled window or - * %NULL if it doesn't exist. - */ -MetaWindow * -meta_window_get_tile_match (MetaWindow *window) -{ - return window->tile_match; -} - -void -meta_window_compute_tile_match (MetaWindow *window) -{ - window->tile_match = meta_window_find_tile_match (window, window->tile_mode); -} - -static MetaWindow * -meta_window_find_tile_match (MetaWindow *window, - MetaTileMode current_mode) -{ - MetaWindow *match; - MetaStack *stack; - MetaTileMode match_tile_mode = META_TILE_NONE; - - if (window->shaded || window->minimized) - return NULL; - - if (current_mode == META_TILE_LEFT) - match_tile_mode = META_TILE_RIGHT; - else if (current_mode == META_TILE_RIGHT) - match_tile_mode = META_TILE_LEFT; - else - return NULL; - - stack = window->display->stack; - - for (match = meta_stack_get_top (stack); - match; - match = meta_stack_get_below (stack, match, FALSE)) - { - if (!match->shaded && - !match->minimized && - match->tile_mode == match_tile_mode && - match->tile_monitor_number == window->tile_monitor_number && - meta_window_get_workspace (match) == meta_window_get_workspace (window)) - break; - } - - if (match) - { - MetaWindow *above, *bottommost, *topmost; - MetaRectangle above_rect, bottommost_rect, topmost_rect; - - if (meta_stack_windows_cmp (window->display->stack, match, window) > 0) - { - topmost = match; - bottommost = window; - } - else - { - topmost = window; - bottommost = match; - } - - meta_window_get_frame_rect (bottommost, &bottommost_rect); - meta_window_get_frame_rect (topmost, &topmost_rect); - - /* - * If we are looking for a tile match while actually being tiled, - * rather than a match for a potential tile mode, then discard - * windows with too much gap or overlap - */ - if (window->tile_mode == current_mode && - !(meta_grab_op_is_resizing (window->display->grab_op) && - window->display->grab_window == window && - window->tile_match != NULL)) - { - int threshold = meta_prefs_get_drag_threshold (); - if (ABS (topmost_rect.x - bottommost_rect.x - bottommost_rect.width) > threshold && - ABS (bottommost_rect.x - topmost_rect.x - topmost_rect.width) > threshold) - return NULL; - } - - /* - * If there's a window stacked in between which is partially visible - * behind the topmost tile we don't consider the tiles to match. - */ - for (above = meta_stack_get_above (stack, bottommost, FALSE); - above && above != topmost; - above = meta_stack_get_above (stack, above, FALSE)) - { - if (above->minimized || - above->monitor != window->monitor || - meta_window_get_workspace (above) != meta_window_get_workspace (window)) - continue; - - meta_window_get_frame_rect (above, &above_rect); - - if (meta_rectangle_overlap (&above_rect, &bottommost_rect) && - meta_rectangle_overlap (&above_rect, &topmost_rect)) - return NULL; - } - } - - return match; -} - -void -meta_window_set_title (MetaWindow *window, - const char *title) -{ - g_free (window->title); - window->title = g_strdup (title); - - if (window->frame) - meta_frame_update_title (window->frame); - - meta_window_update_desc (window); - - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_TITLE]); -} - -void -meta_window_set_wm_class (MetaWindow *window, - const char *wm_class, - const char *wm_instance) -{ - g_free (window->res_class); - g_free (window->res_name); - - window->res_name = g_strdup (wm_instance); - window->res_class = g_strdup (wm_class); - - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_WM_CLASS]); -} - -void -meta_window_set_gtk_dbus_properties (MetaWindow *window, - const char *application_id, - const char *unique_bus_name, - const char *appmenu_path, - const char *menubar_path, - const char *application_object_path, - const char *window_object_path) -{ - g_object_freeze_notify (G_OBJECT (window)); - - g_free (window->gtk_application_id); - window->gtk_application_id = g_strdup (application_id); - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_GTK_APPLICATION_ID]); - - g_free (window->gtk_unique_bus_name); - window->gtk_unique_bus_name = g_strdup (unique_bus_name); - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_GTK_UNIQUE_BUS_NAME]); - - g_free (window->gtk_app_menu_object_path); - window->gtk_app_menu_object_path = g_strdup (appmenu_path); - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_GTK_APP_MENU_OBJECT_PATH]); - - g_free (window->gtk_menubar_object_path); - window->gtk_menubar_object_path = g_strdup (menubar_path); - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_GTK_MENUBAR_OBJECT_PATH]); - - g_free (window->gtk_application_object_path); - window->gtk_application_object_path = g_strdup (application_object_path); - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_GTK_APPLICATION_OBJECT_PATH]); - - g_free (window->gtk_window_object_path); - window->gtk_window_object_path = g_strdup (window_object_path); - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_GTK_WINDOW_OBJECT_PATH]); - - g_object_thaw_notify (G_OBJECT (window)); -} - -static gboolean -check_transient_for_loop (MetaWindow *window, - MetaWindow *parent) -{ - while (parent) - { - if (parent == window) - return TRUE; - parent = parent->transient_for; - } - - return FALSE; -} - -gboolean -meta_window_has_transient_type (MetaWindow *window) -{ - return (window->type == META_WINDOW_DIALOG || - window->type == META_WINDOW_MODAL_DIALOG || - window->type == META_WINDOW_TOOLBAR || - window->type == META_WINDOW_MENU || - window->type == META_WINDOW_UTILITY); -} - -void -meta_window_set_transient_for (MetaWindow *window, - MetaWindow *parent) -{ - if (check_transient_for_loop (window, parent)) - { - meta_warning ("Setting %s transient for %s would create a loop.", - window->desc, parent->desc); - return; - } - - if (meta_window_appears_focused (window) && window->transient_for != NULL) - meta_window_propagate_focus_appearance (window, FALSE); - - /* may now be a dialog */ - if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) - { - meta_window_x11_recalc_window_type (window); - - if (!window->constructing) - { - /* If the window attaches, detaches, or changes attached - * parents, we need to destroy the MetaWindow and let a new one - * be created (which happens as a side effect of - * meta_window_unmanage()). The condition below is correct - * because we know window->transient_for has changed. - */ - if (window->attached || meta_window_should_attach_to_parent (window)) - { - guint32 timestamp; - - timestamp = - meta_display_get_current_time_roundtrip (window->display); - meta_window_unmanage (window, timestamp); - return; - } - } - } - else if (window->attached && parent == NULL) - { - guint32 timestamp; - - timestamp = - meta_display_get_current_time_roundtrip (window->display); - meta_window_unmanage (window, timestamp); - return; - } - /* We know this won't create a reference cycle because we check for loops */ - g_clear_object (&window->transient_for); - window->transient_for = parent ? g_object_ref (parent) : NULL; - - /* update stacking constraints */ - if (!window->override_redirect) - meta_stack_update_transient (window->display->stack, window); - - /* possibly change its group. We treat being a window's transient as - * equivalent to making it your group leader, to work around shortcomings - * in programs such as xmms-- see #328211. - */ - if (window->xtransient_for != None && - window->xgroup_leader != None && - window->xtransient_for != window->xgroup_leader) - meta_window_group_leader_changed (window); - - if (!window->constructing && !window->override_redirect) - meta_window_queue (window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING); - - if (meta_window_appears_focused (window) && window->transient_for != NULL) - meta_window_propagate_focus_appearance (window, TRUE); -} - -void -meta_window_set_opacity (MetaWindow *window, - guint8 opacity) -{ - window->opacity = opacity; - - meta_compositor_window_opacity_changed (window->display->compositor, window); -} - -static void -reset_ignored_crossing_serials (MetaDisplay *display) -{ - int i; - - i = 0; - while (i < N_IGNORED_CROSSING_SERIALS) - { - display->ignored_crossing_serials[i] = 0; - ++i; - } -} - -typedef struct -{ - MetaWindow *window; - int pointer_x; - int pointer_y; -} MetaFocusData; - -static void -mouse_mode_focus (MetaWindow *window, - guint32 timestamp) -{ - MetaDisplay *display = window->display; - - if (window->override_redirect) - return; - - if (window->type != META_WINDOW_DESKTOP) - { - meta_topic (META_DEBUG_FOCUS, - "Focusing %s at time %u.", window->desc, timestamp); - - meta_window_focus (window, timestamp); - - if (meta_prefs_get_auto_raise ()) - meta_display_queue_autoraise_callback (display, window); - else - meta_topic (META_DEBUG_FOCUS, "Auto raise is disabled"); - } - else - { - /* In mouse focus mode, we defocus when the mouse *enters* - * the DESKTOP window, instead of defocusing on LeaveNotify. - * This is because having the mouse enter override-redirect - * child windows unfortunately causes LeaveNotify events that - * we can't distinguish from the mouse actually leaving the - * toplevel window as we expect. But, since we filter out - * EnterNotify events on override-redirect windows, this - * alternative mechanism works great. - */ - if (meta_prefs_get_focus_mode() == G_DESKTOP_FOCUS_MODE_MOUSE && - display->focus_window != NULL) - { - meta_topic (META_DEBUG_FOCUS, - "Unsetting focus from %s due to mouse entering " - "the DESKTOP window", - display->focus_window->desc); - meta_display_unset_input_focus (display, timestamp); - } - } -} - -static gboolean -window_has_pointer_wayland (MetaWindow *window) -{ - ClutterSeat *seat; - ClutterInputDevice *dev; - ClutterStage *stage; - ClutterActor *pointer_actor, *window_actor; - - seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); - dev = clutter_seat_get_pointer (seat); - stage = CLUTTER_STAGE (meta_backend_get_stage (meta_get_backend ())); - pointer_actor = clutter_stage_get_device_actor (stage, dev, NULL); - window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window)); - - return pointer_actor && clutter_actor_contains (window_actor, pointer_actor); -} - -static gboolean -window_has_pointer_x11 (MetaWindow *window) -{ - MetaX11Display *x11_display = window->display->x11_display; - Window root, child; - double root_x, root_y, x, y; - XIButtonState buttons; - XIModifierState mods; - XIGroupState group; - - meta_x11_error_trap_push (x11_display); - XIQueryPointer (x11_display->xdisplay, - META_VIRTUAL_CORE_POINTER_ID, - x11_display->xroot, - &root, &child, - &root_x, &root_y, &x, &y, - &buttons, &mods, &group); - meta_x11_error_trap_pop (x11_display); - free (buttons.mask); - - return meta_x11_display_lookup_x_window (x11_display, child) == window; -} - -gboolean -meta_window_has_pointer (MetaWindow *window) -{ - if (meta_is_wayland_compositor ()) - return window_has_pointer_wayland (window); - else - return window_has_pointer_x11 (window); -} - -static gboolean -window_focus_on_pointer_rest_callback (gpointer data) -{ - MetaFocusData *focus_data = data; - MetaWindow *window = focus_data->window; - MetaDisplay *display = window->display; - MetaBackend *backend = meta_get_backend (); - MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); - graphene_point_t point; - guint32 timestamp; - - if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK) - goto out; - - meta_cursor_tracker_get_pointer (cursor_tracker, &point, NULL); - - if ((int) point.x != focus_data->pointer_x || - (int) point.y != focus_data->pointer_y) - { - focus_data->pointer_x = point.x; - focus_data->pointer_y = point.y; - return G_SOURCE_CONTINUE; - } - - if (!meta_window_has_pointer (window)) - goto out; - - timestamp = meta_display_get_current_time_roundtrip (display); - mouse_mode_focus (window, timestamp); - - out: - display->focus_timeout_id = 0; - return G_SOURCE_REMOVE; -} - -/* The interval, in milliseconds, we use in focus-follows-mouse - * mode to check whether the pointer has stopped moving after a - * crossing event. - */ -#define FOCUS_TIMEOUT_DELAY 25 - -static void -queue_focus_callback (MetaDisplay *display, - MetaWindow *window, - int pointer_x, - int pointer_y) -{ - MetaFocusData *focus_data; - - focus_data = g_new (MetaFocusData, 1); - focus_data->window = window; - focus_data->pointer_x = pointer_x; - focus_data->pointer_y = pointer_y; - - g_clear_handle_id (&display->focus_timeout_id, g_source_remove); - - display->focus_timeout_id = - g_timeout_add_full (G_PRIORITY_DEFAULT, - FOCUS_TIMEOUT_DELAY, - window_focus_on_pointer_rest_callback, - focus_data, - g_free); - g_source_set_name_by_id (display->focus_timeout_id, - "[mutter] window_focus_on_pointer_rest_callback"); -} - -void -meta_window_handle_enter (MetaWindow *window, - guint32 timestamp, - guint root_x, - guint root_y) -{ - MetaDisplay *display = window->display; - - switch (meta_prefs_get_focus_mode ()) - { - case G_DESKTOP_FOCUS_MODE_SLOPPY: - case G_DESKTOP_FOCUS_MODE_MOUSE: - display->mouse_mode = TRUE; - if (window->type != META_WINDOW_DOCK) - { - if (meta_prefs_get_focus_change_on_pointer_rest()) - queue_focus_callback (display, window, root_x, root_y); - else - mouse_mode_focus (window, timestamp); - - /* stop ignoring stuff */ - reset_ignored_crossing_serials (display); - } - break; - case G_DESKTOP_FOCUS_MODE_CLICK: - break; - } - - if (window->type == META_WINDOW_DOCK) - meta_window_raise (window); -} - -void -meta_window_handle_leave (MetaWindow *window) -{ - if (window->type == META_WINDOW_DOCK && !window->has_focus) - meta_window_lower (window); -} - -gboolean -meta_window_handle_ui_frame_event (MetaWindow *window, - const ClutterEvent *event) -{ - if (!window->frame) - return FALSE; - - return meta_ui_frame_handle_event (window->frame->ui_frame, event); -} - -void -meta_window_handle_ungrabbed_event (MetaWindow *window, - const ClutterEvent *event) -{ - MetaDisplay *display = window->display; - gboolean unmodified; - gboolean is_window_grab; - gboolean is_window_button_grab_allowed; - ClutterModifierType grab_mods, event_mods; - ClutterInputDevice *source; - gfloat x, y; - guint button; - - if (window->unmanaging) - return; - - if (event->type != CLUTTER_BUTTON_PRESS && - event->type != CLUTTER_TOUCH_BEGIN) - return; - - if (event->type == CLUTTER_TOUCH_BEGIN) - { - ClutterEventSequence *sequence; - - button = 1; - sequence = clutter_event_get_event_sequence (event); - if (!meta_display_is_pointer_emulating_sequence (window->display, sequence)) - return; - } - else - button = clutter_event_get_button (event); - - if (display->grab_op != META_GRAB_OP_NONE) - return; - - /* Some windows might not ask for input, in which case we might be here - * because we selected for ButtonPress on the root window. In that case, - * we have to take special care not to act for an override-redirect window. - */ - if (window->override_redirect) - return; - - /* Don't focus panels--they must explicitly request focus. - * See bug 160470 - */ - if (window->type != META_WINDOW_DOCK) - { - meta_topic (META_DEBUG_FOCUS, - "Focusing %s due to button %u press (display.c)", - window->desc, button); - meta_window_focus (window, event->any.time); - meta_window_check_alive (window, event->any.time); - } - else - /* However, do allow terminals to lose focus due to new - * window mappings after the user clicks on a panel. - */ - display->allow_terminal_deactivation = TRUE; - - /* We have three passive button grabs: - * - on any button, without modifiers => focuses and maybe raises the window - * - on resize button, with modifiers => start an interactive resizing - * (normally <Super>middle) - * - on move button, with modifiers => start an interactive move - * (normally <Super>left) - * - on menu button, with modifiers => show the window menu - * (normally <Super>right) - * - * We may get here because we actually have a button - * grab on the window, or because we're a wayland - * compositor and thus we see all the events, so we - * need to check if the event is interesting. - * We want an event that is not modified for a window. - * - * We may have other events on the window, for example - * a click on a frame button, but that's not for us to - * care about. Just let the event through. - */ - - grab_mods = meta_display_get_compositor_modifiers (display); - event_mods = clutter_event_get_state (event); - unmodified = (event_mods & grab_mods) == 0; - source = clutter_event_get_source_device (event); - is_window_button_grab_allowed = !display->focus_window || - !meta_window_shortcuts_inhibited (display->focus_window, source); - is_window_grab = (is_window_button_grab_allowed && - ((event_mods & grab_mods) == grab_mods)); - - clutter_event_get_coords (event, &x, &y); - - if (unmodified) - { - if (meta_prefs_get_raise_on_click ()) - meta_window_raise (window); - else - meta_topic (META_DEBUG_FOCUS, - "Not raising window on click due to don't-raise-on-click option"); - } - else if (is_window_grab && (int) button == meta_prefs_get_mouse_button_resize ()) - { - if (window->has_resize_func) - { - gboolean north, south; - gboolean west, east; - MetaRectangle frame_rect; - MetaGrabOp op = META_GRAB_OP_WINDOW_BASE; - - meta_window_get_frame_rect (window, &frame_rect); - - west = x < (frame_rect.x + 1 * frame_rect.width / 3); - east = x > (frame_rect.x + 2 * frame_rect.width / 3); - north = y < (frame_rect.y + 1 * frame_rect.height / 3); - south = y > (frame_rect.y + 2 * frame_rect.height / 3); - - if (west) - op |= META_GRAB_OP_WINDOW_DIR_WEST; - if (east) - op |= META_GRAB_OP_WINDOW_DIR_EAST; - if (north) - op |= META_GRAB_OP_WINDOW_DIR_NORTH; - if (south) - op |= META_GRAB_OP_WINDOW_DIR_SOUTH; - - if (op != META_GRAB_OP_WINDOW_BASE) - meta_display_begin_grab_op (display, - window, - op, - TRUE, - FALSE, - button, - 0, - event->any.time, - x, y); - } - } - else if (is_window_grab && (int) button == meta_prefs_get_mouse_button_menu ()) - { - if (meta_prefs_get_raise_on_click ()) - meta_window_raise (window); - meta_window_show_menu (window, - META_WINDOW_MENU_WM, - x, y); - } - else if (is_window_grab && (int) button == 1) - { - if (window->has_move_func) - { - meta_display_begin_grab_op (display, - window, - META_GRAB_OP_MOVING, - TRUE, - FALSE, - button, - 0, - event->any.time, - x, y); - } - } -} - -gboolean -meta_window_can_maximize (MetaWindow *window) -{ - return window->has_maximize_func; -} - -gboolean -meta_window_can_minimize (MetaWindow *window) -{ - return window->has_minimize_func; -} - -gboolean -meta_window_can_shade (MetaWindow *window) -{ - return window->has_shade_func; -} - -gboolean -meta_window_can_close (MetaWindow *window) -{ - return window->has_close_func; -} - -gboolean -meta_window_is_always_on_all_workspaces (MetaWindow *window) -{ - return window->always_sticky; -} - -gboolean -meta_window_is_above (MetaWindow *window) -{ - return window->wm_state_above; -} - -gboolean -meta_window_allows_move (MetaWindow *window) -{ - return META_WINDOW_ALLOWS_MOVE (window); -} - -gboolean -meta_window_allows_resize (MetaWindow *window) -{ - return META_WINDOW_ALLOWS_RESIZE (window); -} - -void -meta_window_set_urgent (MetaWindow *window, - gboolean urgent) -{ - if (window->urgent == urgent) - return; - - window->urgent = urgent; - g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_URGENT]); - - if (urgent) - g_signal_emit_by_name (window->display, "window-marked-urgent", window); -} - -void -meta_window_grab_op_began (MetaWindow *window, - MetaGrabOp op) -{ - META_WINDOW_GET_CLASS (window)->grab_op_began (window, op); -} - -void -meta_window_grab_op_ended (MetaWindow *window, - MetaGrabOp op) -{ - META_WINDOW_GET_CLASS (window)->grab_op_ended (window, op); -} - -void -meta_window_emit_size_changed (MetaWindow *window) -{ - g_signal_emit (window, window_signals[SIZE_CHANGED], 0); -} - -MetaPlacementRule * -meta_window_get_placement_rule (MetaWindow *window) -{ - return window->placement.rule; -} - -void -meta_window_force_restore_shortcuts (MetaWindow *window, - ClutterInputDevice *source) -{ - META_WINDOW_GET_CLASS (window)->force_restore_shortcuts (window, source); -} - -gboolean -meta_window_shortcuts_inhibited (MetaWindow *window, - ClutterInputDevice *source) -{ - return META_WINDOW_GET_CLASS (window)->shortcuts_inhibited (window, source); -} - -gboolean -meta_window_is_focusable (MetaWindow *window) -{ - g_return_val_if_fail (!window->unmanaging, FALSE); - - return META_WINDOW_GET_CLASS (window)->is_focusable (window); -} - -gboolean -meta_window_can_ping (MetaWindow *window) -{ - g_return_val_if_fail (!window->unmanaging, FALSE); - - return META_WINDOW_GET_CLASS (window)->can_ping (window); -} - -gboolean -meta_window_is_stackable (MetaWindow *window) -{ - return META_WINDOW_GET_CLASS (window)->is_stackable (window); -} - -gboolean -meta_window_is_focus_async (MetaWindow *window) -{ - return META_WINDOW_GET_CLASS (window)->is_focus_async (window); -} - -MetaStackLayer -meta_window_calculate_layer (MetaWindow *window) -{ - return META_WINDOW_GET_CLASS (window)->calculate_layer (window); -} - -/** - * meta_window_get_id: - * @window: a #MetaWindow - * - * Returns the window id associated with window. - * - * Returns: The window id - */ -uint64_t -meta_window_get_id (MetaWindow *window) -{ - return window->id; -} - -/** - * meta_window_get_client_type: - * @window: a #MetaWindow - * - * Returns the #MetaWindowClientType of the window. - * - * Returns: The root ancestor window - */ -MetaWindowClientType -meta_window_get_client_type (MetaWindow *window) -{ - return window->client_type; -} diff --git a/src/core/workspace-private.h b/src/core/workspace-private.h deleted file mode 100644 index a58b2347d..000000000 --- a/src/core/workspace-private.h +++ /dev/null @@ -1,103 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/** - * \file workspace.h Workspaces - * - * A workspace is a set of windows which all live on the same - * screen. (You may also see the name "desktop" around the place, - * which is the EWMH's name for the same thing.) Only one workspace - * of a screen may be active at once; all windows on all other workspaces - * are unmapped. - */ - -/* - * Copyright (C) 2001 Havoc Pennington - * Copyright (C) 2004, 2005 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef META_WORKSPACE_PRIVATE_H -#define META_WORKSPACE_PRIVATE_H - -#include "core/window-private.h" -#include "meta/workspace.h" - -struct _MetaWorkspace -{ - GObject parent_instance; - MetaDisplay *display; - MetaWorkspaceManager *manager; - - GList *windows; - - /* The "MRU list", or "most recently used" list, is a list of - * MetaWindows ordered based on the time the the user interacted - * with the window most recently. - * - * For historical reasons, we keep an MRU list per workspace. - * It used to be used to calculate the default focused window, - * but isn't anymore, as the window next in the stacking order - * can sometimes be not the window the user interacted with last, - */ - GList *mru_list; - - GList *list_containing_self; - - GHashTable *logical_monitor_data; - - MetaRectangle work_area_screen; - GList *screen_region; - GList *screen_edges; - GList *monitor_edges; - GSList *builtin_struts; - GSList *all_struts; - guint work_areas_invalid : 1; - - guint showing_desktop : 1; -}; - -struct _MetaWorkspaceClass -{ - GObjectClass parent_class; -}; - -MetaWorkspace* meta_workspace_new (MetaWorkspaceManager *workspace_manager); -void meta_workspace_remove (MetaWorkspace *workspace); -void meta_workspace_add_window (MetaWorkspace *workspace, - MetaWindow *window); -void meta_workspace_remove_window (MetaWorkspace *workspace, - MetaWindow *window); -void meta_workspace_relocate_windows (MetaWorkspace *workspace, - MetaWorkspace *new_home); - -void meta_workspace_get_work_area_for_logical_monitor (MetaWorkspace *workspace, - MetaLogicalMonitor *logical_monitor, - MetaRectangle *area); - -void meta_workspace_invalidate_work_area (MetaWorkspace *workspace); - -GList* meta_workspace_get_onscreen_region (MetaWorkspace *workspace); -GList * meta_workspace_get_onmonitor_region (MetaWorkspace *workspace, - MetaLogicalMonitor *logical_monitor); - -void meta_workspace_focus_default_window (MetaWorkspace *workspace, - MetaWindow *not_this_one, - guint32 timestamp); - -const char* meta_workspace_get_name (MetaWorkspace *workspace); - -void meta_workspace_index_changed (MetaWorkspace *workspace); - -#endif diff --git a/src/core/workspace.c b/src/core/workspace.c deleted file mode 100644 index 321d3efb0..000000000 --- a/src/core/workspace.c +++ /dev/null @@ -1,1477 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2001 Havoc Pennington - * Copyright (C) 2003 Rob Adams - * Copyright (C) 2004, 2005 Elijah Newren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -/** - * SECTION:workspace - * @title:MetaWorkspace - * @short_description:Workspaces - * - * A workspace is a set of windows which all live on the same - * screen. (You may also see the name "desktop" around the place, - * which is the EWMH's name for the same thing.) Only one workspace - * of a screen may be active at once; all windows on all other workspaces - * are unmapped. - */ - -#include "config.h" - -#include "meta/workspace.h" - -#include <X11/Xatom.h> -#include <string.h> - -#include "backends/meta-backend-private.h" -#include "backends/meta-logical-monitor.h" -#include "cogl/cogl.h" -#include "core/boxes-private.h" -#include "core/meta-workspace-manager-private.h" -#include "core/workspace-private.h" -#include "meta/compositor.h" -#include "meta/meta-x11-errors.h" -#include "meta/prefs.h" -#include "x11/meta-x11-display-private.h" - -void meta_workspace_queue_calc_showing (MetaWorkspace *workspace); -static void focus_ancestor_or_top_window (MetaWorkspace *workspace, - MetaWindow *not_this_one, - guint32 timestamp); - -G_DEFINE_TYPE (MetaWorkspace, meta_workspace, G_TYPE_OBJECT); - -enum -{ - PROP_0, - - PROP_N_WINDOWS, - PROP_WORKSPACE_INDEX, - PROP_ACTIVE, - - PROP_LAST, -}; - -static GParamSpec *obj_props[PROP_LAST]; - -enum -{ - WINDOW_ADDED, - WINDOW_REMOVED, - - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -typedef struct _MetaWorkspaceLogicalMonitorData -{ - GList *logical_monitor_region; - MetaRectangle logical_monitor_work_area; -} MetaWorkspaceLogicalMonitorData; - -typedef struct _MetaWorkspaceFocusableAncestorData -{ - MetaWorkspace *workspace; - MetaWindow *out_window; -} MetaWorkspaceFocusableAncestorData; - -static MetaWorkspaceLogicalMonitorData * -meta_workspace_get_logical_monitor_data (MetaWorkspace *workspace, - MetaLogicalMonitor *logical_monitor) -{ - if (!workspace->logical_monitor_data) - return NULL; - return g_hash_table_lookup (workspace->logical_monitor_data, logical_monitor); -} - -static void -workspace_logical_monitor_data_free (MetaWorkspaceLogicalMonitorData *data) -{ - g_clear_pointer (&data->logical_monitor_region, - meta_rectangle_free_list_and_elements); - g_free (data); -} - -static MetaWorkspaceLogicalMonitorData * -meta_workspace_ensure_logical_monitor_data (MetaWorkspace *workspace, - MetaLogicalMonitor *logical_monitor) -{ - MetaWorkspaceLogicalMonitorData *data; - - data = meta_workspace_get_logical_monitor_data (workspace, logical_monitor); - if (data) - return data; - - if (!workspace->logical_monitor_data) - { - workspace->logical_monitor_data = - g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify) workspace_logical_monitor_data_free); - } - - data = g_new0 (MetaWorkspaceLogicalMonitorData, 1); - g_hash_table_insert (workspace->logical_monitor_data, logical_monitor, data); - - return data; -} - -static void -meta_workspace_clear_logical_monitor_data (MetaWorkspace *workspace) -{ - g_clear_pointer (&workspace->logical_monitor_data, g_hash_table_destroy); -} - -static void -meta_workspace_finalize (GObject *object) -{ - /* Actual freeing done in meta_workspace_remove() for now */ - G_OBJECT_CLASS (meta_workspace_parent_class)->finalize (object); -} - -static void -meta_workspace_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_workspace_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - MetaWorkspace *ws = META_WORKSPACE (object); - - switch (prop_id) - { - case PROP_N_WINDOWS: - /* - * This is reliable, but not very efficient; should we store - * the list length ? - */ - g_value_set_uint (value, g_list_length (ws->windows)); - break; - case PROP_WORKSPACE_INDEX: - g_value_set_uint (value, meta_workspace_index (ws)); - break; - case PROP_ACTIVE: - g_value_set_boolean (value, ws->manager->active_workspace == ws); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -meta_workspace_class_init (MetaWorkspaceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->finalize = meta_workspace_finalize; - object_class->get_property = meta_workspace_get_property; - object_class->set_property = meta_workspace_set_property; - - signals[WINDOW_ADDED] = g_signal_new ("window-added", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 1, - META_TYPE_WINDOW); - signals[WINDOW_REMOVED] = g_signal_new ("window-removed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 1, - META_TYPE_WINDOW); - - obj_props[PROP_N_WINDOWS] = g_param_spec_uint ("n-windows", - "N Windows", - "Number of windows", - 0, G_MAXUINT, 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_WORKSPACE_INDEX] = g_param_spec_uint ("workspace-index", - "Workspace index", - "The workspace's index", - 0, G_MAXUINT, 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_props[PROP_ACTIVE] = g_param_spec_boolean ("active", - "Active", - "Whether the workspace is currently active", - FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, PROP_LAST, obj_props); -} - -static void -meta_workspace_init (MetaWorkspace *workspace) -{ -} - -MetaWorkspace * -meta_workspace_new (MetaWorkspaceManager *workspace_manager) -{ - MetaDisplay *display = workspace_manager->display; - MetaWorkspace *workspace; - GSList *windows, *l; - - workspace = g_object_new (META_TYPE_WORKSPACE, NULL); - - workspace->display = display; - workspace->manager = workspace_manager; - - workspace_manager->workspaces = - g_list_append (workspace_manager->workspaces, workspace); - workspace->windows = NULL; - workspace->mru_list = NULL; - - workspace->work_areas_invalid = TRUE; - workspace->work_area_screen.x = 0; - workspace->work_area_screen.y = 0; - workspace->work_area_screen.width = 0; - workspace->work_area_screen.height = 0; - - workspace->screen_region = NULL; - workspace->screen_edges = NULL; - workspace->monitor_edges = NULL; - workspace->list_containing_self = g_list_prepend (NULL, workspace); - - workspace->builtin_struts = NULL; - workspace->all_struts = NULL; - - workspace->showing_desktop = FALSE; - - /* make sure sticky windows are in our mru_list */ - windows = meta_display_list_windows (display, META_LIST_SORTED); - for (l = windows; l; l = l->next) - if (meta_window_located_on_workspace (l->data, workspace)) - meta_workspace_add_window (workspace, l->data); - g_slist_free (windows); - - return workspace; -} - -/** - * workspace_free_all_struts: - * @workspace: The workspace. - * - * Frees the combined struts list of a workspace. - */ -static void -workspace_free_all_struts (MetaWorkspace *workspace) -{ - if (workspace->all_struts == NULL) - return; - - g_slist_free_full (workspace->all_struts, g_free); - workspace->all_struts = NULL; -} - -/** - * workspace_free_builtin_struts: - * @workspace: The workspace. - * - * Frees the struts list set with meta_workspace_set_builtin_struts - */ -static void -workspace_free_builtin_struts (MetaWorkspace *workspace) -{ - if (workspace->builtin_struts == NULL) - return; - - g_slist_free_full (workspace->builtin_struts, g_free); - workspace->builtin_struts = NULL; -} - -/* Ensure that the workspace is empty by making sure that - * all of our windows are on-all-workspaces. */ -static void -assert_workspace_empty (MetaWorkspace *workspace) -{ - GList *l; - for (l = workspace->windows; l != NULL; l = l->next) - { - MetaWindow *window = l->data; - g_assert (window->on_all_workspaces); - } -} - -void -meta_workspace_remove (MetaWorkspace *workspace) -{ - MetaWorkspaceManager *manager = workspace->display->workspace_manager; - - g_return_if_fail (workspace != manager->active_workspace); - - assert_workspace_empty (workspace); - - manager->workspaces = - g_list_remove (manager->workspaces, workspace); - - meta_workspace_clear_logical_monitor_data (workspace); - - g_list_free (workspace->mru_list); - g_list_free (workspace->list_containing_self); - - workspace_free_builtin_struts (workspace); - - /* screen.c:update_num_workspaces(), which calls us, removes windows from - * workspaces first, which can cause the workareas on the workspace to be - * invalidated (and hence for struts/regions/edges to be freed). - * So, no point trying to double free it; that causes a crash - * anyway. #361804. - */ - - if (!workspace->work_areas_invalid) - { - workspace_free_all_struts (workspace); - meta_rectangle_free_list_and_elements (workspace->screen_region); - meta_rectangle_free_list_and_elements (workspace->screen_edges); - meta_rectangle_free_list_and_elements (workspace->monitor_edges); - } - - g_object_unref (workspace); - - /* don't bother to reset names, pagers can just ignore - * extra ones - */ -} - -void -meta_workspace_add_window (MetaWorkspace *workspace, - MetaWindow *window) -{ - g_return_if_fail (g_list_find (workspace->mru_list, window) == NULL); - - COGL_TRACE_BEGIN_SCOPED (MetaWorkspaceAddWindow, - "Workspace (add window)"); - - workspace->mru_list = g_list_prepend (workspace->mru_list, window); - - workspace->windows = g_list_prepend (workspace->windows, window); - - if (window->struts) - { - meta_topic (META_DEBUG_WORKAREA, - "Invalidating work area of workspace %d since we're adding window %s to it", - meta_workspace_index (workspace), window->desc); - meta_workspace_invalidate_work_area (workspace); - } - - g_signal_emit (workspace, signals[WINDOW_ADDED], 0, window); - g_object_notify_by_pspec (G_OBJECT (workspace), obj_props[PROP_N_WINDOWS]); -} - -void -meta_workspace_remove_window (MetaWorkspace *workspace, - MetaWindow *window) -{ - COGL_TRACE_BEGIN_SCOPED (MetaWorkspaceRemoveWindow, - "Workspace (remove window)"); - - workspace->windows = g_list_remove (workspace->windows, window); - - workspace->mru_list = g_list_remove (workspace->mru_list, window); - g_assert (g_list_find (workspace->mru_list, window) == NULL); - - if (window->struts) - { - meta_topic (META_DEBUG_WORKAREA, - "Invalidating work area of workspace %d since we're removing window %s from it", - meta_workspace_index (workspace), window->desc); - meta_workspace_invalidate_work_area (workspace); - } - - g_signal_emit (workspace, signals[WINDOW_REMOVED], 0, window); - g_object_notify (G_OBJECT (workspace), "n-windows"); -} - -void -meta_workspace_relocate_windows (MetaWorkspace *workspace, - MetaWorkspace *new_home) -{ - GList *copy, *l; - - g_return_if_fail (workspace != new_home); - - /* can't modify list we're iterating over */ - copy = g_list_copy (workspace->windows); - - for (l = copy; l != NULL; l = l->next) - { - MetaWindow *window = l->data; - - if (!window->on_all_workspaces) - meta_window_change_workspace (window, new_home); - } - - g_list_free (copy); - - assert_workspace_empty (workspace); -} - -void -meta_workspace_queue_calc_showing (MetaWorkspace *workspace) -{ - GList *l; - - for (l = workspace->windows; l != NULL; l = l->next) - meta_window_queue (l->data, META_QUEUE_CALC_SHOWING); -} - -static void -workspace_switch_sound(MetaWorkspace *from, - MetaWorkspace *to) -{ - MetaSoundPlayer *player; - MetaWorkspaceLayout layout; - int i, nw, x, y, fi, ti; - const char *e; - - nw = meta_workspace_manager_get_n_workspaces (from->manager); - fi = meta_workspace_index(from); - ti = meta_workspace_index(to); - - meta_workspace_manager_calc_workspace_layout (from->manager, - nw, - fi, - &layout); - - for (i = 0; i < nw; i++) - if (layout.grid[i] == ti) - break; - - if (i >= nw) - { - meta_bug("Failed to find destination workspace in layout"); - goto finish; - } - - y = i / layout.cols; - x = i % layout.cols; - - /* We priorize horizontal over vertical movements here. The - rationale for this is that horizontal movements are probably more - interesting for sound effects because speakers are usually - positioned on a horizontal and not a vertical axis. i.e. your - spatial "Woosh!" effects will easily be able to encode horizontal - movement but not such much vertical movement. */ - - if (x < layout.current_col) - e = "desktop-switch-left"; - else if (x > layout.current_col) - e = "desktop-switch-right"; - else if (y < layout.current_row) - e = "desktop-switch-up"; - else if (y > layout.current_row) - e = "desktop-switch-down"; - else - { - meta_bug("Uh, origin and destination workspace at same logic position!"); - goto finish; - } - - player = meta_display_get_sound_player (from->display); - meta_sound_player_play_from_theme (player, e, "Desktop switched", NULL); - - finish: - meta_workspace_manager_free_workspace_layout (&layout); -} - -/** - * meta_workspace_activate_with_focus: - * @workspace: a #MetaWorkspace - * @focus_this: the #MetaWindow to be focused, or %NULL - * @timestamp: timestamp for @focus_this - * - * Switches to @workspace and possibly activates the window @focus_this. - * - * The window @focus_this is activated by calling meta_window_activate() - * which will unminimize it and transient parents, raise it and give it - * the focus. - * - * If a window is currently being moved by the user, it will be - * moved to @workspace. - * - * The advantage of calling this function instead of meta_workspace_activate() - * followed by meta_window_activate() is that it happens as a unit, so - * no other window gets focused first before @focus_this. - */ -void -meta_workspace_activate_with_focus (MetaWorkspace *workspace, - MetaWindow *focus_this, - guint32 timestamp) -{ - MetaWorkspace *old; - MetaWindow *move_window; - MetaCompositor *comp; - MetaWorkspaceLayout layout1, layout2; - gint num_workspaces, current_space, new_space; - MetaMotionDirection direction; - - meta_verbose ("Activating workspace %d", - meta_workspace_index (workspace)); - - if (workspace->manager->active_workspace == workspace) - { - if (focus_this) - meta_window_activate (focus_this, timestamp); - return; - } - - /* Free any cached pointers to the workspaces's edges from - * a current resize or move operation */ - meta_display_cleanup_edges (workspace->display); - - if (workspace->manager->active_workspace) - workspace_switch_sound (workspace->manager->active_workspace, workspace); - - /* Note that old can be NULL; e.g. when starting up */ - old = workspace->manager->active_workspace; - - workspace->manager->active_workspace = workspace; - - g_signal_emit_by_name (workspace->manager, "active-workspace-changed"); - - g_object_notify_by_pspec (G_OBJECT (workspace), obj_props[PROP_ACTIVE]); - - if (old == NULL) - return; - - g_object_notify_by_pspec (G_OBJECT (old), obj_props[PROP_ACTIVE]); - - /* If the "show desktop" mode is active for either the old workspace - * or the new one *but not both*, then update the - * _net_showing_desktop hint - */ - if (old->showing_desktop != workspace->showing_desktop) - g_signal_emit_by_name (workspace->manager, "showing-desktop-changed"); - - move_window = NULL; - if (meta_grab_op_is_moving (workspace->display->grab_op)) - move_window = workspace->display->grab_window; - - if (move_window != NULL) - { - /* We put the window on the new workspace, flip spaces, - * then remove from old workspace, so the window - * never gets unmapped and we maintain the button grab - * on it. - * - * \bug This comment appears to be the reverse of what happens - */ - if (!meta_window_located_on_workspace (move_window, workspace)) - meta_window_change_workspace (move_window, workspace); - } - - meta_workspace_queue_calc_showing (old); - meta_workspace_queue_calc_showing (workspace); - - /* - * Notify the compositor that the active workspace is changing. - */ - comp = meta_display_get_compositor (workspace->display); - direction = 0; - - current_space = meta_workspace_index (old); - new_space = meta_workspace_index (workspace); - num_workspaces = meta_workspace_manager_get_n_workspaces (workspace->manager); - meta_workspace_manager_calc_workspace_layout (workspace->manager, num_workspaces, - current_space, &layout1); - - meta_workspace_manager_calc_workspace_layout (workspace->manager, num_workspaces, - new_space, &layout2); - - if (meta_get_locale_direction () == META_LOCALE_DIRECTION_RTL) - { - if (layout1.current_col > layout2.current_col) - direction = META_MOTION_RIGHT; - else if (layout1.current_col < layout2.current_col) - direction = META_MOTION_LEFT; - } - else - { - if (layout1.current_col < layout2.current_col) - direction = META_MOTION_RIGHT; - else if (layout1.current_col > layout2.current_col) - direction = META_MOTION_LEFT; - } - - if (layout1.current_row < layout2.current_row) - { - if (!direction) - direction = META_MOTION_DOWN; - else if (direction == META_MOTION_RIGHT) - direction = META_MOTION_DOWN_RIGHT; - else - direction = META_MOTION_DOWN_LEFT; - } - - if (layout1.current_row > layout2.current_row) - { - if (!direction) - direction = META_MOTION_UP; - else if (direction == META_MOTION_RIGHT) - direction = META_MOTION_UP_RIGHT; - else - direction = META_MOTION_UP_LEFT; - } - - meta_workspace_manager_free_workspace_layout (&layout1); - meta_workspace_manager_free_workspace_layout (&layout2); - - meta_compositor_switch_workspace (comp, old, workspace, direction); - - /* This needs to be done after telling the compositor we are switching - * workspaces since focusing a window will cause it to be immediately - * shown and that would confuse the compositor if it didn't know we - * were in a workspace switch. - */ - if (focus_this) - { - meta_window_activate (focus_this, timestamp); - } - else if (move_window) - { - meta_window_raise (move_window); - } - else - { - meta_topic (META_DEBUG_FOCUS, "Focusing default window on new workspace"); - meta_workspace_focus_default_window (workspace, NULL, timestamp); - } - - meta_workspace_manager_workspace_switched (workspace->manager, current_space, - new_space, direction); -} - -void -meta_workspace_activate (MetaWorkspace *workspace, - guint32 timestamp) -{ - meta_workspace_activate_with_focus (workspace, NULL, timestamp); -} - -int -meta_workspace_index (MetaWorkspace *workspace) -{ - int ret; - - ret = g_list_index (workspace->manager->workspaces, workspace); - - if (ret < 0) - meta_bug ("Workspace does not exist to index!"); - - return ret; -} - -void -meta_workspace_index_changed (MetaWorkspace *workspace) -{ - GList *l; - for (l = workspace->windows; l != NULL; l = l->next) - { - MetaWindow *win = l->data; - meta_window_current_workspace_changed (win); - } - - g_object_notify_by_pspec (G_OBJECT (workspace), obj_props[PROP_WORKSPACE_INDEX]); -} - -/** - * meta_workspace_list_windows: - * @workspace: a #MetaWorkspace - * - * Gets windows contained on the workspace, including workspace->windows - * and also sticky windows. Override-redirect windows are not included. - * - * Return value: (transfer container) (element-type MetaWindow): the list of windows. - */ -GList* -meta_workspace_list_windows (MetaWorkspace *workspace) -{ - GSList *display_windows, *l; - GList *workspace_windows; - - display_windows = meta_display_list_windows (workspace->display, - META_LIST_DEFAULT); - - workspace_windows = NULL; - for (l = display_windows; l != NULL; l = l->next) - { - MetaWindow *window = l->data; - - if (meta_window_located_on_workspace (window, workspace)) - workspace_windows = g_list_prepend (workspace_windows, - window); - } - - g_slist_free (display_windows); - - return workspace_windows; -} - -void -meta_workspace_invalidate_work_area (MetaWorkspace *workspace) -{ - GList *windows, *l; - - if (workspace->work_areas_invalid) - { - meta_topic (META_DEBUG_WORKAREA, - "Work area for workspace %d is already invalid", - meta_workspace_index (workspace)); - return; - } - - meta_topic (META_DEBUG_WORKAREA, - "Invalidating work area for workspace %d", - meta_workspace_index (workspace)); - - /* If we are in the middle of a resize or move operation, we - * might have cached pointers to the workspace's edges */ - if (workspace == workspace->manager->active_workspace) - meta_display_cleanup_edges (workspace->display); - - meta_workspace_clear_logical_monitor_data (workspace); - - workspace_free_all_struts (workspace); - - meta_rectangle_free_list_and_elements (workspace->screen_region); - meta_rectangle_free_list_and_elements (workspace->screen_edges); - meta_rectangle_free_list_and_elements (workspace->monitor_edges); - workspace->screen_region = NULL; - workspace->screen_edges = NULL; - workspace->monitor_edges = NULL; - - workspace->work_areas_invalid = TRUE; - - /* redo the size/position constraints on all windows */ - windows = meta_workspace_list_windows (workspace); - - for (l = windows; l != NULL; l = l->next) - { - MetaWindow *w = l->data; - meta_window_queue (w, META_QUEUE_MOVE_RESIZE); - } - - g_list_free (windows); - - meta_display_queue_workarea_recalc (workspace->display); -} - -static MetaStrut * -copy_strut(MetaStrut *original) -{ - return g_memdup2 (original, sizeof (MetaStrut)); -} - -static GSList * -copy_strut_list(GSList *original) -{ - GSList *result = NULL; - - for (; original != NULL; original = original->next) - result = g_slist_prepend (result, copy_strut (original->data)); - - return g_slist_reverse (result); -} - -static void -ensure_work_areas_validated (MetaWorkspace *workspace) -{ - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - GList *windows; - GList *tmp; - GList *logical_monitors, *l; - MetaRectangle display_rect = { 0 }; - MetaRectangle work_area; - - if (!workspace->work_areas_invalid) - return; - - g_assert (workspace->all_struts == NULL); - g_assert (workspace->screen_region == NULL); - g_assert (workspace->screen_edges == NULL); - g_assert (workspace->monitor_edges == NULL); - - meta_display_get_size (workspace->display, - &display_rect.width, - &display_rect.height); - - /* STEP 1: Get the list of struts */ - - workspace->all_struts = copy_strut_list (workspace->builtin_struts); - - windows = meta_workspace_list_windows (workspace); - for (tmp = windows; tmp != NULL; tmp = tmp->next) - { - MetaWindow *win = tmp->data; - GSList *s_iter; - - for (s_iter = win->struts; s_iter != NULL; s_iter = s_iter->next) { - workspace->all_struts = g_slist_prepend (workspace->all_struts, - copy_strut(s_iter->data)); - } - } - g_list_free (windows); - - /* STEP 2: Get the maximal/spanning rects for the onscreen and - * on-single-monitor regions - */ - g_assert (workspace->screen_region == NULL); - - logical_monitors = - meta_monitor_manager_get_logical_monitors (monitor_manager); - for (l = logical_monitors; l; l = l->next) - { - MetaLogicalMonitor *logical_monitor = l->data; - MetaWorkspaceLogicalMonitorData *data; - - g_assert (!meta_workspace_get_logical_monitor_data (workspace, - logical_monitor)); - - data = meta_workspace_ensure_logical_monitor_data (workspace, - logical_monitor); - data->logical_monitor_region = - meta_rectangle_get_minimal_spanning_set_for_region ( - &logical_monitor->rect, - workspace->all_struts); - } - - workspace->screen_region = - meta_rectangle_get_minimal_spanning_set_for_region ( - &display_rect, - workspace->all_struts); - - /* STEP 3: Get the work areas (region-to-maximize-to) for the screen and - * monitors. - */ - work_area = display_rect; /* start with the screen */ - if (workspace->screen_region == NULL) - work_area = meta_rect (0, 0, -1, -1); - else - meta_rectangle_clip_to_region (workspace->screen_region, - FIXED_DIRECTION_NONE, - &work_area); - - /* Lots of paranoia checks, forcing work_area_screen to be sane */ -#define MIN_SANE_AREA 100 - if (work_area.width < MIN_SANE_AREA && - work_area.width != display_rect.width) - { - meta_warning ("struts occupy an unusually large percentage of the screen; " - "available remaining width = %d < %d", - work_area.width, MIN_SANE_AREA); - if (work_area.width < 1) - { - work_area.x = (display_rect.width - MIN_SANE_AREA)/2; - work_area.width = MIN_SANE_AREA; - } - else - { - int amount = (MIN_SANE_AREA - work_area.width)/2; - work_area.x -= amount; - work_area.width += 2*amount; - } - } - if (work_area.height < MIN_SANE_AREA && - work_area.height != display_rect.height) - { - meta_warning ("struts occupy an unusually large percentage of the screen; " - "available remaining height = %d < %d", - work_area.height, MIN_SANE_AREA); - if (work_area.height < 1) - { - work_area.y = (display_rect.height - MIN_SANE_AREA)/2; - work_area.height = MIN_SANE_AREA; - } - else - { - int amount = (MIN_SANE_AREA - work_area.height)/2; - work_area.y -= amount; - work_area.height += 2*amount; - } - } - workspace->work_area_screen = work_area; - meta_topic (META_DEBUG_WORKAREA, - "Computed work area for workspace %d: %d,%d %d x %d", - meta_workspace_index (workspace), - workspace->work_area_screen.x, - workspace->work_area_screen.y, - workspace->work_area_screen.width, - workspace->work_area_screen.height); - - /* Now find the work areas for each monitor */ - for (l = logical_monitors; l; l = l->next) - { - MetaLogicalMonitor *logical_monitor = l->data; - MetaWorkspaceLogicalMonitorData *data; - - data = meta_workspace_get_logical_monitor_data (workspace, - logical_monitor); - work_area = logical_monitor->rect; - - if (!data->logical_monitor_region) - /* FIXME: constraints.c untested with this, but it might be nice for - * a screen reader or magnifier. - */ - work_area = meta_rect (work_area.x, work_area.y, -1, -1); - else - meta_rectangle_clip_to_region (data->logical_monitor_region, - FIXED_DIRECTION_NONE, - &work_area); - - data->logical_monitor_work_area = work_area; - - meta_topic (META_DEBUG_WORKAREA, - "Computed work area for workspace %d " - "monitor %d: %d,%d %d x %d", - meta_workspace_index (workspace), - logical_monitor->number, - data->logical_monitor_work_area.x, - data->logical_monitor_work_area.y, - data->logical_monitor_work_area.width, - data->logical_monitor_work_area.height); - } - - /* STEP 4: Make sure the screen_region is nonempty (separate from step 2 - * since it relies on step 3). - */ - if (workspace->screen_region == NULL) - { - MetaRectangle *nonempty_region; - nonempty_region = g_new (MetaRectangle, 1); - *nonempty_region = workspace->work_area_screen; - workspace->screen_region = g_list_prepend (NULL, nonempty_region); - } - - /* STEP 5: Cache screen and monitor edges for edge resistance and snapping */ - g_assert (workspace->screen_edges == NULL); - g_assert (workspace->monitor_edges == NULL); - workspace->screen_edges = - meta_rectangle_find_onscreen_edges (&display_rect, - workspace->all_struts); - tmp = NULL; - for (l = logical_monitors; l; l = l->next) - { - MetaLogicalMonitor *logical_monitor = l->data; - - tmp = g_list_prepend (tmp, &logical_monitor->rect); - } - workspace->monitor_edges = - meta_rectangle_find_nonintersected_monitor_edges (tmp, - workspace->all_struts); - g_list_free (tmp); - - /* We're all done, YAAY! Record that everything has been validated. */ - workspace->work_areas_invalid = FALSE; -} - -static gboolean -strut_lists_equal (GSList *l, - GSList *m) -{ - for (; l && m; l = l->next, m = m->next) - { - MetaStrut *a = l->data; - MetaStrut *b = m->data; - - if (a->side != b->side || - !meta_rectangle_equal (&a->rect, &b->rect)) - return FALSE; - } - - return l == NULL && m == NULL; -} - -/** - * meta_workspace_set_builtin_struts: - * @workspace: a #MetaWorkspace - * @struts: (element-type Meta.Strut) (transfer none): list of #MetaStrut - * - * Sets a list of struts that will be used in addition to the struts - * of the windows in the workspace when computing the work area of - * the workspace. - */ -void -meta_workspace_set_builtin_struts (MetaWorkspace *workspace, - GSList *struts) -{ - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - MetaDisplay *display = workspace->display; - MetaRectangle display_rect = { 0 }; - GSList *l; - - meta_display_get_size (display, &display_rect.width, &display_rect.height); - - for (l = struts; l; l = l->next) - { - MetaStrut *strut = l->data; - MetaLogicalMonitor *logical_monitor; - - logical_monitor = - meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager, - &strut->rect); - - switch (strut->side) - { - case META_SIDE_TOP: - if (meta_monitor_manager_get_logical_monitor_neighbor (monitor_manager, - logical_monitor, - META_DISPLAY_UP)) - continue; - - strut->rect.height += strut->rect.y; - strut->rect.y = 0; - break; - case META_SIDE_BOTTOM: - if (meta_monitor_manager_get_logical_monitor_neighbor (monitor_manager, - logical_monitor, - META_DISPLAY_DOWN)) - continue; - - strut->rect.height = display_rect.height - strut->rect.y; - break; - case META_SIDE_LEFT: - if (meta_monitor_manager_get_logical_monitor_neighbor (monitor_manager, - logical_monitor, - META_DISPLAY_LEFT)) - continue; - - strut->rect.width += strut->rect.x; - strut->rect.x = 0; - break; - case META_SIDE_RIGHT: - if (meta_monitor_manager_get_logical_monitor_neighbor (monitor_manager, - logical_monitor, - META_DISPLAY_RIGHT)) - continue; - - strut->rect.width = display_rect.width - strut->rect.x; - break; - } - } - - /* Reordering doesn't actually matter, so we don't catch all - * no-impact changes, but this is just a (possibly unnecessary - * anyways) optimization */ - if (strut_lists_equal (struts, workspace->builtin_struts)) - return; - - workspace_free_builtin_struts (workspace); - workspace->builtin_struts = copy_strut_list (struts); - - meta_workspace_invalidate_work_area (workspace); -} - -void -meta_workspace_get_work_area_for_logical_monitor (MetaWorkspace *workspace, - MetaLogicalMonitor *logical_monitor, - MetaRectangle *area) -{ - meta_workspace_get_work_area_for_monitor (workspace, - logical_monitor->number, - area); -} - -/** - * meta_workspace_get_work_area_for_monitor: - * @workspace: a #MetaWorkspace - * @which_monitor: a monitor index - * @area: (out): location to store the work area - * - * Stores the work area for @which_monitor on @workspace - * in @area. - */ -void -meta_workspace_get_work_area_for_monitor (MetaWorkspace *workspace, - int which_monitor, - MetaRectangle *area) -{ - MetaBackend *backend = meta_get_backend(); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - MetaLogicalMonitor *logical_monitor; - MetaWorkspaceLogicalMonitorData *data; - - logical_monitor = - meta_monitor_manager_get_logical_monitor_from_number (monitor_manager, - which_monitor); - g_return_if_fail (logical_monitor != NULL); - - ensure_work_areas_validated (workspace); - data = meta_workspace_get_logical_monitor_data (workspace, logical_monitor); - - g_return_if_fail (data != NULL); - - *area = data->logical_monitor_work_area; -} - -/** - * meta_workspace_get_work_area_all_monitors: - * @workspace: a #MetaWorkspace - * @area: (out): location to store the work area - * - * Stores the work area in @area. - */ -void -meta_workspace_get_work_area_all_monitors (MetaWorkspace *workspace, - MetaRectangle *area) -{ - ensure_work_areas_validated (workspace); - - *area = workspace->work_area_screen; -} - -GList* -meta_workspace_get_onscreen_region (MetaWorkspace *workspace) -{ - ensure_work_areas_validated (workspace); - - return workspace->screen_region; -} - -GList * -meta_workspace_get_onmonitor_region (MetaWorkspace *workspace, - MetaLogicalMonitor *logical_monitor) -{ - MetaWorkspaceLogicalMonitorData *data; - - ensure_work_areas_validated (workspace); - - data = meta_workspace_get_logical_monitor_data (workspace, logical_monitor); - - return data->logical_monitor_region; -} - -#ifdef WITH_VERBOSE_MODE -static const char * -meta_motion_direction_to_string (MetaMotionDirection direction) -{ - switch (direction) - { - case META_MOTION_UP: - return "Up"; - case META_MOTION_DOWN: - return "Down"; - case META_MOTION_LEFT: - return "Left"; - case META_MOTION_RIGHT: - return "Right"; - case META_MOTION_UP_RIGHT: - return "Up-Right"; - case META_MOTION_DOWN_RIGHT: - return "Down-Right"; - case META_MOTION_UP_LEFT: - return "Up-Left"; - case META_MOTION_DOWN_LEFT: - return "Down-Left"; - } - - return "Unknown"; -} -#endif /* WITH_VERBOSE_MODE */ - -/** - * meta_workspace_get_neighbor: - * @workspace: a #MetaWorkspace - * @direction: a #MetaMotionDirection, relative to @workspace - * - * Calculate and retrieve the workspace that is next to @workspace, - * according to @direction and the current workspace layout, as set - * by meta_screen_override_workspace_layout(). - * - * Returns: (transfer none): the workspace next to @workspace, or - * @workspace itself if the neighbor would be outside the layout - */ -MetaWorkspace* -meta_workspace_get_neighbor (MetaWorkspace *workspace, - MetaMotionDirection direction) -{ - MetaWorkspaceLayout layout; - int i, current_space, num_workspaces; - gboolean ltr; - - current_space = meta_workspace_index (workspace); - num_workspaces = meta_workspace_manager_get_n_workspaces (workspace->manager); - meta_workspace_manager_calc_workspace_layout (workspace->manager, num_workspaces, - current_space, &layout); - - meta_verbose ("Getting neighbor of %d in direction %s", - current_space, meta_motion_direction_to_string (direction)); - - ltr = (meta_get_locale_direction () == META_LOCALE_DIRECTION_LTR); - - switch (direction) - { - case META_MOTION_LEFT: - layout.current_col -= ltr ? 1 : -1; - break; - case META_MOTION_RIGHT: - layout.current_col += ltr ? 1 : -1; - break; - case META_MOTION_UP: - layout.current_row -= 1; - break; - case META_MOTION_DOWN: - layout.current_row += 1; - break; - default:; - } - - if (layout.current_col < 0) - layout.current_col = 0; - if (layout.current_col >= layout.cols) - layout.current_col = layout.cols - 1; - if (layout.current_row < 0) - layout.current_row = 0; - if (layout.current_row >= layout.rows) - layout.current_row = layout.rows - 1; - - i = layout.grid[layout.current_row * layout.cols + layout.current_col]; - - if (i < 0) - i = current_space; - - if (i >= num_workspaces) - meta_bug ("calc_workspace_layout left an invalid (too-high) workspace number %d in the grid", - i); - - meta_verbose ("Neighbor workspace is %d at row %d col %d", - i, layout.current_row, layout.current_col); - - meta_workspace_manager_free_workspace_layout (&layout); - - return meta_workspace_manager_get_workspace_by_index (workspace->manager, i); -} - -const char* -meta_workspace_get_name (MetaWorkspace *workspace) -{ - return meta_prefs_get_workspace_name (meta_workspace_index (workspace)); -} - -void -meta_workspace_focus_default_window (MetaWorkspace *workspace, - MetaWindow *not_this_one, - guint32 timestamp) -{ - if (timestamp == META_CURRENT_TIME) - meta_warning ("META_CURRENT_TIME used to choose focus window; " - "focus window may not be correct."); - - if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK || - !workspace->display->mouse_mode) - focus_ancestor_or_top_window (workspace, not_this_one, timestamp); - else - { - MetaWindow * window; - window = meta_display_get_pointer_window (workspace->display, not_this_one); - if (window && - window->type != META_WINDOW_DOCK && - window->type != META_WINDOW_DESKTOP) - { - if (timestamp == META_CURRENT_TIME) - { - - /* We would like for this to never happen. However, if - * it does happen then we kludge since using META_CURRENT_TIME - * can mean ugly race conditions--and we can avoid these - * by allowing EnterNotify events (which come with - * timestamps) to handle focus. - */ - - meta_topic (META_DEBUG_FOCUS, - "Not focusing mouse window %s because EnterNotify events should handle that", - window->desc); - } - else - { - meta_topic (META_DEBUG_FOCUS, - "Focusing mouse window %s", window->desc); - meta_window_focus (window, timestamp); - } - - if (workspace->display->autoraise_window != window && - meta_prefs_get_auto_raise ()) - { - meta_display_queue_autoraise_callback (workspace->display, window); - } - } - else if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_SLOPPY) - focus_ancestor_or_top_window (workspace, not_this_one, timestamp); - else if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_MOUSE) - { - meta_topic (META_DEBUG_FOCUS, - "Setting focus to no_focus_window, since no valid " - "window to focus found."); - meta_display_unset_input_focus (workspace->display, timestamp); - } - } -} - -static gboolean -find_focusable_ancestor (MetaWindow *window, - gpointer user_data) -{ - MetaWorkspaceFocusableAncestorData *data = user_data; - - if (!window->unmanaging && - window->mapped && - !window->hidden && - meta_window_is_focusable (window) && - meta_window_located_on_workspace (window, data->workspace) && - meta_window_showing_on_its_workspace (window)) - { - data->out_window = window; - return FALSE; - } - - return TRUE; -} - -static gboolean -try_to_set_focus_and_check (MetaWindow *window, - MetaWindow *not_this_one, - uint32_t timestamp) -{ - meta_window_focus (window, timestamp); - - /* meta_focus_window() will not change focus for clients using the - * "globally active input" model of input handling, hence defeating - * the assumption that focus should be changed for such windows. - * See https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7 - */ - if (meta_window_is_focus_async (window)) - return TRUE; - - /* meta_window_focus() does not guarantee that focus will end up - * where we expect, it can fail for various reasons, better check - * it did not actually changed or even left focus to the window we - * explicitly want to avoid. - */ - if (not_this_one && - meta_display_get_focus_window (window->display) == not_this_one) - { - meta_warning ("Failed to focus window %s while avoiding %s", - window->desc, not_this_one->desc); - - return FALSE; - } - - return TRUE; -} - -/* Focus ancestor of not_this_one if there is one */ -static void -focus_ancestor_or_top_window (MetaWorkspace *workspace, - MetaWindow *not_this_one, - guint32 timestamp) -{ - MetaWindow *window = NULL; - - if (not_this_one) - meta_topic (META_DEBUG_FOCUS, - "Focusing MRU window excluding %s", not_this_one->desc); - else - meta_topic (META_DEBUG_FOCUS, - "Focusing MRU window"); - - /* First, check to see if we need to focus an ancestor of a window */ - if (not_this_one) - { - MetaWindow *ancestor; - MetaWorkspaceFocusableAncestorData data; - - data = (MetaWorkspaceFocusableAncestorData) { - .workspace = workspace, - }; - meta_window_foreach_ancestor (not_this_one, find_focusable_ancestor, &data); - ancestor = data.out_window; - - if (ancestor) - { - meta_topic (META_DEBUG_FOCUS, - "Focusing %s, ancestor of %s", - ancestor->desc, not_this_one->desc); - - if (try_to_set_focus_and_check (ancestor, not_this_one, timestamp)) - { - /* Also raise the window if in click-to-focus */ - if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK) - meta_window_raise (ancestor); - - return; - } - } - } - - window = meta_stack_get_default_focus_window (workspace->display->stack, - workspace, - not_this_one); - - if (window) - { - meta_topic (META_DEBUG_FOCUS, - "Focusing workspace MRU window %s", window->desc); - if (try_to_set_focus_and_check (window, not_this_one, timestamp)) - { - /* Also raise the window if in click-to-focus */ - if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK) - meta_window_raise (window); - - return; - } - } - - meta_topic (META_DEBUG_FOCUS, - "No MRU window to focus found; focusing no_focus_window."); - meta_display_unset_input_focus (workspace->display, timestamp); -} - -/** - * meta_workspace_get_display: - * @workspace: a #MetaWorkspace - * - * Gets the #MetaDisplay that the workspace is part of. - * - * Return value: (transfer none): the #MetaDisplay for the workspace - */ -MetaDisplay * -meta_workspace_get_display (MetaWorkspace *workspace) -{ - return workspace->display; -} |