summaryrefslogtreecommitdiff
path: root/src/core/display.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/display.c')
-rw-r--r--src/core/display.c5355
1 files changed, 5355 insertions, 0 deletions
diff --git a/src/core/display.c b/src/core/display.c
new file mode 100644
index 00000000..59ec9021
--- /dev/null
+++ b/src/core/display.c
@@ -0,0 +1,5355 @@
+/* Marco X display handler */
+
+/*
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/**
+ * \file display.c Handles operations on an X display.
+ *
+ * The display is represented as a MetaDisplay struct.
+ */
+
+#include <config.h>
+#include "display-private.h"
+#include "util.h"
+#include "main.h"
+#include "screen-private.h"
+#include "window-private.h"
+#include "window-props.h"
+#include "group-props.h"
+#include "frame-private.h"
+#include "errors.h"
+#include "keybindings.h"
+#include "prefs.h"
+#include "resizepopup.h"
+#include "xprops.h"
+#include "workspace.h"
+#include "bell.h"
+#include "effects.h"
+#include "compositor.h"
+#include <X11/Xatom.h>
+#include <X11/cursorfont.h>
+
+#ifdef HAVE_SOLARIS_XINERAMA
+ #include <X11/extensions/xinerama.h>
+#endif
+
+#ifdef HAVE_XFREE_XINERAMA
+ #include <X11/extensions/Xinerama.h>
+#endif
+
+#ifdef HAVE_RANDR
+ #include <X11/extensions/Xrandr.h>
+#endif
+
+#ifdef HAVE_SHAPE
+ #include <X11/extensions/shape.h>
+#endif
+
+#ifdef HAVE_RENDER
+ #include <X11/extensions/Xrender.h>
+#endif
+
+#ifdef HAVE_XKB
+ #include <X11/XKBlib.h>
+#endif
+
+#ifdef HAVE_XCURSOR
+ #include <X11/Xcursor/Xcursor.h>
+#endif
+
+#ifdef HAVE_COMPOSITE_EXTENSIONS
+ #include <X11/extensions/Xcomposite.h>
+ #include <X11/extensions/Xdamage.h>
+ #include <X11/extensions/Xfixes.h>
+ #include <gtk/gtk.h>
+#endif
+
+#include <string.h>
+
+#define GRAB_OP_IS_WINDOW_SWITCH(g) \
+ (g == META_GRAB_OP_KEYBOARD_TABBING_NORMAL || \
+ g == META_GRAB_OP_KEYBOARD_TABBING_DOCK || \
+ g == META_GRAB_OP_KEYBOARD_TABBING_GROUP || \
+ g == META_GRAB_OP_KEYBOARD_ESCAPING_NORMAL || \
+ g == META_GRAB_OP_KEYBOARD_ESCAPING_DOCK || \
+ g == META_GRAB_OP_KEYBOARD_ESCAPING_GROUP)
+
+/**
+ * \defgroup pings 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.
+ */
+
+/**
+ * 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.
+ *
+ * \ingroup pings
+ */
+typedef struct
+{
+ MetaDisplay *display;
+ Window xwindow;
+ guint32 timestamp;
+ MetaWindowPingFunc ping_reply_func;
+ MetaWindowPingFunc ping_timeout_func;
+ void *user_data;
+ guint ping_timeout_id;
+} MetaPingData;
+
+typedef struct
+{
+ MetaDisplay *display;
+ Window xwindow;
+} MetaAutoRaiseData;
+
+/**
+ * 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;
+
+#ifdef WITH_VERBOSE_MODE
+static void meta_spew_event (MetaDisplay *display,
+ XEvent *event);
+#endif
+
+static gboolean event_callback (XEvent *event,
+ gpointer data);
+static Window event_get_modified_window (MetaDisplay *display,
+ XEvent *event);
+static guint32 event_get_time (MetaDisplay *display,
+ XEvent *event);
+static void process_request_frame_extents (MetaDisplay *display,
+ XEvent *event);
+static void process_pong_message (MetaDisplay *display,
+ XEvent *event);
+static void process_selection_request (MetaDisplay *display,
+ XEvent *event);
+static void process_selection_clear (MetaDisplay *display,
+ XEvent *event);
+
+static void update_window_grab_modifiers (MetaDisplay *display);
+
+static void prefs_changed_callback (MetaPreference pref,
+ void *data);
+
+static void sanity_check_timestamps (MetaDisplay *display,
+ guint32 known_good_timestamp);
+
+MetaGroup* get_focussed_group (MetaDisplay *display);
+
+/**
+ * Destructor for MetaPingData structs. Will destroy the
+ * event source for the struct as well.
+ *
+ * \ingroup pings
+ */
+static void
+ping_data_free (MetaPingData *ping_data)
+{
+ /* Remove the timeout */
+ if (ping_data->ping_timeout_id != 0)
+ g_source_remove (ping_data->ping_timeout_id);
+
+ g_free (ping_data);
+}
+
+/**
+ * Frees every pending ping structure for the given X window on the
+ * given display. This means that we also destroy the timeouts.
+ *
+ * \param display The display the window appears on
+ * \param xwindow The X ID of the window whose pings we should remove
+ *
+ * \ingroup pings
+ *
+ */
+static void
+remove_pending_pings_for_window (MetaDisplay *display, Window xwindow)
+{
+ 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->xwindow == xwindow)
+ 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);
+}
+
+
+#ifdef HAVE_STARTUP_NOTIFICATION
+static void
+sn_error_trap_push (SnDisplay *sn_display,
+ Display *xdisplay)
+{
+ MetaDisplay *display;
+ display = meta_display_for_x_display (xdisplay);
+ if (display != NULL)
+ meta_error_trap_push (display);
+}
+
+static void
+sn_error_trap_pop (SnDisplay *sn_display,
+ Display *xdisplay)
+{
+ MetaDisplay *display;
+ display = meta_display_for_x_display (xdisplay);
+ if (display != NULL)
+ meta_error_trap_pop (display, FALSE);
+}
+#endif
+
+static void
+enable_compositor (MetaDisplay *display,
+ gboolean composite_windows)
+{
+ GSList *list;
+
+ if (!META_DISPLAY_HAS_COMPOSITE (display) ||
+ !META_DISPLAY_HAS_DAMAGE (display) ||
+ !META_DISPLAY_HAS_XFIXES (display) ||
+ !META_DISPLAY_HAS_RENDER (display))
+ {
+ meta_warning (_("Missing %s extension required for compositing"),
+ !META_DISPLAY_HAS_COMPOSITE (display) ? "composite" :
+ !META_DISPLAY_HAS_DAMAGE (display) ? "damage" :
+ !META_DISPLAY_HAS_XFIXES (display) ? "xfixes" : "render");
+ return;
+ }
+
+ if (!display->compositor)
+ display->compositor = meta_compositor_new (display);
+
+ if (!display->compositor)
+ return;
+
+ for (list = display->screens; list != NULL; list = list->next)
+ {
+ MetaScreen *screen = list->data;
+
+ meta_compositor_manage_screen (screen->display->compositor,
+ screen);
+
+ if (composite_windows)
+ meta_screen_composite_all_windows (screen);
+ }
+}
+
+static void
+disable_compositor (MetaDisplay *display)
+{
+ GSList *list;
+
+ if (!display->compositor)
+ return;
+
+ for (list = display->screens; list != NULL; list = list->next)
+ {
+ MetaScreen *screen = list->data;
+
+ meta_compositor_unmanage_screen (screen->display->compositor,
+ screen);
+ }
+
+ meta_compositor_destroy (display->compositor);
+ display->compositor = NULL;
+}
+
+/**
+ * Opens a new display, sets it up, initialises all the X extensions
+ * we will need, and adds it to the list of displays.
+ *
+ * \return True if the display was opened successfully, and False
+ * otherwise-- that is, if the display doesn't exist or it already
+ * has a window manager.
+ *
+ * \ingroup main
+ */
+gboolean
+meta_display_open (void)
+{
+ Display *xdisplay;
+ GSList *screens;
+ GSList *tmp;
+ int i;
+ guint32 timestamp;
+
+ /* A list of all atom names, so that we can intern them in one go. */
+ char *atom_names[] = {
+#define item(x) #x,
+#include "atomnames.h"
+#undef item
+ };
+ Atom atoms[G_N_ELEMENTS(atom_names)];
+
+ meta_verbose ("Opening display '%s'\n", XDisplayName (NULL));
+
+ xdisplay = meta_ui_get_display ();
+
+ if (xdisplay == NULL)
+ {
+ meta_warning (_("Failed to open X Window System display '%s'\n"),
+ XDisplayName (NULL));
+ return FALSE;
+ }
+
+ if (meta_is_syncing ())
+ XSynchronize (xdisplay, True);
+
+ g_assert (the_display == NULL);
+ the_display = g_new (MetaDisplay, 1);
+
+ the_display->closing = 0;
+
+ /* here we use XDisplayName which is what the user
+ * probably put in, vs. DisplayString(display) which is
+ * canonicalized by XOpenDisplay()
+ */
+ the_display->name = g_strdup (XDisplayName (NULL));
+ the_display->xdisplay = xdisplay;
+ the_display->error_trap_synced_at_last_pop = TRUE;
+ the_display->error_traps = 0;
+ the_display->error_trap_handler = NULL;
+ the_display->server_grab_count = 0;
+ the_display->display_opening = TRUE;
+
+ the_display->pending_pings = NULL;
+ the_display->autoraise_timeout_id = 0;
+ the_display->autoraise_window = NULL;
+ the_display->focus_window = NULL;
+ the_display->expected_focus_window = NULL;
+ the_display->grab_old_window_stacking = NULL;
+
+ the_display->mouse_mode = TRUE; /* Only relevant for mouse or sloppy focus */
+ the_display->allow_terminal_deactivation = TRUE; /* Only relevant for when a
+ terminal has the focus */
+
+#ifdef HAVE_XSYNC
+ the_display->grab_sync_request_alarm = None;
+#endif
+
+ /* FIXME copy the checks from GDK probably */
+ the_display->static_gravity_works = g_getenv ("MARCO_USE_STATIC_GRAVITY") != NULL;
+
+ meta_bell_init (the_display);
+
+ meta_display_init_keys (the_display);
+
+ update_window_grab_modifiers (the_display);
+
+ meta_prefs_add_listener (prefs_changed_callback, the_display);
+
+ meta_verbose ("Creating %d atoms\n", (int) G_N_ELEMENTS (atom_names));
+ XInternAtoms (the_display->xdisplay, atom_names, G_N_ELEMENTS (atom_names),
+ False, atoms);
+ {
+ int i = 0;
+#define item(x) the_display->atom_##x = atoms[i++];
+#include "atomnames.h"
+#undef item
+ }
+
+ the_display->prop_hooks = NULL;
+ meta_display_init_window_prop_hooks (the_display);
+ the_display->group_prop_hooks = NULL;
+ meta_display_init_group_prop_hooks (the_display);
+
+ /* Offscreen unmapped window used for _NET_SUPPORTING_WM_CHECK,
+ * created in screen_new
+ */
+ the_display->leader_window = None;
+ the_display->timestamp_pinging_window = None;
+
+ the_display->xinerama_cache_invalidated = TRUE;
+
+ the_display->groups_by_leader = NULL;
+
+ the_display->window_with_menu = NULL;
+ the_display->window_menu = NULL;
+
+ the_display->screens = NULL;
+ the_display->active_screen = NULL;
+
+#ifdef HAVE_STARTUP_NOTIFICATION
+ the_display->sn_display = sn_display_new (the_display->xdisplay,
+ sn_error_trap_push,
+ sn_error_trap_pop);
+#endif
+
+ the_display->events = NULL;
+
+ /* Get events */
+ meta_ui_add_event_func (the_display->xdisplay,
+ event_callback,
+ the_display);
+
+ the_display->window_ids = g_hash_table_new (meta_unsigned_long_hash,
+ meta_unsigned_long_equal);
+
+ i = 0;
+ while (i < N_IGNORED_SERIALS)
+ {
+ the_display->ignored_serials[i] = 0;
+ ++i;
+ }
+ the_display->ungrab_should_not_cause_focus_window = None;
+
+ the_display->current_time = CurrentTime;
+ the_display->sentinel_counter = 0;
+
+ the_display->grab_resize_timeout_id = 0;
+ the_display->grab_have_keyboard = FALSE;
+
+#ifdef HAVE_XKB
+ the_display->last_bell_time = 0;
+#endif
+
+ the_display->grab_op = META_GRAB_OP_NONE;
+ the_display->grab_wireframe_active = FALSE;
+ the_display->grab_window = NULL;
+ the_display->grab_screen = NULL;
+ the_display->grab_resize_popup = NULL;
+
+ the_display->grab_edge_resistance_data = NULL;
+
+#ifdef HAVE_XSYNC
+ {
+ int major, minor;
+
+ the_display->have_xsync = FALSE;
+
+ the_display->xsync_error_base = 0;
+ the_display->xsync_event_base = 0;
+
+ /* I don't think we really have to fill these in */
+ major = SYNC_MAJOR_VERSION;
+ minor = SYNC_MINOR_VERSION;
+
+ if (!XSyncQueryExtension (the_display->xdisplay,
+ &the_display->xsync_event_base,
+ &the_display->xsync_error_base) ||
+ !XSyncInitialize (the_display->xdisplay,
+ &major, &minor))
+ {
+ the_display->xsync_error_base = 0;
+ the_display->xsync_event_base = 0;
+ }
+ else
+ the_display->have_xsync = TRUE;
+
+ meta_verbose ("Attempted to init Xsync, found version %d.%d error base %d event base %d\n",
+ major, minor,
+ the_display->xsync_error_base,
+ the_display->xsync_event_base);
+ }
+#else /* HAVE_XSYNC */
+ meta_verbose ("Not compiled with Xsync support\n");
+#endif /* !HAVE_XSYNC */
+
+
+#ifdef HAVE_SHAPE
+ {
+ the_display->have_shape = FALSE;
+
+ the_display->shape_error_base = 0;
+ the_display->shape_event_base = 0;
+
+ if (!XShapeQueryExtension (the_display->xdisplay,
+ &the_display->shape_event_base,
+ &the_display->shape_error_base))
+ {
+ the_display->shape_error_base = 0;
+ the_display->shape_event_base = 0;
+ }
+ else
+ the_display->have_shape = TRUE;
+
+ meta_verbose ("Attempted to init Shape, found error base %d event base %d\n",
+ the_display->shape_error_base,
+ the_display->shape_event_base);
+ }
+#else /* HAVE_SHAPE */
+ meta_verbose ("Not compiled with Shape support\n");
+#endif /* !HAVE_SHAPE */
+
+#ifdef HAVE_RENDER
+ {
+ the_display->have_render = FALSE;
+
+ the_display->render_error_base = 0;
+ the_display->render_event_base = 0;
+
+ if (!XRenderQueryExtension (the_display->xdisplay,
+ &the_display->render_event_base,
+ &the_display->render_error_base))
+ {
+ the_display->render_error_base = 0;
+ the_display->render_event_base = 0;
+ }
+ else
+ the_display->have_render = TRUE;
+
+ meta_verbose ("Attempted to init Render, found error base %d event base %d\n",
+ the_display->render_error_base,
+ the_display->render_event_base);
+ }
+#else /* HAVE_RENDER */
+ meta_verbose ("Not compiled with Render support\n");
+#endif /* !HAVE_RENDER */
+
+#ifdef HAVE_COMPOSITE_EXTENSIONS
+ {
+ the_display->have_composite = FALSE;
+
+ the_display->composite_error_base = 0;
+ the_display->composite_event_base = 0;
+
+ if (!XCompositeQueryExtension (the_display->xdisplay,
+ &the_display->composite_event_base,
+ &the_display->composite_error_base))
+ {
+ the_display->composite_error_base = 0;
+ the_display->composite_event_base = 0;
+ }
+ else
+ {
+ the_display->composite_major_version = 0;
+ the_display->composite_minor_version = 0;
+ if (XCompositeQueryVersion (the_display->xdisplay,
+ &the_display->composite_major_version,
+ &the_display->composite_minor_version))
+ {
+ the_display->have_composite = TRUE;
+ }
+ else
+ {
+ the_display->composite_major_version = 0;
+ the_display->composite_minor_version = 0;
+ }
+ }
+
+ meta_verbose ("Attempted to init Composite, found error base %d event base %d "
+ "extn ver %d %d\n",
+ the_display->composite_error_base,
+ the_display->composite_event_base,
+ the_display->composite_major_version,
+ the_display->composite_minor_version);
+
+ the_display->have_damage = FALSE;
+
+ the_display->damage_error_base = 0;
+ the_display->damage_event_base = 0;
+
+ if (!XDamageQueryExtension (the_display->xdisplay,
+ &the_display->damage_event_base,
+ &the_display->damage_error_base))
+ {
+ the_display->damage_error_base = 0;
+ the_display->damage_event_base = 0;
+ }
+ else
+ the_display->have_damage = TRUE;
+
+ meta_verbose ("Attempted to init Damage, found error base %d event base %d\n",
+ the_display->damage_error_base,
+ the_display->damage_event_base);
+
+ the_display->have_xfixes = FALSE;
+
+ the_display->xfixes_error_base = 0;
+ the_display->xfixes_event_base = 0;
+
+ if (!XFixesQueryExtension (the_display->xdisplay,
+ &the_display->xfixes_event_base,
+ &the_display->xfixes_error_base))
+ {
+ the_display->xfixes_error_base = 0;
+ the_display->xfixes_event_base = 0;
+ }
+ else
+ the_display->have_xfixes = TRUE;
+
+ meta_verbose ("Attempted to init XFixes, found error base %d event base %d\n",
+ the_display->xfixes_error_base,
+ the_display->xfixes_event_base);
+ }
+#else /* HAVE_COMPOSITE_EXTENSIONS */
+ meta_verbose ("Not compiled with Composite support\n");
+#endif /* !HAVE_COMPOSITE_EXTENSIONS */
+
+#ifdef HAVE_XCURSOR
+ {
+ XcursorSetTheme (the_display->xdisplay, meta_prefs_get_cursor_theme ());
+ XcursorSetDefaultSize (the_display->xdisplay, meta_prefs_get_cursor_size ());
+ }
+#else /* HAVE_XCURSOR */
+ meta_verbose ("Not compiled with Xcursor support\n");
+#endif /* !HAVE_XCURSOR */
+
+ /* Create the leader window here. Set its properties and
+ * use the timestamp from one of the PropertyNotify events
+ * that will follow.
+ */
+ {
+ gulong data[1];
+ XEvent event;
+
+ /* We only care about the PropertyChangeMask in the next 30 or so lines of
+ * code. Note that gdk will at some point unset the PropertyChangeMask for
+ * this window, so we can't rely on it still being set later. See bug
+ * 354213 for details.
+ */
+ the_display->leader_window =
+ meta_create_offscreen_window (the_display->xdisplay,
+ DefaultRootWindow (the_display->xdisplay),
+ PropertyChangeMask);
+
+ meta_prop_set_utf8_string_hint (the_display,
+ the_display->leader_window,
+ the_display->atom__NET_WM_NAME,
+ "Marco");
+
+ meta_prop_set_utf8_string_hint (the_display,
+ the_display->leader_window,
+ the_display->atom__MARCO_VERSION,
+ VERSION);
+
+ data[0] = the_display->leader_window;
+ XChangeProperty (the_display->xdisplay,
+ the_display->leader_window,
+ the_display->atom__NET_SUPPORTING_WM_CHECK,
+ XA_WINDOW,
+ 32, PropModeReplace, (guchar*) data, 1);
+
+ XWindowEvent (the_display->xdisplay,
+ the_display->leader_window,
+ PropertyChangeMask,
+ &event);
+
+ timestamp = event.xproperty.time;
+
+ /* Make it painfully clear that we can't rely on PropertyNotify events on
+ * this window, as per bug 354213.
+ */
+ XSelectInput(the_display->xdisplay,
+ the_display->leader_window,
+ NoEventMask);
+ }
+
+ /* Make a little window used only for pinging the server for timestamps; note
+ * that meta_create_offscreen_window already selects for PropertyChangeMask.
+ */
+ the_display->timestamp_pinging_window =
+ meta_create_offscreen_window (the_display->xdisplay,
+ DefaultRootWindow (the_display->xdisplay),
+ PropertyChangeMask);
+
+ the_display->last_focus_time = timestamp;
+ the_display->last_user_time = timestamp;
+ the_display->compositor = NULL;
+
+ screens = NULL;
+
+ i = 0;
+ while (i < ScreenCount (xdisplay))
+ {
+ MetaScreen *screen;
+
+ screen = meta_screen_new (the_display, i, timestamp);
+
+ if (screen)
+ screens = g_slist_prepend (screens, screen);
+ ++i;
+ }
+
+ the_display->screens = screens;
+
+ if (screens == NULL)
+ {
+ /* This would typically happen because all the screens already
+ * have window managers.
+ */
+ meta_display_close (the_display, timestamp);
+ return FALSE;
+ }
+
+ /* We don't composite the windows here because they will be composited
+ faster with the call to meta_screen_manage_all_windows further down
+ the code */
+ if (meta_prefs_get_compositing_manager ())
+ enable_compositor (the_display, FALSE);
+
+ meta_display_grab (the_display);
+
+ /* Now manage all existing windows */
+ tmp = the_display->screens;
+ while (tmp != NULL)
+ {
+ MetaScreen *screen = tmp->data;
+
+ meta_screen_manage_all_windows (screen);
+
+ tmp = tmp->next;
+ }
+
+ {
+ Window focus;
+ int ret_to;
+
+ /* kinda bogus because GetInputFocus has no possible errors */
+ meta_error_trap_push (the_display);
+
+ /* FIXME: This is totally broken; see comment 9 of bug 88194 about this */
+ focus = None;
+ ret_to = RevertToPointerRoot;
+ XGetInputFocus (the_display->xdisplay, &focus, &ret_to);
+
+ /* Force a new FocusIn (does this work?) */
+
+ /* Use the same timestamp that was passed to meta_screen_new(),
+ * as it is the most recent timestamp.
+ */
+ if (focus == None || focus == PointerRoot)
+ /* Just focus the no_focus_window on the first screen */
+ meta_display_focus_the_no_focus_window (the_display,
+ the_display->screens->data,
+ timestamp);
+ else
+ {
+ MetaWindow * window;
+ window = meta_display_lookup_x_window (the_display, focus);
+ if (window)
+ meta_display_set_input_focus_window (the_display, window, FALSE, timestamp);
+ else
+ /* Just focus the no_focus_window on the first screen */
+ meta_display_focus_the_no_focus_window (the_display,
+ the_display->screens->data,
+ timestamp);
+ }
+
+ meta_error_trap_pop (the_display, FALSE);
+ }
+
+ meta_display_ungrab (the_display);
+
+ /* Done opening new display */
+ the_display->display_opening = FALSE;
+
+ return TRUE;
+}
+
+static void
+listify_func (gpointer key, gpointer value, gpointer data)
+{
+ GSList **listp;
+
+ listp = data;
+ *listp = g_slist_prepend (*listp, value);
+}
+
+static gint
+ptrcmp (gconstpointer a, gconstpointer b)
+{
+ if (a < b)
+ return -1;
+ else if (a > b)
+ return 1;
+ else
+ return 0;
+}
+
+GSList*
+meta_display_list_windows (MetaDisplay *display)
+{
+ GSList *winlist;
+ GSList *tmp;
+ GSList *prev;
+
+ winlist = NULL;
+ g_hash_table_foreach (display->window_ids,
+ listify_func,
+ &winlist);
+
+ /* 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;
+ }
+
+ return winlist;
+}
+
+void
+meta_display_close (MetaDisplay *display,
+ guint32 timestamp)
+{
+ GSList *tmp;
+
+ g_assert (display != NULL);
+
+ if (display->closing != 0)
+ {
+ /* The display's already been closed. */
+ return;
+ }
+
+ if (display->error_traps > 0)
+ meta_bug ("Display closed with error traps pending\n");
+
+ display->closing += 1;
+
+ meta_prefs_remove_listener (prefs_changed_callback, display);
+
+ meta_display_remove_autoraise_callback (display);
+
+ if (display->grab_old_window_stacking)
+ g_list_free (display->grab_old_window_stacking);
+
+ /* Stop caring about events */
+ meta_ui_remove_event_func (display->xdisplay,
+ event_callback,
+ display);
+
+ /* Free all screens */
+ tmp = display->screens;
+ while (tmp != NULL)
+ {
+ MetaScreen *screen = tmp->data;
+ meta_screen_free (screen, timestamp);
+ tmp = tmp->next;
+ }
+
+ g_slist_free (display->screens);
+ display->screens = NULL;
+
+#ifdef HAVE_STARTUP_NOTIFICATION
+ if (display->sn_display)
+ {
+ sn_display_unref (display->sn_display);
+ display->sn_display = NULL;
+ }
+#endif
+
+ /* Must be after all calls to meta_window_free() since they
+ * unregister windows
+ */
+ g_hash_table_destroy (display->window_ids);
+
+ if (display->leader_window != None)
+ XDestroyWindow (display->xdisplay, display->leader_window);
+
+ XFlush (display->xdisplay);
+
+ meta_display_free_window_prop_hooks (display);
+ meta_display_free_group_prop_hooks (display);
+
+ g_free (display->name);
+
+ meta_display_shutdown_keys (display);
+
+ if (display->compositor)
+ meta_compositor_destroy (display->compositor);
+
+ g_free (display);
+ display = NULL;
+
+ meta_quit (META_EXIT_SUCCESS);
+}
+
+MetaScreen*
+meta_display_screen_for_root (MetaDisplay *display,
+ Window xroot)
+{
+ GSList *tmp;
+
+ tmp = display->screens;
+ while (tmp != NULL)
+ {
+ MetaScreen *screen = tmp->data;
+
+ if (xroot == screen->xroot)
+ return screen;
+
+ tmp = tmp->next;
+ }
+
+ return NULL;
+}
+
+MetaScreen*
+meta_display_screen_for_xwindow (MetaDisplay *display,
+ Window xwindow)
+{
+ XWindowAttributes attr;
+ int result;
+
+ meta_error_trap_push (display);
+ attr.screen = NULL;
+ result = XGetWindowAttributes (display->xdisplay, xwindow, &attr);
+ meta_error_trap_pop (display, TRUE);
+
+ /* Note, XGetWindowAttributes is on all kinds of crack
+ * and returns 1 on success 0 on failure, rather than Success
+ * on success.
+ */
+ if (result == 0 || attr.screen == NULL)
+ return NULL;
+
+ return meta_display_screen_for_x_screen (display, attr.screen);
+}
+
+MetaScreen*
+meta_display_screen_for_x_screen (MetaDisplay *display,
+ Screen *xscreen)
+{
+ GSList *tmp;
+
+ tmp = display->screens;
+ while (tmp != NULL)
+ {
+ MetaScreen *screen = tmp->data;
+
+ if (xscreen == screen->xscreen)
+ return screen;
+
+ tmp = tmp->next;
+ }
+
+ return NULL;
+}
+
+/* Grab/ungrab routines taken from fvwm */
+void
+meta_display_grab (MetaDisplay *display)
+{
+ if (display->server_grab_count == 0)
+ {
+ XGrabServer (display->xdisplay);
+ }
+ display->server_grab_count += 1;
+ meta_verbose ("Grabbing display, grab count now %d\n",
+ display->server_grab_count);
+}
+
+void
+meta_display_ungrab (MetaDisplay *display)
+{
+ if (display->server_grab_count == 0)
+ meta_bug ("Ungrabbed non-grabbed server\n");
+
+ display->server_grab_count -= 1;
+ if (display->server_grab_count == 0)
+ {
+ /* FIXME we want to purge all pending "queued" stuff
+ * at this point, such as window hide/show
+ */
+ XUngrabServer (display->xdisplay);
+ XFlush (display->xdisplay);
+ }
+
+ meta_verbose ("Ungrabbing display, grab count now %d\n",
+ display->server_grab_count);
+}
+
+/**
+ * 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.
+ *
+ * \param xdisplay An X display
+ * \return 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->xdisplay == xdisplay)
+ return the_display;
+
+ meta_warning ("Could not find display for X display %p, probably going to crash\n",
+ xdisplay);
+
+ return NULL;
+}
+
+/**
+ * Accessor for the singleton MetaDisplay.
+ *
+ * \return The only MetaDisplay there is. This can be NULL, but only
+ * during startup.
+ */
+MetaDisplay*
+meta_get_display (void)
+{
+ return the_display;
+}
+
+#ifdef WITH_VERBOSE_MODE
+static gboolean dump_events = TRUE;
+#endif
+
+static gboolean
+grab_op_is_mouse_only (MetaGrabOp op)
+{
+ switch (op)
+ {
+ case META_GRAB_OP_MOVING:
+ case META_GRAB_OP_RESIZING_SE:
+ case META_GRAB_OP_RESIZING_S:
+ case META_GRAB_OP_RESIZING_SW:
+ case META_GRAB_OP_RESIZING_N:
+ case META_GRAB_OP_RESIZING_NE:
+ case META_GRAB_OP_RESIZING_NW:
+ case META_GRAB_OP_RESIZING_W:
+ case META_GRAB_OP_RESIZING_E:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+static gboolean
+grab_op_is_mouse (MetaGrabOp op)
+{
+ switch (op)
+ {
+ case META_GRAB_OP_MOVING:
+ case META_GRAB_OP_RESIZING_SE:
+ case META_GRAB_OP_RESIZING_S:
+ case META_GRAB_OP_RESIZING_SW:
+ case META_GRAB_OP_RESIZING_N:
+ case META_GRAB_OP_RESIZING_NE:
+ case META_GRAB_OP_RESIZING_NW:
+ case META_GRAB_OP_RESIZING_W:
+ case META_GRAB_OP_RESIZING_E:
+ case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
+ case META_GRAB_OP_KEYBOARD_RESIZING_S:
+ case META_GRAB_OP_KEYBOARD_RESIZING_N:
+ case META_GRAB_OP_KEYBOARD_RESIZING_W:
+ case META_GRAB_OP_KEYBOARD_RESIZING_E:
+ 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:
+ case META_GRAB_OP_KEYBOARD_MOVING:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+static gboolean
+grab_op_is_keyboard (MetaGrabOp op)
+{
+ switch (op)
+ {
+ case META_GRAB_OP_KEYBOARD_MOVING:
+ case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
+ case META_GRAB_OP_KEYBOARD_RESIZING_S:
+ case META_GRAB_OP_KEYBOARD_RESIZING_N:
+ case META_GRAB_OP_KEYBOARD_RESIZING_W:
+ case META_GRAB_OP_KEYBOARD_RESIZING_E:
+ 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:
+ case META_GRAB_OP_KEYBOARD_TABBING_NORMAL:
+ case META_GRAB_OP_KEYBOARD_TABBING_DOCK:
+ case META_GRAB_OP_KEYBOARD_TABBING_GROUP:
+ case META_GRAB_OP_KEYBOARD_ESCAPING_NORMAL:
+ case META_GRAB_OP_KEYBOARD_ESCAPING_DOCK:
+ case META_GRAB_OP_KEYBOARD_ESCAPING_GROUP:
+ case META_GRAB_OP_KEYBOARD_WORKSPACE_SWITCHING:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+gboolean
+meta_grab_op_is_resizing (MetaGrabOp op)
+{
+ switch (op)
+ {
+ case META_GRAB_OP_RESIZING_SE:
+ case META_GRAB_OP_RESIZING_S:
+ case META_GRAB_OP_RESIZING_SW:
+ case META_GRAB_OP_RESIZING_N:
+ case META_GRAB_OP_RESIZING_NE:
+ case META_GRAB_OP_RESIZING_NW:
+ case META_GRAB_OP_RESIZING_W:
+ case META_GRAB_OP_RESIZING_E:
+ case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
+ case META_GRAB_OP_KEYBOARD_RESIZING_S:
+ case META_GRAB_OP_KEYBOARD_RESIZING_N:
+ case META_GRAB_OP_KEYBOARD_RESIZING_W:
+ case META_GRAB_OP_KEYBOARD_RESIZING_E:
+ 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:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+gboolean
+meta_grab_op_is_moving (MetaGrabOp op)
+{
+ switch (op)
+ {
+ case META_GRAB_OP_MOVING:
+ case META_GRAB_OP_KEYBOARD_MOVING:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+/* Get time of current event, or CurrentTime if none. */
+guint32
+meta_display_get_current_time (MetaDisplay *display)
+{
+ return display->current_time;
+}
+
+/* Get a timestamp, even if it means a roundtrip */
+guint32
+meta_display_get_current_time_roundtrip (MetaDisplay *display)
+{
+ guint32 timestamp;
+
+ timestamp = meta_display_get_current_time (display);
+ if (timestamp == CurrentTime)
+ {
+ XEvent property_event;
+
+ /* Using the property XA_PRIMARY because it's safe; nothing
+ * would use it as a property. The type doesn't matter.
+ */
+ XChangeProperty (display->xdisplay,
+ display->timestamp_pinging_window,
+ XA_PRIMARY, XA_STRING, 8,
+ PropModeAppend, NULL, 0);
+ XWindowEvent (display->xdisplay,
+ display->timestamp_pinging_window,
+ PropertyChangeMask,
+ &property_event);
+ timestamp = property_event.xproperty.time;
+ }
+
+ sanity_check_timestamps (display, timestamp);
+
+ return timestamp;
+}
+
+static void
+add_ignored_serial (MetaDisplay *display,
+ unsigned long serial)
+{
+ int i;
+
+ /* don't add the same serial more than once */
+ if (display->ignored_serials[N_IGNORED_SERIALS-1] == serial)
+ return;
+
+ /* shift serials to the left */
+ i = 0;
+ while (i < (N_IGNORED_SERIALS - 1))
+ {
+ display->ignored_serials[i] = display->ignored_serials[i+1];
+ ++i;
+ }
+ /* put new one on the end */
+ display->ignored_serials[i] = serial;
+}
+
+static gboolean
+serial_is_ignored (MetaDisplay *display,
+ unsigned long serial)
+{
+ int i;
+
+ i = 0;
+ while (i < N_IGNORED_SERIALS)
+ {
+ if (display->ignored_serials[i] == serial)
+ return TRUE;
+ ++i;
+ }
+ return FALSE;
+}
+
+static void
+reset_ignores (MetaDisplay *display)
+{
+ int i;
+
+ i = 0;
+ while (i < N_IGNORED_SERIALS)
+ {
+ display->ignored_serials[i] = 0;
+ ++i;
+ }
+
+ display->ungrab_should_not_cause_focus_window = None;
+}
+
+static gboolean
+window_raise_with_delay_callback (void *data)
+{
+ MetaWindow *window;
+ MetaAutoRaiseData *auto_raise;
+
+ auto_raise = data;
+
+ meta_topic (META_DEBUG_FOCUS,
+ "In autoraise callback for window 0x%lx\n",
+ auto_raise->xwindow);
+
+ auto_raise->display->autoraise_timeout_id = 0;
+ auto_raise->display->autoraise_window = NULL;
+
+ window = meta_display_lookup_x_window (auto_raise->display,
+ auto_raise->xwindow);
+
+ if (window == NULL)
+ return FALSE;
+
+ /* 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->screen->stack) != window)
+ {
+ int x, y, root_x, root_y;
+ Window root, child;
+ unsigned int mask;
+ gboolean same_screen;
+ gboolean point_in_window;
+
+ meta_error_trap_push (window->display);
+ same_screen = XQueryPointer (window->display->xdisplay,
+ window->xwindow,
+ &root, &child,
+ &root_x, &root_y, &x, &y, &mask);
+ meta_error_trap_pop (window->display, TRUE);
+
+ point_in_window =
+ (window->frame && POINT_IN_RECT (root_x, root_y, window->frame->rect)) ||
+ (window->frame == NULL && POINT_IN_RECT (root_x, root_y, window->rect));
+ if (same_screen && point_in_window)
+ meta_window_raise (window);
+ else
+ meta_topic (META_DEBUG_FOCUS,
+ "Pointer not inside window, not raising %s\n",
+ window->desc);
+ }
+
+ return FALSE;
+}
+
+void
+meta_display_queue_autoraise_callback (MetaDisplay *display,
+ MetaWindow *window)
+{
+ MetaAutoRaiseData *auto_raise_data;
+
+ meta_topic (META_DEBUG_FOCUS,
+ "Queuing an autoraise timeout for %s with delay %d\n",
+ window->desc,
+ meta_prefs_get_auto_raise_delay ());
+
+ auto_raise_data = g_new (MetaAutoRaiseData, 1);
+ auto_raise_data->display = window->display;
+ auto_raise_data->xwindow = window->xwindow;
+
+ if (display->autoraise_timeout_id != 0)
+ g_source_remove (display->autoraise_timeout_id);
+
+ display->autoraise_timeout_id =
+ g_timeout_add_full (G_PRIORITY_DEFAULT,
+ meta_prefs_get_auto_raise_delay (),
+ window_raise_with_delay_callback,
+ auto_raise_data,
+ g_free);
+ display->autoraise_window = window;
+}
+
+#if 0
+static void
+handle_net_restack_window (MetaDisplay* display,
+ XEvent *event)
+{
+ MetaWindow *window;
+
+ window = meta_display_lookup_x_window (display,
+ event->xclient.window);
+
+ if (window)
+ {
+ /* FIXME: The EWMH includes a sibling for the restack request, but we
+ * (stupidly) don't currently support these types of raises.
+ *
+ * Also, unconditionally following these is REALLY stupid--we should
+ * combine this code with the stuff in
+ * meta_window_configure_request() which is smart about whether to
+ * follow the request or do something else (though not smart enough
+ * and is also too stupid to handle the sibling stuff).
+ */
+ switch (event->xclient.data.l[2])
+ {
+ case Above:
+ meta_window_raise (window);
+ break;
+ case Below:
+ meta_window_lower (window);
+ break;
+ case TopIf:
+ case BottomIf:
+ case Opposite:
+ break;
+ }
+ }
+}
+#endif
+
+/* We do some of our event handling in core/frames.c, which expects
+ * GDK events delivered by GTK+. However, since the transition to
+ * client side windows, we can't let GDK see button events, since the
+ * client-side tracking of implicit and explicit grabs it does will
+ * get confused by our direct use of X grabs.
+ *
+ * So we do a very minimal GDK => GTK event conversion here and send on the
+ * events we care about, and then filter them out so they don't go
+ * through the normal GDK event handling.
+ *
+ * To reduce the amount of code, the only events fields filled out
+ * below are the ones that frames.c uses. If frames.c is modified to
+ * use more fields, more fields need to be filled out below.
+ *
+ * https://github.com/stefano-k/Mate-Desktop-Environment/commit/b0e5fb03eb21dae8f02692f11ef391bfc5ccba33
+ */
+
+static gboolean maybe_send_event_to_gtk(MetaDisplay* display, XEvent* xevent)
+{
+ /* We're always using the default display */
+ GdkDisplay* gdk_display = gdk_display_get_default();
+ GdkEvent gdk_event;
+ GdkWindow* gdk_window;
+ Window window;
+
+ switch (xevent->type)
+ {
+ case ButtonPress:
+ case ButtonRelease:
+ window = xevent->xbutton.window;
+ break;
+
+ case MotionNotify:
+ window = xevent->xmotion.window;
+ break;
+
+ case EnterNotify:
+
+ case LeaveNotify:
+ window = xevent->xcrossing.window;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ gdk_window = gdk_window_lookup_for_display(gdk_display, window);
+
+ if (gdk_window == NULL)
+ {
+ return FALSE;
+ }
+
+ /* If GDK already things it has a grab, we better let it see events; this
+ * is the menu-navigation case and events need to get sent to the appropriate
+ * (client-side) subwindow for individual menu items.
+ */
+
+ if (gdk_display_pointer_is_grabbed(gdk_display))
+ {
+ return FALSE;
+ }
+
+ memset(&gdk_event, 0, sizeof(gdk_event));
+
+ switch (xevent->type)
+ {
+
+ case ButtonPress:
+
+ case ButtonRelease:
+
+ if (xevent->type == ButtonPress)
+ {
+ GtkSettings* settings = gtk_settings_get_default();
+
+ int double_click_time;
+ int double_click_distance;
+
+ g_object_get (settings,
+ "gtk-double-click-time", &double_click_time,
+ "gtk-double-click-distance", &double_click_distance,
+ NULL);
+
+ if (xevent->xbutton.button == display->button_click_number &&
+ xevent->xbutton.window == display->button_click_window &&
+ xevent->xbutton.time < display->button_click_time + double_click_time &&
+ ABS(xevent->xbutton.x - display->button_click_x) <= double_click_distance &&
+ ABS (xevent->xbutton.y - display->button_click_y) <= double_click_distance)
+ {
+
+ gdk_event.button.type = GDK_2BUTTON_PRESS;
+ display->button_click_number = 0;
+ }
+ else
+ {
+ gdk_event.button.type = GDK_BUTTON_PRESS;
+ display->button_click_number = xevent->xbutton.button;
+ display->button_click_window = xevent->xbutton.window;
+ display->button_click_time = xevent->xbutton.time;
+ display->button_click_x = xevent->xbutton.x;
+ display->button_click_y = xevent->xbutton.y;
+ }
+ }
+ else
+ {
+ gdk_event.button.type = GDK_BUTTON_RELEASE;
+ }
+
+ gdk_event.button.window = gdk_window;
+ gdk_event.button.button = xevent->xbutton.button;
+ gdk_event.button.time = xevent->xbutton.time;
+ gdk_event.button.x = xevent->xbutton.x;
+ gdk_event.button.y = xevent->xbutton.y;
+ gdk_event.button.x_root = xevent->xbutton.x_root;
+ gdk_event.button.y_root = xevent->xbutton.y_root;
+
+ break;
+
+ case MotionNotify:
+ gdk_event.motion.type = GDK_MOTION_NOTIFY;
+ gdk_event.motion.window = gdk_window;
+ break;
+
+ case EnterNotify:
+
+ case LeaveNotify:
+ gdk_event.crossing.type = xevent->type == EnterNotify ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
+ gdk_event.crossing.window = gdk_window;
+ gdk_event.crossing.x = xevent->xcrossing.x;
+ gdk_event.crossing.y = xevent->xcrossing.y;
+ break;
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+
+ /* If we've gotten here, we've filled in the gdk_event and should send it on */
+ gtk_main_do_event(&gdk_event);
+ return TRUE;
+}
+
+/**
+ * This is the most important function in the whole program. It is the heart,
+ * it is the nexus, it is the Grand Central Station of Marco's world.
+ * When we create a MetaDisplay, we ask GDK to pass *all* events for *all*
+ * windows to this function. So every time anything happens that we might
+ * want to know about, this function gets called. You see why it gets a bit
+ * busy around here. Most of this function is a ginormous switch statement
+ * dealing with all the kinds of events that might turn up.
+ *
+ * \param event The event that just happened
+ * \param data The MetaDisplay that events are coming from, cast to a gpointer
+ * so that it can be sent to a callback
+ *
+ * \ingroup main
+ */
+static gboolean event_callback(XEvent* event, gpointer data)
+{
+ MetaWindow *window;
+ MetaWindow *property_for_window;
+ MetaDisplay *display;
+ Window modified;
+ gboolean frame_was_receiver;
+ gboolean filter_out_event;
+
+ display = data;
+
+#ifdef WITH_VERBOSE_MODE
+ if (dump_events)
+ meta_spew_event (display, event);
+#endif
+
+#ifdef HAVE_STARTUP_NOTIFICATION
+ sn_display_process_event (display->sn_display, event);
+#endif
+
+ filter_out_event = FALSE;
+ display->current_time = event_get_time (display, event);
+ display->xinerama_cache_invalidated = TRUE;
+
+ modified = event_get_modified_window (display, event);
+
+ if (event->type == ButtonPress)
+ {
+ /* filter out scrollwheel */
+ if (event->xbutton.button == 4 ||
+ event->xbutton.button == 5)
+ return FALSE;
+ }
+ else if (event->type == UnmapNotify)
+ {
+ if (meta_ui_window_should_not_cause_focus (display->xdisplay,
+ modified))
+ {
+ add_ignored_serial (display, event->xany.serial);
+ meta_topic (META_DEBUG_FOCUS,
+ "Adding EnterNotify serial %lu to ignored focus serials\n",
+ event->xany.serial);
+ }
+ }
+ else if (event->type == LeaveNotify &&
+ event->xcrossing.mode == NotifyUngrab &&
+ modified == display->ungrab_should_not_cause_focus_window)
+ {
+ add_ignored_serial (display, event->xany.serial);
+ meta_topic (META_DEBUG_FOCUS,
+ "Adding LeaveNotify serial %lu to ignored focus serials\n",
+ event->xany.serial);
+ }
+
+ if (modified != None)
+ window = meta_display_lookup_x_window (display, modified);
+ else
+ window = NULL;
+
+ /* We only want to respond to _NET_WM_USER_TIME property notify
+ * events on _NET_WM_USER_TIME_WINDOW windows; in particular,
+ * responding to UnmapNotify events is kind of bad.
+ */
+ property_for_window = NULL;
+ if (window && modified == window->user_time_window)
+ {
+ property_for_window = window;
+ window = NULL;
+ }
+
+
+ frame_was_receiver = FALSE;
+ if (window &&
+ window->frame &&
+ modified == window->frame->xwindow)
+ {
+ /* Note that if the frame and the client both have an
+ * XGrabButton (as is normal with our setup), the event
+ * goes to the frame.
+ */
+ frame_was_receiver = TRUE;
+ meta_topic (META_DEBUG_EVENTS, "Frame was receiver of event for %s\n",
+ window->desc);
+ }
+
+#ifdef HAVE_XSYNC
+ if (META_DISPLAY_HAS_XSYNC (display) &&
+ event->type == (display->xsync_event_base + XSyncAlarmNotify) &&
+ ((XSyncAlarmNotifyEvent*)event)->alarm == display->grab_sync_request_alarm)
+ {
+ filter_out_event = TRUE; /* GTK doesn't want to see this really */
+
+ if (display->grab_op != META_GRAB_OP_NONE &&
+ display->grab_window != NULL &&
+ grab_op_is_mouse (display->grab_op))
+ meta_window_handle_mouse_grab_op_event (display->grab_window, event);
+ }
+#endif /* HAVE_XSYNC */
+
+#ifdef HAVE_SHAPE
+ if (META_DISPLAY_HAS_SHAPE (display) &&
+ event->type == (display->shape_event_base + ShapeNotify))
+ {
+ filter_out_event = TRUE; /* GTK doesn't want to see this really */
+
+ if (window && !frame_was_receiver)
+ {
+ XShapeEvent *sev = (XShapeEvent*) event;
+
+ if (sev->kind == ShapeBounding)
+ {
+ if (sev->shaped && !window->has_shape)
+ {
+ window->has_shape = TRUE;
+ meta_topic (META_DEBUG_SHAPES,
+ "Window %s now has a shape\n",
+ window->desc);
+ }
+ else if (!sev->shaped && window->has_shape)
+ {
+ window->has_shape = FALSE;
+ meta_topic (META_DEBUG_SHAPES,
+ "Window %s no longer has a shape\n",
+ window->desc);
+ }
+ else
+ {
+ meta_topic (META_DEBUG_SHAPES,
+ "Window %s shape changed\n",
+ window->desc);
+ }
+
+ if (window->frame)
+ {
+ window->frame->need_reapply_frame_shape = TRUE;
+ meta_warning("from event callback\n");
+ meta_window_queue (window, META_QUEUE_MOVE_RESIZE);
+ }
+ }
+ }
+ else
+ {
+ meta_topic (META_DEBUG_SHAPES,
+ "ShapeNotify not on a client window (window %s frame_was_receiver = %d)\n",
+ window ? window->desc : "(none)",
+ frame_was_receiver);
+ }
+ }
+#endif /* HAVE_SHAPE */
+
+ if (window && ((event->type == KeyPress) || (event->type == ButtonPress)))
+ {
+ if (CurrentTime == 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.\n");
+ }
+ else
+ {
+ meta_window_set_user_time (window, display->current_time);
+ sanity_check_timestamps (display, display->current_time);
+ }
+ }
+
+ switch (event->type)
+ {
+ case KeyPress:
+ case KeyRelease:
+ meta_display_process_key_event (display, window, event);
+ break;
+ case ButtonPress:
+ if ((window &&
+ grab_op_is_mouse (display->grab_op) &&
+ display->grab_button != (int) event->xbutton.button &&
+ display->grab_window == window) ||
+ grab_op_is_keyboard (display->grab_op))
+ {
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "Ending grab op %u on window %s due to button press\n",
+ display->grab_op,
+ (display->grab_window ?
+ display->grab_window->desc :
+ "none"));
+ if (GRAB_OP_IS_WINDOW_SWITCH (display->grab_op))
+ {
+ MetaScreen *screen;
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "Syncing to old stack positions.\n");
+ screen =
+ meta_display_screen_for_root (display, event->xany.window);
+
+ if (screen!=NULL)
+ meta_stack_set_positions (screen->stack,
+ display->grab_old_window_stacking);
+ }
+ meta_display_end_grab_op (display,
+ event->xbutton.time);
+ }
+ else if (window && display->grab_op == META_GRAB_OP_NONE)
+ {
+ gboolean begin_move = FALSE;
+ unsigned int grab_mask;
+ gboolean unmodified;
+
+ grab_mask = display->window_grab_modifiers;
+ if (g_getenv ("MARCO_DEBUG_BUTTON_GRABS"))
+ grab_mask |= ControlMask;
+
+ /* Two possible sources of an unmodified event; one is a
+ * client that's letting button presses pass through to the
+ * frame, the other is our focus_window_grab on unmodified
+ * button 1. So for all such events we focus the window.
+ */
+ unmodified = (event->xbutton.state & grab_mask) == 0;
+
+ if (unmodified ||
+ event->xbutton.button == 1)
+ {
+ /* don't focus if frame received, will be lowered in
+ * frames.c or special-cased if the click was on a
+ * minimize/close button.
+ */
+ if (!frame_was_receiver)
+ {
+ 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\n");
+
+ /* 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 unmodified button %u press (display.c)\n",
+ window->desc, event->xbutton.button);
+ meta_window_focus (window, event->xbutton.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;
+ }
+
+ /* you can move on alt-click but not on
+ * the click-to-focus
+ */
+ if (!unmodified)
+ begin_move = TRUE;
+ }
+ else if (!unmodified && event->xbutton.button == meta_prefs_get_mouse_button_resize())
+ {
+ if (window->has_resize_func)
+ {
+ gboolean north, south;
+ gboolean west, east;
+ int root_x, root_y;
+ MetaGrabOp op;
+
+ meta_window_get_position (window, &root_x, &root_y);
+
+ west = event->xbutton.x_root < (root_x + 1 * window->rect.width / 3);
+ east = event->xbutton.x_root > (root_x + 2 * window->rect.width / 3);
+ north = event->xbutton.y_root < (root_y + 1 * window->rect.height / 3);
+ south = event->xbutton.y_root > (root_y + 2 * window->rect.height / 3);
+
+ if (north && west)
+ op = META_GRAB_OP_RESIZING_NW;
+ else if (north && east)
+ op = META_GRAB_OP_RESIZING_NE;
+ else if (south && west)
+ op = META_GRAB_OP_RESIZING_SW;
+ else if (south && east)
+ op = META_GRAB_OP_RESIZING_SE;
+ else if (north)
+ op = META_GRAB_OP_RESIZING_N;
+ else if (west)
+ op = META_GRAB_OP_RESIZING_W;
+ else if (east)
+ op = META_GRAB_OP_RESIZING_E;
+ else if (south)
+ op = META_GRAB_OP_RESIZING_S;
+ else /* Middle region is no-op to avoid user triggering wrong action */
+ op = META_GRAB_OP_NONE;
+
+ if (op != META_GRAB_OP_NONE)
+ meta_display_begin_grab_op (display,
+ window->screen,
+ window,
+ op,
+ TRUE,
+ FALSE,
+ event->xbutton.button,
+ 0,
+ event->xbutton.time,
+ event->xbutton.x_root,
+ event->xbutton.y_root);
+ }
+ }
+ else if (event->xbutton.button == meta_prefs_get_mouse_button_menu())
+ {
+ if (meta_prefs_get_raise_on_click ())
+ meta_window_raise (window);
+ meta_window_show_menu (window,
+ event->xbutton.x_root,
+ event->xbutton.y_root,
+ event->xbutton.button,
+ event->xbutton.time);
+ }
+
+ if (!frame_was_receiver && unmodified)
+ {
+ /* This is from our synchronous grab since
+ * it has no modifiers and was on the client window
+ */
+ int mode;
+
+ /* When clicking a different app in click-to-focus
+ * in application-based mode, and the different
+ * app is not a dock or desktop, eat the focus click.
+ */
+ if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK &&
+ meta_prefs_get_application_based () &&
+ !window->has_focus &&
+ window->type != META_WINDOW_DOCK &&
+ window->type != META_WINDOW_DESKTOP &&
+ (display->focus_window == NULL ||
+ !meta_window_same_application (window,
+ display->focus_window)))
+ mode = AsyncPointer; /* eat focus click */
+ else
+ mode = ReplayPointer; /* give event back */
+
+ meta_verbose ("Allowing events mode %s time %u\n",
+ mode == AsyncPointer ? "AsyncPointer" : "ReplayPointer",
+ (unsigned int)event->xbutton.time);
+
+ XAllowEvents (display->xdisplay,
+ mode, event->xbutton.time);
+ }
+
+ if (begin_move && window->has_move_func)
+ {
+ meta_display_begin_grab_op (display,
+ window->screen,
+ window,
+ META_GRAB_OP_MOVING,
+ TRUE,
+ FALSE,
+ event->xbutton.button,
+ 0,
+ event->xbutton.time,
+ event->xbutton.x_root,
+ event->xbutton.y_root);
+ }
+ }
+ break;
+ case ButtonRelease:
+ if (display->grab_window == window &&
+ grab_op_is_mouse (display->grab_op))
+ meta_window_handle_mouse_grab_op_event (window, event);
+ break;
+ case MotionNotify:
+ if (display->grab_window == window &&
+ grab_op_is_mouse (display->grab_op))
+ meta_window_handle_mouse_grab_op_event (window, event);
+ break;
+ case EnterNotify:
+ if (display->grab_window == window &&
+ grab_op_is_mouse (display->grab_op))
+ {
+ meta_window_handle_mouse_grab_op_event (window, event);
+ break;
+ }
+
+ /* If the mouse switches screens, active the default window on the new
+ * screen; this will make keybindings and workspace-launched items
+ * actually appear on the right screen.
+ */
+ {
+ MetaScreen *new_screen =
+ meta_display_screen_for_root (display, event->xcrossing.root);
+
+ if (new_screen != NULL && display->active_screen != new_screen)
+ meta_workspace_focus_default_window (new_screen->active_workspace,
+ NULL,
+ event->xcrossing.time);
+ }
+
+ /* Check if we've entered a window; do this even if window->has_focus to
+ * avoid races.
+ */
+ if (window && !serial_is_ignored (display, event->xany.serial) &&
+ event->xcrossing.mode != NotifyGrab &&
+ event->xcrossing.mode != NotifyUngrab &&
+ event->xcrossing.detail != NotifyInferior &&
+ meta_display_focus_sentinel_clear (display))
+ {
+ switch (meta_prefs_get_focus_mode ())
+ {
+ case META_FOCUS_MODE_SLOPPY:
+ case META_FOCUS_MODE_MOUSE:
+ display->mouse_mode = TRUE;
+ if (window->type != META_WINDOW_DOCK &&
+ window->type != META_WINDOW_DESKTOP)
+ {
+ meta_topic (META_DEBUG_FOCUS,
+ "Focusing %s due to enter notify with serial %lu "
+ "at time %lu, and setting display->mouse_mode to "
+ "TRUE.\n",
+ window->desc,
+ event->xany.serial,
+ event->xcrossing.time);
+
+ meta_window_focus (window, event->xcrossing.time);
+
+ /* stop ignoring stuff */
+ reset_ignores (display);
+
+ if (meta_prefs_get_auto_raise ())
+ {
+ meta_display_queue_autoraise_callback (display, window);
+ }
+ else
+ {
+ meta_topic (META_DEBUG_FOCUS,
+ "Auto raise is disabled\n");
+ }
+ }
+ /* 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 (window->type == META_WINDOW_DESKTOP &&
+ meta_prefs_get_focus_mode() == META_FOCUS_MODE_MOUSE &&
+ display->expected_focus_window != NULL)
+ {
+ meta_topic (META_DEBUG_FOCUS,
+ "Unsetting focus from %s due to mouse entering "
+ "the DESKTOP window\n",
+ display->expected_focus_window->desc);
+ meta_display_focus_the_no_focus_window (display,
+ window->screen,
+ event->xcrossing.time);
+ }
+ break;
+ case META_FOCUS_MODE_CLICK:
+ break;
+ }
+
+ if (window->type == META_WINDOW_DOCK)
+ meta_window_raise (window);
+ }
+ break;
+ case LeaveNotify:
+ if (display->grab_window == window &&
+ grab_op_is_mouse (display->grab_op))
+ meta_window_handle_mouse_grab_op_event (window, event);
+ else if (window != NULL)
+ {
+ if (window->type == META_WINDOW_DOCK &&
+ event->xcrossing.mode != NotifyGrab &&
+ event->xcrossing.mode != NotifyUngrab &&
+ !window->has_focus)
+ meta_window_lower (window);
+ }
+ break;
+ case FocusIn:
+ case FocusOut:
+ if (window)
+ {
+ meta_window_notify_focus (window, event);
+ }
+ else if (meta_display_xwindow_is_a_no_focus_window (display,
+ event->xany.window))
+ {
+ meta_topic (META_DEBUG_FOCUS,
+ "Focus %s event received on no_focus_window 0x%lx "
+ "mode %s detail %s\n",
+ event->type == FocusIn ? "in" :
+ event->type == FocusOut ? "out" :
+ "???",
+ event->xany.window,
+ meta_event_mode_to_string (event->xfocus.mode),
+ meta_event_detail_to_string (event->xfocus.detail));
+ }
+ else
+ {
+ MetaScreen *screen =
+ meta_display_screen_for_root(display,
+ event->xany.window);
+ if (screen == NULL)
+ break;
+
+ meta_topic (META_DEBUG_FOCUS,
+ "Focus %s event received on root window 0x%lx "
+ "mode %s detail %s\n",
+ event->type == FocusIn ? "in" :
+ event->type == FocusOut ? "out" :
+ "???",
+ event->xany.window,
+ meta_event_mode_to_string (event->xfocus.mode),
+ meta_event_detail_to_string (event->xfocus.detail));
+
+ if (event->type == FocusIn &&
+ event->xfocus.detail == NotifyDetailNone)
+ {
+ meta_topic (META_DEBUG_FOCUS,
+ "Focus got set to None, probably due to "
+ "brain-damage in the X protocol (see bug "
+ "125492). Setting the default focus window.\n");
+ meta_workspace_focus_default_window (screen->active_workspace,
+ NULL,
+ meta_display_get_current_time_roundtrip (display));
+ }
+ else if (event->type == FocusIn &&
+ event->xfocus.mode == NotifyNormal &&
+ event->xfocus.detail == NotifyInferior)
+ {
+ meta_topic (META_DEBUG_FOCUS,
+ "Focus got set to root window, probably due to "
+ "mate-session logout dialog usage (see bug "
+ "153220). Setting the default focus window.\n");
+ meta_workspace_focus_default_window (screen->active_workspace,
+ NULL,
+ meta_display_get_current_time_roundtrip (display));
+ }
+
+ }
+ break;
+ case KeymapNotify:
+ break;
+ case Expose:
+ break;
+ case GraphicsExpose:
+ break;
+ case NoExpose:
+ break;
+ case VisibilityNotify:
+ break;
+ case CreateNotify:
+ break;
+
+ case DestroyNotify:
+ if (window)
+ {
+ /* FIXME: It sucks that DestroyNotify events don't come with
+ * a timestamp; could we do something better here? Maybe X
+ * will change one day?
+ */
+ guint32 timestamp;
+ timestamp = meta_display_get_current_time_roundtrip (display);
+
+ if (display->grab_op != META_GRAB_OP_NONE &&
+ display->grab_window == window)
+ meta_display_end_grab_op (display, timestamp);
+
+ if (frame_was_receiver)
+ {
+ meta_warning ("Unexpected destruction of frame 0x%lx, not sure if this should silently fail or be considered a bug\n",
+ window->frame->xwindow);
+ meta_error_trap_push (display);
+ meta_window_destroy_frame (window->frame->window);
+ meta_error_trap_pop (display, FALSE);
+ }
+ else
+ {
+ /* Unmanage destroyed window */
+ meta_window_free (window, timestamp);
+ window = NULL;
+ }
+ }
+ break;
+ case UnmapNotify:
+ if (window)
+ {
+ /* FIXME: It sucks that UnmapNotify events don't come with
+ * a timestamp; could we do something better here? Maybe X
+ * will change one day?
+ */
+ guint32 timestamp;
+ timestamp = meta_display_get_current_time_roundtrip (display);
+
+ if (display->grab_op != META_GRAB_OP_NONE &&
+ display->grab_window == window &&
+ ((window->frame == NULL) || !window->frame->mapped))
+ meta_display_end_grab_op (display, timestamp);
+
+ if (!frame_was_receiver)
+ {
+ if (window->unmaps_pending == 0)
+ {
+ meta_topic (META_DEBUG_WINDOW_STATE,
+ "Window %s withdrawn\n",
+ window->desc);
+
+ meta_effect_run_close (window, NULL, NULL);
+
+ /* Unmanage withdrawn window */
+ window->withdrawn = TRUE;
+ meta_window_free (window, timestamp);
+ window = NULL;
+ }
+ else
+ {
+ window->unmaps_pending -= 1;
+ meta_topic (META_DEBUG_WINDOW_STATE,
+ "Received pending unmap, %d now pending\n",
+ window->unmaps_pending);
+ }
+ }
+
+ /* Unfocus on UnmapNotify, do this after the possible
+ * window_free above so that window_free can see if window->has_focus
+ * and move focus to another window
+ */
+ if (window)
+ meta_window_notify_focus (window, event);
+ }
+ break;
+ case MapNotify:
+ break;
+ case MapRequest:
+ if (window == NULL)
+ {
+ window = meta_window_new (display, event->xmaprequest.window,
+ FALSE);
+ }
+ /* if frame was receiver it's some malicious send event or something */
+ else if (!frame_was_receiver && window)
+ {
+ meta_verbose ("MapRequest on %s mapped = %d minimized = %d\n",
+ window->desc, window->mapped, window->minimized);
+ if (window->minimized)
+ {
+ meta_window_unminimize (window);
+ if (window->workspace != window->screen->active_workspace)
+ {
+ meta_verbose ("Changing workspace due to MapRequest mapped = %d minimized = %d\n",
+ window->mapped, window->minimized);
+ meta_window_change_workspace (window,
+ window->screen->active_workspace);
+ }
+ }
+ }
+ break;
+ case ReparentNotify:
+ break;
+ case ConfigureNotify:
+ /* Handle screen resize */
+ {
+ MetaScreen *screen;
+
+ screen = meta_display_screen_for_root (display,
+ event->xconfigure.window);
+
+ if (screen != NULL)
+ {
+#ifdef HAVE_RANDR
+ /* do the resize the official way */
+ XRRUpdateConfiguration (event);
+#else
+ /* poke around in Xlib */
+ screen->xscreen->width = event->xconfigure.width;
+ screen->xscreen->height = event->xconfigure.height;
+#endif
+
+ meta_screen_resize (screen,
+ event->xconfigure.width,
+ event->xconfigure.height);
+ }
+ }
+ break;
+ case ConfigureRequest:
+ /* This comment and code is found in both twm and fvwm */
+ /*
+ * According to the July 27, 1988 ICCCM draft, we should ignore size and
+ * position fields in the WM_NORMAL_HINTS property when we map a window.
+ * Instead, we'll read the current geometry. Therefore, we should respond
+ * to configuration requests for windows which have never been mapped.
+ */
+ if (window == NULL)
+ {
+ unsigned int xwcm;
+ XWindowChanges xwc;
+
+ xwcm = event->xconfigurerequest.value_mask &
+ (CWX | CWY | CWWidth | CWHeight | CWBorderWidth);
+
+ xwc.x = event->xconfigurerequest.x;
+ xwc.y = event->xconfigurerequest.y;
+ xwc.width = event->xconfigurerequest.width;
+ xwc.height = event->xconfigurerequest.height;
+ xwc.border_width = event->xconfigurerequest.border_width;
+
+ meta_verbose ("Configuring withdrawn window to %d,%d %dx%d border %d (some values may not be in mask)\n",
+ xwc.x, xwc.y, xwc.width, xwc.height, xwc.border_width);
+ meta_error_trap_push (display);
+ XConfigureWindow (display->xdisplay, event->xconfigurerequest.window,
+ xwcm, &xwc);
+ meta_error_trap_pop (display, FALSE);
+ }
+ else
+ {
+ if (!frame_was_receiver)
+ meta_window_configure_request (window, event);
+ }
+ break;
+ case GravityNotify:
+ break;
+ case ResizeRequest:
+ break;
+ case CirculateNotify:
+ break;
+ case CirculateRequest:
+ break;
+ case PropertyNotify:
+ {
+ MetaGroup *group;
+ MetaScreen *screen;
+
+ if (window && !frame_was_receiver)
+ meta_window_property_notify (window, event);
+ else if (property_for_window && !frame_was_receiver)
+ meta_window_property_notify (property_for_window, event);
+
+ group = meta_display_lookup_group (display,
+ event->xproperty.window);
+ if (group != NULL)
+ meta_group_property_notify (group, event);
+
+ screen = NULL;
+ if (window == NULL &&
+ group == NULL) /* window/group != NULL means it wasn't a root window */
+ screen = meta_display_screen_for_root (display,
+ event->xproperty.window);
+
+ if (screen != NULL)
+ {
+ if (event->xproperty.atom ==
+ display->atom__NET_DESKTOP_LAYOUT)
+ meta_screen_update_workspace_layout (screen);
+ else if (event->xproperty.atom ==
+ display->atom__NET_DESKTOP_NAMES)
+ meta_screen_update_workspace_names (screen);
+#if 0
+ else if (event->xproperty.atom ==
+ display->atom__NET_RESTACK_WINDOW)
+ handle_net_restack_window (display, event);
+#endif
+
+ /* we just use this property as a sentinel to avoid
+ * certain race conditions. See the comment for the
+ * sentinel_counter variable declaration in display.h
+ */
+ if (event->xproperty.atom ==
+ display->atom__MARCO_SENTINEL)
+ {
+ meta_display_decrement_focus_sentinel (display);
+ }
+ }
+ }
+ break;
+ case SelectionClear:
+ /* do this here instead of at end of function
+ * so we can return
+ */
+
+ /* FIXME: Clearing display->current_time here makes no sense to
+ * me; who put this here and why?
+ */
+ display->current_time = CurrentTime;
+
+ process_selection_clear (display, event);
+ /* Note that processing that may have resulted in
+ * closing the display... so return right away.
+ */
+ return FALSE;
+ case SelectionRequest:
+ process_selection_request (display, event);
+ break;
+ case SelectionNotify:
+ break;
+ case ColormapNotify:
+ if (window && !frame_was_receiver)
+ window->colormap = event->xcolormap.colormap;
+ break;
+ case ClientMessage:
+ if (window)
+ {
+ if (!frame_was_receiver)
+ meta_window_client_message (window, event);
+ }
+ else
+ {
+ MetaScreen *screen;
+
+ screen = meta_display_screen_for_root (display,
+ event->xclient.window);
+
+ if (screen)
+ {
+ if (event->xclient.message_type ==
+ display->atom__NET_CURRENT_DESKTOP)
+ {
+ int space;
+ MetaWorkspace *workspace;
+ guint32 time;
+
+ space = event->xclient.data.l[0];
+ time = event->xclient.data.l[1];
+
+ meta_verbose ("Request to change current workspace to %d with "
+ "specified timestamp of %u\n",
+ space, time);
+
+ workspace =
+ meta_screen_get_workspace_by_index (screen,
+ space);
+
+ /* Handle clients using the older version of the spec... */
+ if (time == 0 && workspace)
+ {
+ meta_warning ("Received a NET_CURRENT_DESKTOP message "
+ "from a broken (outdated) client who sent "
+ "a 0 timestamp\n");
+ time = meta_display_get_current_time_roundtrip (display);
+ }
+
+ if (workspace)
+ meta_workspace_activate (workspace, time);
+ else
+ meta_verbose ("Don't know about workspace %d\n", space);
+ }
+ else if (event->xclient.message_type ==
+ display->atom__NET_NUMBER_OF_DESKTOPS)
+ {
+ int num_spaces;
+
+ num_spaces = event->xclient.data.l[0];
+
+ meta_verbose ("Request to set number of workspaces to %d\n",
+ num_spaces);
+
+ meta_prefs_set_num_workspaces (num_spaces);
+ }
+ else if (event->xclient.message_type ==
+ display->atom__NET_SHOWING_DESKTOP)
+ {
+ gboolean showing_desktop;
+ guint32 timestamp;
+
+ showing_desktop = event->xclient.data.l[0] != 0;
+ /* FIXME: Braindead protocol doesn't have a timestamp */
+ timestamp = meta_display_get_current_time_roundtrip (display);
+ meta_verbose ("Request to %s desktop\n",
+ showing_desktop ? "show" : "hide");
+
+ if (showing_desktop)
+ meta_screen_show_desktop (screen, timestamp);
+ else
+ {
+ meta_screen_unshow_desktop (screen);
+ meta_workspace_focus_default_window (screen->active_workspace, NULL, timestamp);
+ }
+ }
+ else if (event->xclient.message_type ==
+ display->atom__MARCO_RESTART_MESSAGE)
+ {
+ meta_verbose ("Received restart request\n");
+ meta_restart ();
+ }
+ else if (event->xclient.message_type ==
+ display->atom__MARCO_RELOAD_THEME_MESSAGE)
+ {
+ meta_verbose ("Received reload theme request\n");
+ meta_ui_set_current_theme (meta_prefs_get_theme (),
+ TRUE);
+ meta_display_retheme_all ();
+ }
+ else if (event->xclient.message_type ==
+ display->atom__MARCO_SET_KEYBINDINGS_MESSAGE)
+ {
+ meta_verbose ("Received set keybindings request = %d\n",
+ (int) event->xclient.data.l[0]);
+ meta_set_keybindings_disabled (!event->xclient.data.l[0]);
+ }
+ else if (event->xclient.message_type ==
+ display->atom__MARCO_TOGGLE_VERBOSE)
+ {
+ meta_verbose ("Received toggle verbose message\n");
+ meta_set_verbose (!meta_is_verbose ());
+ }
+ else if (event->xclient.message_type ==
+ display->atom_WM_PROTOCOLS)
+ {
+ meta_verbose ("Received WM_PROTOCOLS message\n");
+
+ if ((Atom)event->xclient.data.l[0] == display->atom__NET_WM_PING)
+ {
+ process_pong_message (display, event);
+
+ /* We don't want ping reply events going into
+ * the GTK+ event loop because gtk+ will treat
+ * them as ping requests and send more replies.
+ */
+ filter_out_event = TRUE;
+ }
+ }
+ }
+
+ if (event->xclient.message_type ==
+ display->atom__NET_REQUEST_FRAME_EXTENTS)
+ {
+ meta_verbose ("Received _NET_REQUEST_FRAME_EXTENTS message\n");
+ process_request_frame_extents (display, event);
+ }
+ }
+ break;
+ case MappingNotify:
+ {
+ gboolean ignore_current;
+
+ ignore_current = FALSE;
+
+ /* Check whether the next event is an identical MappingNotify
+ * event. If it is, ignore the current event, we'll update
+ * when we get the next one.
+ */
+ if (XPending (display->xdisplay))
+ {
+ XEvent next_event;
+
+ XPeekEvent (display->xdisplay, &next_event);
+
+ if (next_event.type == MappingNotify &&
+ next_event.xmapping.request == event->xmapping.request)
+ ignore_current = TRUE;
+ }
+
+ if (!ignore_current)
+ {
+ /* Let XLib know that there is a new keyboard mapping.
+ */
+ XRefreshKeyboardMapping (&event->xmapping);
+ meta_display_process_mapping_event (display, event);
+ }
+ }
+ break;
+ default:
+#ifdef HAVE_XKB
+ if (event->type == display->xkb_base_event_type)
+ {
+ XkbAnyEvent *xkb_ev = (XkbAnyEvent *) event;
+
+ switch (xkb_ev->xkb_type)
+ {
+ case XkbBellNotify:
+ if (XSERVER_TIME_IS_BEFORE(display->last_bell_time,
+ xkb_ev->time - 100))
+ {
+ display->last_bell_time = xkb_ev->time;
+ meta_bell_notify (display, xkb_ev);
+ }
+ break;
+ }
+ }
+#endif
+ break;
+ }
+
+ if (display->compositor)
+ {
+ meta_compositor_process_event (display->compositor, event, window);
+ }
+
+ /* generates event on wrong window.
+ * https://github.com/stefano-k/Mate-Desktop-Environment/commit/b0e5fb03eb21dae8f02692f11ef391bfc5ccba33
+ */
+ if (maybe_send_event_to_gtk(display, event))
+ {
+ filter_out_event = TRUE;
+ }
+
+ display->current_time = CurrentTime;
+ return filter_out_event;
+}
+
+/* Return the window this has to do with, if any, rather
+ * than the frame or root window that was selecting
+ * for substructure
+ */
+static Window
+event_get_modified_window (MetaDisplay *display,
+ XEvent *event)
+{
+ switch (event->type)
+ {
+ case KeyPress:
+ case KeyRelease:
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ case FocusIn:
+ case FocusOut:
+ case KeymapNotify:
+ case Expose:
+ case GraphicsExpose:
+ case NoExpose:
+ case VisibilityNotify:
+ case ResizeRequest:
+ case PropertyNotify:
+ case SelectionClear:
+ case SelectionRequest:
+ case SelectionNotify:
+ case ColormapNotify:
+ case ClientMessage:
+ case EnterNotify:
+ case LeaveNotify:
+ return event->xany.window;
+
+ case CreateNotify:
+ return event->xcreatewindow.window;
+
+ case DestroyNotify:
+ return event->xdestroywindow.window;
+
+ case UnmapNotify:
+ return event->xunmap.window;
+
+ case MapNotify:
+ return event->xmap.window;
+
+ case MapRequest:
+ return event->xmaprequest.window;
+
+ case ReparentNotify:
+ return event->xreparent.window;
+
+ case ConfigureNotify:
+ return event->xconfigure.window;
+
+ case ConfigureRequest:
+ return event->xconfigurerequest.window;
+
+ case GravityNotify:
+ return event->xgravity.window;
+
+ case CirculateNotify:
+ return event->xcirculate.window;
+
+ case CirculateRequest:
+ return event->xcirculaterequest.window;
+
+ case MappingNotify:
+ return None;
+
+ default:
+#ifdef HAVE_SHAPE
+ if (META_DISPLAY_HAS_SHAPE (display) &&
+ event->type == (display->shape_event_base + ShapeNotify))
+ {
+ XShapeEvent *sev = (XShapeEvent*) event;
+ return sev->window;
+ }
+#endif
+
+ return None;
+ }
+}
+
+static guint32
+event_get_time (MetaDisplay *display,
+ XEvent *event)
+{
+ switch (event->type)
+ {
+ case KeyPress:
+ case KeyRelease:
+ return event->xkey.time;
+
+ case ButtonPress:
+ case ButtonRelease:
+ return event->xbutton.time;
+
+ case MotionNotify:
+ return event->xmotion.time;
+
+ case PropertyNotify:
+ return event->xproperty.time;
+
+ case SelectionClear:
+ case SelectionRequest:
+ case SelectionNotify:
+ return event->xselection.time;
+
+ case EnterNotify:
+ case LeaveNotify:
+ return event->xcrossing.time;
+
+ case FocusIn:
+ case FocusOut:
+ case KeymapNotify:
+ case Expose:
+ case GraphicsExpose:
+ case NoExpose:
+ case MapNotify:
+ case UnmapNotify:
+ case VisibilityNotify:
+ case ResizeRequest:
+ case ColormapNotify:
+ case ClientMessage:
+ case CreateNotify:
+ case DestroyNotify:
+ case MapRequest:
+ case ReparentNotify:
+ case ConfigureNotify:
+ case ConfigureRequest:
+ case GravityNotify:
+ case CirculateNotify:
+ case CirculateRequest:
+ case MappingNotify:
+ default:
+ return CurrentTime;
+ }
+}
+
+#ifdef WITH_VERBOSE_MODE
+const char*
+meta_event_detail_to_string (int d)
+{
+ const char *detail = "???";
+ switch (d)
+ {
+ /* We are an ancestor in the A<->B focus change relationship */
+ case NotifyAncestor:
+ detail = "NotifyAncestor";
+ break;
+ case NotifyDetailNone:
+ detail = "NotifyDetailNone";
+ break;
+ /* We are a descendant in the A<->B focus change relationship */
+ case NotifyInferior:
+ detail = "NotifyInferior";
+ break;
+ case NotifyNonlinear:
+ detail = "NotifyNonlinear";
+ break;
+ case NotifyNonlinearVirtual:
+ detail = "NotifyNonlinearVirtual";
+ break;
+ case NotifyPointer:
+ detail = "NotifyPointer";
+ break;
+ case NotifyPointerRoot:
+ detail = "NotifyPointerRoot";
+ break;
+ case NotifyVirtual:
+ detail = "NotifyVirtual";
+ break;
+ }
+
+ return detail;
+}
+#endif /* WITH_VERBOSE_MODE */
+
+#ifdef WITH_VERBOSE_MODE
+const char*
+meta_event_mode_to_string (int m)
+{
+ const char *mode = "???";
+ switch (m)
+ {
+ case NotifyNormal:
+ mode = "NotifyNormal";
+ break;
+ case NotifyGrab:
+ mode = "NotifyGrab";
+ break;
+ case NotifyUngrab:
+ mode = "NotifyUngrab";
+ break;
+ /* not sure any X implementations are missing this, but
+ * it seems to be absent from some docs.
+ */
+#ifdef NotifyWhileGrabbed
+ case NotifyWhileGrabbed:
+ mode = "NotifyWhileGrabbed";
+ break;
+#endif
+ }
+
+ return mode;
+}
+#endif /* WITH_VERBOSE_MODE */
+
+#ifdef WITH_VERBOSE_MODE
+static const char*
+stack_mode_to_string (int mode)
+{
+ switch (mode)
+ {
+ case Above:
+ return "Above";
+ case Below:
+ return "Below";
+ case TopIf:
+ return "TopIf";
+ case BottomIf:
+ return "BottomIf";
+ case Opposite:
+ return "Opposite";
+ }
+
+ return "Unknown";
+}
+#endif /* WITH_VERBOSE_MODE */
+
+#ifdef WITH_VERBOSE_MODE
+static char*
+key_event_description (Display *xdisplay,
+ XEvent *event)
+{
+ KeySym keysym;
+ const char *str;
+
+ keysym = XKeycodeToKeysym (xdisplay, event->xkey.keycode, 0);
+
+ str = XKeysymToString (keysym);
+
+ return g_strdup_printf ("Key '%s' state 0x%x",
+ str ? str : "none", event->xkey.state);
+}
+#endif /* WITH_VERBOSE_MODE */
+
+#ifdef HAVE_XSYNC
+#ifdef WITH_VERBOSE_MODE
+static gint64
+sync_value_to_64 (const XSyncValue *value)
+{
+ gint64 v;
+
+ v = XSyncValueLow32 (*value);
+ v |= (((gint64)XSyncValueHigh32 (*value)) << 32);
+
+ return v;
+}
+#endif /* WITH_VERBOSE_MODE */
+
+#ifdef WITH_VERBOSE_MODE
+static const char*
+alarm_state_to_string (XSyncAlarmState state)
+{
+ switch (state)
+ {
+ case XSyncAlarmActive:
+ return "Active";
+ case XSyncAlarmInactive:
+ return "Inactive";
+ case XSyncAlarmDestroyed:
+ return "Destroyed";
+ default:
+ return "(unknown)";
+ }
+}
+#endif /* WITH_VERBOSE_MODE */
+
+#endif /* HAVE_XSYNC */
+
+#ifdef WITH_VERBOSE_MODE
+static void
+meta_spew_event (MetaDisplay *display,
+ XEvent *event)
+{
+ const char *name = NULL;
+ char *extra = NULL;
+ char *winname;
+ MetaScreen *screen;
+
+ if (!meta_is_verbose())
+ return;
+
+ /* filter overnumerous events */
+ if (event->type == Expose || event->type == MotionNotify ||
+ event->type == NoExpose)
+ return;
+
+ switch (event->type)
+ {
+ case KeyPress:
+ name = "KeyPress";
+ extra = key_event_description (display->xdisplay, event);
+ break;
+ case KeyRelease:
+ name = "KeyRelease";
+ extra = key_event_description (display->xdisplay, event);
+ break;
+ case ButtonPress:
+ name = "ButtonPress";
+ extra = g_strdup_printf ("button %u state 0x%x x %d y %d root 0x%lx same_screen %d",
+ event->xbutton.button,
+ event->xbutton.state,
+ event->xbutton.x,
+ event->xbutton.y,
+ event->xbutton.root,
+ event->xbutton.same_screen);
+ break;
+ case ButtonRelease:
+ name = "ButtonRelease";
+ extra = g_strdup_printf ("button %u state 0x%x x %d y %d root 0x%lx same_screen %d",
+ event->xbutton.button,
+ event->xbutton.state,
+ event->xbutton.x,
+ event->xbutton.y,
+ event->xbutton.root,
+ event->xbutton.same_screen);
+ break;
+ case MotionNotify:
+ name = "MotionNotify";
+ extra = g_strdup_printf ("win: 0x%lx x: %d y: %d",
+ event->xmotion.window,
+ event->xmotion.x,
+ event->xmotion.y);
+ break;
+ case EnterNotify:
+ name = "EnterNotify";
+ extra = g_strdup_printf ("win: 0x%lx root: 0x%lx subwindow: 0x%lx mode: %s detail: %s focus: %d x: %d y: %d",
+ event->xcrossing.window,
+ event->xcrossing.root,
+ event->xcrossing.subwindow,
+ meta_event_mode_to_string (event->xcrossing.mode),
+ meta_event_detail_to_string (event->xcrossing.detail),
+ event->xcrossing.focus,
+ event->xcrossing.x,
+ event->xcrossing.y);
+ break;
+ case LeaveNotify:
+ name = "LeaveNotify";
+ extra = g_strdup_printf ("win: 0x%lx root: 0x%lx subwindow: 0x%lx mode: %s detail: %s focus: %d x: %d y: %d",
+ event->xcrossing.window,
+ event->xcrossing.root,
+ event->xcrossing.subwindow,
+ meta_event_mode_to_string (event->xcrossing.mode),
+ meta_event_detail_to_string (event->xcrossing.detail),
+ event->xcrossing.focus,
+ event->xcrossing.x,
+ event->xcrossing.y);
+ break;
+ case FocusIn:
+ name = "FocusIn";
+ extra = g_strdup_printf ("detail: %s mode: %s\n",
+ meta_event_detail_to_string (event->xfocus.detail),
+ meta_event_mode_to_string (event->xfocus.mode));
+ break;
+ case FocusOut:
+ name = "FocusOut";
+ extra = g_strdup_printf ("detail: %s mode: %s\n",
+ meta_event_detail_to_string (event->xfocus.detail),
+ meta_event_mode_to_string (event->xfocus.mode));
+ break;
+ case KeymapNotify:
+ name = "KeymapNotify";
+ break;
+ case Expose:
+ name = "Expose";
+ break;
+ case GraphicsExpose:
+ name = "GraphicsExpose";
+ break;
+ case NoExpose:
+ name = "NoExpose";
+ break;
+ case VisibilityNotify:
+ name = "VisibilityNotify";
+ break;
+ case CreateNotify:
+ name = "CreateNotify";
+ extra = g_strdup_printf ("parent: 0x%lx window: 0x%lx",
+ event->xcreatewindow.parent,
+ event->xcreatewindow.window);
+ break;
+ case DestroyNotify:
+ name = "DestroyNotify";
+ extra = g_strdup_printf ("event: 0x%lx window: 0x%lx",
+ event->xdestroywindow.event,
+ event->xdestroywindow.window);
+ break;
+ case UnmapNotify:
+ name = "UnmapNotify";
+ extra = g_strdup_printf ("event: 0x%lx window: 0x%lx from_configure: %d",
+ event->xunmap.event,
+ event->xunmap.window,
+ event->xunmap.from_configure);
+ break;
+ case MapNotify:
+ name = "MapNotify";
+ extra = g_strdup_printf ("event: 0x%lx window: 0x%lx override_redirect: %d",
+ event->xmap.event,
+ event->xmap.window,
+ event->xmap.override_redirect);
+ break;
+ case MapRequest:
+ name = "MapRequest";
+ extra = g_strdup_printf ("window: 0x%lx parent: 0x%lx\n",
+ event->xmaprequest.window,
+ event->xmaprequest.parent);
+ break;
+ case ReparentNotify:
+ name = "ReparentNotify";
+ extra = g_strdup_printf ("window: 0x%lx parent: 0x%lx event: 0x%lx\n",
+ event->xreparent.window,
+ event->xreparent.parent,
+ event->xreparent.event);
+ break;
+ case ConfigureNotify:
+ name = "ConfigureNotify";
+ extra = g_strdup_printf ("x: %d y: %d w: %d h: %d above: 0x%lx override_redirect: %d",
+ event->xconfigure.x,
+ event->xconfigure.y,
+ event->xconfigure.width,
+ event->xconfigure.height,
+ event->xconfigure.above,
+ event->xconfigure.override_redirect);
+ break;
+ case ConfigureRequest:
+ name = "ConfigureRequest";
+ extra = g_strdup_printf ("parent: 0x%lx window: 0x%lx x: %d %sy: %d %sw: %d %sh: %d %sborder: %d %sabove: %lx %sstackmode: %s %s",
+ event->xconfigurerequest.parent,
+ event->xconfigurerequest.window,
+ event->xconfigurerequest.x,
+ event->xconfigurerequest.value_mask &
+ CWX ? "" : "(unset) ",
+ event->xconfigurerequest.y,
+ event->xconfigurerequest.value_mask &
+ CWY ? "" : "(unset) ",
+ event->xconfigurerequest.width,
+ event->xconfigurerequest.value_mask &
+ CWWidth ? "" : "(unset) ",
+ event->xconfigurerequest.height,
+ event->xconfigurerequest.value_mask &
+ CWHeight ? "" : "(unset) ",
+ event->xconfigurerequest.border_width,
+ event->xconfigurerequest.value_mask &
+ CWBorderWidth ? "" : "(unset)",
+ event->xconfigurerequest.above,
+ event->xconfigurerequest.value_mask &
+ CWSibling ? "" : "(unset)",
+ stack_mode_to_string (event->xconfigurerequest.detail),
+ event->xconfigurerequest.value_mask &
+ CWStackMode ? "" : "(unset)");
+ break;
+ case GravityNotify:
+ name = "GravityNotify";
+ break;
+ case ResizeRequest:
+ name = "ResizeRequest";
+ extra = g_strdup_printf ("width = %d height = %d",
+ event->xresizerequest.width,
+ event->xresizerequest.height);
+ break;
+ case CirculateNotify:
+ name = "CirculateNotify";
+ break;
+ case CirculateRequest:
+ name = "CirculateRequest";
+ break;
+ case PropertyNotify:
+ {
+ char *str;
+ const char *state;
+
+ name = "PropertyNotify";
+
+ meta_error_trap_push (display);
+ str = XGetAtomName (display->xdisplay,
+ event->xproperty.atom);
+ meta_error_trap_pop (display, TRUE);
+
+ if (event->xproperty.state == PropertyNewValue)
+ state = "PropertyNewValue";
+ else if (event->xproperty.state == PropertyDelete)
+ state = "PropertyDelete";
+ else
+ state = "???";
+
+ extra = g_strdup_printf ("atom: %s state: %s",
+ str ? str : "(unknown atom)",
+ state);
+ meta_XFree (str);
+ }
+ break;
+ case SelectionClear:
+ name = "SelectionClear";
+ break;
+ case SelectionRequest:
+ name = "SelectionRequest";
+ break;
+ case SelectionNotify:
+ name = "SelectionNotify";
+ break;
+ case ColormapNotify:
+ name = "ColormapNotify";
+ break;
+ case ClientMessage:
+ {
+ char *str;
+ name = "ClientMessage";
+ meta_error_trap_push (display);
+ str = XGetAtomName (display->xdisplay,
+ event->xclient.message_type);
+ meta_error_trap_pop (display, TRUE);
+ extra = g_strdup_printf ("type: %s format: %d\n",
+ str ? str : "(unknown atom)",
+ event->xclient.format);
+ meta_XFree (str);
+ }
+ break;
+ case MappingNotify:
+ name = "MappingNotify";
+ break;
+ default:
+#ifdef HAVE_XSYNC
+ if (META_DISPLAY_HAS_XSYNC (display) &&
+ event->type == (display->xsync_event_base + XSyncAlarmNotify))
+ {
+ XSyncAlarmNotifyEvent *aevent = (XSyncAlarmNotifyEvent*) event;
+
+ name = "XSyncAlarmNotify";
+ extra =
+ g_strdup_printf ("alarm: 0x%lx"
+ " counter_value: %" G_GINT64_FORMAT
+ " alarm_value: %" G_GINT64_FORMAT
+ " time: %u alarm state: %s",
+ aevent->alarm,
+ (gint64) sync_value_to_64 (&aevent->counter_value),
+ (gint64) sync_value_to_64 (&aevent->alarm_value),
+ (unsigned int)aevent->time,
+ alarm_state_to_string (aevent->state));
+ }
+ else
+#endif /* HAVE_XSYNC */
+#ifdef HAVE_SHAPE
+ if (META_DISPLAY_HAS_SHAPE (display) &&
+ event->type == (display->shape_event_base + ShapeNotify))
+ {
+ XShapeEvent *sev = (XShapeEvent*) event;
+
+ name = "ShapeNotify";
+
+ extra =
+ g_strdup_printf ("kind: %s "
+ "x: %d y: %d w: %u h: %u "
+ "shaped: %d",
+ sev->kind == ShapeBounding ?
+ "ShapeBounding" :
+ (sev->kind == ShapeClip ?
+ "ShapeClip" : "(unknown)"),
+ sev->x, sev->y, sev->width, sev->height,
+ sev->shaped);
+ }
+ else
+#endif /* HAVE_SHAPE */
+ {
+ name = "(Unknown event)";
+ extra = g_strdup_printf ("type: %d", event->xany.type);
+ }
+ break;
+ }
+
+ screen = meta_display_screen_for_root (display, event->xany.window);
+
+ if (screen)
+ winname = g_strdup_printf ("root %d", screen->number);
+ else
+ winname = g_strdup_printf ("0x%lx", event->xany.window);
+
+ meta_topic (META_DEBUG_EVENTS,
+ "%s on %s%s %s %sserial %lu\n", name, winname,
+ extra ? ":" : "", extra ? extra : "",
+ event->xany.send_event ? "SEND " : "",
+ event->xany.serial);
+
+ g_free (winname);
+
+ if (extra)
+ g_free (extra);
+}
+#endif /* WITH_VERBOSE_MODE */
+
+MetaWindow*
+meta_display_lookup_x_window (MetaDisplay *display,
+ Window xwindow)
+{
+ return g_hash_table_lookup (display->window_ids, &xwindow);
+}
+
+void
+meta_display_register_x_window (MetaDisplay *display,
+ Window *xwindowp,
+ MetaWindow *window)
+{
+ g_return_if_fail (g_hash_table_lookup (display->window_ids, xwindowp) == NULL);
+
+ g_hash_table_insert (display->window_ids, xwindowp, window);
+}
+
+void
+meta_display_unregister_x_window (MetaDisplay *display,
+ Window xwindow)
+{
+ g_return_if_fail (g_hash_table_lookup (display->window_ids, &xwindow) != NULL);
+
+ g_hash_table_remove (display->window_ids, &xwindow);
+
+ /* Remove any pending pings */
+ remove_pending_pings_for_window (display, xwindow);
+}
+
+gboolean
+meta_display_xwindow_is_a_no_focus_window (MetaDisplay *display,
+ Window xwindow)
+{
+ gboolean is_a_no_focus_window = FALSE;
+ GSList *temp = display->screens;
+ while (temp != NULL) {
+ MetaScreen *screen = temp->data;
+ if (screen->no_focus_window == xwindow) {
+ is_a_no_focus_window = TRUE;
+ break;
+ }
+ temp = temp->next;
+ }
+
+ return is_a_no_focus_window;
+}
+
+Cursor
+meta_display_create_x_cursor (MetaDisplay *display,
+ MetaCursor cursor)
+{
+ Cursor xcursor;
+ guint glyph;
+
+ switch (cursor)
+ {
+ case META_CURSOR_DEFAULT:
+ glyph = XC_left_ptr;
+ break;
+ case META_CURSOR_NORTH_RESIZE:
+ glyph = XC_top_side;
+ break;
+ case META_CURSOR_SOUTH_RESIZE:
+ glyph = XC_bottom_side;
+ break;
+ case META_CURSOR_WEST_RESIZE:
+ glyph = XC_left_side;
+ break;
+ case META_CURSOR_EAST_RESIZE:
+ glyph = XC_right_side;
+ break;
+ case META_CURSOR_SE_RESIZE:
+ glyph = XC_bottom_right_corner;
+ break;
+ case META_CURSOR_SW_RESIZE:
+ glyph = XC_bottom_left_corner;
+ break;
+ case META_CURSOR_NE_RESIZE:
+ glyph = XC_top_right_corner;
+ break;
+ case META_CURSOR_NW_RESIZE:
+ glyph = XC_top_left_corner;
+ break;
+ case META_CURSOR_MOVE_OR_RESIZE_WINDOW:
+ glyph = XC_fleur;
+ break;
+ case META_CURSOR_BUSY:
+ glyph = XC_watch;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ glyph = 0; /* silence compiler */
+ break;
+ }
+
+ xcursor = XCreateFontCursor (display->xdisplay, glyph);
+
+ return xcursor;
+}
+
+static Cursor
+xcursor_for_op (MetaDisplay *display,
+ MetaGrabOp op)
+{
+ MetaCursor cursor = META_CURSOR_DEFAULT;
+
+ switch (op)
+ {
+ case META_GRAB_OP_RESIZING_SE:
+ case META_GRAB_OP_KEYBOARD_RESIZING_SE:
+ cursor = META_CURSOR_SE_RESIZE;
+ break;
+ case META_GRAB_OP_RESIZING_S:
+ case META_GRAB_OP_KEYBOARD_RESIZING_S:
+ cursor = META_CURSOR_SOUTH_RESIZE;
+ break;
+ case META_GRAB_OP_RESIZING_SW:
+ case META_GRAB_OP_KEYBOARD_RESIZING_SW:
+ cursor = META_CURSOR_SW_RESIZE;
+ break;
+ case META_GRAB_OP_RESIZING_N:
+ case META_GRAB_OP_KEYBOARD_RESIZING_N:
+ cursor = META_CURSOR_NORTH_RESIZE;
+ break;
+ case META_GRAB_OP_RESIZING_NE:
+ case META_GRAB_OP_KEYBOARD_RESIZING_NE:
+ cursor = META_CURSOR_NE_RESIZE;
+ break;
+ case META_GRAB_OP_RESIZING_NW:
+ case META_GRAB_OP_KEYBOARD_RESIZING_NW:
+ cursor = META_CURSOR_NW_RESIZE;
+ break;
+ case META_GRAB_OP_RESIZING_W:
+ case META_GRAB_OP_KEYBOARD_RESIZING_W:
+ cursor = META_CURSOR_WEST_RESIZE;
+ break;
+ case META_GRAB_OP_RESIZING_E:
+ case META_GRAB_OP_KEYBOARD_RESIZING_E:
+ cursor = META_CURSOR_EAST_RESIZE;
+ break;
+ case META_GRAB_OP_MOVING:
+ case META_GRAB_OP_KEYBOARD_MOVING:
+ case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
+ cursor = META_CURSOR_MOVE_OR_RESIZE_WINDOW;
+ break;
+
+ default:
+ break;
+ }
+
+ if (cursor == META_CURSOR_DEFAULT)
+ return None;
+ return meta_display_create_x_cursor (display, cursor);
+}
+
+void
+meta_display_set_grab_op_cursor (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaGrabOp op,
+ gboolean change_pointer,
+ Window grab_xwindow,
+ guint32 timestamp)
+{
+ Cursor cursor;
+
+ cursor = xcursor_for_op (display, op);
+
+#define GRAB_MASK (PointerMotionMask | \
+ ButtonPressMask | ButtonReleaseMask | \
+ EnterWindowMask | LeaveWindowMask)
+
+ if (change_pointer)
+ {
+ meta_error_trap_push_with_return (display);
+ XChangeActivePointerGrab (display->xdisplay,
+ GRAB_MASK,
+ cursor,
+ timestamp);
+
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "Changed pointer with XChangeActivePointerGrab()\n");
+
+ if (meta_error_trap_pop_with_return (display, FALSE) != Success)
+ {
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "Error trapped from XChangeActivePointerGrab()\n");
+ if (display->grab_have_pointer)
+ display->grab_have_pointer = FALSE;
+ }
+ }
+ else
+ {
+ g_assert (screen != NULL);
+
+ meta_error_trap_push (display);
+ if (XGrabPointer (display->xdisplay,
+ grab_xwindow,
+ False,
+ GRAB_MASK,
+ GrabModeAsync, GrabModeAsync,
+ screen->xroot,
+ cursor,
+ timestamp) == GrabSuccess)
+ {
+ display->grab_have_pointer = TRUE;
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "XGrabPointer() returned GrabSuccess time %u\n",
+ timestamp);
+ }
+ else
+ {
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "XGrabPointer() failed time %u\n",
+ timestamp);
+ }
+ meta_error_trap_pop (display, TRUE);
+ }
+
+#undef GRAB_MASK
+
+ if (cursor != None)
+ XFreeCursor (display->xdisplay, cursor);
+}
+
+gboolean
+meta_display_begin_grab_op (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ MetaGrabOp op,
+ gboolean pointer_already_grabbed,
+ gboolean frame_action,
+ int button,
+ gulong modmask,
+ guint32 timestamp,
+ int root_x,
+ int root_y)
+{
+ Window grab_xwindow;
+
+ if (grab_op_is_mouse (op) && meta_grab_op_is_moving (op))
+ {
+ if (display->compositor)
+ {
+ meta_compositor_begin_move (display->compositor,
+ window, &window->rect,
+ root_x, root_y);
+ }
+ }
+
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "Doing grab op %u on window %s button %d pointer already grabbed: %d pointer pos %d,%d\n",
+ op, window ? window->desc : "none", button, pointer_already_grabbed,
+ root_x, root_y);
+
+ if (display->grab_op != META_GRAB_OP_NONE)
+ {
+ if (window)
+ meta_warning ("Attempt to perform window operation %u on window %s when operation %u on %s already in effect\n",
+ op, window->desc, display->grab_op,
+ display->grab_window ? display->grab_window->desc : "none");
+ return FALSE;
+ }
+
+ if (window &&
+ (meta_grab_op_is_moving (op) || meta_grab_op_is_resizing (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;
+ }
+ }
+
+ /* FIXME:
+ * If we have no MetaWindow we do our best
+ * and try to do the grab on the RootWindow.
+ * This will fail if anyone else has any
+ * key grab on the RootWindow.
+ */
+ if (window)
+ grab_xwindow = window->frame ? window->frame->xwindow : window->xwindow;
+ else
+ grab_xwindow = screen->xroot;
+
+ display->grab_have_pointer = FALSE;
+
+ if (pointer_already_grabbed)
+ display->grab_have_pointer = TRUE;
+
+ meta_display_set_grab_op_cursor (display, screen, op, FALSE, grab_xwindow,
+ timestamp);
+
+ if (!display->grab_have_pointer)
+ {
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "XGrabPointer() failed\n");
+ return FALSE;
+ }
+
+ /* Grab keys for keyboard ops and mouse move/resizes; see #126497 */
+ if (grab_op_is_keyboard (op) || grab_op_is_mouse_only (op))
+ {
+ if (window)
+ display->grab_have_keyboard =
+ meta_window_grab_all_keys (window, timestamp);
+
+ else
+ display->grab_have_keyboard =
+ meta_screen_grab_all_keys (screen, timestamp);
+
+ if (!display->grab_have_keyboard)
+ {
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "grabbing all keys failed, ungrabbing pointer\n");
+ XUngrabPointer (display->xdisplay, timestamp);
+ display->grab_have_pointer = FALSE;
+ return FALSE;
+ }
+ }
+
+ display->grab_op = op;
+ display->grab_window = window;
+ display->grab_screen = screen;
+ display->grab_xwindow = grab_xwindow;
+ display->grab_button = button;
+ display->grab_mask = modmask;
+ 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.tv_sec = 0;
+ display->grab_last_moveresize_time.tv_usec = 0;
+ display->grab_motion_notify_time = 0;
+ display->grab_old_window_stacking = NULL;
+#ifdef HAVE_XSYNC
+ display->grab_sync_request_alarm = None;
+ display->grab_last_user_action_was_snap = FALSE;
+#endif
+ display->grab_was_cancelled = FALSE;
+ display->grab_frame_action = frame_action;
+
+ if (display->grab_resize_timeout_id)
+ {
+ g_source_remove (display->grab_resize_timeout_id);
+ display->grab_resize_timeout_id = 0;
+ }
+
+ if (display->grab_window)
+ {
+ meta_window_get_client_root_coords (display->grab_window,
+ &display->grab_initial_window_pos);
+ display->grab_anchor_window_pos = display->grab_initial_window_pos;
+
+ display->grab_wireframe_active =
+ (meta_prefs_get_reduced_resources () && !meta_prefs_get_mate_accessibility ()) &&
+ (meta_grab_op_is_resizing (display->grab_op) ||
+ meta_grab_op_is_moving (display->grab_op));
+
+ if (display->grab_wireframe_active)
+ {
+ meta_window_calc_showing (display->grab_window);
+ meta_window_begin_wireframe (window);
+ }
+
+#ifdef HAVE_XSYNC
+ if (!display->grab_wireframe_active &&
+ meta_grab_op_is_resizing (display->grab_op) &&
+ display->grab_window->sync_request_counter != None)
+ {
+ XSyncAlarmAttributes values;
+ XSyncValue init;
+
+ meta_error_trap_push_with_return (display);
+
+ /* Set the counter to 0, so we know that the application's
+ * responses to the client messages will always trigger
+ * a PositiveTransition
+ */
+
+ XSyncIntToValue (&init, 0);
+ XSyncSetCounter (display->xdisplay,
+ display->grab_window->sync_request_counter, init);
+
+ display->grab_window->sync_request_serial = 0;
+ display->grab_window->sync_request_time.tv_sec = 0;
+ display->grab_window->sync_request_time.tv_usec = 0;
+
+ values.trigger.counter = display->grab_window->sync_request_counter;
+ values.trigger.value_type = XSyncAbsolute;
+ values.trigger.test_type = XSyncPositiveTransition;
+ XSyncIntToValue (&values.trigger.wait_value,
+ display->grab_window->sync_request_serial + 1);
+
+ /* After triggering, increment test_value by this.
+ * (NOT wait_value above)
+ */
+ XSyncIntToValue (&values.delta, 1);
+
+ /* we want events (on by default anyway) */
+ values.events = True;
+
+ display->grab_sync_request_alarm = XSyncCreateAlarm (display->xdisplay,
+ XSyncCACounter |
+ XSyncCAValueType |
+ XSyncCAValue |
+ XSyncCATestType |
+ XSyncCADelta |
+ XSyncCAEvents,
+ &values);
+
+ if (meta_error_trap_pop_with_return (display, FALSE) != Success)
+ display->grab_sync_request_alarm = None;
+
+ meta_topic (META_DEBUG_RESIZING,
+ "Created update alarm 0x%lx\n",
+ display->grab_sync_request_alarm);
+ }
+#endif
+ }
+
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "Grab op %u on window %s successful\n",
+ display->grab_op, window ? window->desc : "(null)");
+
+ g_assert (display->grab_window != NULL || display->grab_screen != NULL);
+ g_assert (display->grab_op != META_GRAB_OP_NONE);
+
+ /* If this is a move or resize, cache the window edges for
+ * resistance/snapping
+ */
+ if (meta_grab_op_is_resizing (display->grab_op) ||
+ meta_grab_op_is_moving (display->grab_op))
+ {
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "Computing edges to resist-movement or snap-to for %s.\n",
+ window->desc);
+ meta_display_compute_resistance_and_snapping_edges (display);
+ }
+
+ /* Save the old stacking */
+ if (GRAB_OP_IS_WINDOW_SWITCH (display->grab_op))
+ {
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "Saving old stack positions; old pointer was %p.\n",
+ display->grab_old_window_stacking);
+ display->grab_old_window_stacking =
+ meta_stack_get_positions (screen->stack);
+ }
+
+ /* Do this last, after everything is set up. */
+ switch (op)
+ {
+ case META_GRAB_OP_KEYBOARD_TABBING_NORMAL:
+ meta_screen_ensure_tab_popup (screen,
+ META_TAB_LIST_NORMAL,
+ META_TAB_SHOW_ICON);
+ break;
+ case META_GRAB_OP_KEYBOARD_ESCAPING_NORMAL:
+ meta_screen_ensure_tab_popup (screen,
+ META_TAB_LIST_NORMAL,
+ META_TAB_SHOW_INSTANTLY);
+ break;
+
+ case META_GRAB_OP_KEYBOARD_TABBING_DOCK:
+ meta_screen_ensure_tab_popup (screen,
+ META_TAB_LIST_DOCKS,
+ META_TAB_SHOW_ICON);
+ break;
+ case META_GRAB_OP_KEYBOARD_ESCAPING_DOCK:
+ meta_screen_ensure_tab_popup (screen,
+ META_TAB_LIST_DOCKS,
+ META_TAB_SHOW_INSTANTLY);
+ break;
+ case META_GRAB_OP_KEYBOARD_TABBING_GROUP:
+ meta_screen_ensure_tab_popup (screen,
+ META_TAB_LIST_GROUP,
+ META_TAB_SHOW_ICON);
+ break;
+ case META_GRAB_OP_KEYBOARD_ESCAPING_GROUP:
+ meta_screen_ensure_tab_popup (screen,
+ META_TAB_LIST_GROUP,
+ META_TAB_SHOW_INSTANTLY);
+
+ case META_GRAB_OP_KEYBOARD_WORKSPACE_SWITCHING:
+ meta_screen_ensure_workspace_popup (screen);
+ break;
+
+ default:
+ break;
+ }
+
+ if (display->grab_window)
+ {
+ meta_window_refresh_resize_popup (display->grab_window);
+ }
+
+ return TRUE;
+}
+
+void
+meta_display_end_grab_op (MetaDisplay *display,
+ guint32 timestamp)
+{
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "Ending grab op %u at time %u\n", display->grab_op, timestamp);
+
+ if (display->grab_op == META_GRAB_OP_NONE)
+ return;
+
+ if (display->grab_window != NULL)
+ display->grab_window->shaken_loose = FALSE;
+
+ if (display->grab_window != NULL &&
+ !meta_prefs_get_raise_on_click () &&
+ (meta_grab_op_is_moving (display->grab_op) ||
+ meta_grab_op_is_resizing (display->grab_op)))
+ {
+ /* 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 (!display->grab_threshold_movement_reached)
+ meta_window_raise (display->grab_window);
+ }
+
+ if (GRAB_OP_IS_WINDOW_SWITCH (display->grab_op) ||
+ display->grab_op == META_GRAB_OP_KEYBOARD_WORKSPACE_SWITCHING)
+ {
+ meta_ui_tab_popup_free (display->grab_screen->tab_popup);
+ display->grab_screen->tab_popup = NULL;
+
+ /* If the ungrab here causes an EnterNotify, ignore it for
+ * sloppy focus
+ */
+ display->ungrab_should_not_cause_focus_window = display->grab_xwindow;
+ }
+
+ /* If this was a move or resize clear out the edge cache */
+ if (meta_grab_op_is_resizing (display->grab_op) ||
+ meta_grab_op_is_moving (display->grab_op))
+ {
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "Clearing out the edges for resistance/snapping");
+ meta_display_cleanup_edges (display);
+ }
+
+ if (display->grab_old_window_stacking != NULL)
+ {
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "Clearing out the old stack position, which was %p.\n",
+ display->grab_old_window_stacking);
+ g_list_free (display->grab_old_window_stacking);
+ display->grab_old_window_stacking = NULL;
+ }
+
+ if (display->grab_wireframe_active)
+ {
+ display->grab_wireframe_active = FALSE;
+ meta_window_end_wireframe (display->grab_window);
+
+ if (!display->grab_was_cancelled)
+ {
+ if (meta_grab_op_is_moving (display->grab_op))
+ meta_window_move (display->grab_window,
+ TRUE,
+ display->grab_wireframe_rect.x,
+ display->grab_wireframe_rect.y);
+ if (meta_grab_op_is_resizing (display->grab_op))
+ meta_window_resize_with_gravity (display->grab_window,
+ TRUE,
+ display->grab_wireframe_rect.width,
+ display->grab_wireframe_rect.height,
+ meta_resize_gravity_from_grab_op (display->grab_op));
+ }
+ meta_window_calc_showing (display->grab_window);
+ }
+
+ if (display->compositor &&
+ display->grab_window &&
+ grab_op_is_mouse (display->grab_op) &&
+ meta_grab_op_is_moving (display->grab_op))
+ {
+ meta_compositor_end_move (display->compositor,
+ display->grab_window);
+ }
+
+ if (display->grab_have_pointer)
+ {
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "Ungrabbing pointer with timestamp %u\n", timestamp);
+ XUngrabPointer (display->xdisplay, timestamp);
+ }
+
+ if (display->grab_have_keyboard)
+ {
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "Ungrabbing all keys timestamp %u\n", timestamp);
+ if (display->grab_window)
+ meta_window_ungrab_all_keys (display->grab_window, timestamp);
+ else
+ meta_screen_ungrab_all_keys (display->grab_screen, timestamp);
+ }
+
+#ifdef HAVE_XSYNC
+ if (display->grab_sync_request_alarm != None)
+ {
+ XSyncDestroyAlarm (display->xdisplay,
+ display->grab_sync_request_alarm);
+ display->grab_sync_request_alarm = None;
+ }
+#endif /* HAVE_XSYNC */
+
+ display->grab_window = NULL;
+ display->grab_screen = NULL;
+ display->grab_xwindow = None;
+ display->grab_op = META_GRAB_OP_NONE;
+
+ if (display->grab_resize_popup)
+ {
+ meta_ui_resize_popup_free (display->grab_resize_popup);
+ display->grab_resize_popup = NULL;
+ }
+
+ if (display->grab_resize_timeout_id)
+ {
+ g_source_remove (display->grab_resize_timeout_id);
+ display->grab_resize_timeout_id = 0;
+ }
+}
+
+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;
+}
+
+static void
+meta_change_button_grab (MetaDisplay *display,
+ Window xwindow,
+ gboolean grab,
+ gboolean sync,
+ int button,
+ int modmask)
+{
+ unsigned int ignored_mask;
+
+ meta_verbose ("%s 0x%lx sync = %d button = %d modmask 0x%x\n",
+ grab ? "Grabbing" : "Ungrabbing",
+ xwindow,
+ sync, button, modmask);
+
+ meta_error_trap_push (display);
+
+ ignored_mask = 0;
+ while (ignored_mask <= display->ignored_modifier_mask)
+ {
+ if (ignored_mask & ~(display->ignored_modifier_mask))
+ {
+ /* Not a combination of ignored modifiers
+ * (it contains some non-ignored modifiers)
+ */
+ ++ignored_mask;
+ continue;
+ }
+
+ if (meta_is_debugging ())
+ meta_error_trap_push_with_return (display);
+
+ /* GrabModeSync means freeze until XAllowEvents */
+
+ if (grab)
+ XGrabButton (display->xdisplay, button, modmask | ignored_mask,
+ xwindow, False,
+ ButtonPressMask | ButtonReleaseMask |
+ PointerMotionMask | PointerMotionHintMask,
+ sync ? GrabModeSync : GrabModeAsync,
+ GrabModeAsync,
+ False, None);
+ else
+ XUngrabButton (display->xdisplay, button, modmask | ignored_mask,
+ xwindow);
+
+ if (meta_is_debugging ())
+ {
+ int result;
+
+ result = meta_error_trap_pop_with_return (display, FALSE);
+
+ if (result != Success)
+ meta_verbose ("Failed to %s button %d with mask 0x%x for window 0x%lx error code %d\n",
+ grab ? "grab" : "ungrab",
+ button, modmask | ignored_mask, xwindow, result);
+ }
+
+ ++ignored_mask;
+ }
+
+ meta_error_trap_pop (display, FALSE);
+}
+
+void
+meta_display_grab_window_buttons (MetaDisplay *display,
+ Window xwindow)
+{
+ /* 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\n", 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 (display->window_grab_modifiers != 0)
+ {
+ gboolean debug = g_getenv ("MARCO_DEBUG_BUTTON_GRABS") != NULL;
+ int i;
+ for (i = 1; i < 4; i++)
+ {
+ meta_change_button_grab (display, xwindow,
+ TRUE,
+ FALSE,
+ i, display->window_grab_modifiers);
+
+ /* This is for debugging, since I end up moving the Xnest
+ * otherwise ;-)
+ */
+ if (debug)
+ meta_change_button_grab (display, xwindow,
+ TRUE,
+ FALSE,
+ i, ControlMask);
+ }
+
+ /* 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 (display, xwindow,
+ TRUE,
+ FALSE,
+ 1, display->window_grab_modifiers | ShiftMask);
+ }
+}
+
+void
+meta_display_ungrab_window_buttons (MetaDisplay *display,
+ Window xwindow)
+{
+ gboolean debug;
+ int i;
+
+ if (display->window_grab_modifiers == 0)
+ return;
+
+ debug = g_getenv ("MARCO_DEBUG_BUTTON_GRABS") != NULL;
+ i = 1;
+ while (i < 4)
+ {
+ meta_change_button_grab (display, xwindow,
+ FALSE, FALSE, i,
+ display->window_grab_modifiers);
+
+ if (debug)
+ meta_change_button_grab (display, xwindow,
+ FALSE, FALSE, i, ControlMask);
+
+ ++i;
+ }
+}
+
+/* Grab buttons we only grab while unfocused in click-to-focus mode */
+#define MAX_FOCUS_BUTTON 4
+void
+meta_display_grab_focus_window_button (MetaDisplay *display,
+ MetaWindow *window)
+{
+ /* Grab button 1 for activating unfocused windows */
+ meta_verbose ("Grabbing unfocused window buttons for %s\n", window->desc);
+
+#if 0
+ /* FIXME:115072 */
+ /* Don't grab at all unless in click to focus mode. In click to
+ * focus, we may sometimes be clever about intercepting and eating
+ * the focus click. But in mouse focus, we never do that since the
+ * focus window may not be raised, and who wants to think about
+ * mouse focus anyway.
+ */
+ if (meta_prefs_get_focus_mode () != META_FOCUS_MODE_CLICK)
+ {
+ meta_verbose (" (well, not grabbing since not in click to focus mode)\n");
+ return;
+ }
+#endif
+
+ if (window->have_focus_click_grab)
+ {
+ meta_verbose (" (well, not grabbing since we already have the grab)\n");
+ 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()
+ */
+
+ {
+ int i = 1;
+ while (i < MAX_FOCUS_BUTTON)
+ {
+ meta_change_button_grab (display,
+ window->xwindow,
+ TRUE, TRUE,
+ i, 0);
+
+ ++i;
+ }
+
+ window->have_focus_click_grab = TRUE;
+ }
+}
+
+void
+meta_display_ungrab_focus_window_button (MetaDisplay *display,
+ MetaWindow *window)
+{
+ meta_verbose ("Ungrabbing unfocused window buttons for %s\n", window->desc);
+
+ if (!window->have_focus_click_grab)
+ return;
+
+ {
+ int i = 1;
+ while (i < MAX_FOCUS_BUTTON)
+ {
+ meta_change_button_grab (display, window->xwindow,
+ FALSE, FALSE, i, 0);
+
+ ++i;
+ }
+
+ window->have_focus_click_grab = FALSE;
+ }
+}
+
+void
+meta_display_increment_event_serial (MetaDisplay *display)
+{
+ /* We just make some random X request */
+ XDeleteProperty (display->xdisplay, display->leader_window,
+ display->atom__MOTIF_WM_HINTS);
+}
+
+void
+meta_display_update_active_window_hint (MetaDisplay *display)
+{
+ GSList *tmp;
+
+ gulong data[1];
+
+ if (display->focus_window)
+ data[0] = display->focus_window->xwindow;
+ else
+ data[0] = None;
+
+ tmp = display->screens;
+ while (tmp != NULL)
+ {
+ MetaScreen *screen = tmp->data;
+
+ meta_error_trap_push (display);
+ XChangeProperty (display->xdisplay, screen->xroot,
+ display->atom__NET_ACTIVE_WINDOW,
+ XA_WINDOW,
+ 32, PropModeReplace, (guchar*) data, 1);
+
+ meta_error_trap_pop (display, FALSE);
+
+ tmp = tmp->next;
+ }
+}
+
+void
+meta_display_queue_retheme_all_windows (MetaDisplay *display)
+{
+ GSList* windows;
+ GSList *tmp;
+
+ windows = meta_display_list_windows (display);
+ tmp = windows;
+ while (tmp != NULL)
+ {
+ MetaWindow *window = tmp->data;
+
+ meta_window_queue (window, META_QUEUE_MOVE_RESIZE);
+ if (window->frame)
+ {
+ window->frame->need_reapply_frame_shape = TRUE;
+
+ meta_frame_queue_draw (window->frame);
+ }
+
+ tmp = tmp->next;
+ }
+
+ g_slist_free (windows);
+}
+
+void
+meta_display_retheme_all (void)
+{
+ meta_display_queue_retheme_all_windows (meta_get_display ());
+}
+
+void
+meta_display_set_cursor_theme (const char *theme,
+ int size)
+{
+#ifdef HAVE_XCURSOR
+ GSList *tmp;
+
+ MetaDisplay *display = meta_get_display ();
+
+ XcursorSetTheme (display->xdisplay, theme);
+ XcursorSetDefaultSize (display->xdisplay, size);
+
+ tmp = display->screens;
+ while (tmp != NULL)
+ {
+ MetaScreen *screen = tmp->data;
+
+ meta_screen_update_cursor (screen);
+
+ tmp = tmp->next;
+ }
+
+#endif
+}
+
+/**
+ * Stores whether syncing is currently enabled.
+ */
+static gboolean is_syncing = FALSE;
+
+/**
+ * Returns whether X synchronisation is currently enabled.
+ *
+ * \return true if we must wait for events whenever we send X requests;
+ * false otherwise.
+ *
+ * \bug 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 Marco is running, so it's rather
+ * pointless.
+ */
+gboolean
+meta_is_syncing (void)
+{
+ return is_syncing;
+}
+
+/**
+ * A handy way to turn on synchronisation on or off for every display.
+ *
+ * \bug Of course there is only one display ever anyway, so this can
+ * be rather hugely simplified.
+ */
+void
+meta_set_syncing (gboolean setting)
+{
+ if (setting != is_syncing)
+ {
+ is_syncing = setting;
+ if (meta_get_display ())
+ XSynchronize (meta_get_display ()->xdisplay, is_syncing);
+ }
+}
+
+/**
+ * How long, in milliseconds, we should wait after pinging a window
+ * before deciding it's not going to get back to us.
+ */
+#define PING_TIMEOUT_DELAY 5000
+
+/**
+ * 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.
+ *
+ * \param data All the information about this ping. It is a MetaPingData
+ * cast to a void* in order to be passable to a timeout function.
+ * This function will also free this parameter.
+ *
+ * \return Always returns false, because this function is called as a
+ * timeout and we don't want to run the timer again.
+ *
+ * \ingroup pings
+ */
+static gboolean
+meta_display_ping_timeout (gpointer data)
+{
+ MetaPingData *ping_data;
+
+ ping_data = data;
+
+ ping_data->ping_timeout_id = 0;
+
+ meta_topic (META_DEBUG_PING,
+ "Ping %u on window %lx timed out\n",
+ ping_data->timestamp, ping_data->xwindow);
+
+ (* ping_data->ping_timeout_func) (ping_data->display, ping_data->xwindow,
+ ping_data->timestamp, ping_data->user_data);
+
+ ping_data->display->pending_pings =
+ g_slist_remove (ping_data->display->pending_pings,
+ ping_data);
+ ping_data_free (ping_data);
+
+ return FALSE;
+}
+
+/**
+ * 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.
+ *
+ * \param display The MetaDisplay that the window is on
+ * \param window The MetaWindow to send the ping to
+ * \param timestamp The timestamp of the ping. Used for uniqueness.
+ * Cannot be CurrentTime; use a real timestamp!
+ * \param ping_reply_func The callback to call if we get a response.
+ * \param ping_timeout_func The callback to call if we don't get a response.
+ * \param user_data Arbitrary data that will be passed to the callback
+ * function. (In practice it's often a pointer to
+ * the window.)
+ *
+ * \bug This should probably be a method on windows, rather than displays
+ * for one of their windows.
+ *
+ * \ingroup pings
+ */
+void
+meta_display_ping_window (MetaDisplay *display,
+ MetaWindow *window,
+ guint32 timestamp,
+ MetaWindowPingFunc ping_reply_func,
+ MetaWindowPingFunc ping_timeout_func,
+ gpointer user_data)
+{
+ MetaPingData *ping_data;
+
+ if (timestamp == CurrentTime)
+ {
+ meta_warning ("Tried to ping a window with CurrentTime! Not allowed.\n");
+ return;
+ }
+
+ if (!window->net_wm_ping)
+ {
+ if (ping_reply_func)
+ (* ping_reply_func) (display, window->xwindow, timestamp, user_data);
+
+ return;
+ }
+
+ ping_data = g_new (MetaPingData, 1);
+ ping_data->display = display;
+ ping_data->xwindow = window->xwindow;
+ ping_data->timestamp = timestamp;
+ ping_data->ping_reply_func = ping_reply_func;
+ ping_data->ping_timeout_func = ping_timeout_func;
+ ping_data->user_data = user_data;
+ ping_data->ping_timeout_id = g_timeout_add (PING_TIMEOUT_DELAY,
+ meta_display_ping_timeout,
+ ping_data);
+
+ display->pending_pings = g_slist_prepend (display->pending_pings, ping_data);
+
+ meta_topic (META_DEBUG_PING,
+ "Sending ping with timestamp %u to window %s\n",
+ timestamp, window->desc);
+ meta_window_send_icccm_message (window,
+ display->atom__NET_WM_PING,
+ timestamp);
+}
+
+static void
+process_request_frame_extents (MetaDisplay *display,
+ XEvent *event)
+{
+ /* The X window whose frame extents will be set. */
+ Window xwindow = event->xclient.window;
+ unsigned long data[4] = { 0, 0, 0, 0 };
+
+ MotifWmHints *hints = NULL;
+ gboolean hints_set = FALSE;
+
+ meta_verbose ("Setting frame extents for 0x%lx\n", xwindow);
+
+ /* See if the window is decorated. */
+ hints_set = meta_prop_get_motif_hints (display,
+ xwindow,
+ display->atom__MOTIF_WM_HINTS,
+ &hints);
+ if ((hints_set && hints->decorations) || !hints_set)
+ {
+ int top = 0;
+ int bottom = 0;
+ int left = 0;
+ int right = 0;
+
+ MetaScreen *screen;
+
+ screen = meta_display_screen_for_xwindow (display,
+ event->xclient.window);
+ if (screen == NULL)
+ {
+ meta_warning ("Received request to set _NET_FRAME_EXTENTS "
+ "on 0x%lx which is on a screen we are not managing\n",
+ event->xclient.window);
+ meta_XFree (hints);
+ return;
+ }
+
+ /* Return estimated frame extents for a normal window. */
+ meta_ui_theme_get_frame_borders (screen->ui,
+ META_FRAME_TYPE_NORMAL,
+ 0,
+ &top,
+ &bottom,
+ &left,
+ &right);
+
+ data[0] = left;
+ data[1] = right;
+ data[2] = top;
+ data[3] = bottom;
+ }
+
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Setting _NET_FRAME_EXTENTS on unmanaged window 0x%lx "
+ "to top = %lu, left = %lu, bottom = %lu, right = %lu\n",
+ xwindow, data[0], data[1], data[2], data[3]);
+
+ meta_error_trap_push (display);
+ XChangeProperty (display->xdisplay, xwindow,
+ display->atom__NET_FRAME_EXTENTS,
+ XA_CARDINAL,
+ 32, PropModeReplace, (guchar*) data, 4);
+ meta_error_trap_pop (display, FALSE);
+
+ meta_XFree (hints);
+}
+
+/**
+ * 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.
+ *
+ * \param display the display we got the pong from
+ * \param event the XEvent which is a pong; we can tell which
+ * ping it corresponds to because it bears the
+ * same timestamp.
+ *
+ * \ingroup pings
+ */
+static void
+process_pong_message (MetaDisplay *display,
+ XEvent *event)
+{
+ GSList *tmp;
+ guint32 timestamp = event->xclient.data.l[1];
+
+ meta_topic (META_DEBUG_PING, "Received a pong with timestamp %u\n",
+ timestamp);
+
+ for (tmp = display->pending_pings; tmp; tmp = tmp->next)
+ {
+ MetaPingData *ping_data = tmp->data;
+
+ if (timestamp == ping_data->timestamp)
+ {
+ meta_topic (META_DEBUG_PING,
+ "Matching ping found for pong %u\n",
+ ping_data->timestamp);
+
+ /* Remove the ping data from the list */
+ display->pending_pings = g_slist_remove (display->pending_pings,
+ ping_data);
+
+ /* Remove the timeout */
+ if (ping_data->ping_timeout_id != 0)
+ {
+ g_source_remove (ping_data->ping_timeout_id);
+ ping_data->ping_timeout_id = 0;
+ }
+
+ /* Call callback */
+ (* ping_data->ping_reply_func) (display,
+ ping_data->xwindow,
+ ping_data->timestamp,
+ ping_data->user_data);
+
+ ping_data_free (ping_data);
+
+ break;
+ }
+ }
+}
+
+/**
+ * Finds whether a window has any pings waiting on it.
+ *
+ * \param display The MetaDisplay of the window.
+ * \param window The MetaWindow whose pings we want to know about.
+ *
+ * \return True if there is at least one ping which has been sent
+ * to the window without getting a response; false otherwise.
+ *
+ * \bug This should probably be a method on windows, rather than displays
+ * for one of their windows.
+ *
+ * \ingroup pings
+ */
+gboolean
+meta_display_window_has_pending_pings (MetaDisplay *display,
+ MetaWindow *window)
+{
+ GSList *tmp;
+
+ for (tmp = display->pending_pings; tmp; tmp = tmp->next)
+ {
+ MetaPingData *ping_data = tmp->data;
+
+ if (ping_data->xwindow == window->xwindow)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+MetaGroup*
+get_focussed_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_focussed_group(w->display))))
+
+static MetaWindow*
+find_tab_forward (MetaDisplay *display,
+ MetaTabList type,
+ MetaScreen *screen,
+ 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 (window->screen == screen &&
+ IN_TAB_CHAIN (window, type))
+ return window;
+
+ tmp = tmp->next;
+ }
+
+ tmp = workspace->mru_list;
+ while (tmp != start && tmp != NULL)
+ {
+ 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,
+ MetaScreen *screen,
+ 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 (window->screen == screen &&
+ 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;
+}
+
+GList*
+meta_display_get_tab_list (MetaDisplay *display,
+ MetaTabList type,
+ MetaScreen *screen,
+ MetaWorkspace *workspace)
+{
+ GList *tab_list;
+
+ g_return_val_if_fail (workspace != NULL, NULL);
+
+ /* Windows sellout mode - MRU order. Collect unminimized windows
+ * then minimized so minimized windows aren't in the way so much.
+ */
+ {
+ GList *tmp;
+
+ tab_list = NULL;
+ tmp = workspace->mru_list;
+ while (tmp != NULL)
+ {
+ MetaWindow *window = tmp->data;
+
+ if (!window->minimized &&
+ window->screen == screen &&
+ IN_TAB_CHAIN (window, type))
+ tab_list = g_list_prepend (tab_list, window);
+
+ tmp = tmp->next;
+ }
+ }
+
+ {
+ GList *tmp;
+
+ tmp = workspace->mru_list;
+ while (tmp != NULL)
+ {
+ MetaWindow *window = tmp->data;
+
+ if (window->minimized &&
+ window->screen == screen &&
+ IN_TAB_CHAIN (window, type))
+ tab_list = g_list_prepend (tab_list, window);
+
+ tmp = tmp->next;
+ }
+ }
+
+ tab_list = g_list_reverse (tab_list);
+
+ {
+ GSList *tmp;
+ MetaWindow *l_window;
+
+ tmp = meta_display_list_windows (display);
+
+ /* Go through all windows */
+ while (tmp != NULL)
+ {
+ l_window=tmp->data;
+
+ /* Check to see if it demands attention */
+ if (l_window->wm_state_demands_attention &&
+ l_window->workspace!=workspace &&
+ IN_TAB_CHAIN (l_window, type))
+ {
+ /* if it does, add it to the popup */
+ tab_list = g_list_prepend (tab_list, l_window);
+ }
+
+ tmp = tmp->next;
+ } /* End while tmp!=NULL */
+ }
+
+ return tab_list;
+}
+
+MetaWindow*
+meta_display_get_tab_next (MetaDisplay *display,
+ MetaTabList type,
+ MetaScreen *screen,
+ MetaWorkspace *workspace,
+ MetaWindow *window,
+ gboolean backward)
+{
+ gboolean skip;
+ GList *tab_list;
+ MetaWindow *ret;
+ tab_list = meta_display_get_tab_list(display,
+ type,
+ screen,
+ workspace);
+
+ if (tab_list == NULL)
+ return NULL;
+
+ if (window != NULL)
+ {
+ g_assert (window->display == display);
+
+ if (backward)
+ ret = find_tab_backward (display, type, screen, workspace,
+ g_list_find (tab_list,
+ window),
+ TRUE);
+ else
+ ret = find_tab_forward (display, type, screen, 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, screen, workspace,
+ tab_list, skip);
+ else
+ ret = find_tab_forward (display, type, screen, workspace,
+ tab_list, skip);
+ }
+
+ g_list_free (tab_list);
+ return ret;
+}
+
+MetaWindow*
+meta_display_get_tab_current (MetaDisplay *display,
+ MetaTabList type,
+ MetaScreen *screen,
+ MetaWorkspace *workspace)
+{
+ MetaWindow *window;
+
+ window = display->focus_window;
+
+ if (window != NULL &&
+ window->screen == screen &&
+ IN_TAB_CHAIN (window, type) &&
+ (workspace == NULL ||
+ meta_window_located_on_workspace (window, workspace)))
+ return window;
+ else
+ return NULL;
+}
+
+int
+meta_resize_gravity_from_grab_op (MetaGrabOp op)
+{
+ int gravity;
+
+ gravity = -1;
+ switch (op)
+ {
+ case META_GRAB_OP_RESIZING_SE:
+ case META_GRAB_OP_KEYBOARD_RESIZING_SE:
+ gravity = NorthWestGravity;
+ break;
+ case META_GRAB_OP_KEYBOARD_RESIZING_S:
+ case META_GRAB_OP_RESIZING_S:
+ gravity = NorthGravity;
+ break;
+ case META_GRAB_OP_KEYBOARD_RESIZING_SW:
+ case META_GRAB_OP_RESIZING_SW:
+ gravity = NorthEastGravity;
+ break;
+ case META_GRAB_OP_KEYBOARD_RESIZING_N:
+ case META_GRAB_OP_RESIZING_N:
+ gravity = SouthGravity;
+ break;
+ case META_GRAB_OP_KEYBOARD_RESIZING_NE:
+ case META_GRAB_OP_RESIZING_NE:
+ gravity = SouthWestGravity;
+ break;
+ case META_GRAB_OP_KEYBOARD_RESIZING_NW:
+ case META_GRAB_OP_RESIZING_NW:
+ gravity = SouthEastGravity;
+ break;
+ case META_GRAB_OP_KEYBOARD_RESIZING_E:
+ case META_GRAB_OP_RESIZING_E:
+ gravity = WestGravity;
+ break;
+ case META_GRAB_OP_KEYBOARD_RESIZING_W:
+ case META_GRAB_OP_RESIZING_W:
+ gravity = EastGravity;
+ break;
+ case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
+ gravity = CenterGravity;
+ break;
+ default:
+ break;
+ }
+
+ return gravity;
+}
+
+static MetaScreen*
+find_screen_for_selection (MetaDisplay *display,
+ Window owner,
+ Atom selection)
+{
+ GSList *tmp;
+
+ tmp = display->screens;
+ while (tmp != NULL)
+ {
+ MetaScreen *screen = tmp->data;
+
+ if (screen->wm_sn_selection_window == owner &&
+ screen->wm_sn_atom == selection)
+ return screen;
+
+ tmp = tmp->next;
+ }
+
+ return NULL;
+}
+
+/* from fvwm2, Copyright Matthias Clasen, Dominik Vogt */
+static gboolean
+convert_property (MetaDisplay *display,
+ MetaScreen *screen,
+ Window w,
+ Atom target,
+ Atom property)
+{
+#define N_TARGETS 4
+ Atom conversion_targets[N_TARGETS];
+ long icccm_version[] = { 2, 0 };
+
+ conversion_targets[0] = display->atom_TARGETS;
+ conversion_targets[1] = display->atom_MULTIPLE;
+ conversion_targets[2] = display->atom_TIMESTAMP;
+ conversion_targets[3] = display->atom_VERSION;
+
+ meta_error_trap_push_with_return (display);
+ if (target == display->atom_TARGETS)
+ XChangeProperty (display->xdisplay, w, property,
+ XA_ATOM, 32, PropModeReplace,
+ (unsigned char *)conversion_targets, N_TARGETS);
+ else if (target == display->atom_TIMESTAMP)
+ XChangeProperty (display->xdisplay, w, property,
+ XA_INTEGER, 32, PropModeReplace,
+ (unsigned char *)&screen->wm_sn_timestamp, 1);
+ else if (target == display->atom_VERSION)
+ XChangeProperty (display->xdisplay, w, property,
+ XA_INTEGER, 32, PropModeReplace,
+ (unsigned char *)icccm_version, 2);
+ else
+ {
+ meta_error_trap_pop_with_return (display, FALSE);
+ return FALSE;
+ }
+
+ if (meta_error_trap_pop_with_return (display, FALSE) != Success)
+ return FALSE;
+
+ /* Be sure the PropertyNotify has arrived so we
+ * can send SelectionNotify
+ */
+ /* FIXME the error trap pop synced anyway, right? */
+ meta_topic (META_DEBUG_SYNC, "Syncing on %s\n", G_STRFUNC);
+ XSync (display->xdisplay, False);
+
+ return TRUE;
+}
+
+/* from fvwm2, Copyright Matthias Clasen, Dominik Vogt */
+static void
+process_selection_request (MetaDisplay *display,
+ XEvent *event)
+{
+ XSelectionEvent reply;
+ MetaScreen *screen;
+
+ screen = find_screen_for_selection (display,
+ event->xselectionrequest.owner,
+ event->xselectionrequest.selection);
+
+ if (screen == NULL)
+ {
+ char *str;
+
+ meta_error_trap_push (display);
+ str = XGetAtomName (display->xdisplay,
+ event->xselectionrequest.selection);
+ meta_error_trap_pop (display, TRUE);
+
+ meta_verbose ("Selection request with selection %s window 0x%lx not a WM_Sn selection we recognize\n",
+ str ? str : "(bad atom)", event->xselectionrequest.owner);
+
+ meta_XFree (str);
+
+ return;
+ }
+
+ reply.type = SelectionNotify;
+ reply.display = display->xdisplay;
+ reply.requestor = event->xselectionrequest.requestor;
+ reply.selection = event->xselectionrequest.selection;
+ reply.target = event->xselectionrequest.target;
+ reply.property = None;
+ reply.time = event->xselectionrequest.time;
+
+ if (event->xselectionrequest.target == display->atom_MULTIPLE)
+ {
+ if (event->xselectionrequest.property != None)
+ {
+ Atom type, *adata;
+ int i, format;
+ unsigned long num, rest;
+ unsigned char *data;
+
+ meta_error_trap_push_with_return (display);
+ if (XGetWindowProperty (display->xdisplay,
+ event->xselectionrequest.requestor,
+ event->xselectionrequest.property, 0, 256, False,
+ display->atom_ATOM_PAIR,
+ &type, &format, &num, &rest, &data) != Success)
+ {
+ meta_error_trap_pop_with_return (display, TRUE);
+ return;
+ }
+
+ if (meta_error_trap_pop_with_return (display, TRUE) == Success)
+ {
+ /* FIXME: to be 100% correct, should deal with rest > 0,
+ * but since we have 4 possible targets, we will hardly ever
+ * meet multiple requests with a length > 8
+ */
+ adata = (Atom*)data;
+ i = 0;
+ while (i < (int) num)
+ {
+ if (!convert_property (display, screen,
+ event->xselectionrequest.requestor,
+ adata[i], adata[i+1]))
+ adata[i+1] = None;
+ i += 2;
+ }
+
+ meta_error_trap_push (display);
+ XChangeProperty (display->xdisplay,
+ event->xselectionrequest.requestor,
+ event->xselectionrequest.property,
+ display->atom_ATOM_PAIR,
+ 32, PropModeReplace, data, num);
+ meta_error_trap_pop (display, FALSE);
+ meta_XFree (data);
+ }
+ }
+ }
+ else
+ {
+ if (event->xselectionrequest.property == None)
+ event->xselectionrequest.property = event->xselectionrequest.target;
+
+ if (convert_property (display, screen,
+ event->xselectionrequest.requestor,
+ event->xselectionrequest.target,
+ event->xselectionrequest.property))
+ reply.property = event->xselectionrequest.property;
+ }
+
+ XSendEvent (display->xdisplay,
+ event->xselectionrequest.requestor,
+ False, 0L, (XEvent*)&reply);
+
+ meta_verbose ("Handled selection request\n");
+}
+
+static void
+process_selection_clear (MetaDisplay *display,
+ XEvent *event)
+{
+ /* We need to unmanage the screen on which we lost the selection */
+ MetaScreen *screen;
+
+ screen = find_screen_for_selection (display,
+ event->xselectionclear.window,
+ event->xselectionclear.selection);
+
+
+ if (screen != NULL)
+ {
+ meta_verbose ("Got selection clear for screen %d on display %s\n",
+ screen->number, display->name);
+
+ meta_display_unmanage_screen (&display,
+ screen,
+ event->xselectionclear.time);
+
+ if (!display)
+ the_display = NULL;
+
+ /* display and screen may both be invalid memory... */
+
+ return;
+ }
+
+ {
+ char *str;
+
+ meta_error_trap_push (display);
+ str = XGetAtomName (display->xdisplay,
+ event->xselectionclear.selection);
+ meta_error_trap_pop (display, TRUE);
+
+ meta_verbose ("Selection clear with selection %s window 0x%lx not a WM_Sn selection we recognize\n",
+ str ? str : "(bad atom)", event->xselectionclear.window);
+
+ meta_XFree (str);
+ }
+}
+
+void
+meta_display_unmanage_screen (MetaDisplay **displayp,
+ MetaScreen *screen,
+ guint32 timestamp)
+{
+ MetaDisplay *display = *displayp;
+
+ meta_verbose ("Unmanaging screen %d on display %s\n",
+ screen->number, display->name);
+
+ g_return_if_fail (g_slist_find (display->screens, screen) != NULL);
+
+ meta_screen_free (screen, timestamp);
+ display->screens = g_slist_remove (display->screens, screen);
+
+ if (display->screens == NULL)
+ {
+ meta_display_close (display, timestamp);
+ *displayp = NULL;
+ }
+}
+
+void
+meta_display_unmanage_windows_for_screen (MetaDisplay *display,
+ MetaScreen *screen,
+ guint32 timestamp)
+{
+ GSList *tmp;
+ GSList *winlist;
+
+ winlist = meta_display_list_windows (display);
+ winlist = g_slist_sort (winlist, meta_display_stack_cmp);
+
+ /* Unmanage all windows */
+ tmp = winlist;
+ while (tmp != NULL)
+ {
+ meta_window_free (tmp->data, timestamp);
+
+ 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;
+
+ if (aw->screen == bw->screen)
+ return meta_stack_windows_cmp (aw->screen->stack, aw, bw);
+ /* Then assume screens are stacked by number */
+ else if (aw->screen->number < bw->screen->number)
+ return -1;
+ else if (aw->screen->number > bw->screen->number)
+ return 1;
+ else
+ return 0; /* not reached in theory, if windows on same display */
+}
+
+void
+meta_display_devirtualize_modifiers (MetaDisplay *display,
+ 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 |= display->meta_mask;
+ if (modifiers & META_VIRTUAL_HYPER_MASK)
+ *mask |= display->hyper_mask;
+ if (modifiers & META_VIRTUAL_SUPER_MASK)
+ *mask |= display->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
+update_window_grab_modifiers (MetaDisplay *display)
+
+{
+ MetaVirtualModifier virtual_mods;
+ unsigned int mods;
+
+ virtual_mods = meta_prefs_get_mouse_button_mods ();
+ meta_display_devirtualize_modifiers (display, virtual_mods,
+ &mods);
+
+ display->window_grab_modifiers = mods;
+}
+
+static void
+prefs_changed_callback (MetaPreference pref,
+ void *data)
+{
+ MetaDisplay *display = data;
+
+ /* It may not be obvious why we regrab on focus mode
+ * change; it's because we handle focus clicks a
+ * bit differently for the different focus modes.
+ */
+ if (pref == META_PREF_MOUSE_BUTTON_MODS ||
+ pref == META_PREF_FOCUS_MODE)
+ {
+ MetaDisplay *display = data;
+ GSList *windows;
+ GSList *tmp;
+
+ windows = meta_display_list_windows (display);
+
+ /* Ungrab all */
+ tmp = windows;
+ while (tmp != NULL)
+ {
+ MetaWindow *w = tmp->data;
+ meta_display_ungrab_window_buttons (display, w->xwindow);
+ meta_display_ungrab_focus_window_button (display, w);
+ tmp = tmp->next;
+ }
+
+ /* change our modifier */
+ if (pref == META_PREF_MOUSE_BUTTON_MODS)
+ update_window_grab_modifiers (display);
+
+ /* Grab all */
+ tmp = windows;
+ while (tmp != NULL)
+ {
+ MetaWindow *w = tmp->data;
+ if (w->type != META_WINDOW_DOCK)
+ {
+ meta_display_grab_focus_window_button (display, w);
+ meta_display_grab_window_buttons (display, w->xwindow);
+ }
+ tmp = tmp->next;
+ }
+
+ g_slist_free (windows);
+ }
+ else if (pref == META_PREF_AUDIBLE_BELL)
+ {
+ meta_bell_set_audible (display, meta_prefs_bell_is_audible ());
+ }
+ else if (pref == META_PREF_COMPOSITING_MANAGER)
+ {
+ gboolean cm = meta_prefs_get_compositing_manager ();
+
+ if (cm)
+ enable_compositor (display, TRUE);
+ else
+ disable_compositor (display);
+ }
+}
+
+void
+meta_display_increment_focus_sentinel (MetaDisplay *display)
+{
+ unsigned long data[1];
+
+ data[0] = meta_display_get_current_time (display);
+
+ XChangeProperty (display->xdisplay,
+ ((MetaScreen*) display->screens->data)->xroot,
+ display->atom__MARCO_SENTINEL,
+ XA_CARDINAL,
+ 32, PropModeReplace, (guchar*) data, 1);
+
+ display->sentinel_counter += 1;
+}
+
+void
+meta_display_decrement_focus_sentinel (MetaDisplay *display)
+{
+ display->sentinel_counter -= 1;
+
+ if (display->sentinel_counter < 0)
+ display->sentinel_counter = 0;
+}
+
+gboolean
+meta_display_focus_sentinel_clear (MetaDisplay *display)
+{
+ return (display->sentinel_counter == 0);
+}
+
+static void
+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...\n",
+ 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...\n",
+ display->last_user_time, timestamp);
+ display->last_user_time = timestamp;
+
+ windows = meta_display_list_windows (display);
+ 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...\n",
+ window->desc, window->net_wm_user_time);
+ window->net_wm_user_time = timestamp;
+ }
+
+ tmp = tmp->next;
+ }
+
+ g_slist_free (windows);
+ }
+}
+
+static gboolean
+timestamp_too_old (MetaDisplay *display,
+ 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 (though we would want to rename to
+ * timestamp_too_old_or_in_future).
+ */
+
+ if (*timestamp == CurrentTime)
+ {
+ meta_warning ("Got a request to focus %s with a timestamp of 0. This "
+ "shouldn't happen!\n",
+ window ? window->desc : "the no_focus_window");
+ meta_print_backtrace ();
+ *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))
+ {
+ meta_topic (META_DEBUG_FOCUS,
+ "Ignoring focus request for %s since %u "
+ "is less than %u and %u.\n",
+ window ? window->desc : "the no_focus_window",
+ *timestamp,
+ display->last_user_time,
+ display->last_focus_time);
+ return TRUE;
+ }
+ else
+ {
+ meta_topic (META_DEBUG_FOCUS,
+ "Received focus request for %s which is newer than most "
+ "recent user_time, but less recent than "
+ "last_focus_time (%u < %u < %u); adjusting "
+ "accordingly. (See bug 167358)\n",
+ window ? window->desc : "the no_focus_window",
+ display->last_user_time,
+ *timestamp,
+ display->last_focus_time);
+ *timestamp = display->last_focus_time;
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+void
+meta_display_set_input_focus_window (MetaDisplay *display,
+ MetaWindow *window,
+ gboolean focus_frame,
+ guint32 timestamp)
+{
+ if (timestamp_too_old (display, window, &timestamp))
+ return;
+
+ meta_error_trap_push (display);
+ XSetInputFocus (display->xdisplay,
+ focus_frame ? window->frame->xwindow : window->xwindow,
+ RevertToPointerRoot,
+ timestamp);
+ meta_error_trap_pop (display, FALSE);
+
+ display->expected_focus_window = window;
+ display->last_focus_time = timestamp;
+ display->active_screen = window->screen;
+
+ if (window != display->autoraise_window)
+ meta_display_remove_autoraise_callback (window->display);
+}
+
+void
+meta_display_focus_the_no_focus_window (MetaDisplay *display,
+ MetaScreen *screen,
+ guint32 timestamp)
+{
+ if (timestamp_too_old (display, NULL, &timestamp))
+ return;
+
+ XSetInputFocus (display->xdisplay,
+ screen->no_focus_window,
+ RevertToPointerRoot,
+ timestamp);
+ display->expected_focus_window = NULL;
+ display->last_focus_time = timestamp;
+ display->active_screen = screen;
+
+ meta_display_remove_autoraise_callback (display);
+}
+
+void
+meta_display_remove_autoraise_callback (MetaDisplay *display)
+{
+ if (display->autoraise_timeout_id != 0)
+ {
+ g_source_remove (display->autoraise_timeout_id);
+ display->autoraise_timeout_id = 0;
+ display->autoraise_window = NULL;
+ }
+}
+
+#ifdef HAVE_COMPOSITE_EXTENSIONS
+void
+meta_display_get_compositor_version (MetaDisplay *display,
+ int *major,
+ int *minor)
+{
+ *major = display->composite_major_version;
+ *minor = display->composite_minor_version;
+}
+#endif
+
+Display *
+meta_display_get_xdisplay (MetaDisplay *display)
+{
+ return display->xdisplay;
+}
+
+MetaCompositor *
+meta_display_get_compositor (MetaDisplay *display)
+{
+ return display->compositor;
+}
+
+GSList *
+meta_display_get_screens (MetaDisplay *display)
+{
+ return display->screens;
+}
+
+gboolean
+meta_display_has_shape (MetaDisplay *display)
+{
+ return META_DISPLAY_HAS_SHAPE (display);
+}
+
+MetaWindow *
+meta_display_get_focus_window (MetaDisplay *display)
+{
+ return display->focus_window;
+}
+
+#ifdef HAVE_COMPOSITE_EXTENSIONS
+int
+meta_display_get_damage_event_base (MetaDisplay *display)
+{
+ return display->damage_event_base;
+}
+#endif
+
+#ifdef HAVE_COMPOSITE_EXTENSIONS
+#ifdef HAVE_SHAPE
+int
+meta_display_get_shape_event_base (MetaDisplay *display)
+{
+ return display->shape_event_base;
+}
+#endif
+#endif