You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5663 lines
172 KiB

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* Muffin 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., 51 Franklin Street - Suite 500, Boston, MA
* 02110-1335, USA.
*/
/**
* SECTION:display
* @title: MetaDisplay
* @short_description: Handles operations on an X display.
*
* The display is represented as a MetaDisplay struct.
*/
#define _XOPEN_SOURCE 600 /* for gethostname() */
#include <config.h>
#include "display-private.h"
#include <meta/util.h>
#include <meta/main.h>
#include "screen-private.h"
#include "window-private.h"
#include "window-props.h"
#include "group-props.h"
#include "frame.h"
#include <meta/errors.h>
#include "keybindings-private.h"
#include <meta/prefs.h>
#include "resizepopup.h"
#include "xprops.h"
#include "workspace-private.h"
#include "bell.h"
#include <meta/compositor.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include "muffin-enum-types.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_XKB
#include <X11/XKBlib.h>
#endif
#ifdef HAVE_XCURSOR
#include <X11/Xcursor/Xcursor.h>
#endif
#include <X11/extensions/Xrender.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xdamage.h>
#include <X11/extensions/Xfixes.h>
#include <string.h>
#include <unistd.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;
G_DEFINE_TYPE(MetaDisplay, meta_display, G_TYPE_OBJECT);
/* Signals */
enum
{
FOCUS_WINDOW,
WINDOW_CREATED,
WINDOW_DEMANDS_ATTENTION,
WINDOW_MARKED_URGENT,
GRAB_OP_BEGIN,
GRAB_OP_END,
ZOOM_SCROLL_IN,
ZOOM_SCROLL_OUT,
BELL,
LAST_SIGNAL
};
enum {
PROP_0,
PROP_FOCUS_WINDOW
};
static guint display_signals [LAST_SIGNAL] = { 0 };
/*
* 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 update_mouse_zoom_modifiers (MetaDisplay *display);
static void prefs_changed_callback (MetaPreference pref,
void *data);
static void sanity_check_timestamps (MetaDisplay *display,
guint32 known_good_timestamp);
static MetaGroup* get_focussed_group (MetaDisplay *display);
static void
meta_display_get_property(GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaDisplay *display = META_DISPLAY (object);
switch (prop_id)
{
case PROP_FOCUS_WINDOW:
g_value_set_object (value, display->focus_window);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_display_set_property(GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_display_class_init (MetaDisplayClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = meta_display_get_property;
object_class->set_property = meta_display_set_property;
display_signals[WINDOW_CREATED] =
g_signal_new ("window-created",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 1, META_TYPE_WINDOW);
display_signals[WINDOW_DEMANDS_ATTENTION] =
g_signal_new ("window-demands-attention",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 1, META_TYPE_WINDOW);
display_signals[WINDOW_MARKED_URGENT] =
g_signal_new ("window-marked-urgent",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 1,
META_TYPE_WINDOW);
display_signals[GRAB_OP_BEGIN] =
g_signal_new ("grab-op-begin",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 3,
META_TYPE_SCREEN,
META_TYPE_WINDOW,
META_TYPE_GRAB_OP);
display_signals[GRAB_OP_END] =
g_signal_new ("grab-op-end",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 3,
META_TYPE_SCREEN,
META_TYPE_WINDOW,
META_TYPE_GRAB_OP);
display_signals[ZOOM_SCROLL_IN] =
g_signal_new ("zoom-scroll-in",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
display_signals[ZOOM_SCROLL_OUT] =
g_signal_new ("zoom-scroll-out",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
display_signals[BELL] =
g_signal_new ("bell",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 1, META_TYPE_WINDOW);
g_object_class_install_property (object_class,
PROP_FOCUS_WINDOW,
g_param_spec_object ("focus-window",
"Focus window",
"Currently focused window",
META_TYPE_WINDOW,
G_PARAM_READABLE));
}
/*
* 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);
ping_data->ping_timeout_id = 0;
}
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);
}
#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
meta_display_init (MetaDisplay *disp)
{
/* Some stuff could go in here that's currently in _open,
* but it doesn't really matter. */
}
/*
* 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
*/
LOCAL_SYMBOL gboolean
meta_display_open (void)
{
Display *xdisplay;
GSList *screens;
GSList *tmp;
int i;
guint32 timestamp;
char buf[257];
/* A list of all atom names, so that we can intern them in one go. */
char *atom_names[] = {
#define item(x) #x,
#include <meta/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_object_new (META_TYPE_DISPLAY, NULL);
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;
if (gethostname (buf, sizeof(buf)-1) == 0)
{
buf[sizeof(buf)-1] = '\0';
the_display->hostname = g_strdup (buf);
}
else
the_display->hostname = NULL;
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 */
the_display->rebuild_keybinding_idle_id = 0;
/* FIXME copy the checks from GDK probably */
the_display->static_gravity_works = g_getenv ("MUFFIN_USE_STATIC_GRAVITY") != NULL;
meta_bell_init (the_display);
meta_display_init_keys (the_display);
update_window_grab_modifiers (the_display);
update_mouse_zoom_modifiers (the_display);
the_display->mouse_zoom_enabled = meta_prefs_get_mouse_zoom_enabled ();
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 <meta/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->monitor_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_CROSSING_SERIALS)
{
the_display->ignored_crossing_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_window = NULL;
the_display->grab_screen = NULL;
the_display->grab_resize_popup = NULL;
the_display->grab_tile_mode = META_TILE_NONE;
the_display->grab_tile_monitor_number = -1;
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;
XSyncSetPriority (the_display->xdisplay, None, 10);
}
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 */
{
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);
}
{
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);
}
#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,
"Mutter (Muffin)");
/* The GNOME keybindings capplet should include both the Muffin and Metacity
* keybindings */
meta_prop_set_utf8_string_hint (the_display,
the_display->leader_window,
the_display->atom__GNOME_WM_KEYBINDINGS,
"Muffin,Metacity");
meta_prop_set_utf8_string_hint (the_display,
the_display->leader_window,
the_display->atom__MUFFIN_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 */
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);
}
meta_display_ungrab (the_display);
/* Done opening new display */
the_display->display_opening = FALSE;
return TRUE;
}
static gint
ptrcmp (gconstpointer a, gconstpointer b)
{
if (a < b)
return -1;
else if (a > b)
return 1;
else
return 0;
}
/**
* meta_display_list_windows:
* @display: a #MetaDisplay
* @flags: options for listing
*
* Lists windows for the display, the @flags parameter for
* now determines whether override-redirect windows will be
* included.
*
* Return value: (transfer container) (element-type MetaWindow): the list of windows.
*/
GSList*
meta_display_list_windows (MetaDisplay *display,
MetaListWindowsFlags flags)
{
GSList *winlist;
GSList *tmp;
GSList *prev;
GHashTableIter iter;
gpointer key, value;
winlist = NULL;
g_hash_table_iter_init (&iter, display->window_ids);
while (g_hash_table_iter_next (&iter, &key, &value))
{
MetaWindow *window = value;
if (!window->override_redirect ||
(flags & META_LIST_INCLUDE_OVERRIDE_REDIRECT) != 0)
winlist = g_slist_prepend (winlist, window);
}
/* Uniquify the list, since both frame windows and plain
* windows are in the hash
*/
winlist = g_slist_sort (winlist, ptrcmp);
prev = NULL;
tmp = winlist;
while (tmp != NULL)
{
GSList *next;
next = tmp->next;
if (next &&
next->data == tmp->data)
{
/* Delete tmp from list */
if (prev)
prev->next = next;
if (tmp == winlist)
winlist = next;
g_slist_free_1 (tmp);
/* leave prev unchanged */
}
else
{
prev = tmp;
}
tmp = next;
}
return winlist;
}
LOCAL_SYMBOL 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_unmanage() 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->hostname);
g_free (display->name);
meta_display_shutdown_keys (display);
if (display->compositor)
meta_compositor_destroy (display->compositor);
g_object_unref (display);
the_display = NULL;
meta_quit (META_EXIT_SUCCESS);
}
/**
* meta_display_screen_for_root:
* @display: a #MetaDisplay
*
* Return the #MetaScreen corresponding to a specified X root window ID.
*
* Return Value: (transfer none): the screen for the specified root window ID, or %NULL
*/
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;
}
LOCAL_SYMBOL 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);
/* 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;