Merge branch 'backport' into work
[dana/openbox.git] / openbox / client.c
index d768ecc..fe67b78 100644 (file)
 #include "debug.h"
 #include "startupnotify.h"
 #include "dock.h"
-#include "xerror.h"
 #include "screen.h"
 #include "moveresize.h"
+#include "ping.h"
 #include "place.h"
-#include "prop.h"
-#include "extensions.h"
 #include "frame.h"
 #include "session.h"
 #include "event.h"
 #include "grab.h"
+#include "prompt.h"
 #include "focus.h"
 #include "stacking.h"
 #include "openbox.h"
@@ -41,6 +40,8 @@
 #include "mouse.h"
 #include "render/render.h"
 #include "gettext.h"
+#include "obt/display.h"
+#include "obt/prop.h"
 
 #ifdef HAVE_UNISTD_H
 #  include <unistd.h>
@@ -66,9 +67,10 @@ typedef struct
     gpointer data;
 } ClientCallback;
 
-GList            *client_list          = NULL;
+GList            *client_list           = NULL;
 
-static GSList *client_destroy_notifies = NULL;
+static GSList  *client_destroy_notifies = NULL;
+static RrImage *client_default_icon     = NULL;
 
 static void client_get_all(ObClient *self, gboolean real);
 static void client_get_startup_id(ObClient *self);
@@ -79,6 +81,10 @@ static void client_get_state(ObClient *self);
 static void client_get_shaped(ObClient *self);
 static void client_get_mwm_hints(ObClient *self);
 static void client_get_colormap(ObClient *self);
+static void client_set_desktop_recursive(ObClient *self,
+                                         guint target,
+                                         gboolean donthide,
+                                         gboolean dontraise);
 static void client_change_allowed_actions(ObClient *self);
 static void client_change_state(ObClient *self);
 static void client_change_wm_state(ObClient *self);
@@ -99,10 +105,24 @@ static GSList *client_search_all_top_parents_internal(ObClient *self,
                                                       ObStackingLayer layer);
 static void client_call_notifies(ObClient *self, GSList *list);
 static void client_ping_event(ObClient *self, gboolean dead);
+static void client_prompt_kill(ObClient *self);
 
 
 void client_startup(gboolean reconfig)
 {
+    if ((client_default_icon = RrImageCacheFind(ob_rr_icons,
+                                                ob_rr_theme->def_win_icon,
+                                                ob_rr_theme->def_win_icon_w,
+                                                ob_rr_theme->def_win_icon_h)))
+        RrImageRef(client_default_icon);
+    else {
+        client_default_icon = RrImageNew(ob_rr_icons);
+        RrImageAddPicture(client_default_icon,
+                          ob_rr_theme->def_win_icon,
+                          ob_rr_theme->def_win_icon_w,
+                          ob_rr_theme->def_win_icon_h);
+    }
+
     if (reconfig) return;
 
     client_set_list();
@@ -110,6 +130,9 @@ void client_startup(gboolean reconfig)
 
 void client_shutdown(gboolean reconfig)
 {
+    RrImageUnref(client_default_icon);
+    client_default_icon = NULL;
+
     if (reconfig) return;
 }
 
@@ -161,8 +184,8 @@ void client_set_list(void)
     } else
         windows = NULL;
 
-    PROP_SETA32(RootWindow(ob_display, ob_screen),
-                net_client_list, window, (gulong*)windows, size);
+    OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST, WINDOW,
+                    (gulong*)windows, size);
 
     if (windows)
         g_free(windows);
@@ -170,108 +193,34 @@ void client_set_list(void)
     stacking_set_list();
 }
 
-void client_manage_all(void)
-{
-    guint i, j, nchild;
-    Window w, *children;
-    XWMHints *wmhints;
-    XWindowAttributes attrib;
-
-    XQueryTree(ob_display, RootWindow(ob_display, ob_screen),
-               &w, &w, &children, &nchild);
-
-    /* remove all icon windows from the list */
-    for (i = 0; i < nchild; i++) {
-        if (children[i] == None) continue;
-        wmhints = XGetWMHints(ob_display, children[i]);
-        if (wmhints) {
-            if ((wmhints->flags & IconWindowHint) &&
-                (wmhints->icon_window != children[i]))
-                for (j = 0; j < nchild; j++)
-                    if (children[j] == wmhints->icon_window) {
-                        children[j] = None;
-                        break;
-                    }
-            XFree(wmhints);
-        }
-    }
-
-    for (i = 0; i < nchild; ++i) {
-        if (children[i] == None)
-            continue;
-        if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
-            if (attrib.override_redirect) continue;
-
-            if (attrib.map_state != IsUnmapped)
-                client_manage(children[i]);
-        }
-    }
-    XFree(children);
-}
-
-void client_manage(Window window)
+void client_manage(Window window, ObPrompt *prompt)
 {
     ObClient *self;
-    XEvent e;
-    XWindowAttributes attrib;
     XSetWindowAttributes attrib_set;
-    XWMHints *wmhint;
     gboolean activate = FALSE;
     ObAppSettings *settings;
     gboolean transient = FALSE;
     Rect place, *monitor;
     Time launch_time, map_time;
 
-    grab_server(TRUE);
-
-    /* check if it has already been unmapped by the time we started
-       mapping. the grab does a sync so we don't have to here */
-    if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) ||
-        XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e))
-    {
-        XPutBackEvent(ob_display, &e);
-
-        ob_debug("Trying to manage unmapped window. Aborting that.\n");
-        grab_server(FALSE);
-        return; /* don't manage it */
-    }
-
-    /* make sure it isn't an override-redirect window */
-    if (!XGetWindowAttributes(ob_display, window, &attrib) ||
-        attrib.override_redirect)
-    {
-        grab_server(FALSE);
-        return; /* don't manage it */
-    }
-
-    /* is the window a docking app */
-    if ((wmhint = XGetWMHints(ob_display, window))) {
-        if ((wmhint->flags & StateHint) &&
-            wmhint->initial_state == WithdrawnState)
-        {
-            dock_add(window, wmhint);
-            grab_server(FALSE);
-            XFree(wmhint);
-            return;
-        }
-        XFree(wmhint);
-    }
-
-    ob_debug("Managing window: 0x%lx\n", window);
+    ob_debug("Managing window: 0x%lx", window);
 
     map_time = event_get_server_time();
 
-    /* choose the events we want to receive on the CLIENT window */
-    attrib_set.event_mask = CLIENT_EVENTMASK;
+    /* choose the events we want to receive on the CLIENT window
+       (ObPrompt windows can request events too) */
+    attrib_set.event_mask = CLIENT_EVENTMASK |
+        (prompt ? prompt->event_mask : 0);
     attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
-    XChangeWindowAttributes(ob_display, window,
+    XChangeWindowAttributes(obt_display, window,
                             CWEventMask|CWDontPropagate, &attrib_set);
 
     /* create the ObClient struct, and populate it from the hints on the
        window */
     self = g_new0(ObClient, 1);
-    self->obwin.type = Window_Client;
+    self->obwin.type = OB_WINDOW_CLASS_CLIENT;
     self->window = window;
+    self->prompt = prompt;
 
     /* non-zero defaults */
     self->wmstate = WithdrawnState; /* make sure it gets updated first time */
@@ -281,12 +230,19 @@ void client_manage(Window window)
     /* get all the stuff off the window */
     client_get_all(self, TRUE);
 
-    ob_debug("Window type: %d\n", self->type);
-    ob_debug("Window group: 0x%x\n", self->group?self->group->leader:0);
+    ob_debug("Window type: %d", self->type);
+    ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
+
+    /* now we have all of the window's information so we can set this up.
+       do this before creating the frame, so it can tell that we are still
+       mapping and doesn't go applying things right away */
+    client_setup_decor_and_functions(self, FALSE);
 
     /* specify that if we exit, the window should not be destroyed and
-       should be reparented back to root automatically */
-    XChangeSaveSet(ob_display, window, SetModeInsert);
+       should be reparented back to root automatically, unless we are managing
+       an internal ObPrompt window  */
+    if (!self->prompt)
+        XChangeSaveSet(obt_display, window, SetModeInsert);
 
     /* create the decoration frame for the client window */
     self->frame = frame_new(self);
@@ -304,11 +260,8 @@ void client_manage(Window window)
     /* the session should get the last say though */
     client_restore_session_state(self);
 
-    /* now we have all of the window's information so we can set this up */
-    client_setup_decor_and_functions(self, FALSE);
-
     /* tell startup notification that this app started */
-    launch_time = sn_app_started(self->startup_id, self->class);
+    launch_time = sn_app_started(self->startup_id, self->class, self->name);
 
     /* do this after we have a frame.. it uses the frame to help determine the
        WM_STATE to apply. */
@@ -334,7 +287,7 @@ void client_manage(Window window)
     }
 
     /* remove the client's border */
-    XSetWindowBorderWidth(ob_display, self->window, 0);
+    XSetWindowBorderWidth(obt_display, self->window, 0);
 
     /* adjust the frame to the client's size before showing or placing
        the window */
