Commit 31b21155 authored by Havoc Pennington's avatar Havoc Pennington Committed by Havoc Pennington
Browse files

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.
parent 538a06fd
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
......
......@@ -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);
}
......@@ -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.
*/
......
......@@ -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;
......
/* 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
......
......@@ -60,6 +60,10 @@ struct _MetaScreen
MetaCursor current_cursor;
Window wm_sn_selection_window;
Atom wm_sn_atom;
Time wm_sn_timestamp;