summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHavoc Pennington <hp@pobox.com>2002-06-08 23:55:27 +0000
committerHavoc Pennington <hp@src.gnome.org>2002-06-08 23:55:27 +0000
commit31b211550f557947f67ef42dbdf2875c1622eafa (patch)
treed3a0e2f2405dfcda146d156fe0a5b2fd3cea08f8
parent538a06fd55b969f4649c8c6f003c5ac1b56a7d95 (diff)
downloadmetacity-31b211550f557947f67ef42dbdf2875c1622eafa.tar.gz
don't die on bad atom name
2002-06-08 Havoc Pennington <hp@pobox.com> * src/xprops.c (meta_prop_get_utf8_string): don't die on bad atom name * src/display.c (meta_display_close): don't unmanage windows here, do it in screen_free and then closing the display unmanages windows as a side effect of unmanaging the screen (meta_display_unmanage_screen): new function (process_selection_clear, process_selection_request): handle selection stuff (meta_spew_event): don't crash on client message containing invalid atom (meta_spew_event): don't crash on property notify with invalid atom * src/main.c (main): add --replace option to replace existing window manager. * src/screen.c: implement holding manager selection. * src/display.c (meta_display_open): add new selection-related atoms.
-rw-r--r--ChangeLog24
-rw-r--r--src/display.c294
-rw-r--r--src/display.h12
-rw-r--r--src/main.c4
-rw-r--r--src/screen.c135
-rw-r--r--src/screen.h4
-rw-r--r--src/util.c13
-rw-r--r--src/util.h3
-rw-r--r--src/xprops.c2
9 files changed, 463 insertions, 28 deletions
diff --git a/ChangeLog b/ChangeLog
index 770cb563..3a283928 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,29 @@
2002-06-08 Havoc Pennington <hp@pobox.com>
+ * src/xprops.c (meta_prop_get_utf8_string): don't die on bad atom
+ name
+
+ * src/display.c (meta_display_close): don't unmanage windows here,
+ do it in screen_free and then closing the display unmanages
+ windows as a side effect of unmanaging the screen
+ (meta_display_unmanage_screen): new function
+ (process_selection_clear, process_selection_request): handle
+ selection stuff
+ (meta_spew_event): don't crash on client message containing
+ invalid atom
+ (meta_spew_event): don't crash on property notify with invalid
+ atom
+
+ * src/main.c (main): add --replace option to replace existing
+ window manager.
+
+ * src/screen.c: implement holding manager selection.
+
+ * src/display.c (meta_display_open): add new selection-related
+ atoms.
+
+2002-06-08 Havoc Pennington <hp@pobox.com>
+
* src/screen.c (meta_screen_new): select keypress/keyrelease
events on root window, this may fix the bug where keybindings
didn't work if you didn't have a focused window.
diff --git a/src/display.c b/src/display.c
index 53d9b640..1dcdb948 100644
--- a/src/display.c
+++ b/src/display.c
@@ -71,7 +71,10 @@ static guint32 event_get_time (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 gint
unsigned_long_equal (gconstpointer v1,
@@ -217,7 +220,13 @@ meta_display_open (const char *name)
"WM_CLIENT_MACHINE",
"_NET_WM_WORKAREA",
"_NET_SHOW_DESKTOP",
- "_NET_DESKTOP_LAYOUT"
+ "_NET_DESKTOP_LAYOUT",
+ "MANAGER",
+ "TARGETS",
+ "MULTIPLE",
+ "TIMESTAMP",
+ "VERSION",
+ "ATOM_PAIR"
};
Atom atoms[G_N_ELEMENTS(atom_names)];
@@ -340,6 +349,12 @@ meta_display_open (const char *name)
display->atom_net_wm_workarea = atoms[56];
display->atom_net_show_desktop = atoms[57];
display->atom_net_desktop_layout = atoms[58];
+ display->atom_manager = atoms[59];
+ display->atom_targets = atoms[60];
+ display->atom_multiple = atoms[61];
+ display->atom_timestamp = atoms[62];
+ display->atom_version = atoms[63];
+ display->atom_atom_pair = atoms[64];
/* Offscreen unmapped window used for _NET_SUPPORTING_WM_CHECK,
* created in screen_new
@@ -529,26 +544,11 @@ meta_display_list_windows (MetaDisplay *display)
void
meta_display_close (MetaDisplay *display)
{
- GSList *winlist;
GSList *tmp;
if (display->error_traps > 0)
meta_bug ("Display closed with error traps pending\n");
- winlist = meta_display_list_windows (display);
-
- /* Unmanage all windows */
- meta_display_grab (display);
- tmp = winlist;
- while (tmp != NULL)
- {
- meta_window_free (tmp->data);
-
- tmp = tmp->next;
- }
- g_slist_free (winlist);
- meta_display_ungrab (display);
-
if (display->autoraise_timeout_id != 0)
{
g_source_remove (display->autoraise_timeout_id);
@@ -1373,8 +1373,18 @@ event_callback (XEvent *event,
}
break;
case SelectionClear:
+ /* do this here instead of at end of function
+ * so we can return
+ */
+ 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;
break;
case SelectionRequest:
+ process_selection_request (display, event);
break;
case SelectionNotify:
break;
@@ -1899,7 +1909,7 @@ meta_spew_event (MetaDisplay *display,
extra = g_strdup_printf ("atom: %s state: %s",
str ? str : "(unknown atom)",
state);
- XFree (str);
+ meta_XFree (str);
}
break;
case SelectionClear:
@@ -1925,7 +1935,7 @@ meta_spew_event (MetaDisplay *display,
extra = g_strdup_printf ("type: %s format: %d\n",
str ? str : "(unknown atom)",
event->xclient.format);
- XFree (str);
+ meta_XFree (str);
}
break;
case MappingNotify:
@@ -2989,3 +2999,249 @@ meta_rectangle_intersect (MetaRectangle *src1,
return return_val;
}
+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 Dominik Vogt I assume, but it
+ * was unmarked.
+ */
+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 (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 (display);
+ return FALSE;
+ }
+
+ if (meta_error_trap_pop (display) != Success)
+ return FALSE;
+
+ /* Be sure the PropertyNotify has arrived so we
+ * can send SelectionNotify
+ */
+ XSync (display->xdisplay, False);
+
+ return TRUE;
+}
+
+/* from fvwm2, Copyright Dominik Vogt I assume, but it
+ * was unmarked.
+ */
+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);
+
+ 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 (display);
+ XGetWindowProperty (display->xdisplay,
+ event->xselectionrequest.requestor,
+ event->xselectionrequest.property, 0, 256, False,
+ display->atom_atom_pair,
+ &type, &format, &num, &rest, &data);
+ if (meta_error_trap_pop (display) == 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);
+ 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);
+
+ /* 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);
+
+ 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 *display,
+ MetaScreen *screen)
+{
+ 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);
+ display->screens = g_slist_remove (display->screens, screen);
+
+ if (display->screens == NULL)
+ meta_display_close (display);
+}
+
+void
+meta_display_unmanage_windows_for_screen (MetaDisplay *display,
+ MetaScreen *screen)
+{
+ GSList *tmp;
+ GSList *winlist;
+
+ winlist = meta_display_list_windows (display);
+
+ /* Unmanage all windows */
+ tmp = winlist;
+ while (tmp != NULL)
+ {
+ meta_window_free (tmp->data);
+
+ tmp = tmp->next;
+ }
+ g_slist_free (winlist);
+}
diff --git a/src/display.h b/src/display.h
index 92a94a09..52b8292f 100644
--- a/src/display.h
+++ b/src/display.h
@@ -131,6 +131,12 @@ struct _MetaDisplay
Atom atom_net_wm_workarea;
Atom atom_net_show_desktop;
Atom atom_net_desktop_layout;
+ Atom atom_manager;
+ Atom atom_targets;
+ Atom atom_multiple;
+ Atom atom_timestamp;
+ Atom atom_version;
+ Atom atom_atom_pair;
/* This is the actual window from focus events,
* not the one we last set
@@ -225,6 +231,12 @@ void meta_display_grab (MetaDisplay *display);
void meta_display_ungrab (MetaDisplay *display);
gboolean meta_display_is_double_click (MetaDisplay *display);
+void meta_display_unmanage_screen (MetaDisplay *display,
+ MetaScreen *screen);
+
+void meta_display_unmanage_windows_for_screen (MetaDisplay *display,
+ MetaScreen *screen);
+
/* A given MetaWindow may have various X windows that "belong"
* to it, such as the frame window.
*/
diff --git a/src/main.c b/src/main.c
index d5009e2a..c0522059 100644
--- a/src/main.c
+++ b/src/main.c
@@ -127,7 +127,9 @@ main (int argc, char **argv)
strcmp (arg, "-?") == 0)
usage ();
else if (strcmp (arg, "--sm-disable") == 0)
- disable_sm = TRUE;
+ disable_sm = TRUE;
+ else if (strcmp (arg, "--replace") == 0)
+ meta_set_replace_current_wm (TRUE);
else if (strstr (arg, "--display=") == arg)
{
const char *disp;
diff --git a/src/screen.c b/src/screen.c
index 758ddd55..e5b68ca1 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -1,7 +1,10 @@
/* Metacity X screen handler */
/*
- * Copyright (C) 2001 Havoc Pennington
+ * Copyright (C) 2001, 2002 Havoc Pennington
+ * Copyright (C) 2002 Red Hat Inc.
+ * Some ICCCM manager selection code derived from fvwm2,
+ * Copyright (C) 2001 Dominik Vogt and fvwm2 team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -38,6 +41,7 @@
#include <X11/Xatom.h>
#include <locale.h>
#include <string.h>
+#include <stdio.h>
static char* get_screen_name (MetaDisplay *display,
int number);
@@ -168,6 +172,14 @@ meta_screen_new (MetaDisplay *display,
Window xroot;
Display *xdisplay;
XWindowAttributes attr;
+ Window new_wm_sn_owner;
+ Window current_wm_sn_owner;
+ gboolean replace_current_wm;
+ Atom wm_sn_atom;
+ char buf[128];
+ Time manager_timestamp;
+
+ replace_current_wm = meta_get_replace_current_wm ();
/* Only display->name, display->xdisplay, and display->error_traps
* can really be used in this function, since normally screens are
@@ -191,7 +203,98 @@ meta_screen_new (MetaDisplay *display,
return NULL;
}
- /* Select our root window events */
+ sprintf (buf, "WM_S%d", number);
+ wm_sn_atom = XInternAtom (xdisplay, buf, False);
+
+ current_wm_sn_owner = XGetSelectionOwner (xdisplay, wm_sn_atom);
+
+ if (current_wm_sn_owner != None)
+ {
+ XSetWindowAttributes attrs;
+
+ if (!replace_current_wm)
+ {
+ meta_warning (_("Screen %d on display \"%s\" already has a window manager; try using the --replace option to override the current window manager.\n"),
+ number, display->name);
+
+ return NULL;
+ }
+
+ /* We want to find out when the current selection owner dies */
+ meta_error_trap_push (display);
+ attrs.event_mask = StructureNotifyMask;
+ XChangeWindowAttributes (xdisplay,
+ current_wm_sn_owner, CWEventMask, &attrs);
+ if (meta_error_trap_pop (display) != Success)
+ current_wm_sn_owner = None; /* don't wait for it to die later on */
+ }
+
+ new_wm_sn_owner = XCreateSimpleWindow (xdisplay, xroot,
+ -100, -100, 1, 1, 0, 0, 0);
+
+ {
+ /* Generate a timestamp */
+ XSetWindowAttributes attrs;
+ XEvent event;
+
+ attrs.event_mask = PropertyChangeMask;
+ XChangeWindowAttributes (xdisplay, new_wm_sn_owner, CWEventMask, &attrs);
+
+ XChangeProperty (xdisplay,
+ new_wm_sn_owner, XA_WM_CLASS, XA_STRING, 8,
+ PropModeAppend, NULL, 0);
+ XWindowEvent (xdisplay, new_wm_sn_owner, PropertyChangeMask, &event);
+ attrs.event_mask = NoEventMask;
+ XChangeWindowAttributes (display->xdisplay,
+ new_wm_sn_owner, CWEventMask, &attrs);
+
+ manager_timestamp = event.xproperty.time;
+ }
+
+ XSetSelectionOwner (xdisplay, wm_sn_atom, new_wm_sn_owner,
+ manager_timestamp);
+
+ if (XGetSelectionOwner (xdisplay, wm_sn_atom) != new_wm_sn_owner)
+ {
+ meta_warning (_("Could not acquire window manager selection on screen %d display \"%s\"\n"),
+ number, display->name);
+
+ XDestroyWindow (xdisplay, new_wm_sn_owner);
+
+ return NULL;
+ }
+
+ {
+ /* Send client message indicating that we are now the WM */
+ XClientMessageEvent ev;
+
+ ev.type = ClientMessage;
+ ev.window = xroot;
+ ev.message_type = display->atom_manager;
+ ev.format = 32;
+ ev.data.l[0] = manager_timestamp;
+ ev.data.l[1] = wm_sn_atom;
+
+ XSendEvent (xdisplay, xroot, False, StructureNotifyMask, (XEvent*)&ev);
+ }
+
+ /* Wait for old window manager to go away */
+ if (current_wm_sn_owner != None)
+ {
+ XEvent event;
+
+ /* We sort of block infinitely here which is probably lame. */
+
+ meta_verbose ("Waiting for old window manager to exit\n");
+ do
+ {
+ XWindowEvent (xdisplay, current_wm_sn_owner,
+ StructureNotifyMask, &event);
+ }
+ while (event.type != DestroyNotify);
+ }
+
+ /* select our root window events */
meta_error_trap_push (display);
/* We need to or with the existing event mask since
@@ -208,13 +311,16 @@ meta_screen_new (MetaDisplay *display,
FocusChangeMask | attr.your_event_mask);
if (meta_error_trap_pop (display) != Success)
{
- meta_warning (_("Screen %d on display '%s' already has a window manager\n"),
+ meta_warning (_("Screen %d on display \"%s\" already has a window manager\n"),
number, display->name);
+
+ XDestroyWindow (xdisplay, new_wm_sn_owner);
+
return NULL;
}
screen = g_new (MetaScreen, 1);
-
+
screen->display = display;
screen->number = number;
screen->screen_name = get_screen_name (display, number);
@@ -226,6 +332,10 @@ meta_screen_new (MetaDisplay *display,
screen->default_xvisual = DefaultVisualOfScreen (screen->xscreen);
screen->default_depth = DefaultDepthOfScreen (screen->xscreen);
+ screen->wm_sn_selection_window = new_wm_sn_owner;
+ screen->wm_sn_atom = wm_sn_atom;
+ screen->wm_sn_timestamp = manager_timestamp;
+
screen->work_area_idle = 0;
screen->rows_of_workspaces = 1;
@@ -385,7 +495,15 @@ meta_screen_new (MetaDisplay *display,
void
meta_screen_free (MetaScreen *screen)
-{
+{
+ MetaDisplay *display;
+
+ display = screen->display;
+
+ meta_display_grab (display);
+
+ meta_display_unmanage_windows_for_screen (display, screen);
+
meta_prefs_remove_listener (prefs_changed_callback, screen);
meta_screen_ungrab_keys (screen);
@@ -397,14 +515,19 @@ meta_screen_free (MetaScreen *screen)
meta_error_trap_push (screen->display);
XSelectInput (screen->display->xdisplay, screen->xroot, 0);
if (meta_error_trap_pop (screen->display) != Success)
- meta_warning (_("Could not release screen %d on display '%s'\n"),
+ meta_warning (_("Could not release screen %d on display \"%s\"\n"),
screen->number, screen->display->name);
+ XDestroyWindow (screen->display->xdisplay,
+ screen->wm_sn_selection_window);
+
if (screen->work_area_idle != 0)
g_source_remove (screen->work_area_idle);
g_free (screen->screen_name);
g_free (screen);
+
+ meta_display_ungrab (display);
}
void
diff --git a/src/screen.h b/src/screen.h
index f9782037..b76d7ac1 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -60,6 +60,10 @@ struct _MetaScreen
MetaCursor current_cursor;
+ Window wm_sn_selection_window;
+ Atom wm_sn_atom;
+ Time wm_sn_timestamp;
+
MetaXineramaScreenInfo *xinerama_infos;
int n_xinerama_infos;
diff --git a/src/util.c b/src/util.c
index a48edf44..3c5c9855 100644
--- a/src/util.c
+++ b/src/util.c
@@ -31,6 +31,7 @@
static gboolean is_verbose = FALSE;
static gboolean is_debugging = FALSE;
+static gboolean replace_current = FALSE;
static int no_prefix = 0;
static FILE* logfile = NULL;
@@ -109,6 +110,18 @@ meta_set_debugging (gboolean setting)
is_debugging = setting;
}
+gboolean
+meta_get_replace_current_wm (void)
+{
+ return replace_current;
+}
+
+void
+meta_set_replace_current_wm (gboolean setting)
+{
+ replace_current = setting;
+}
+
void
meta_debug_spew (const char *format, ...)
{
diff --git a/src/util.h b/src/util.h
index 52b87c97..2ba94d5f 100644
--- a/src/util.h
+++ b/src/util.h
@@ -30,7 +30,8 @@ gboolean meta_is_debugging (void);
void meta_set_debugging (gboolean setting);
gboolean meta_is_syncing (void);
void meta_set_syncing (gboolean setting);
-
+gboolean meta_get_replace_current_wm (void);
+void meta_set_replace_current_wm (gboolean setting);
void meta_debug_spew (const char *format,
...) G_GNUC_PRINTF (1, 2);
diff --git a/src/xprops.c b/src/xprops.c
index f5cd838a..bb539c30 100644
--- a/src/xprops.c
+++ b/src/xprops.c
@@ -280,7 +280,7 @@ meta_prop_get_utf8_string (MetaDisplay *display,
name = XGetAtomName (display->xdisplay, xatom);
meta_warning (_("Property %s on window 0x%lx contained invalid UTF-8\n"),
name, xwindow);
- XFree (name);
+ meta_XFree (name);
XFree (str);
return FALSE;