@@ -347,7 +300,7 @@ void client_manage(Window window)
 
     /* figure out placement for the window if the window is new */
     if (ob_state() == OB_STATE_RUNNING) {
-        ob_debug("Positioned: %s @ %d %d\n",
+        ob_debug("Positioned: %s @ %d %d",
                  (!self->positioned ? "no" :
                   (self->positioned == PPosition ? "program specified" :
                    (self->positioned == USPosition ? "user specified" :
@@ -355,7 +308,7 @@ void client_manage(Window window)
                      "program + user specified" :
                      "BADNESS !?")))), place.x, place.y);
 
-        ob_debug("Sized: %s @ %d %d\n",
+        ob_debug("Sized: %s @ %d %d",
                  (!self->sized ? "no" :
                   (self->sized == PSize ? "program specified" :
                    (self->sized == USSize ? "user specified" :
@@ -427,7 +380,7 @@ void client_manage(Window window)
         place.width = MIN(place.width, a->width);
         place.height = MIN(place.height, a->height);
 
-        ob_debug("setting window size to %dx%d\n", place.width, place.height);
+        ob_debug("setting window size to %dx%d", place.width, place.height);
 
         /* get the size of the client back */
         place.width -= self->frame->size.left + self->frame->size.right;
@@ -437,11 +390,11 @@ void client_manage(Window window)
     }
 
     ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
-             "some restrictions may apply\n",
+             "some restrictions may apply",
              self->window, place.x, place.y, place.width, place.height);
     if (self->session)
         ob_debug("  but session requested %d, %d  %d x %d instead, "
-                 "overriding\n",
+                 "overriding",
                  self->session->x, self->session->y,
                  self->session->w, self->session->h);
 
@@ -456,7 +409,7 @@ void client_manage(Window window)
     g_free(monitor);
     monitor = NULL;
 
-    ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s\n",
+    ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s",
                   activate ? "yes" : "no");
     if (activate) {
         gboolean raise = FALSE;
@@ -464,7 +417,7 @@ void client_manage(Window window)
         /* This is focus stealing prevention */
         ob_debug_type(OB_DEBUG_FOCUS,
                       "Want to focus new window 0x%x at time %u "
-                      "launched at %u (last user interaction time %u)\n",
+                      "launched at %u (last user interaction time %u)",
                       self->window, map_time, launch_time,
                       event_last_user_time);
 
@@ -474,7 +427,7 @@ void client_manage(Window window)
             ob_debug_type(OB_DEBUG_FOCUS,
                           "Not focusing the window because the user is inside "
                           "an Openbox menu or is move/resizing a window and "
-                          "we don't want to interrupt them\n");
+                          "we don't want to interrupt them");
         }
 
         /* if it's on another desktop */
@@ -488,7 +441,7 @@ void client_manage(Window window)
             raise = TRUE;
             ob_debug_type(OB_DEBUG_FOCUS,
                           "Not focusing the window because its on another "
-                          "desktop\n");
+                          "desktop");
         }
         /* If something is focused, and it's not our relative... */
         else if (focus_client && client_search_focus_tree_full(self) == NULL &&
@@ -505,14 +458,14 @@ void client_manage(Window window)
                 activate = FALSE;
                 ob_debug_type(OB_DEBUG_FOCUS,
                               "Not focusing the window because the user is "
-                              "working in another window\n");
+                              "working in another window");
             }
             /* If its a transient (and its parents aren't focused) */
             else if (client_has_parent(self)) {
                 activate = FALSE;
                 ob_debug_type(OB_DEBUG_FOCUS,
                               "Not focusing the window because it is a "
-                              "transient, and its relatives aren't focused\n");
+                              "transient, and its relatives aren't focused");
             }
             /* Don't steal focus from globally active clients.
                I stole this idea from KWin. It seems nice.
@@ -523,7 +476,7 @@ void client_manage(Window window)
                 activate = FALSE;
                 ob_debug_type(OB_DEBUG_FOCUS,
                               "Not focusing the window because a globally "
-                              "active client has focus\n");
+                              "active client has focus");
             }
             /* Don't move focus if it's not going to go to this window
                anyway */
@@ -532,7 +485,7 @@ void client_manage(Window window)
                 raise = TRUE;
                 ob_debug_type(OB_DEBUG_FOCUS,
                               "Not focusing the window because another window "
-                              "would get the focus anyway\n");
+                              "would get the focus anyway");
             }
             else if (!(self->desktop == screen_desktop ||
                        self->desktop == DESKTOP_ALL))
@@ -548,7 +501,7 @@ void client_manage(Window window)
         if (!activate) {
             ob_debug_type(OB_DEBUG_FOCUS,
                           "Focus stealing prevention activated for %s at "
-                          "time %u (last user interactioon time %u)\n",
+                          "time %u (last user interactioon time %u)",
                           self->title, map_time, event_last_user_time);
             /* if the client isn't focused, then hilite it so the user
                knows it is there */
@@ -593,7 +546,7 @@ void client_manage(Window window)
 
     /* add to client list/map */
     client_list = g_list_append(client_list, self);
-    g_hash_table_insert(window_map, &self->window, self);
+    window_add(&self->window, CLIENT_AS_WINDOW(self));
 
     /* this has to happen after we're in the client_list */
     if (STRUT_EXISTS(self->strut))
@@ -605,10 +558,8 @@ void client_manage(Window window)
     /* free the ObAppSettings shallow copy */
     g_free(settings);
 
-    ob_debug("Managed window 0x%lx plate 0x%x (%s)\n",
+    ob_debug("Managed window 0x%lx plate 0x%x (%s)",
              window, self->frame->window, self->class);
-
-    return;
 }
 
 
@@ -617,7 +568,7 @@ ObClient *client_fake_manage(Window window)
     ObClient *self;
     ObAppSettings *settings;
 
-    ob_debug("Pretend-managing window: %lx\n", window);
+    ob_debug("Pretend-managing window: %lx", window);
 
     /* do this minimal stuff to figure out the client's decorations */
 
@@ -635,7 +586,7 @@ ObClient *client_fake_manage(Window window)
     self->frame = frame_new(self);
     frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
 
-    ob_debug("gave extents left %d right %d top %d bottom %d\n",
+    ob_debug("gave extents left %d right %d top %d bottom %d",
              self->frame->size.left, self->frame->size.right,
              self->frame->size.top, self->frame->size.bottom);
 
@@ -653,11 +604,10 @@ void client_unmanage_all(void)
 
 void client_unmanage(ObClient *self)
 {
-    guint j;
     GSList *it;
     gulong ignore_start;
 
-    ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)\n",
+    ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)",
              self->window, self->frame->window,
              self->class, self->title ? self->title : "");
 
@@ -665,7 +615,7 @@ void client_unmanage(ObClient *self)
 
     /* we dont want events no more. do this before hiding the frame so we
        don't generate more events */
-    XSelectInput(ob_display, self->window, NoEventMask);
+    XSelectInput(obt_display, self->window, NoEventMask);
 
     /* ignore enter events from the unmap so it doesnt mess with the focus */
     if (!config_focus_under_mouse)
@@ -673,15 +623,17 @@ void client_unmanage(ObClient *self)
 
     frame_hide(self->frame);
     /* flush to send the hide to the server quickly */
-    XFlush(ob_display);
+    XFlush(obt_display);
 
     if (!config_focus_under_mouse)
         event_end_ignore_all_enters(ignore_start);
 
     mouse_grab_for_client(self, FALSE);
 
-    /* remove the window from our save set */
-    XChangeSaveSet(ob_display, self->window, SetModeDelete);
+    /* remove the window from our save set, unless we are managing an internal
+       ObPrompt window */
+    if (!self->prompt)
+        XChangeSaveSet(obt_display, self->window, SetModeDelete);
 
     /* update the focus lists */
     focus_order_remove(self);
@@ -690,9 +642,13 @@ void client_unmanage(ObClient *self)
         focus_client = NULL;
     }
 
+    /* if we're prompting to kill the client, close that */
+    prompt_unref(self->kill_prompt);
+    self->kill_prompt = NULL;
+
     client_list = g_list_remove(client_list, self);
     stacking_remove(self);
-    g_hash_table_remove(window_map, &self->window);
+    window_remove(self->window);
 
     /* once the client is out of the list, update the struts to remove its
        influence */
@@ -745,7 +701,7 @@ void client_unmanage(ObClient *self)
         self->decorations = 0; /* unmanaged windows have no decor */
 
         /* give the client its border back */
-        XSetWindowBorderWidth(ob_display, self->window, self->border_width);
+        XSetWindowBorderWidth(obt_display, self->window, self->border_width);
 
         client_move_resize(self, a.x, a.y, a.width, a.height);
     }
@@ -758,35 +714,34 @@ void client_unmanage(ObClient *self)
     if (ob_state() != OB_STATE_EXITING) {
         /* these values should not be persisted across a window
            unmapping/mapping */
-        PROP_ERASE(self->window, net_wm_desktop);
-        PROP_ERASE(self->window, net_wm_state);
-        PROP_ERASE(self->window, wm_state);
+        OBT_PROP_ERASE(self->window, NET_WM_DESKTOP);
+        OBT_PROP_ERASE(self->window, NET_WM_STATE);
+        OBT_PROP_ERASE(self->window, WM_STATE);
     } else {
         /* if we're left in an unmapped state, the client wont be mapped.
            this is bad, since we will no longer be managing the window on
            restart */
-        XMapWindow(ob_display, self->window);
+        XMapWindow(obt_display, self->window);
     }
 
     /* these should not be left on the window ever.  other window managers
        don't necessarily use them and it will mess them up (like compiz) */
-    PROP_ERASE(self->window, net_wm_visible_name);
-    PROP_ERASE(self->window, net_wm_visible_icon_name);
+    OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_NAME);
+    OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_ICON_NAME);
 
     /* update the list hints */
     client_set_list();
 
-    ob_debug("Unmanaged window 0x%lx\n", self->window);
+    ob_debug("Unmanaged window 0x%lx", self->window);
 
     /* free all data allocated in the client struct */
+    RrImageUnref(self->icon_set);
     g_slist_free(self->transients);
-    for (j = 0; j < self->nicons; ++j)
-        g_free(self->icons[j].data);
-    if (self->nicons > 0)
-        g_free(self->icons);
+    g_free(self->startup_id);
     g_free(self->wm_command);
     g_free(self->title);
     g_free(self->icon_title);
+    g_free(self->original_title);
     g_free(self->name);
     g_free(self->class);
     g_free(self->role);
@@ -824,16 +779,18 @@ static ObAppSettings *client_get_settings_state(ObClient *self)
             !g_pattern_match(app->name, strlen(self->name), self->name, NULL))
             match = FALSE;
         else if (app->class &&
-                !g_pattern_match(app->class,
-                                 strlen(self->class), self->class, NULL))
+                 !g_pattern_match(app->class,
+                                  strlen(self->class), self->class, NULL))
             match = FALSE;
         else if (app->role &&
                  !g_pattern_match(app->role,
                                   strlen(self->role), self->role, NULL))
             match = FALSE;
+        else if ((signed)app->type >= 0 && app->type != self->type)
+            match = FALSE;
 
         if (match) {
-            ob_debug("Window matching: %s\n", app->name);
+            ob_debug("Window matching: %s", app->name);
 
             /* copy the settings to our struct, overriding the existing
                settings if they are not defaults */
@@ -888,17 +845,17 @@ static void client_restore_session_state(ObClient *self)
     GList *it;
 
     ob_debug_type(OB_DEBUG_SM,
-                  "Restore session for client %s\n", self->title);
+                  "Restore session for client %s", self->title);
 
     if (!(it = session_state_find(self))) {
         ob_debug_type(OB_DEBUG_SM,
-                      "Session data not found for client %s\n", self->title);
+                      "Session data not found for client %s", self->title);
         return;
     }
 
     self->session = it->data;
 
-    ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s\n",
+    ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s",
                   self->title);
 
     RECT_SET_POINT(self->area, self->session->x, self->session->y);
@@ -908,13 +865,13 @@ static void client_restore_session_state(ObClient *self)
         self->area.width = self->session->w;
     if (self->session->h > 0)
         self->area.height = self->session->h;
-    XResizeWindow(ob_display, self->window,
+    XResizeWindow(obt_display, self->window,
                   self->area.width, self->area.height);
 
     self->desktop = (self->session->desktop == DESKTOP_ALL ?
                      self->session->desktop :
                      MIN(screen_num_desktops - 1, self->session->desktop));
-    PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
+    OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
 
     self->shaded = self->session->shaded;
     self->iconic = self->session->iconic;
@@ -973,6 +930,7 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
     gint fw, fh;
     Rect desired;
     guint i;
+    gboolean found_mon;
 
     RECT_SET(desired, *x, *y, w, h);
     frame_rect_to_frame(self->frame, &desired);
@@ -1022,18 +980,26 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
             rudeb = TRUE;
     }
 
+    /* we iterate through every monitor that the window is at least partially
+       on, to make sure it is obeying the rules on them all
+
+       if the window does not appear on any monitors, then use the first one
+    */
+    found_mon = FALSE;
     for (i = 0; i < screen_num_monitors; ++i) {
         Rect *a;
 
         if (!screen_physical_area_monitor_contains(i, &desired)) {
-            if (i < screen_num_monitors - 1)
+            if (i < screen_num_monitors - 1 || found_mon)
                 continue;
 
             /* the window is not inside any monitor! so just use the first
                one */
             a = screen_area(self->desktop, 0, NULL);
-        } else
+        } else {
+            found_mon = TRUE;
             a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
+        }
 
         /* This makes sure windows aren't entirely outside of the screen so you
            can't see them at all.
@@ -1132,10 +1098,11 @@ static void client_get_all(ObClient *self, gboolean real)
 
 static void client_get_startup_id(ObClient *self)
 {
-    if (!(PROP_GETS(self->window, net_startup_id, utf8, &self->startup_id)))
+    if (!(OBT_PROP_GETS(self->window, NET_STARTUP_ID, utf8,
+                        &self->startup_id)))
         if (self->group)
-            PROP_GETS(self->group->leader,
-                      net_startup_id, utf8, &self->startup_id);
+            OBT_PROP_GETS(self->group->leader,
+                          NET_STARTUP_ID, utf8, &self->startup_id);
 }
 
 static void client_get_area(ObClient *self)
@@ -1143,14 +1110,14 @@ static void client_get_area(ObClient *self)
     XWindowAttributes wattrib;
     Status ret;
 
-    ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
+    ret = XGetWindowAttributes(obt_display, self->window, &wattrib);
     g_assert(ret != BadWindow);
 
     RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
     POINT_SET(self->root_pos, wattrib.x, wattrib.y);
     self->border_width = wattrib.border_width;
 
-    ob_debug("client area: %d %d  %d %d  bw %d\n", wattrib.x, wattrib.y,
+    ob_debug("client area: %d %d  %d %d  bw %d", wattrib.x, wattrib.y,
              wattrib.width, wattrib.height, wattrib.border_width);
 }
 
@@ -1158,12 +1125,12 @@ static void client_get_desktop(ObClient *self)
 {
     guint32 d = screen_num_desktops; /* an always-invalid value */
 
-    if (PROP_GET32(self->window, net_wm_desktop, cardinal, &d)) {
+    if (OBT_PROP_GET32(self->window, NET_WM_DESKTOP, CARDINAL, &d)) {
         if (d >= screen_num_desktops && d != DESKTOP_ALL)
             self->desktop = screen_num_desktops - 1;
         else
             self->desktop = d;
-        ob_debug("client requested desktop 0x%x\n", self->desktop);
+        ob_debug("client requested desktop 0x%x", self->desktop);
     } else {
         GSList *it;
         gboolean first = TRUE;
@@ -1186,7 +1153,7 @@ static void client_get_desktop(ObClient *self)
         if (all != screen_num_desktops) {
             self->desktop = all;
 
-            ob_debug("client desktop set from parents: 0x%x\n",
+            ob_debug("client desktop set from parents: 0x%x",
                      self->desktop);
         }
         /* try get from the startup-notification protocol */
@@ -1194,13 +1161,13 @@ static void client_get_desktop(ObClient *self)
             if (self->desktop >= screen_num_desktops &&
                 self->desktop != DESKTOP_ALL)
                 self->desktop = screen_num_desktops - 1;
-            ob_debug("client desktop set from startup-notification: 0x%x\n",
+            ob_debug("client desktop set from startup-notification: 0x%x",
                      self->desktop);
         }
         /* defaults to the current desktop */
         else {
             self->desktop = screen_desktop;
-            ob_debug("client desktop set to the current desktop: %d\n",
+            ob_debug("client desktop set to the current desktop: %d",
                      self->desktop);
         }
     }
@@ -1211,32 +1178,32 @@ static void client_get_state(ObClient *self)
     guint32 *state;
     guint num;
 
-    if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
+    if (OBT_PROP_GETA32(self->window, NET_WM_STATE, ATOM, &state, &num)) {
         gulong i;
         for (i = 0; i < num; ++i) {
-            if (state[i] == prop_atoms.net_wm_state_modal)
+            if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
                 self->modal = TRUE;
-            else if (state[i] == prop_atoms.net_wm_state_shaded)
+            else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
                 self->shaded = TRUE;
-            else if (state[i] == prop_atoms.net_wm_state_hidden)
+            else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
                 self->iconic = TRUE;
-            else if (state[i] == prop_atoms.net_wm_state_skip_taskbar)
+            else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
                 self->skip_taskbar = TRUE;
-            else if (state[i] == prop_atoms.net_wm_state_skip_pager)
+            else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
                 self->skip_pager = TRUE;
-            else if (state[i] == prop_atoms.net_wm_state_fullscreen)
+            else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
                 self->fullscreen = TRUE;
-            else if (state[i] == prop_atoms.net_wm_state_maximized_vert)
+            else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
                 self->max_vert = TRUE;
-            else if (state[i] == prop_atoms.net_wm_state_maximized_horz)
+            else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
                 self->max_horz = TRUE;
-            else if (state[i] == prop_atoms.net_wm_state_above)
+            else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
                 self->above = TRUE;
-            else if (state[i] == prop_atoms.net_wm_state_below)
+            else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
                 self->below = TRUE;
-            else if (state[i] == prop_atoms.net_wm_state_demands_attention)
+            else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
                 self->demands_attention = TRUE;
-            else if (state[i] == prop_atoms.ob_wm_state_undecorated)
+            else if (state[i] == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
                 self->undecorated = TRUE;
         }
 
@@ -1248,14 +1215,14 @@ static void client_get_shaped(ObClient *self)
 {
     self->shaped = FALSE;
 #ifdef   SHAPE
-    if (extensions_shape) {
+    if (obt_display_extension_shape) {
         gint foo;
         guint ufoo;
         gint s;
 
-        XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
+        XShapeSelectInput(obt_display, self->window, ShapeNotifyMask);
 
-        XShapeQueryExtents(ob_display, self->window, &s, &foo,
+        XShapeQueryExtents(obt_display, self->window, &s, &foo,
                            &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
                            &ufoo);
         self->shaped = (s != 0);
@@ -1269,22 +1236,22 @@ void client_update_transient_for(ObClient *self)
     ObClient *target = NULL;
     gboolean trangroup = FALSE;
 
-    if (XGetTransientForHint(ob_display, self->window, &t)) {
+    if (XGetTransientForHint(obt_display, self->window, &t)) {
         if (t != self->window) { /* cant be transient to itself! */
-            target = g_hash_table_lookup(window_map, &t);
+            ObWindow *tw = window_find(t);
             /* if this happens then we need to check for it*/
-            g_assert(target != self);
-            if (target && !WINDOW_IS_CLIENT(target)) {
-                /* this can happen when a dialog is a child of
-                   a dockapp, for example */
-                target = NULL;
+            g_assert(tw != CLIENT_AS_WINDOW(self));
+            if (tw && WINDOW_IS_CLIENT(tw)) {
+                /* watch out for windows with a parent that is something
+                   different, like a dockapp for example */
+                target = WINDOW_AS_CLIENT(tw);
             }
         }
 
         /* Setting the transient_for to Root is actually illegal, however
            applications from time have done this to specify transient for
            their group */
-        if (!target && self->group && t == RootWindow(ob_display, ob_screen))
+        if (!target && self->group && t == obt_root(ob_screen))
             trangroup = TRUE;
     } else if (self->group && self->transient)
         trangroup = TRUE;
@@ -1420,8 +1387,8 @@ static void client_get_mwm_hints(ObClient *self)
 
     self->mwmhints.flags = 0; /* default to none */
 
-    if (PROP_GETA32(self->window, motif_wm_hints, motif_wm_hints,
-                    &hints, &num)) {
+    if (OBT_PROP_GETA32(self->window, MOTIF_WM_HINTS, MOTIF_WM_HINTS,
+                        &hints, &num)) {
         if (num >= OB_MWM_ELEMENTS) {
             self->mwmhints.flags = hints[0];
             self->mwmhints.functions = hints[1];
@@ -1440,26 +1407,27 @@ void client_get_type_and_transientness(ObClient *self)
     self->type = -1;
     self->transient = FALSE;
 
-    if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) {
+    if (OBT_PROP_GETA32(self->window, NET_WM_WINDOW_TYPE, ATOM, &val, &num)) {
         /* use the first value that we know about in the array */
         for (i = 0; i < num; ++i) {
-            if (val[i] == prop_atoms.net_wm_window_type_desktop)
+            if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
                 self->type = OB_CLIENT_TYPE_DESKTOP;
-            else if (val[i] == prop_atoms.net_wm_window_type_dock)
+            else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK))
                 self->type = OB_CLIENT_TYPE_DOCK;
-            else if (val[i] == prop_atoms.net_wm_window_type_toolbar)
+            else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR))
                 self->type = OB_CLIENT_TYPE_TOOLBAR;
-            else if (val[i] == prop_atoms.net_wm_window_type_menu)
+            else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
                 self->type = OB_CLIENT_TYPE_MENU;
-            else if (val[i] == prop_atoms.net_wm_window_type_utility)
+            else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY))
                 self->type = OB_CLIENT_TYPE_UTILITY;
-            else if (val[i] == prop_atoms.net_wm_window_type_splash)
+            else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH))
                 self->type = OB_CLIENT_TYPE_SPLASH;
-            else if (val[i] == prop_atoms.net_wm_window_type_dialog)
+            else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG))
                 self->type = OB_CLIENT_TYPE_DIALOG;
-            else if (val[i] == prop_atoms.net_wm_window_type_normal)
+            else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL))
                 self->type = OB_CLIENT_TYPE_NORMAL;
-            else if (val[i] == prop_atoms.kde_net_wm_window_type_override) {
+            else if (val[i] == OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE))
+            {
                 /* prevent this window from getting any decor or
                    functionality */
                 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
@@ -1473,7 +1441,7 @@ void client_get_type_and_transientness(ObClient *self)
         g_free(val);
     }
 
-    if (XGetTransientForHint(ob_display, self->window, &t))
+    if (XGetTransientForHint(obt_display, self->window, &t))
         self->transient = TRUE;
 
     if (self->type == (ObClientType) -1) {
@@ -1499,26 +1467,26 @@ void client_get_type_and_transientness(ObClient *self)
 void client_update_protocols(ObClient *self)
 {
     guint32 *proto;
-    guint num_return, i;
+    guint num_ret, i;
 
     self->focus_notify = FALSE;
     self->delete_window = FALSE;
 
-    if (PROP_GETA32(self->window, wm_protocols, atom, &proto, &num_return)) {
-        for (i = 0; i < num_return; ++i) {
-            if (proto[i] == prop_atoms.wm_delete_window)
+    if (OBT_PROP_GETA32(self->window, WM_PROTOCOLS, ATOM, &proto, &num_ret)) {
+        for (i = 0; i < num_ret; ++i) {
+            if (proto[i] == OBT_PROP_ATOM(WM_DELETE_WINDOW))
                 /* this means we can request the window to close */
                 self->delete_window = TRUE;
-            else if (proto[i] == prop_atoms.wm_take_focus)
+            else if (proto[i] == OBT_PROP_ATOM(WM_TAKE_FOCUS))
                 /* if this protocol is requested, then the window will be
                    notified whenever we want it to receive focus */
                 self->focus_notify = TRUE;
-            else if (proto[i] == prop_atoms.net_wm_ping)
+            else if (proto[i] == OBT_PROP_ATOM(NET_WM_PING))
                 /* if this protocol is requested, then the window will allow
                    pings to determine if it is still alive */
                 self->ping = TRUE;
 #ifdef SYNC
-            else if (proto[i] == prop_atoms.net_wm_sync_request)
+            else if (proto[i] == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST))
                 /* if this protocol is requested, then resizing the
                    window will be synchronized between the frame and the
                    client */
@@ -1534,18 +1502,19 @@ void client_update_sync_request_counter(ObClient *self)
 {
     guint32 i;
 
-    if (PROP_GET32(self->window, net_wm_sync_request_counter, cardinal, &i)) {
+    if (OBT_PROP_GET32(self->window, NET_WM_SYNC_REQUEST_COUNTER, CARDINAL,&i))
+    {
         self->sync_counter = i;
     } else
         self->sync_counter = None;
 }
 #endif
 
-void client_get_colormap(ObClient *self)
+static void client_get_colormap(ObClient *self)
 {
     XWindowAttributes wa;
 
-    if (XGetWindowAttributes(ob_display, self->window, &wa))
+    if (XGetWindowAttributes(obt_display, self->window, &wa))
         client_update_colormap(self, wa.colormap);
 }
 
@@ -1553,7 +1522,7 @@ void client_update_colormap(ObClient *self, Colormap colormap)
 {
     if (colormap == self->colormap) return;
 
-    ob_debug("Setting client %s colormap: 0x%x\n", self->title, colormap);
+    ob_debug("Setting client %s colormap: 0x%x", self->title, colormap);
 
     if (client_focused(self)) {
         screen_install_colormap(self, FALSE); /* uninstall old one */
@@ -1572,12 +1541,12 @@ void client_update_normal_hints(ObClient *self)
     self->min_ratio = 0.0f;
     self->max_ratio = 0.0f;
     SIZE_SET(self->size_inc, 1, 1);
-    SIZE_SET(self->base_size, 0, 0);
+    SIZE_SET(self->base_size, -1, -1);
     SIZE_SET(self->min_size, 0, 0);
     SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
 
     /* get the hints from the window */
-    if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
+    if (XGetWMNormalHints(obt_display, self->window, &size, &ret)) {
         /* normal windows can't request placement! har har
         if (!client_normal(self))
         */
@@ -1608,15 +1577,15 @@ void client_update_normal_hints(ObClient *self)
         if (size.flags & PResizeInc && size.width_inc && size.height_inc)
             SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
 
-        ob_debug("Normal hints: min size (%d %d) max size (%d %d)\n   "
-                 "size inc (%d %d) base size (%d %d)\n",
+        ob_debug("Normal hints: min size (%d %d) max size (%d %d)",
                  self->min_size.width, self->min_size.height,
-                 self->max_size.width, self->max_size.height,
+                 self->max_size.width, self->max_size.height);
+        ob_debug("size inc (%d %d) base size (%d %d)",
                  self->size_inc.width, self->size_inc.height,
                  self->base_size.width, self->base_size.height);
     }
     else
-        ob_debug("Normal hints: not set\n");
+        ob_debug("Normal hints: not set");
 }
 
 void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
@@ -1789,43 +1758,43 @@ static void client_change_allowed_actions(ObClient *self)
 
     /* desktop windows are kept on all desktops */
     if (self->type != OB_CLIENT_TYPE_DESKTOP)
-        actions[num++] = prop_atoms.net_wm_action_change_desktop;
+        actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
 
     if (self->functions & OB_CLIENT_FUNC_SHADE)
-        actions[num++] = prop_atoms.net_wm_action_shade;
+        actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
     if (self->functions & OB_CLIENT_FUNC_CLOSE)
-        actions[num++] = prop_atoms.net_wm_action_close;
+        actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
     if (self->functions & OB_CLIENT_FUNC_MOVE)
-        actions[num++] = prop_atoms.net_wm_action_move;
+        actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
     if (self->functions & OB_CLIENT_FUNC_ICONIFY)
-        actions[num++] = prop_atoms.net_wm_action_minimize;
+        actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
     if (self->functions & OB_CLIENT_FUNC_RESIZE)
-        actions[num++] = prop_atoms.net_wm_action_resize;
+        actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
     if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
-        actions[num++] = prop_atoms.net_wm_action_fullscreen;
+        actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
     if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
-        actions[num++] = prop_atoms.net_wm_action_maximize_horz;
-        actions[num++] = prop_atoms.net_wm_action_maximize_vert;
+        actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
+        actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
     }
     if (self->functions & OB_CLIENT_FUNC_ABOVE)
-        actions[num++] = prop_atoms.net_wm_action_above;
+        actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
     if (self->functions & OB_CLIENT_FUNC_BELOW)
-        actions[num++] = prop_atoms.net_wm_action_below;
+        actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
     if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
-        actions[num++] = prop_atoms.ob_wm_action_undecorate;
+        actions[num++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
+
+    OBT_PROP_SETA32(self->window, NET_WM_ALLOWED_ACTIONS, ATOM, actions, num);
 
-    PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
+    /* make sure the window isn't breaking any rules now
 
-    /* make sure the window isn't breaking any rules now */
+       don't check ICONIFY here.  just cuz a window can't iconify doesnt mean
+       it can't be iconified with its parent
+    */
 
     if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
         if (self->frame) client_shade(self, FALSE);
         else self->shaded = FALSE;
     }
-    if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
-        if (self->frame) client_iconify(self, FALSE, TRUE, FALSE);
-        else self->iconic = FALSE;
-    }
     if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
         if (self->frame) client_fullscreen(self, FALSE);
         else self->fullscreen = FALSE;
@@ -1844,7 +1813,7 @@ void client_update_wmhints(ObClient *self)
     /* assume a window takes input if it doesnt specify */
     self->can_focus = TRUE;
 
-    if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
+    if ((hints = XGetWMHints(obt_display, self->window)) != NULL) {
         gboolean ur;
 
         if (hints->flags & InputHint)
@@ -1930,12 +1899,13 @@ void client_update_title(ObClient *self)
     gchar *visible = NULL;
 
     g_free(self->title);
+    g_free(self->original_title);
 
     /* try netwm */
-    if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) {
+    if (!OBT_PROP_GETS(self->window, NET_WM_NAME, utf8, &data)) {
         /* try old x stuff */
-        if (!(PROP_GETS(self->window, wm_name, locale, &data)
-              || PROP_GETS(self->window, wm_name, utf8, &data))) {
+        if (!(OBT_PROP_GETS(self->window, WM_NAME, locale, &data)
+              || OBT_PROP_GETS(self->window, WM_NAME, utf8, &data))) {
             if (self->transient) {
     /*
     GNOME alert windows are not given titles:
@@ -1946,6 +1916,7 @@ void client_update_title(ObClient *self)
                 data = g_strdup("Unnamed Window");
         }
     }
+    self->original_title = g_strdup(data);
 
     if (self->client_machine) {
         visible = g_strdup_printf("%s (%s)", data, self->client_machine);
@@ -1955,11 +1926,14 @@ void client_update_title(ObClient *self)
 
     if (self->not_responding) {
         data = visible;
-        visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
+        if (self->kill_level > 0)
+            visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
+        else
+            visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
         g_free(data);
     }
 
-    PROP_SETS(self->window, net_wm_visible_name, visible);
+    OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, utf8, visible);
     self->title = visible;
 
     if (self->frame)
@@ -1970,10 +1944,10 @@ void client_update_title(ObClient *self)
     g_free(self->icon_title);
 
     /* try netwm */
-    if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data))
+    if (!OBT_PROP_GETS(self->window, NET_WM_ICON_NAME, utf8, &data))
         /* try old x stuff */
-        if (!(PROP_GETS(self->window, wm_icon_name, locale, &data) ||
-              PROP_GETS(self->window, wm_icon_name, utf8, &data)))
+        if (!(OBT_PROP_GETS(self->window, WM_ICON_NAME, locale, &data) ||
+              OBT_PROP_GETS(self->window, WM_ICON_NAME, utf8, &data)))
             data = g_strdup(self->title);
 
     if (self->client_machine) {
@@ -1984,11 +1958,14 @@ void client_update_title(ObClient *self)
 
     if (self->not_responding) {
         data = visible;
-        visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
+        if (self->kill_level > 0)
+            visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
+        else
+            visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
         g_free(data);
     }
 
-    PROP_SETS(self->window, net_wm_visible_icon_name, visible);
+    OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, utf8, visible);
     self->icon_title = visible;
 }
 
@@ -1999,8 +1976,9 @@ void client_update_strut(ObClient *self)
     gboolean got = FALSE;
     StrutPartial strut;
 
-    if (PROP_GETA32(self->window, net_wm_strut_partial, cardinal,
-                    &data, &num)) {
+    if (OBT_PROP_GETA32(self->window, NET_WM_STRUT_PARTIAL, CARDINAL,
+                        &data, &num))
+    {
         if (num == 12) {
             got = TRUE;
             STRUT_PARTIAL_SET(strut,
@@ -2012,7 +1990,7 @@ void client_update_strut(ObClient *self)
     }
 
     if (!got &&
-        PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
+        OBT_PROP_GETA32(self->window, NET_WM_STRUT, CARDINAL, &data, &num)) {
         if (num == 4) {
             Rect *a;
 
@@ -2051,101 +2029,129 @@ void client_update_icons(ObClient *self)
     guint num;
     guint32 *data;
     guint w, h, i, j;
+    guint num_seen;  /* number of icons present */
+    RrImage *img;
 
-    for (i = 0; i < self->nicons; ++i)
-        g_free(self->icons[i].data);
-    if (self->nicons > 0)
-        g_free(self->icons);
-    self->nicons = 0;
+    img = NULL;
 
-    if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
+    /* grab the server, because we might be setting the window's icon and
+       we don't want them to set it in between and we overwrite their own
+       icon */
+    grab_server(TRUE);
+
+    if (OBT_PROP_GETA32(self->window, NET_WM_ICON, CARDINAL, &data, &num)) {
         /* figure out how many valid icons are in here */
         i = 0;
-        while (num - i > 2) {
+        num_seen = 0;
+        while (i + 2 < num) { /* +2 is to make sure there is a w and h */
             w = data[i++];
             h = data[i++];
-            i += w * h;
-            if (i > num || w*h == 0) break;
-            ++self->nicons;
+            /* watch for the data being too small for the specified size,
+               or for zero sized icons. */
+            if (i + w*h > num || w == 0 || h == 0) break;
+
+            /* convert it to the right bit order for ObRender */
+            for (j = 0; j < w*h; ++j)
+                data[i+j] =
+                    (((data[i+j] >> 24) & 0xff) << RrDefaultAlphaOffset) +
+                    (((data[i+j] >> 16) & 0xff) << RrDefaultRedOffset)   +
+                    (((data[i+j] >>  8) & 0xff) << RrDefaultGreenOffset) +
+                    (((data[i+j] >>  0) & 0xff) << RrDefaultBlueOffset);
+
+            /* is it in the cache? */
+            img = RrImageCacheFind(ob_rr_icons, &data[i], w, h);
+            if (img) RrImageRef(img); /* own it */
+
+            i += w*h;
+            ++num_seen;
+
+            /* don't bother looping anymore if we already found it in the cache
+               since we'll just use that! */
+            if (img) break;
         }
 
-        self->icons = g_new(ObClientIcon, self->nicons);
-
-        /* store the icons */
-        i = 0;
-        for (j = 0; j < self->nicons; ++j) {
-            guint x, y, t;
-
-            w = self->icons[j].width = data[i++];
-            h = self->icons[j].height = data[i++];
-
-            if (w*h == 0) continue;
-
-            self->icons[j].data = g_new(RrPixel32, w * h);
-            for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
-                if (x >= w) {
-                    x = 0;
-                    ++y;
-                }
-                self->icons[j].data[t] =
-                    (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
-                    (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
-                    (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
-                    (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
+        /* if it's not in the cache yet, then add it to the cache now.
+           we have already converted it to the correct bit order above */
+        if (!img && num_seen > 0) {
+            img = RrImageNew(ob_rr_icons);
+            i = 0;
+            for (j = 0; j < num_seen; ++j) {
+                w = data[i++];
+                h = data[i++];
+                RrImageAddPicture(img, &data[i], w, h);
+                i += w*h;
             }
-            g_assert(i <= num);
         }
 
         g_free(data);
-    } else {
+    }
+
+    /* if we didn't find an image from the NET_WM_ICON stuff, then try the
+       legacy X hints */
+    if (!img) {
         XWMHints *hints;
 
-        if ((hints = XGetWMHints(ob_display, self->window))) {
+        if ((hints = XGetWMHints(obt_display, self->window))) {
             if (hints->flags & IconPixmapHint) {
-                self->nicons = 1;
-                self->icons = g_new(ObClientIcon, self->nicons);
-                xerror_set_ignore(TRUE);
-                if (!RrPixmapToRGBA(ob_rr_inst,
-                                    hints->icon_pixmap,
-                                    (hints->flags & IconMaskHint ?
-                                     hints->icon_mask : None),
-                                    &self->icons[0].width,
-                                    &self->icons[0].height,
-                                    &self->icons[0].data))
-                {
-                    g_free(self->icons);
-                    self->nicons = 0;
+                gboolean xicon;
+                obt_display_ignore_errors(TRUE);
+                xicon = RrPixmapToRGBA(ob_rr_inst,
+                                       hints->icon_pixmap,
+                                       (hints->flags & IconMaskHint ?
+                                        hints->icon_mask : None),
+                                       (gint*)&w, (gint*)&h, &data);
+                obt_display_ignore_errors(FALSE);
+
+
+                if (xicon) {
+                    if (w > 0 && h > 0) {
+                        /* is this icon in the cache yet? */
+                        img = RrImageCacheFind(ob_rr_icons, data, w, h);
+                        if (img) RrImageRef(img); /* own it */
+
+                        /* if not, then add it */
+                        if (!img) {
+                            img = RrImageNew(ob_rr_icons);
+                            RrImageAddPicture(img, data, w, h);
+                        }
+                    }
+
+                    g_free(data);
                 }
-                xerror_set_ignore(FALSE);
             }
             XFree(hints);
         }
     }
 
-    /* set the default icon onto the window
-       in theory, this could be a race, but if a window doesn't set an icon
-       or removes it entirely, it's not very likely it is going to set one
-       right away afterwards
+    /* set the client's icons to be whatever we found */
+    RrImageUnref(self->icon_set);
+    self->icon_set = img;
 
-       if it has parents, then one of them will have an icon already
+    /* if the client has no icon at all, then we set a default icon onto it.
+       but, if it has parents, then one of them will have an icon already
     */
-    if (self->nicons == 0 && !self->parents) {
+    if (!self->icon_set && !self->parents) {
         RrPixel32 *icon = ob_rr_theme->def_win_icon;
-        gulong *data;
-
-        data = g_new(gulong, 48*48+2);
-        data[0] = data[1] =  48;
-        for (i = 0; i < 48*48; ++i)
-            data[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
+        gulong *ldata; /* use a long here to satisfy OBT_PROP_SETA32 */
+
+        w = ob_rr_theme->def_win_icon_w;
+        h = ob_rr_theme->def_win_icon_h;
+        ldata = g_new(gulong, w*h+2);
+        ldata[0] = w;
+        ldata[1] = h;
+        for (i = 0; i < w*h; ++i)
+            ldata[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
                 (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
                 (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
                 (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
-        PROP_SETA32(self->window, net_wm_icon, cardinal, data, 48*48+2);
-        g_free(data);
+        OBT_PROP_SETA32(self->window, NET_WM_ICON, CARDINAL, ldata, w*h+2);
+        g_free(ldata);
     } else if (self->frame)
         /* don't draw the icon empty if we're just setting one now anyways,
            we'll get the property change any second */
         frame_adjust_icon(self->frame);
+
+    grab_server(FALSE);
 }
 
 void client_update_icon_geometry(ObClient *self)
@@ -2155,12 +2161,14 @@ void client_update_icon_geometry(ObClient *self)
 
     RECT_SET(self->icon_geometry, 0, 0, 0, 0);
 
-    if (PROP_GETA32(self->window, net_wm_icon_geometry, cardinal, &data, &num)
-        && num == 4)
+    if (OBT_PROP_GETA32(self->window, NET_WM_ICON_GEOMETRY, CARDINAL,
+                        &data, &num))
     {
-        /* don't let them set it with an area < 0 */
-        RECT_SET(self->icon_geometry, data[0], data[1],
-                 MAX(data[2],0), MAX(data[3],0));
+        if (num == 4)
+            /* don't let them set it with an area < 0 */
+            RECT_SET(self->icon_geometry, data[0], data[1],
+                     MAX(data[2],0), MAX(data[3],0));
+        g_free(data);
     }
 }
 
@@ -2171,23 +2179,23 @@ static void client_get_session_ids(ObClient *self)
     gchar *s;
     gchar **ss;
 
-    if (!PROP_GET32(self->window, wm_client_leader, window, &leader))
+    if (!OBT_PROP_GET32(self->window, WM_CLIENT_LEADER, WINDOW, &leader))
         leader = None;
 
     /* get the SM_CLIENT_ID */
     got = FALSE;
     if (leader)
-        got = PROP_GETS(leader, sm_client_id, locale, &self->sm_client_id);
+        got = OBT_PROP_GETS(leader, SM_CLIENT_ID, locale, &self->sm_client_id);
     if (!got)
-        PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id);
+        OBT_PROP_GETS(self->window, SM_CLIENT_ID, locale, &self->sm_client_id);
 
     /* get the WM_CLASS (name and class). make them "" if they are not
        provided */
     got = FALSE;
     if (leader)
-        got = PROP_GETSS(leader, wm_class, locale, &ss);
+        got = OBT_PROP_GETSS(leader, WM_CLASS, locale, &ss);
     if (!got)
-        got = PROP_GETSS(self->window, wm_class, locale, &ss);
+        got = OBT_PROP_GETSS(self->window, WM_CLASS, locale, &ss);
 
     if (got) {
         if (ss[0]) {
@@ -2204,9 +2212,9 @@ static void client_get_session_ids(ObClient *self)
     /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
     got = FALSE;
     if (leader)
-        got = PROP_GETS(leader, wm_window_role, locale, &s);
+        got = OBT_PROP_GETS(leader, WM_WINDOW_ROLE, locale, &s);
     if (!got)
-        got = PROP_GETS(self->window, wm_window_role, locale, &s);
+        got = OBT_PROP_GETS(self->window, WM_WINDOW_ROLE, locale, &s);
 
     if (got)
         self->role = s;
@@ -2217,9 +2225,9 @@ static void client_get_session_ids(ObClient *self)
     got = FALSE;
 
     if (leader)
-        got = PROP_GETSS(leader, wm_command, locale, &ss);
+        got = OBT_PROP_GETSS(leader, WM_COMMAND, locale, &ss);
     if (!got)
-        got = PROP_GETSS(self->window, wm_command, locale, &ss);
+        got = OBT_PROP_GETSS(self->window, WM_COMMAND, locale, &ss);
 
     if (got) {
         /* merge/mash them all together */
@@ -2242,9 +2250,9 @@ static void client_get_session_ids(ObClient *self)
     /* get the WM_CLIENT_MACHINE */
     got = FALSE;
     if (leader)
-        got = PROP_GETS(leader, wm_client_machine, locale, &s);
+        got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, locale, &s);
     if (!got)
-        got = PROP_GETS(self->window, wm_client_machine, locale, &s);
+        got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, locale, &s);
 
     if (got) {
         gchar localhost[128];
@@ -2259,7 +2267,7 @@ static void client_get_session_ids(ObClient *self)
 
         /* see if it has the PID set too (the PID requires that the
            WM_CLIENT_MACHINE be set) */
-        if (PROP_GET32(self->window, net_wm_pid, cardinal, &pid))
+        if (OBT_PROP_GET32(self->window, NET_WM_PID, CARDINAL, &pid))
             self->pid = pid;
     }
 }
@@ -2279,12 +2287,12 @@ static void client_change_wm_state(ObClient *self)
         self->wmstate = NormalState;
 
     if (old != self->wmstate) {
-        PROP_MSG(self->window, kde_wm_change_state,
-                 self->wmstate, 1, 0, 0);
+        OBT_PROP_MSG(ob_screen, self->window, KDE_WM_CHANGE_STATE,
+                     self->wmstate, 1, 0, 0, 0);
 
         state[0] = self->wmstate;
         state[1] = None;
-        PROP_SETA32(self->window, wm_state, wm_state, state, 2);
+        OBT_PROP_SETA32(self->window, WM_STATE, WM_STATE, state, 2);
     }
 }
 
@@ -2295,30 +2303,30 @@ static void client_change_state(ObClient *self)
 
     num = 0;
     if (self->modal)
-        netstate[num++] = prop_atoms.net_wm_state_modal;
+        netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
     if (self->shaded)
-        netstate[num++] = prop_atoms.net_wm_state_shaded;
+        netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
     if (self->iconic)
-        netstate[num++] = prop_atoms.net_wm_state_hidden;
+        netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
     if (self->skip_taskbar)
-        netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
+        netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
     if (self->skip_pager)
-        netstate[num++] = prop_atoms.net_wm_state_skip_pager;
+        netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
     if (self->fullscreen)
-        netstate[num++] = prop_atoms.net_wm_state_fullscreen;
+        netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
     if (self->max_vert)
-        netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
+        netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
     if (self->max_horz)
-        netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
+        netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
     if (self->above)
-        netstate[num++] = prop_atoms.net_wm_state_above;
+        netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
     if (self->below)
-        netstate[num++] = prop_atoms.net_wm_state_below;
+        netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
     if (self->demands_attention)
-        netstate[num++] = prop_atoms.net_wm_state_demands_attention;
+        netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
     if (self->undecorated)
-        netstate[num++] = prop_atoms.ob_wm_state_undecorated;
-    PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
+        netstate[num++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
+    OBT_PROP_SETA32(self->window, NET_WM_STATE, ATOM, netstate, num);
 
     if (self->frame)
         frame_adjust_state(self->frame);
@@ -2398,7 +2406,15 @@ static ObStackingLayer calc_layer(ObClient *self)
               (self->decorations == 0 &&
                !(self->max_horz && self->max_vert) &&
                RECT_EQUAL(self->area, *monitor))) &&
-             (client_focused(self) || client_search_focus_tree(self)))
+             /* you are fullscreen while you or your children are focused.. */
+             (client_focused(self) || client_search_focus_tree(self) ||
+              /* you can be fullscreen if you're on another desktop */
+              (self->desktop != screen_desktop &&
+               self->desktop != DESKTOP_ALL) ||
+              /* and you can also be fullscreen if the focused client is on
+                 another monitor, or nothing else is focused */
+              (!focus_client ||
+               client_monitor(focus_client) != client_monitor(self))))
         l = OB_STACKING_LAYER_FULLSCREEN;
     else if (self->above) l = OB_STACKING_LAYER_ABOVE;
     else if (self->below) l = OB_STACKING_LAYER_BELOW;
@@ -2424,23 +2440,54 @@ static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
         stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
     }
 
+    /* we've been restacked */
+    self->visited = TRUE;
+
     for (it = self->transients; it; it = g_slist_next(it))
         client_calc_layer_recursive(it->data, orig,
                                     self->layer);
 }
 
+static void client_calc_layer_internal(ObClient *self)
+{
+    GSList *sit;
+
+    /* transients take on the layer of their parents */
+    sit = client_search_all_top_parents(self);
+
+    for (; sit; sit = g_slist_next(sit))
+        client_calc_layer_recursive(sit->data, self, 0);
+}
+
 void client_calc_layer(ObClient *self)
 {
-    ObClient *orig;
-    GSList *it;
+    GList *it;
 
-    orig = self;
+    /* skip over stuff above fullscreen layer */
+    for (it = stacking_list; it; it = g_list_next(it))
+        if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
 
-    /* transients take on the layer of their parents */
-    it = client_search_all_top_parents(self);
+    /* find the windows in the fullscreen layer, and mark them not-visited */
+    for (; it; it = g_list_next(it)) {
+        if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
+        else if (WINDOW_IS_CLIENT(it->data))
+            WINDOW_AS_CLIENT(it->data)->visited = FALSE;
+    }
+
+    client_calc_layer_internal(self);
 
-    for (; it; it = g_slist_next(it))
-        client_calc_layer_recursive(it->data, orig, 0);
+    /* skip over stuff above fullscreen layer */
+    for (it = stacking_list; it; it = g_list_next(it))
+        if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
+
+    /* now recalc any windows in the fullscreen layer which have not
+       had their layer recalced already */
+    for (; it; it = g_list_next(it)) {
+        if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
+        else if (WINDOW_IS_CLIENT(it->data) &&
+                 !WINDOW_AS_CLIENT(it->data)->visited)
+            client_calc_layer_internal(it->data);
+    }
 }
 
 gboolean client_should_show(ObClient *self)
@@ -2460,6 +2507,10 @@ gboolean client_show(ObClient *self)
     gboolean show = FALSE;
 
     if (client_should_show(self)) {
+        /* replay pending pointer event before showing the window, in case it
+           should be going to something under the window */
+        mouse_replay_pointer();
+
         frame_show(self->frame);
         show = TRUE;
 
@@ -2501,6 +2552,10 @@ gboolean client_hide(ObClient *self)
            so trying to ignore them is futile in case 3 anyways
         */
 
+        /* replay pending pointer event before hiding the window, in case it
+           should be going to the window */
+        mouse_replay_pointer();
+
         frame_hide(self->frame);
         hide = TRUE;
 
@@ -2577,7 +2632,7 @@ static void client_apply_startup_state(ObClient *self,
        pre-max/pre-fullscreen values
     */
     client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE);
-    ob_debug("placed window 0x%x at %d, %d with size %d x %d\n",
+    ob_debug("placed window 0x%x at %d, %d with size %d x %d",
              self->window, x, y, w, h);
     /* save the area, and make it where it should be for the premax stuff */
     oldarea = self->area;
@@ -2617,7 +2672,7 @@ static void client_apply_startup_state(ObClient *self,
     client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
 
     /* set the desktop hint, to make sure that it always exists */
-    PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
+    OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
 
     /* nothing to do for the other states:
        skip_taskbar
@@ -2762,7 +2817,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
             0 : self->max_ratio;
 
         /* base size is substituted with min size if not specified */
-        if (self->base_size.width || self->base_size.height) {
+        if (self->base_size.width >= 0 || self->base_size.height >= 0) {
             basew = self->base_size.width;
             baseh = self->base_size.height;
         } else {
@@ -2858,6 +2913,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
 void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
                       gboolean user, gboolean final, gboolean force_reply)
 {
+    Rect oldframe;
     gint oldw, oldh;
     gboolean send_resize_client;
     gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
@@ -2880,6 +2936,7 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
 
     oldw = self->area.width;
     oldh = self->area.height;
+    oldframe = self->frame->area;
     RECT_SET(self->area, x, y, w, h);
 
     /* for app-requested resizes, always resize if 'resized' is true.
@@ -2891,7 +2948,7 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
 
     /* if the client is enlarging, then resize the client before the frame */
     if (send_resize_client && (w > oldw || h > oldh)) {
-        XMoveResizeWindow(ob_display, self->window,
+        XMoveResizeWindow(obt_display, self->window,
                           self->frame->size.left, self->frame->size.top,
                           MAX(w, oldw), MAX(h, oldh));
         frame_adjust_client_area(self->frame);
@@ -2914,6 +2971,10 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
         if (!user)
             ignore_start = event_start_ignore_all_enters();
 
+        /* replay pending pointer event before move the window, in case it
+           would change what window gets the event */
+        mouse_replay_pointer();
+
         frame_adjust_area(self->frame, fmoved, fresized, FALSE);
 
         if (!user)
@@ -2953,11 +3014,11 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
         XEvent event;
 
         event.type = ConfigureNotify;
-        event.xconfigure.display = ob_display;
+        event.xconfigure.display = obt_display;
         event.xconfigure.event = self->window;
         event.xconfigure.window = self->window;
 
-        ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d\n",
+        ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d",
                  self->title, self->root_pos.x, self->root_pos.y, w, h);
 
         /* root window real coords */
@@ -2979,11 +3040,19 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
      */
     if (send_resize_client && (w <= oldw || h <= oldh)) {
         frame_adjust_client_area(self->frame);
-        XMoveResizeWindow(ob_display, self->window,
+        XMoveResizeWindow(obt_display, self->window,
                           self->frame->size.left, self->frame->size.top, w, h);
     }
 
-    XFlush(ob_display);
+    XFlush(obt_display);
+
+    /* if it moved between monitors, then this can affect the stacking
+       layer of this window or others - for fullscreen windows */
+    if (screen_find_monitor(&self->frame->area) !=
+        screen_find_monitor(&oldframe))
+    {
+        client_calc_layer(self);
+    }
 }
 
 void client_fullscreen(ObClient *self, gboolean fs)
@@ -3026,7 +3095,7 @@ void client_fullscreen(ObClient *self, gboolean fs)
         RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
     }
 
-    ob_debug("Window %s going fullscreen (%d)\n",
+    ob_debug("Window %s going fullscreen (%d)",
              self->title, self->fullscreen);
 
     client_setup_decor_and_functions(self, FALSE);
@@ -3052,7 +3121,7 @@ static void client_iconify_recursive(ObClient *self,
 
 
     if (self->iconic != iconic) {
-        ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
+        ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
                  self->window);
 
         if (iconic) {
@@ -3192,45 +3261,138 @@ void client_shade(ObClient *self, gboolean shade)
 
 static void client_ping_event(ObClient *self, gboolean dead)
 {
-    self->not_responding = dead;
-    client_update_title(self);
+    if (self->not_responding != dead) {
+        self->not_responding = dead;
+        client_update_title(self);
+
+        if (dead)
+            /* the client isn't responding, so ask to kill it */
+            client_prompt_kill(self);
+        else {
+            /* it came back to life ! */
+
+            if (self->kill_prompt) {
+                prompt_unref(self->kill_prompt);
+                self->kill_prompt = NULL;
+            }
+
+            self->kill_level = 0;
+        }
+    }
 }
 
 void client_close(ObClient *self)
 {
     if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
 
+    /* if closing an internal obprompt, that is just cancelling it */
+    if (self->prompt) {
+        prompt_cancel(self->prompt);
+        return;
+    }
+
     /* in the case that the client provides no means to requesting that it
        close, we just kill it */
     if (!self->delete_window)
         /* don't use client_kill(), we should only kill based on PID in
            response to a lack of PING replies */
-        XKillClient(ob_display, self->window);
-    else if (self->not_responding)
-        client_kill(self);
+        XKillClient(obt_display, self->window);
     else {
-        PROP_MSG_TO(self->window, self->window, wm_protocols,
-                    prop_atoms.wm_delete_window, event_curtime, 0, 0, 0,
-                    NoEventMask);
+        /* request the client to close with WM_DELETE_WINDOW */
+        OBT_PROP_MSG_TO(self->window, self->window, WM_PROTOCOLS,
+                        OBT_PROP_ATOM(WM_DELETE_WINDOW), event_curtime,
+                        0, 0, 0, NoEventMask);
 
+        /* we're trying to close the window, so see if it is responding. if it
+           is not, then we will let them kill the window */
         if (self->ping)
             ping_start(self, client_ping_event);
+
+        /* if we already know the window isn't responding (maybe they clicked
+           no in the kill dialog but it hasn't come back to life), then show
+           the kill dialog */
+        if (self->not_responding)
+            client_prompt_kill(self);
+    }
+}
+
+#define OB_KILL_RESULT_NO 0
+#define OB_KILL_RESULT_YES 1
+
+static void client_kill_requested(ObPrompt *p, gint result, gpointer data)
+{
+    ObClient *self = data;
+
+    if (result == OB_KILL_RESULT_YES)
+        client_kill(self);
+
+    prompt_unref(self->kill_prompt);
+    self->kill_prompt = NULL;
+}
+
+static void client_prompt_kill(ObClient *self)
+{
+    /* check if we're already prompting */
+    if (!self->kill_prompt) {
+        ObPromptAnswer answers[] = {
+            { _("No"), OB_KILL_RESULT_NO },
+            { _("Yes"), OB_KILL_RESULT_YES }
+        };
+        gchar *m;
+
+        if (client_on_localhost(self)) {
+            const gchar *sig;
+
+            if (self->kill_level == 0)
+                sig = "terminate";
+            else
+                sig = "kill";
+
+            m = g_strdup_printf
+                (_("The window \"%s\" does not seem to be responding.  Do you want to force it to exit by sending the %s signal?"), self->original_title, sig);
+        }
+        else
+            m = g_strdup_printf
+                (_("The window \"%s\" does not seem to be responding.  Do you want to disconnect it from the X server?"), self->original_title);
+
+
+        self->kill_prompt = prompt_new(m, answers,
+                                       sizeof(answers)/sizeof(answers[0]),
+                                       OB_KILL_RESULT_NO, /* default = no */
+                                       OB_KILL_RESULT_NO, /* cancel = no */
+                                       client_kill_requested, self);
+        g_free(m);
     }
+
+    prompt_show(self->kill_prompt, self, TRUE);
 }
 
 void client_kill(ObClient *self)
 {
-    if (!self->client_machine && self->pid) {
+    /* don't kill our own windows */
+    if (self->prompt) return;
+
+    if (client_on_localhost(self) && self->pid) {
         /* running on the local host */
-        if (!self->kill_tried_term) {
+        if (self->kill_level == 0) {
+            ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
+                     self->window, self->pid);
             kill(self->pid, SIGTERM);
-            self->kill_tried_term = TRUE;
+            ++self->kill_level;
+
+            /* show that we're trying to kill it */
+            client_update_title(self);
         }
-        else
+        else {
+            ob_debug("killing window 0x%x with pid %lu, with SIGKILL",
+                     self->window, self->pid);
             kill(self->pid, SIGKILL); /* kill -9 */
+        }
+    }
+    else {
+        /* running on a remote host */
+        XKillClient(obt_display, self->window);
     }
-    else
-        XKillClient(ob_display, self->window);
 }
 
 void client_hilite(ObClient *self, gboolean hilite)
@@ -3249,23 +3411,23 @@ void client_hilite(ObClient *self, gboolean hilite)
     }
 }
 
-void client_set_desktop_recursive(ObClient *self,
-                                  guint target,
-                                  gboolean donthide,
-                                  gboolean dontraise)
+static void client_set_desktop_recursive(ObClient *self,
+                                         guint target,
+                                         gboolean donthide,
+                                         gboolean dontraise)
 {
     guint old;
     GSList *it;
 
     if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
 
-        ob_debug("Setting desktop %u\n", target+1);
+        ob_debug("Setting desktop %u", target+1);
 
         g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
 
         old = self->desktop;
         self->desktop = target;
-        PROP_SET32(self->window, net_wm_desktop, cardinal, target);
+        OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, target);
         /* the frame can display the current desktop state */
         frame_adjust_state(self->frame);
         /* 'move' the window to the new desktop */
@@ -3321,11 +3483,12 @@ gboolean client_validate(ObClient *self)
 {
     XEvent e;
 
-    XSync(ob_display, FALSE); /* get all events on the server */
+    XSync(obt_display, FALSE); /* get all events on the server */
 
-    if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
-        XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
-        XPutBackEvent(ob_display, &e);
+    if (XCheckTypedWindowEvent(obt_display, self->window, DestroyNotify, &e) ||
+        XCheckTypedWindowEvent(obt_display, self->window, UnmapNotify, &e))
+    {
+        XPutBackEvent(obt_display, &e);
         return FALSE;
     }
 
@@ -3360,9 +3523,9 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
     gboolean below = self->below;
     gint i;
 
-    if (!(action == prop_atoms.net_wm_state_add ||
-          action == prop_atoms.net_wm_state_remove ||
-          action == prop_atoms.net_wm_state_toggle))
+    if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) ||
+          action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) ||
+          action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)))
         /* an invalid action was passed to the client message, ignore it */
         return;
 
@@ -3372,103 +3535,103 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
         if (!state) continue;
 
         /* if toggling, then pick whether we're adding or removing */
-        if (action == prop_atoms.net_wm_state_toggle) {
-            if (state == prop_atoms.net_wm_state_modal)
-                action = modal ? prop_atoms.net_wm_state_remove :
-                    prop_atoms.net_wm_state_add;
-            else if (state == prop_atoms.net_wm_state_maximized_vert)
-                action = self->max_vert ? prop_atoms.net_wm_state_remove :
-                    prop_atoms.net_wm_state_add;
-            else if (state == prop_atoms.net_wm_state_maximized_horz)
-                action = self->max_horz ? prop_atoms.net_wm_state_remove :
-                    prop_atoms.net_wm_state_add;
-            else if (state == prop_atoms.net_wm_state_shaded)
-                action = shaded ? prop_atoms.net_wm_state_remove :
-                    prop_atoms.net_wm_state_add;
-            else if (state == prop_atoms.net_wm_state_skip_taskbar)
+        if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) {
+            if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
+                action = modal ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
+                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
+                action = self->max_vert ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
+                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
+                action = self->max_horz ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
+                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
+                action = shaded ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
+                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
                 action = self->skip_taskbar ?
-                    prop_atoms.net_wm_state_remove :
-                    prop_atoms.net_wm_state_add;
-            else if (state == prop_atoms.net_wm_state_skip_pager)
+                    OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
+                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
                 action = self->skip_pager ?
-                    prop_atoms.net_wm_state_remove :
-                    prop_atoms.net_wm_state_add;
-            else if (state == prop_atoms.net_wm_state_hidden)
+                    OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
+                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
                 action = self->iconic ?
-                    prop_atoms.net_wm_state_remove :
-                    prop_atoms.net_wm_state_add;
-            else if (state == prop_atoms.net_wm_state_fullscreen)
+                    OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
+                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
                 action = fullscreen ?
-                    prop_atoms.net_wm_state_remove :
-                    prop_atoms.net_wm_state_add;
-            else if (state == prop_atoms.net_wm_state_above)
-                action = self->above ? prop_atoms.net_wm_state_remove :
-                    prop_atoms.net_wm_state_add;
-            else if (state == prop_atoms.net_wm_state_below)
-                action = self->below ? prop_atoms.net_wm_state_remove :
-                    prop_atoms.net_wm_state_add;
-            else if (state == prop_atoms.net_wm_state_demands_attention)
+                    OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
+                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
+                action = self->above ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
+                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
+                action = self->below ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
+                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
                 action = self->demands_attention ?
-                    prop_atoms.net_wm_state_remove :
-                    prop_atoms.net_wm_state_add;
-            else if (state == prop_atoms.ob_wm_state_undecorated)
-                action = undecorated ? prop_atoms.net_wm_state_remove :
-                    prop_atoms.net_wm_state_add;
+                    OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
+                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+            else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
+                action = undecorated ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
+                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
         }
 
-        if (action == prop_atoms.net_wm_state_add) {
-            if (state == prop_atoms.net_wm_state_modal) {
+        if (action == OBT_PROP_ATOM(NET_WM_STATE_ADD)) {
+            if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
                 modal = TRUE;
-            } else if (state == prop_atoms.net_wm_state_maximized_vert) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
                 max_vert = TRUE;
-            } else if (state == prop_atoms.net_wm_state_maximized_horz) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
                 max_horz = TRUE;
-            } else if (state == prop_atoms.net_wm_state_shaded) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
                 shaded = TRUE;
-            } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
                 self->skip_taskbar = TRUE;
-            } else if (state == prop_atoms.net_wm_state_skip_pager) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
                 self->skip_pager = TRUE;
-            } else if (state == prop_atoms.net_wm_state_hidden) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
                 iconic = TRUE;
-            } else if (state == prop_atoms.net_wm_state_fullscreen) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
                 fullscreen = TRUE;
-            } else if (state == prop_atoms.net_wm_state_above) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
                 above = TRUE;
                 below = FALSE;
-            } else if (state == prop_atoms.net_wm_state_below) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
                 above = FALSE;
                 below = TRUE;
-            } else if (state == prop_atoms.net_wm_state_demands_attention) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
                 demands_attention = TRUE;
-            } else if (state == prop_atoms.ob_wm_state_undecorated) {
+            } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
                 undecorated = TRUE;
             }
 
-        } else { /* action == prop_atoms.net_wm_state_remove */
-            if (state == prop_atoms.net_wm_state_modal) {
+        } else { /* action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) */
+            if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
                 modal = FALSE;
-            } else if (state == prop_atoms.net_wm_state_maximized_vert) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
                 max_vert = FALSE;
-            } else if (state == prop_atoms.net_wm_state_maximized_horz) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
                 max_horz = FALSE;
-            } else if (state == prop_atoms.net_wm_state_shaded) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
                 shaded = FALSE;
-            } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
                 self->skip_taskbar = FALSE;
-            } else if (state == prop_atoms.net_wm_state_skip_pager) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
                 self->skip_pager = FALSE;
-            } else if (state == prop_atoms.net_wm_state_hidden) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
                 iconic = FALSE;
-            } else if (state == prop_atoms.net_wm_state_fullscreen) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
                 fullscreen = FALSE;
-            } else if (state == prop_atoms.net_wm_state_above) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
                 above = FALSE;
-            } else if (state == prop_atoms.net_wm_state_below) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
                 below = FALSE;
-            } else if (state == prop_atoms.net_wm_state_demands_attention) {
+            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
                 demands_attention = FALSE;
-            } else if (state == prop_atoms.ob_wm_state_undecorated) {
+            } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
                 undecorated = FALSE;
             }
         }
@@ -3560,12 +3723,12 @@ gboolean client_focus(ObClient *self)
 
     if (!client_can_focus(self)) {
         ob_debug_type(OB_DEBUG_FOCUS,
-                      "Client %s can't be focused\n", self->title);
+                      "Client %s can't be focused", self->title);
         return FALSE;
     }
 
     ob_debug_type(OB_DEBUG_FOCUS,
-                  "Focusing client \"%s\" (0x%x) at time %u\n",
+                  "Focusing client \"%s\" (0x%x) at time %u",
                   self->title, self->window, event_curtime);
 
     /* if using focus_delay, stop the timer now so that focus doesn't
@@ -3581,35 +3744,35 @@ gboolean client_focus(ObClient *self)
     */
     event_cancel_all_key_grabs();
 
-    xerror_set_ignore(TRUE);
-    xerror_occured = FALSE;
+    obt_display_ignore_errors(TRUE);
 
     if (self->can_focus) {
         /* This can cause a BadMatch error with CurrentTime, or if an app
            passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
-        XSetInputFocus(ob_display, self->window, RevertToPointerRoot,
+        XSetInputFocus(obt_display, self->window, RevertToPointerRoot,
                        event_curtime);
     }
 
     if (self->focus_notify) {
         XEvent ce;
         ce.xclient.type = ClientMessage;
-        ce.xclient.message_type = prop_atoms.wm_protocols;
-        ce.xclient.display = ob_display;
+        ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
+        ce.xclient.display = obt_display;
         ce.xclient.window = self->window;
         ce.xclient.format = 32;
-        ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
+        ce.xclient.data.l[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS);
         ce.xclient.data.l[1] = event_curtime;
         ce.xclient.data.l[2] = 0l;
         ce.xclient.data.l[3] = 0l;
         ce.xclient.data.l[4] = 0l;
-        XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
+        XSendEvent(obt_display, self->window, FALSE, NoEventMask, &ce);
     }
 
-    xerror_set_ignore(FALSE);
+    obt_display_ignore_errors(FALSE);
 
-    ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d\n", xerror_occured);
-    return !xerror_occured;
+    ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d",
+                  obt_display_error_occured);
+    return !obt_display_error_occured;
 }
 
 static void client_present(ObClient *self, gboolean here, gboolean raise,
@@ -3683,53 +3846,21 @@ gboolean client_focused(ObClient *self)
     return self == focus_client;
 }
 
-static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
-{
-    guint i;
-    gulong min_diff, min_i;
 
-    if (!self->nicons) {
-        ObClientIcon *parent = NULL;
-        GSList *it;
-
-        for (it = self->parents; it; it = g_slist_next(it)) {
-            ObClient *c = it->data;
-            if ((parent = client_icon_recursive(c, w, h)))
-                break;
-        }
-
-        return parent;
-    }
-
-    /* some kind of crappy approximation to find the icon closest in size to
-       what we requested, but icons are generally all the same ratio as
-       eachother so it's good enough. */
-
-    min_diff = ABS(self->icons[0].width - w) + ABS(self->icons[0].height - h);
-    min_i = 0;
 
-    for (i = 1; i < self->nicons; ++i) {
-        gulong diff;
-
-        diff = ABS(self->icons[i].width - w) + ABS(self->icons[i].height - h);
-        if (diff < min_diff) {
-            min_diff = diff;
-            min_i = i;
-        }
-    }
-    return &self->icons[min_i];
-}
-
-const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
+RrImage* client_icon(ObClient *self)
 {
-    ObClientIcon *ret;
-    static ObClientIcon deficon;
+    RrImage *ret = NULL;
 
-    if (!(ret = client_icon_recursive(self, w, h))) {
-        deficon.width = deficon.height = 48;
-        deficon.data = ob_rr_theme->def_win_icon;
-        ret = &deficon;
+    if (self->icon_set)
+        ret = self->icon_set;
+    else if (self->parents) {
+        GSList *it;
+        for (it = self->parents; it && !ret; it = g_slist_next(it))
+            ret = client_icon(it->data);
     }
+    if (!ret)
+        ret = client_default_icon;
     return ret;
 }
 
@@ -3934,15 +4065,15 @@ static void detect_edge(Rect area, ObDirection dir,
             g_assert_not_reached();
     }
 
-    ob_debug("my head %d size %d\n", my_head, my_size);
-    ob_debug("head %d tail %d deest %d\n", head, tail, *dest);
+    ob_debug("my head %d size %d", my_head, my_size);
+    ob_debug("head %d tail %d deest %d", head, tail, *dest);
     if (!skip_head) {
-        ob_debug("using near edge %d\n", head);
+        ob_debug("using near edge %d", head);
         *dest = head;
         *near_edge = TRUE;
     }
     else if (!skip_tail) {
-        ob_debug("using far edge %d\n", tail);
+        ob_debug("using far edge %d", tail);
         *dest = tail;
         *near_edge = FALSE;
     }
@@ -4007,7 +4138,7 @@ void client_find_edge_directional(ObClient *self, ObDirection dir,
             cur->desktop != screen_desktop)
             continue;
 
-        ob_debug("trying window %s\n", cur->title);
+        ob_debug("trying window %s", cur->title);
 
         detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
                     my_edge_size, dest, near_edge);
@@ -4128,10 +4259,10 @@ void client_find_resize_directional(ObClient *self, ObDirection side,
         g_assert_not_reached();
     }
 
-    ob_debug("head %d dir %d\n", head, dir);
+    ob_debug("head %d dir %d", head, dir);
     client_find_edge_directional(self, dir, head, 1,
                                  e_start, e_size, &e, &near);
-    ob_debug("edge %d\n", e);
+    ob_debug("edge %d", e);
     *x = self->frame->area.x;
     *y = self->frame->area.y;
     *w = self->frame->area.width;
@@ -4200,3 +4331,9 @@ gboolean client_has_group_siblings(ObClient *self)
 {
     return self->group && self->group->members->next;
 }
+
+/*! Returns TRUE if the client is running on the same machine as Openbox */
+gboolean client_on_localhost(ObClient *self)
+{
+    return self->client_machine == NULL;
+}