]> Dogcows Code - chaz/openbox/blobdiff - openbox/client.c
Merge branch 'backport' into work
[chaz/openbox] / openbox / client.c
index 2f374bbff5051250d00be751679e92149dbf1cfc..a62aa5b6c9f2e94a7c96553824e43c19aaae79b0 100644 (file)
@@ -1,5 +1,5 @@
 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
-   
+
    client.c for the Openbox window manager
    Copyright (c) 2006        Mikael Magnusson
    Copyright (c) 2003-2007   Dana Jansens
 #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 "propwin.h"
 #include "stacking.h"
 #include "openbox.h"
+#include "hooks.h"
 #include "group.h"
 #include "config.h"
 #include "menuframe.h"
 #include "keyboard.h"
 #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>
 #endif
 
+#ifdef HAVE_SIGNAL_H
+#  include <signal.h> /* for kill() */
+#endif
+
 #include <glib.h>
 #include <X11/Xutil.h>
 
@@ -62,9 +68,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);
@@ -73,8 +80,11 @@ static void client_get_area(ObClient *self);
 static void client_get_desktop(ObClient *self);
 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);
@@ -88,15 +98,30 @@ static void client_update_transient_tree(ObClient *self,
                                          gboolean oldgtran, gboolean newgtran,
                                          ObClient* oldparent,
                                          ObClient *newparent);
-static void client_present(ObClient *self, gboolean here, gboolean raise);
+static void client_present(ObClient *self, gboolean here, gboolean raise,
+                           gboolean unshade);
 static GSList *client_search_all_top_parents_internal(ObClient *self,
                                                       gboolean bylayer,
                                                       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();
@@ -104,6 +129,9 @@ void client_startup(gboolean reconfig)
 
 void client_shutdown(gboolean reconfig)
 {
+    RrImageUnref(client_default_icon);
+    client_default_icon = NULL;
+
     if (reconfig) return;
 }
 
@@ -140,7 +168,7 @@ void client_remove_destroy_notify(ObClientCallback func)
     }
 }
 
-void client_set_list()
+void client_set_list(void)
 {
     Window *windows, *win_it;
     GList *it;
@@ -155,8 +183,8 @@ void client_set_list()
     } 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);
@@ -164,118 +192,63 @@ void client_set_list()
     stacking_set_list();
 }
 
-void client_manage_all()
-{
-    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;
-    gint placex, placey, placew, placeh;
     gboolean transient = FALSE;
+    Rect place, *monitor;
+    Time launch_time, map_time;
+    guint32 user_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", window);
 
-    ob_debug("Managing window: 0x%lx\n", 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 */
     self->gravity = NorthWestGravity;
     self->desktop = screen_num_desktops; /* always an invalid value */
-    self->user_time = focus_client ? focus_client->user_time : CurrentTime;
 
     /* get all the stuff off the window */
     client_get_all(self, TRUE);
 
+    ob_debug("Window type: %d", self->type);
+    ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
+    ob_debug("Window name: %s class: %s role: %s", self->name, self->class, self->role);
+
+    /* per-app settings override stuff from client_get_all, and return the
+       settings for other uses too. the returned settings is a shallow copy,
+       that needs to be freed with g_free(). */
+    settings = client_get_settings_state(self);
+
+    /* 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);
@@ -286,20 +259,14 @@ void client_manage(Window window)
        time now */
     grab_server(FALSE);
 
-    /* per-app settings override stuff from client_get_all, and return the
-       settings for other uses too. the returned settings is a shallow copy,
-       that needs to be freed with g_free(). */
-    settings = client_get_settings_state(self);
     /* 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, self->name);
 
-    {
-        Time t = sn_app_started(self->startup_id, self->class);
-        if (t) self->user_time = t;
-    }
+    if (!OBT_PROP_GET32(self->window, NET_WM_USER_TIME, CARDINAL, &user_time))
+        user_time = map_time;
 
     /* do this after we have a frame.. it uses the frame to help determine the
        WM_STATE to apply. */
@@ -314,24 +281,20 @@ void client_manage(Window window)
     /* focus the new window? */
     if (ob_state() != OB_STATE_STARTING &&
         (!self->session || self->session->focused) &&
-        !self->iconic &&
         /* this means focus=true for window is same as config_focus_new=true */
         ((config_focus_new || (settings && settings->focus == 1)) ||
          client_search_focus_tree_full(self)) &&
+        /* NET_WM_USER_TIME 0 when mapping means don't focus */
+        (user_time != 0) &&
         /* this checks for focus=false for the window */
         (!settings || settings->focus != 0) &&
-        /* note the check against type Normal/Dialog/Utility,
-           not client_normal(self), which would also include other types.
-           in this case we want more strict rules for focus */
-        (self->type == OB_CLIENT_TYPE_NORMAL ||
-         self->type == OB_CLIENT_TYPE_UTILITY ||
-         self->type == OB_CLIENT_TYPE_DIALOG))
+        focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE))
     {
         activate = TRUE;
     }
 
     /* 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 */
@@ -339,35 +302,34 @@ void client_manage(Window window)
     frame_adjust_client_area(self->frame);
 
     /* where the frame was placed is where the window was originally */
-    placex = self->area.x;
-    placey = self->area.y;
-    placew = self->area.width;
-    placeh = self->area.height;
+    place = self->area;
+    monitor = screen_physical_area_monitor(screen_find_monitor(&place));
 
     /* 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" :
                     (self->positioned == (PPosition | USPosition) ?
                      "program + user specified" :
-                     "BADNESS !?")))), placex, placey);
+                     "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" :
                     (self->sized == (PSize | USSize) ?
                      "program + user specified" :
-                     "BADNESS !?")))), placew, placeh);
+                     "BADNESS !?")))), place.width, place.height);
 
         /* splash screens are also returned as TRUE for transient,
            and so will be forced on screen below */
-        transient = place_client(self, &placex, &placey, settings);
+        transient = place_client(self, &place.x, &place.y, settings);
 
         /* make sure the window is visible. */
-        client_find_onscreen(self, &placex, &placey, placew, placeh,
+        client_find_onscreen(self, &place.x, &place.y,
+                             place.width, place.height,
                              /* non-normal clients has less rules, and
                                 windows that are being restored from a
                                 session do also. we can assume you want
@@ -384,9 +346,16 @@ void client_manage(Window window)
                              */
                              ob_state() == OB_STATE_RUNNING &&
                              (transient ||
-                              (!(self->positioned & USPosition) &&
+                              (!((self->positioned & USPosition) ||
+                                 (settings && settings->pos_given)) &&
                                client_normal(self) &&
-                               !self->session)));
+                               !self->session &&
+                               /* don't move oldschool fullscreen windows to
+                                  fit inside the struts (fixes Acroread, which
+                                  makes its fullscreen window fit the screen
+                                  but it is not USSize'd or USPosition'd) */
+                               !(self->decorations == 0 &&
+                                 RECT_EQUAL(place, *monitor)))));
     }
 
     /* if the window isn't user-sized, then make it fit inside
@@ -400,34 +369,39 @@ void client_manage(Window window)
     */
     if (ob_state() == OB_STATE_RUNNING &&
         (transient ||
-         (!(self->sized & USSize) &&
+         (!(self->sized & USSize || self->positioned & USPosition) &&
           client_normal(self) &&
-          !self->session)))
+          !self->session &&
+          /* don't shrink oldschool fullscreen windows to fit inside the
+             struts (fixes Acroread, which makes its fullscreen window
+             fit the screen but it is not USSize'd or USPosition'd) */
+          !(self->decorations == 0 && RECT_EQUAL(place, *monitor)))))
     {
-        /* make a copy to modify */
-        Rect a = *screen_area_monitor(self->desktop, client_monitor(self));
+        Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
 
-        /* shrink by the frame's area */
-        a.width -= self->frame->size.left + self->frame->size.right;
-        a.height -= self->frame->size.top + self->frame->size.bottom;
+        /* get the size of the frame */
+        place.width += self->frame->size.left + self->frame->size.right;
+        place.height += self->frame->size.top + self->frame->size.bottom;
 
         /* fit the window inside the area */
-        if (placew > a.width || self->area.height > a.height) {
-            placew = MIN(self->area.width, a.width);
-            placeh = MIN(self->area.height, a.height);
+        place.width = MIN(place.width, a->width);
+        place.height = MIN(place.height, a->height);
 
-            ob_debug("setting window size to %dx%d\n",
-                     self->area.width, self->area.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;
+        place.height -= self->frame->size.top + self->frame->size.bottom;
 
+        g_free(a);
+    }
 
     ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
-             "some restrictions may apply\n",
-             self->window, placex, placey, placew, placeh);
+             "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);
 
@@ -436,53 +410,84 @@ void client_manage(Window window)
 
        this also places the window
     */
-    client_apply_startup_state(self, placex, placey, placew, placeh);
+    client_apply_startup_state(self, place.x, place.y,
+                               place.width, place.height);
+
+    g_free(monitor);
+    monitor = NULL;
 
+    ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s",
+                  activate ? "yes" : "no");
     if (activate) {
-        guint32 last_time = focus_client ?
-            focus_client->user_time : CurrentTime;
+        gboolean raise = FALSE;
+        gboolean relative_focused;
+        gboolean parent_focused;
+
+        parent_focused = (focus_client != NULL &&
+                          client_search_focus_parent(self));
+        relative_focused = (focus_client != NULL &&
+                            (client_search_focus_tree_full(self) != NULL ||
+                             client_search_focus_group_full(self) != NULL));
 
         /* This is focus stealing prevention */
         ob_debug_type(OB_DEBUG_FOCUS,
-                      "Want to focus new window 0x%x with time %u "
-                      "(last time %u)\n",
-                      self->window, self->user_time, last_time);
+                      "Want to focus new window 0x%x at time %u "
+                      "launched at %u (last user interaction time %u)",
+                      self->window, map_time, launch_time,
+                      event_last_user_time);
+        ob_debug_type(OB_DEBUG_FOCUS,
+                      "Current focus_client: %s",
+                      (focus_client ? focus_client->title : "(none)"));
+        ob_debug_type(OB_DEBUG_FOCUS,
+                      "parent focused: %d  relative focused: %d",
+                      parent_focused, relative_focused);
+
+        if (menu_frame_visible || moveresize_in_progress) {
+            activate = FALSE;
+            raise = TRUE;
+            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");
+        }
 
         /* if it's on another desktop */
-        if (!(self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
-            && /* the timestamp is from before you changed desktops */
-            self->user_time && screen_desktop_user_time &&
-            !event_time_after(self->user_time, screen_desktop_user_time))
+        else if (!(self->desktop == screen_desktop ||
+                   self->desktop == DESKTOP_ALL) &&
+                 /* the timestamp is from before you changed desktops */
+                 launch_time && screen_desktop_user_time &&
+                 !event_time_after(launch_time, screen_desktop_user_time))
         {
             activate = FALSE;
+            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 &&
-                 client_search_focus_group_full(self) == NULL)
-        {
-            /* If time stamp is old, don't steal focus */
-            if (self->user_time && last_time &&
-                !event_time_after(self->user_time, last_time))
+        /* If something is focused... */
+        else if (focus_client) {
+            /* If the user is working in another window right now, then don't
+               steal focus */
+            if (!parent_focused &&
+                event_last_user_time && launch_time &&
+                event_time_after(event_last_user_time, launch_time) &&
+                event_last_user_time != launch_time &&
+                event_time_after(event_last_user_time,
+                                 map_time - OB_EVENT_USER_TIME_DELAY))
             {
                 activate = FALSE;
                 ob_debug_type(OB_DEBUG_FOCUS,
-                              "Not focusing the window because the time is "
-                              "too old\n");
+                              "Not focusing the window because the user is "
+                              "working in another window that is not "
+                              "its parent");
             }
-            /* If its a transient (and parents aren't focused) and the time
-               is ambiguous (either the current focus target doesn't have
-               a timestamp, or they are the same (we probably inherited it
-               from them) */
-            else if (client_has_parent(self) &&
-                     (!last_time || self->user_time == last_time))
-            {
+            /* If the new window is a transient (and its relatives aren't
+               focused) */
+            else if (client_has_parent(self) && !relative_focused) {
                 activate = FALSE;
                 ob_debug_type(OB_DEBUG_FOCUS,
                               "Not focusing the window because it is a "
-                              "transient, and the time is very ambiguous\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.
@@ -493,26 +498,42 @@ 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 */
             else if (client_focus_target(self) != self) {
                 activate = FALSE;
+                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");
+            }
+            /* Don't move focus if the window is not visible on the current
+               desktop and none of its relatives are focused */
+            else if (!(self->desktop == screen_desktop ||
+                       self->desktop == DESKTOP_ALL) &&
+                     !relative_focused)
+            {
+                activate = FALSE;
+                raise = TRUE;
+                ob_debug_type(OB_DEBUG_FOCUS,
+                              "Not focusing the window because it is on "
+                              "another desktop and no relatives are focused ");
             }
         }
 
         if (!activate) {
             ob_debug_type(OB_DEBUG_FOCUS,
-                          "Focus stealing prevention activated for %s with "
-                          "time %u (last time %u)\n",
-                          self->title, self->user_time, last_time);
+                          "Focus stealing prevention activated for %s at "
+                          "time %u (last user interaction 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 */
             client_hilite(self, TRUE);
+            /* we may want to raise it even tho we're not activating it */
+            if (raise && !client_restore_session_stacking(self))
+                stacking_raise(CLIENT_AS_WINDOW(self));
         }
     }
     else {
@@ -532,16 +553,25 @@ void client_manage(Window window)
     /* this has to happen before we try focus the window, but we want it to
        happen after the client's stacking has been determined or it looks bad
     */
-    client_show(self);
+    {
+        gulong ignore_start;
+        if (!config_focus_under_mouse)
+            ignore_start = event_start_ignore_all_enters();
+
+        client_show(self);
+
+        if (!config_focus_under_mouse)
+            event_end_ignore_all_enters(ignore_start);
+    }
 
     if (activate) {
         gboolean stacked = client_restore_session_stacking(self);
-        client_present(self, FALSE, !stacked);
+        client_present(self, FALSE, !stacked, TRUE);
     }
 
     /* 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))
@@ -553,19 +583,18 @@ 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;
+    hooks_queue(OB_HOOK_WIN_NEW, self);
 }
 
-
 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 */
 
@@ -583,8 +612,8 @@ 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",
-             self->frame->size.left, self->frame->size.right, 
+    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);
 
     /* free the ObAppSettings shallow copy */
@@ -593,19 +622,18 @@ ObClient *client_fake_manage(Window window)
     return self;
 }
 
-void client_unmanage_all()
+void client_unmanage_all(void)
 {
-    while (client_list != NULL)
+    while (client_list)
         client_unmanage(client_list->data);
 }
 
 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 : "");
 
@@ -613,26 +641,28 @@ 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 (!client_focused(self) || !config_focus_under_mouse)
+    if (!config_focus_under_mouse)
         ignore_start = event_start_ignore_all_enters();
 
     frame_hide(self->frame);
     /* flush to send the hide to the server quickly */
-    XFlush(ob_display);
+    XFlush(obt_display);
 
-    if (!client_focused(self) || !config_focus_under_mouse)
+    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);
 
-    /* kill the property windows */
-    propwin_remove(self->user_time_window, OB_PROPWIN_USER_TIME, self);
+    /* this can't be queued to run later */
+    hooks_run(OB_HOOK_WIN_CLOSE, self);
 
     /* update the focus lists */
     focus_order_remove(self);
@@ -641,9 +671,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 */
@@ -696,7 +730,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);
     }
@@ -709,30 +743,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) */
+    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);
@@ -770,16 +808,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 */
@@ -834,17 +874,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);
@@ -854,13 +894,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;
@@ -914,47 +954,25 @@ void client_move_onscreen(ObClient *self, gboolean rude)
 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
                               gboolean rude)
 {
-    Rect *mon_a, *all_a;
     gint ox = *x, oy = *y;
     gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
     gint fw, fh;
     Rect desired;
+    guint i;
+    gboolean found_mon;
 
     RECT_SET(desired, *x, *y, w, h);
-    all_a = screen_area(self->desktop);
-    mon_a = screen_area_monitor(self->desktop, screen_find_monitor(&desired));
+    frame_rect_to_frame(self->frame, &desired);
 
     /* get where the frame would be */
-    frame_client_gravity(self->frame, x, y, w, h);
+    frame_client_gravity(self->frame, x, y);
 
     /* get the requested size of the window with decorations */
     fw = self->frame->size.left + w + self->frame->size.right;
     fh = self->frame->size.top + h + self->frame->size.bottom;
 
-    /* This makes sure windows aren't entirely outside of the screen so you
-       can't see them at all.
-       It makes sure 10% of the window is on the screen at least. At don't let
-       it move itself off the top of the screen, which would hide the titlebar
-       on you. (The user can still do this if they want too, it's only limiting
-       the application.
-
-       XXX watch for xinerama dead areas...
-    */
-    if (client_normal(self)) {
-        if (!self->strut.right && *x + fw/10 >= all_a->x + all_a->width - 1)
-            *x = all_a->x + all_a->width - fw/10;
-        if (!self->strut.bottom && *y + fh/10 >= all_a->y + all_a->height - 1)
-            *y = all_a->y + all_a->height - fh/10;
-        if (!self->strut.left && *x + fw*9/10 - 1 < all_a->x)
-            *x = all_a->x - fw*9/10;
-        if (!self->strut.top && *y + fh*9/10 - 1 < all_a->y)
-            *y = all_a->y - fw*9/10;
-    }
-
-    /* If rudeness wasn't requested, then figure out of the client is currently
-       entirely on the screen. If it is, and the position isn't changing by
-       request, and it is enlarging, then be rude even though it wasn't
-       requested */
+    /* If rudeness wasn't requested, then still be rude in a given direction
+       if the client is not moving, only resizing in that direction */
     if (!rude) {
         Point oldtl, oldtr, oldbl, oldbr;
         Point newtl, newtr, newbl, newbr;
@@ -991,22 +1009,64 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
             rudeb = TRUE;
     }
 
-    /* This here doesn't let windows even a pixel outside the struts/screen.
-     * When called from client_manage, programs placing themselves are
-     * forced completely onscreen, while things like
-     * xterm -geometry resolution-width/2 will work fine. Trying to
-     * place it completely offscreen will be handled in the above code.
-     * Sorry for this confused comment, i am tired. */
-    if (rudel && !self->strut.left && *x < mon_a->x) *x = mon_a->x;
-    if (ruder && !self->strut.right && *x + fw > mon_a->x + mon_a->width)
-        *x = mon_a->x + MAX(0, mon_a->width - fw);
+    /* 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 || found_mon)
+                continue;
+
+            /* the window is not inside any monitor! so just use the first
+               one */
+            a = screen_area(self->desktop, 0, NULL);
+        } else {
+            found_mon = TRUE;
+            a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
+        }
 
-    if (rudet && !self->strut.top && *y < mon_a->y) *y = mon_a->y;
-    if (rudeb && !self->strut.bottom && *y + fh > mon_a->y + mon_a->height)
-        *y = mon_a->y + MAX(0, mon_a->height - fh);
+        /* This makes sure windows aren't entirely outside of the screen so you
+           can't see them at all.
+           It makes sure 10% of the window is on the screen at least. And don't
+           let it move itself off the top of the screen, which would hide the
+           titlebar on you. (The user can still do this if they want too, it's
+           only limiting the application.
+        */
+        if (client_normal(self)) {
+            if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1)
+                *x = a->x + a->width - fw/10;
+            if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1)
+                *y = a->y + a->height - fh/10;
+            if (!self->strut.left && *x + fw*9/10 - 1 < a->x)
+                *x = a->x - fw*9/10;
+            if (!self->strut.top && *y + fh*9/10 - 1 < a->y)
+                *y = a->y - fh*9/10;
+        }
+
+        /* This here doesn't let windows even a pixel outside the
+           struts/screen. When called from client_manage, programs placing
+           themselves are forced completely onscreen, while things like
+           xterm -geometry resolution-width/2 will work fine. Trying to
+           place it completely offscreen will be handled in the above code.
+           Sorry for this confused comment, i am tired. */
+        if (rudel && !self->strut.left && *x < a->x) *x = a->x;
+        if (ruder && !self->strut.right && *x + fw > a->x + a->width)
+            *x = a->x + MAX(0, a->width - fw);
+
+        if (rudet && !self->strut.top && *y < a->y) *y = a->y;
+        if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height)
+            *y = a->y + MAX(0, a->height - fh);
+
+        g_free(a);
+    }
 
     /* get where the client should be */
-    frame_frame_gravity(self->frame, x, y, w, h);
+    frame_frame_gravity(self->frame, x, y);
 
     return ox != *x || oy != *y;
 }
@@ -1054,7 +1114,7 @@ static void client_get_all(ObClient *self, gboolean real)
         if (self->type == OB_CLIENT_TYPE_DESKTOP)
             self->desktop = DESKTOP_ALL;
     }
-  
+
 #ifdef SYNC
     client_update_sync_request_counter(self);
 #endif
@@ -1062,33 +1122,31 @@ static void client_get_all(ObClient *self, gboolean real)
     client_get_colormap(self);
     client_update_strut(self);
     client_update_icons(self);
-    client_update_user_time_window(self);
-    if (!self->user_time_window) /* check if this would have been called */
-        client_update_user_time(self);
     client_update_icon_geometry(self);
 }
 
 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)
 {
     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);
 }
 
@@ -1096,12 +1154,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;
@@ -1123,16 +1181,24 @@ static void client_get_desktop(ObClient *self)
         }
         if (all != screen_num_desktops) {
             self->desktop = all;
+
+            ob_debug("client desktop set from parents: 0x%x",
+                     self->desktop);
         }
         /* try get from the startup-notification protocol */
         else if (sn_get_desktop(self->startup_id, &self->desktop)) {
             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",
+                     self->desktop);
         }
         /* defaults to the current desktop */
-        else
+        else {
             self->desktop = screen_desktop;
+            ob_debug("client desktop set to the current desktop: %d",
+                     self->desktop);
+        }
     }
 }
 
@@ -1140,33 +1206,33 @@ 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;
         }
 
@@ -1177,18 +1243,18 @@ static void client_get_state(ObClient *self)
 static void client_get_shaped(ObClient *self)
 {
     self->shaped = FALSE;
-#ifdef   SHAPE
-    if (extensions_shape) {
+#ifdef 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);
+        self->shaped = !!s;
     }
 #endif
 }
@@ -1199,22 +1265,22 @@ void client_update_transient_for(ObClient *self)
     ObClient *target = NULL;
     gboolean trangroup = FALSE;
 
-    if (XGetTransientForHint(ob_display, self->window, &t)) {
-        if (t != self->window) { /* cant be transient to itself! */
-            target = g_hash_table_lookup(window_map, &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;
+    if (XGetTransientForHint(obt_display, self->window, &t)) {
+        if (t != self->window) { /* can't be transient to itself! */
+            ObWindow *tw = window_find(t);
+            /* if this happens then we need to check for it */
+            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;
@@ -1223,7 +1289,7 @@ void client_update_transient_for(ObClient *self)
                                  self->transient_for_group, trangroup,
                                  client_direct_parent(self), target);
     self->transient_for_group = trangroup;
-                          
+
 }
 
 static void client_update_transient_tree(ObClient *self,
@@ -1249,94 +1315,50 @@ static void client_update_transient_tree(ObClient *self,
       transient windows as their children.
       * * */
 
-
     /* No change has occured */
     if (oldgroup == newgroup &&
         oldgtran == newgtran &&
         oldparent == newparent) return;
 
-    /** Remove the client from the transient tree wherever it has changed **/
+    /** Remove the client from the transient tree **/
 
-    /* If the window is becoming a direct transient for a window in its group
-       then any group transients which were our children and are now becoming
-       our parents need to stop being our children.
-
-       Group transients can't be children of group transients already, but
-       we could have any number of direct parents above up, any of which could
-       be transient for the group, and we need to remove it from our children.
-    */
-    if (!oldgtran && oldparent != newparent && newparent != NULL &&
-        newgroup != NULL && newgroup == oldgroup &&
-        client_normal(newparent))
-    {
-        ObClient *look = client_search_top_direct_parent(newparent);
-        self->transients = g_slist_remove(self->transients, look);
-        look->parents = g_slist_remove(look->parents, self);
-    }
-            
-
-    /* If the group changed, or if we are just becoming transient for the
-       group, then we need to remove any old group transient windows
-       from our children. But if we were already transient for the group, then
-       other group transients are not our children. */
-    if ((oldgroup != newgroup || (newgtran && oldgtran != newgtran)) &&
-        oldgroup != NULL && !oldgtran)
-    {
-        for (it = self->transients; it; it = next) {
-            next = g_slist_next(it);
-            c = it->data;
-            if (c->group == oldgroup && client_normal(self)) {
-                self->transients = g_slist_delete_link(self->transients, it);
-                c->parents = g_slist_remove(c->parents, self);
-            }
-        }
-    }
-
-    /* If we used to be transient for a group and now we are not, or we're
-       transient for a new group, then we need to remove ourselves from all
-       our ex-parents */
-    if (oldgtran && (oldgroup != newgroup || oldgtran != newgtran))
-    {
-        for (it = self->parents; it; it = next) {
-            next = g_slist_next(it);
-            c = it->data;
-            if (!c->transient_for_group && client_normal(c)) {
-                c->transients = g_slist_remove(c->transients, self);
-                self->parents = g_slist_delete_link(self->parents, it);
-            }
-        }
+    for (it = self->transients; it; it = next) {
+        next = g_slist_next(it);
+        c = it->data;
+        self->transients = g_slist_delete_link(self->transients, it);
+        c->parents = g_slist_remove(c->parents, self);
     }
-    /* If we used to be transient for a single window and we are no longer
-       transient for it, then we need to remove ourself from its children */
-    else if (oldparent && oldparent != newparent &&
-             client_normal(oldparent))
-    {
-        oldparent->transients = g_slist_remove(oldparent->transients, self);
-        self->parents = g_slist_remove(self->parents, oldparent);
+    for (it = self->parents; it; it = next) {
+        next = g_slist_next(it);
+        c = it->data;
+        self->parents = g_slist_delete_link(self->parents, it);
+        c->transients = g_slist_remove(c->transients, self);
     }
 
-    /** Re-add the client to the transient tree wherever it has changed **/
+    /** Re-add the client to the transient tree **/
 
-    /* If we're now transient for a group and we weren't transient for it
-       before then we need to add ourselves to all our new parents */
-    if (newgtran && (oldgroup != newgroup || oldgtran != newgtran))
-    {
-        for (it = oldgroup->members; it; it = g_slist_next(it)) {
+    /* If we're transient for a group then we need to add ourselves to all our
+       parents */
+    if (newgtran) {
+        for (it = newgroup->members; it; it = g_slist_next(it)) {
             c = it->data;
-            if (c != self && !c->transient_for_group && client_normal(c))
+            if (c != self &&
+                !client_search_top_direct_parent(c)->transient_for_group &&
+                client_normal(c))
             {
                 c->transients = g_slist_prepend(c->transients, self);
                 self->parents = g_slist_prepend(self->parents, c);
             }
         }
     }
-    /* If we are now transient for a single window which we weren't before,
-       we need to add ourselves to its children
 
-       WARNING: Cyclical transient ness is possible if two windows are
+    /* If we are now transient for a single window we need to add ourselves to
+       its children
+
+       WARNING: Cyclical transient-ness is possible if two windows are
        transient for eachother.
     */
-    else if (newparent && newparent != oldparent &&
+    else if (newparent &&
              /* don't make ourself its child if it is already our child */
              !client_is_direct_child(self, newparent) &&
              client_normal(newparent))
@@ -1345,9 +1367,8 @@ static void client_update_transient_tree(ObClient *self,
         self->parents = g_slist_prepend(self->parents, newparent);
     }
 
-    /* If the group changed then we need to add any new group transient
-       windows to our children. But if we're transient for the group, then
-       other group transients are not our children.
+    /* Add any group transient windows to our children. But if we're transient
+       for the group, then other group transients are not our children.
 
        WARNING: Cyclical transient-ness is possible. For e.g. if:
        A is transient for the group
@@ -1355,7 +1376,9 @@ static void client_update_transient_tree(ObClient *self,
        C is transient for B
        A can't be transient for C or we have a cycle
     */
-    if (oldgroup != newgroup && newgroup != NULL && !newgtran &&
+    if (!newgtran && newgroup &&
+        (!newparent ||
+         !client_search_top_direct_parent(newparent)->transient_for_group) &&
         client_normal(self))
     {
         for (it = newgroup->members; it; it = g_slist_next(it)) {
@@ -1369,17 +1392,31 @@ static void client_update_transient_tree(ObClient *self,
             }
         }
     }
+
+    /** If we change our group transient-ness, our children change their
+        effective group transient-ness, which affects how they relate to other
+        group windows **/
+
+    for (it = self->transients; it; it = g_slist_next(it)) {
+        c = it->data;
+        if (!c->transient_for_group)
+            client_update_transient_tree(c, c->group, c->group,
+                                         c->transient_for_group,
+                                         c->transient_for_group,
+                                         client_direct_parent(c),
+                                         client_direct_parent(c));
+    }
 }
 
-static void client_get_mwm_hints(ObClient *self)
+void client_get_mwm_hints(ObClient *self)
 {
     guint num;
     guint32 *hints;
 
     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];
@@ -1397,27 +1434,28 @@ 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 |
@@ -1431,9 +1469,9 @@ 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) {
         /*the window type hint was not set, which means we either classify
           ourself as a normal window or a dialog, depending on if we are a
@@ -1457,22 +1495,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] == 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 */
@@ -1488,18 +1530,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);
 }
 
@@ -1507,12 +1550,12 @@ 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 */
         self->colormap = colormap;
-        screen_install_colormap(self, FALSE); /* install new one */
+        screen_install_colormap(self, TRUE); /* install new one */
     } else
         self->colormap = colormap;
 }
@@ -1526,12 +1569,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))
         */
@@ -1552,16 +1595,25 @@ void client_update_normal_hints(ObClient *self)
 
         if (size.flags & PMinSize)
             SIZE_SET(self->min_size, size.min_width, size.min_height);
-    
+
         if (size.flags & PMaxSize)
             SIZE_SET(self->max_size, size.max_width, size.max_height);
-    
+
         if (size.flags & PBaseSize)
             SIZE_SET(self->base_size, size.base_width, size.base_height);
-    
+
         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)",
+                 self->min_size.width, self->min_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");
 }
 
 void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
@@ -1596,11 +1648,16 @@ void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
     switch (self->type) {
     case OB_CLIENT_TYPE_NORMAL:
         /* normal windows retain all of the possible decorations and
-           functionality, and are the only windows that you can fullscreen */
+           functionality, and can be fullscreen */
         self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
         break;
 
     case OB_CLIENT_TYPE_DIALOG:
+        /* sometimes apps make dialog windows fullscreen for some reason (for
+           e.g. kpdf does this..) */
+        self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
+        break;
+
     case OB_CLIENT_TYPE_UTILITY:
         /* these windows don't have anything added or removed by default */
         break;
@@ -1700,12 +1757,8 @@ void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
 
     /* finally, the user can have requested no decorations, which overrides
        everything (but doesnt give it a border if it doesnt have one) */
-    if (self->undecorated) {
-        if (config_theme_keepborder)
-            self->decorations &= OB_FRAME_DECOR_BORDER;
-        else
-            self->decorations = 0;
-    }
+    if (self->undecorated)
+        self->decorations = 0;
 
     /* if we don't have a titlebar, then we cannot shade! */
     if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
@@ -1722,7 +1775,8 @@ void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
     client_change_allowed_actions(self);
 
     if (reconfig)
-        client_reconfigure(self);
+        /* force reconfigure to make sure decorations are updated */
+        client_reconfigure(self, TRUE);
 }
 
 static void client_change_allowed_actions(ObClient *self)
@@ -1732,43 +1786,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;
@@ -1780,21 +1834,14 @@ static void client_change_allowed_actions(ObClient *self)
     }
 }
 
-void client_reconfigure(ObClient *self)
-{
-    client_configure(self, self->area.x, self->area.y,
-                     self->area.width, self->area.height,
-                     FALSE, TRUE);
-}
-
 void client_update_wmhints(ObClient *self)
 {
     XWMHints *hints;
 
-    /* assume a window takes input if it doesnt specify */
+    /* assume a window takes input if it doesn't 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)
@@ -1823,7 +1870,7 @@ void client_update_wmhints(ObClient *self)
             ObGroup *oldgroup = self->group;
 
             /* remove from the old group if there was one */
-            if (self->group != NULL) {
+            if (self->group) {
                 group_remove(self->group, self);
                 self->group = NULL;
             }
@@ -1880,22 +1927,24 @@ 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:
-                  http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
-                */
+    /*
+    GNOME alert windows are not given titles:
+    http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
+    */
                 data = g_strdup("");
             } else
-                data = g_strdup("Unnamed Window");
+                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);
@@ -1903,7 +1952,16 @@ void client_update_title(ObClient *self)
     } else
         visible = data;
 
-    PROP_SETS(self->window, net_wm_visible_name, visible);
+    if (self->not_responding) {
+        data = visible;
+        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);
+    }
+
+    OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, utf8, visible);
     self->title = visible;
 
     if (self->frame)
@@ -1914,10 +1972,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) {
@@ -1926,7 +1984,16 @@ void client_update_title(ObClient *self)
     } else
         visible = data;
 
-    PROP_SETS(self->window, net_wm_visible_icon_name, visible);
+    if (self->not_responding) {
+        data = visible;
+        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);
+    }
+
+    OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, utf8, visible);
     self->icon_title = visible;
 }
 
@@ -1937,8 +2004,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,
@@ -1950,14 +2018,14 @@ 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) {
-            const Rect *a;
+            Rect *a;
 
             got = TRUE;
 
             /* use the screen's width/height */
-            a = screen_physical_area();
+            a = screen_physical_area_all_monitors();
 
             STRUT_PARTIAL_SET(strut,
                               data[0], data[2], data[1], data[3],
@@ -1965,6 +2033,7 @@ void client_update_strut(ObClient *self)
                               a->x, a->x + a->width - 1,
                               a->y, a->y + a->height - 1,
                               a->x, a->x + a->width - 1);
+            g_free(a);
         }
         g_free(data);
     }
@@ -1988,153 +2057,128 @@ 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++;
-                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[self->nicons-1].width,
-                                    &self->icons[self->nicons-1].height,
-                                    &self->icons[self->nicons-1].data)){
-                    g_free(&self->icons[self->nicons-1]);
-                    self->nicons--;
+                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 */
-    if (self->nicons == 0) {
-        RrPixel32 *icon = ob_rr_theme->def_win_icon;
-        gulong *data;
+    /* set the client's icons to be whatever we found */
+    RrImageUnref(self->icon_set);
+    self->icon_set = img;
 
-        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) +
+    /* 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->icon_set && !self->parents) {
+        RrPixel32 *icon = ob_rr_theme->def_win_icon;
+        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);
-}
-
-void client_update_user_time(ObClient *self)
-{
-    guint32 time;
-    gboolean got = FALSE;
-
-    if (self->user_time_window)
-        got = PROP_GET32(self->user_time_window,
-                         net_wm_user_time, cardinal, &time);
-    if (!got)
-        got = PROP_GET32(self->window, net_wm_user_time, cardinal, &time);
-
-    if (got) {
-        /* we set this every time, not just when it grows, because in practice
-           sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
-           backward we don't want all windows to stop focusing. we'll just
-           assume noone is setting times older than the last one, cuz that
-           would be pretty stupid anyways
-        */
-        self->user_time = time;
-
-        /*ob_debug("window %s user time %u\n", self->title, time);*/
-    }
-}
-
-void client_update_user_time_window(ObClient *self)
-{
-    guint32 w;
-
-    if (!PROP_GET32(self->window, net_wm_user_time_window, window, &w))
-        w = None;
-
-    if (w != self->user_time_window) {
-        /* remove the old window */
-        propwin_remove(self->user_time_window, OB_PROPWIN_USER_TIME, self);
-        self->user_time_window = None;
 
-        if (self->group && self->group->leader == w) {
-            ob_debug_type(OB_DEBUG_APP_BUGS, "Window is setting its "
-                          "_NET_WM_USER_TYPE_WINDOW to its group leader\n");
-            /* do it anyways..? */
-        }
-        else if (w == self->window) {
-            ob_debug_type(OB_DEBUG_APP_BUGS, "Window is setting its "
-                          "_NET_WM_USER_TIME_WINDOW to itself\n");
-            w = None; /* don't do it */
-        }
-
-        /* add the new window */
-        propwin_add(w, OB_PROPWIN_USER_TIME, self);
-        self->user_time_window = w;
-
-        /* and update from it */
-        client_update_user_time(self);
-    }
+    grab_server(FALSE);
 }
 
 void client_update_icon_geometry(ObClient *self)
@@ -2144,12 +2188,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);
     }
 }
 
@@ -2160,23 +2206,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]) {
@@ -2193,9 +2239,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;
@@ -2206,9 +2252,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 */
@@ -2231,12 +2277,13 @@ 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];
+        guint32 pid;
 
         gethostname(localhost, 127);
         localhost[127] = '\0';
@@ -2244,6 +2291,11 @@ static void client_get_session_ids(ObClient *self)
             self->client_machine = s;
         else
             g_free(s);
+
+        /* see if it has the PID set too (the PID requires that the
+           WM_CLIENT_MACHINE be set) */
+        if (OBT_PROP_GET32(self->window, NET_WM_PID, CARDINAL, &pid))
+            self->pid = pid;
     }
 }
 
@@ -2262,12 +2314,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);
     }
 }
 
@@ -2278,30 +2330,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);
@@ -2364,6 +2416,9 @@ gboolean client_has_parent(ObClient *self)
 static ObStackingLayer calc_layer(ObClient *self)
 {
     ObStackingLayer l;
+    Rect *monitor;
+
+    monitor = screen_physical_area_monitor(client_monitor(self));
 
     if (self->type == OB_CLIENT_TYPE_DESKTOP)
         l = OB_STACKING_LAYER_DESKTOP;
@@ -2377,15 +2432,23 @@ static ObStackingLayer calc_layer(ObClient *self)
               */
               (self->decorations == 0 &&
                !(self->max_horz && self->max_vert) &&
-               RECT_EQUAL(self->area,
-                          *screen_physical_area_monitor
-                          (client_monitor(self))))) &&
-             (client_focused(self) || client_search_focus_tree(self)))
+               RECT_EQUAL(self->area, *monitor))) &&
+             /* 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;
     else l = OB_STACKING_LAYER_NORMAL;
 
+    g_free(monitor);
+
     return l;
 }
 
@@ -2404,23 +2467,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);
+
+    /* 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;
 
-    for (; it; it = g_slist_next(it))
-        client_calc_layer_recursive(it->data, orig, 0);
+    /* 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)
@@ -2431,7 +2525,7 @@ gboolean client_should_show(ObClient *self)
         return FALSE;
     if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
         return TRUE;
-    
+
     return FALSE;
 }
 
@@ -2440,6 +2534,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;
 
@@ -2448,6 +2546,8 @@ gboolean client_show(ObClient *self)
            desktop!
         */
         client_change_wm_state(self);
+
+        hooks_queue(OB_HOOK_WIN_VISIBLE, self);
     }
     return show;
 }
@@ -2458,16 +2558,26 @@ gboolean client_hide(ObClient *self)
 
     if (!client_should_show(self)) {
         if (self == focus_client) {
-            /* if there is a grab going on, then we need to cancel it. if we
-               move focus during the grab, applications will get
-               NotifyWhileGrabbed events and ignore them !
-
-               actions should not rely on being able to move focus during an
-               interactive grab.
-            */
             event_cancel_all_key_grabs();
         }
 
+        /* We don't need to ignore enter events here.
+           The window can hide/iconify in 3 different ways:
+           1 - through an x message. in this case we ignore all enter events
+               caused by responding to the x message (unless underMouse)
+           2 - by a keyboard action. in this case we ignore all enter events
+               caused by the action
+           3 - by a mouse action. in this case they are doing stuff with the
+               mouse and focus _should_ move.
+
+           Also in action_end, we simulate an enter event that can't be ignored
+           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;
 
@@ -2476,6 +2586,8 @@ gboolean client_hide(ObClient *self)
            desktop!
         */
         client_change_wm_state(self);
+
+        hooks_queue(OB_HOOK_WIN_INVISIBLE, self);
     }
     return hide;
 }
@@ -2514,7 +2626,6 @@ gboolean client_enter_focusable(ObClient *self)
             self->type != OB_CLIENT_TYPE_DESKTOP);
 }
 
-
 static void client_apply_startup_state(ObClient *self,
                                        gint x, gint y, gint w, gint h)
 {
@@ -2544,11 +2655,11 @@ 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",
-             self->window, self->area.x, self->area.y,
-             self->area.width, self->area.height);
-    oldarea = self->area;              /* save the area */
-    RECT_SET(self->area, x, y, w, h);  /* put where it should be for the premax stuff */
+    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;
+    RECT_SET(self->area, x, y, w, h);
 
     /* apply the states. these are in a carefully crafted order.. */
 
@@ -2562,7 +2673,7 @@ static void client_apply_startup_state(ObClient *self,
         client_shade(self, TRUE);
     if (demands_attention)
         client_hilite(self, TRUE);
-  
+
     if (max_vert && max_horz)
         client_maximize(self, TRUE, 0);
     else if (max_vert)
@@ -2570,14 +2681,21 @@ static void client_apply_startup_state(ObClient *self,
     else if (max_horz)
         client_maximize(self, TRUE, 1);
 
-    /* if the window hasn't been configured yet, then do so now */
-    if (!fullscreen && !max_vert && !max_horz) {
-        self->area = oldarea;
-        client_configure(self, x, y, w, h, FALSE, TRUE);
-    }
+    /* if the window hasn't been configured yet, then do so now, in fact the
+       x,y,w,h may _not_ be the same as the area rect, which can end up
+       meaning that the client isn't properly moved/resized by the fullscreen
+       function
+       pho can cause this because it maps at size of the screen but not 0,0
+       so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
+       then fullscreen'ing makes it go to 0,0 which it thinks it already is at
+       cuz thats where the pre-fullscreen will be. however the actual area is
+       not, so this needs to be called even if we have fullscreened/maxed
+    */
+    self->area = oldarea;
+    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
@@ -2650,108 +2768,26 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
                           gint *logicalw, gint *logicalh,
                           gboolean user)
 {
-    Rect desired_area = {*x, *y, *w, *h};
+    Rect desired = {*x, *y, *w, *h};
+    frame_rect_to_frame(self->frame, &desired);
 
-    /* make the frame recalculate its dimentions n shit without changing
+    /* make the frame recalculate its dimensions n shit without changing
        anything visible for real, this way the constraints below can work with
        the updated frame dimensions. */
     frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
 
-    /* work within the prefered sizes given by the window */
-    if (!(*w == self->area.width && *h == self->area.height)) {
-        gint basew, baseh, minw, minh;
-
-        /* base size is substituted with min size if not specified */
-        if (self->base_size.width || self->base_size.height) {
-            basew = self->base_size.width;
-            baseh = self->base_size.height;
-        } else {
-            basew = self->min_size.width;
-            baseh = self->min_size.height;
-        }
-        /* min size is substituted with base size if not specified */
-        if (self->min_size.width || self->min_size.height) {
-            minw = self->min_size.width;
-            minh = self->min_size.height;
-        } else {
-            minw = self->base_size.width;
-            minh = self->base_size.height;
-        }
+    /* gets the frame's position */
+    frame_client_gravity(self->frame, x, y);
 
-        /* if this is a user-requested resize, then check against min/max
-           sizes */
+    /* these positions are frame positions, not client positions */
 
-        /* smaller than min size or bigger than max size? */
-        if (*w > self->max_size.width) *w = self->max_size.width;
-        if (*w < minw) *w = minw;
-        if (*h > self->max_size.height) *h = self->max_size.height;
-        if (*h < minh) *h = minh;
+    /* set the size and position if fullscreen */
+    if (self->fullscreen) {
+        Rect *a;
+        guint i;
 
-        *w -= basew;
-        *h -= baseh;
-
-        /* keep to the increments */
-        *w /= self->size_inc.width;
-        *h /= self->size_inc.height;
-
-        /* you cannot resize to nothing */
-        if (basew + *w < 1) *w = 1 - basew;
-        if (baseh + *h < 1) *h = 1 - baseh;
-  
-        /* save the logical size */
-        *logicalw = self->size_inc.width > 1 ? *w : *w + basew;
-        *logicalh = self->size_inc.height > 1 ? *h : *h + baseh;
-
-        *w *= self->size_inc.width;
-        *h *= self->size_inc.height;
-
-        *w += basew;
-        *h += baseh;
-
-        /* adjust the height to match the width for the aspect ratios.
-           for this, min size is not substituted for base size ever. */
-        *w -= self->base_size.width;
-        *h -= self->base_size.height;
-
-        if (!self->fullscreen) {
-            if (self->min_ratio)
-                if (*h * self->min_ratio > *w) {
-                    *h = (gint)(*w / self->min_ratio);
-
-                    /* you cannot resize to nothing */
-                    if (*h < 1) {
-                        *h = 1;
-                        *w = (gint)(*h * self->min_ratio);
-                    }
-                }
-            if (self->max_ratio)
-                if (*h * self->max_ratio < *w) {
-                    *h = (gint)(*w / self->max_ratio);
-
-                    /* you cannot resize to nothing */
-                    if (*h < 1) {
-                        *h = 1;
-                        *w = (gint)(*h * self->min_ratio);
-                    }
-                }
-        }
-
-        *w += self->base_size.width;
-        *h += self->base_size.height;
-    }
-
-    /* gets the frame's position */
-    frame_client_gravity(self->frame, x, y, *w, *h);
-
-    /* these positions are frame positions, not client positions */
-
-    /* set the size and position if fullscreen */
-    if (self->fullscreen) {
-        Rect *a;
-        guint i;
-
-        i = screen_find_monitor(&desired_area);
-        a = screen_physical_area_monitor(i);
+        i = screen_find_monitor(&desired);
+        a = screen_physical_area_monitor(i);
 
         *x = a->x;
         *y = a->y;
@@ -2760,12 +2796,16 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
 
         user = FALSE; /* ignore if the client can't be moved/resized when it
                          is fullscreening */
+
+        g_free(a);
     } else if (self->max_horz || self->max_vert) {
         Rect *a;
         guint i;
 
-        i = screen_find_monitor(&desired_area);
-        a = screen_area_monitor(self->desktop, i);
+        /* use all possible struts when maximizing to the full screen */
+        i = screen_find_monitor(&desired);
+        a = screen_area(self->desktop, i,
+                        (self->max_horz && self->max_vert ? NULL : &desired));
 
         /* set the size and position if maximized */
         if (self->max_horz) {
@@ -2779,10 +2819,105 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
 
         user = FALSE; /* ignore if the client can't be moved/resized when it
                          is maximizing */
+
+        g_free(a);
     }
 
     /* gets the client's position */
-    frame_frame_gravity(self->frame, x, y, *w, *h);
+    frame_frame_gravity(self->frame, x, y);
+
+    /* work within the preferred sizes given by the window, these may have
+       changed rather than it's requested width and height, so always run
+       through this code */
+    {
+        gint basew, baseh, minw, minh;
+        gint incw, inch;
+        gfloat minratio, maxratio;
+
+        incw = self->fullscreen || self->max_horz ? 1 : self->size_inc.width;
+        inch = self->fullscreen || self->max_vert ? 1 : self->size_inc.height;
+        minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
+            0 : self->min_ratio;
+        maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
+            0 : self->max_ratio;
+
+        /* base size is substituted with min size if not specified */
+        if (self->base_size.width >= 0 || self->base_size.height >= 0) {
+            basew = self->base_size.width;
+            baseh = self->base_size.height;
+        } else {
+            basew = self->min_size.width;
+            baseh = self->min_size.height;
+        }
+        /* min size is substituted with base size if not specified */
+        if (self->min_size.width || self->min_size.height) {
+            minw = self->min_size.width;
+            minh = self->min_size.height;
+        } else {
+            minw = self->base_size.width;
+            minh = self->base_size.height;
+        }
+
+        /* This comment is no longer true */
+        /* if this is a user-requested resize, then check against min/max
+           sizes */
+
+        /* smaller than min size or bigger than max size? */
+        if (*w > self->max_size.width) *w = self->max_size.width;
+        if (*w < minw) *w = minw;
+        if (*h > self->max_size.height) *h = self->max_size.height;
+        if (*h < minh) *h = minh;
+
+        *w -= basew;
+        *h -= baseh;
+
+        /* keep to the increments */
+        *w /= incw;
+        *h /= inch;
+
+        /* you cannot resize to nothing */
+        if (basew + *w < 1) *w = 1 - basew;
+        if (baseh + *h < 1) *h = 1 - baseh;
+
+        /* save the logical size */
+        *logicalw = incw > 1 ? *w : *w + basew;
+        *logicalh = inch > 1 ? *h : *h + baseh;
+
+        *w *= incw;
+        *h *= inch;
+
+        *w += basew;
+        *h += baseh;
+
+        /* adjust the height to match the width for the aspect ratios.
+           for this, min size is not substituted for base size ever. */
+        *w -= self->base_size.width;
+        *h -= self->base_size.height;
+
+        if (minratio)
+            if (*h * minratio > *w) {
+                *h = (gint)(*w / minratio);
+
+                /* you cannot resize to nothing */
+                if (*h < 1) {
+                    *h = 1;
+                    *w = (gint)(*h * minratio);
+                }
+            }
+        if (maxratio)
+            if (*h * maxratio < *w) {
+                *h = (gint)(*w / maxratio);
+
+                /* you cannot resize to nothing */
+                if (*h < 1) {
+                    *h = 1;
+                    *w = (gint)(*h * minratio);
+                }
+            }
+
+        *w += self->base_size.width;
+        *h += self->base_size.height;
+    }
 
     /* these override the above states! if you cant move you can't move! */
     if (user) {
@@ -2800,13 +2935,13 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
     g_assert(*h > 0);
 }
 
-
 void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
-                      gboolean user, gboolean final)
+                      gboolean user, gboolean final, gboolean force_reply)
 {
+    Rect oldframe;
     gint oldw, oldh;
     gboolean send_resize_client;
-    gboolean moved = FALSE, resized = FALSE;
+    gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
     gboolean fmoved, fresized;
     guint fdecor = self->frame->decorations;
     gboolean fhorz = self->frame->max_horz;
@@ -2826,6 +2961,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.
@@ -2837,7 +2973,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);
@@ -2855,40 +2991,59 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
     }
 
     /* adjust the frame */
-    if (fmoved || fresized)
+    if (fmoved || fresized) {
+        gulong ignore_start;
+        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)
+            event_end_ignore_all_enters(ignore_start);
+    }
+
+    if (!user || final) {
+        gint oldrx = self->root_pos.x;
+        gint oldry = self->root_pos.y;
+        /* we have reset the client to 0 border width, so don't include
+           it in these coords */
+        POINT_SET(self->root_pos,
+                  self->frame->area.x + self->frame->size.left -
+                  self->border_width,
+                  self->frame->area.y + self->frame->size.top -
+                  self->border_width);
+        if (self->root_pos.x != oldrx || self->root_pos.y != oldry)
+            rootmoved = TRUE;
+    }
+
     /* This is kinda tricky and should not be changed.. let me explain!
 
        When user = FALSE, then the request is coming from the application
        itself, and we are more strict about when to send a synthetic
        ConfigureNotify.  We strictly follow the rules of the ICCCM sec 4.1.5
-       in this case.
+       in this case (if force_reply is true)
 
        When user = TRUE, then the request is coming from "us", like when we
-       maximize a window or sometihng.  In this case we are more lenient.  We
+       maximize a window or something.  In this case we are more lenient.  We
        used to follow the same rules as above, but _Java_ Swing can't handle
        this. So just to appease Swing, when user = TRUE, we always send
        a synthetic ConfigureNotify to give the window its root coordinates.
     */
-    if ((!user && !resized) || (user && final))
+    if ((!user && !resized && (rootmoved || force_reply)) ||
+        (user && final && rootmoved))
     {
         XEvent event;
 
-        /* we have reset the client to 0 border width, so don't include
-           it in these coords */
-        POINT_SET(self->root_pos,
-                  self->frame->area.x + self->frame->size.left -
-                  self->border_width,
-                  self->frame->area.y + self->frame->size.top -
-                  self->border_width);
-
         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 */
@@ -2910,11 +3065,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)
@@ -2930,7 +3093,7 @@ void client_fullscreen(ObClient *self, gboolean fs)
     if (fs) {
         self->pre_fullscreen_area = self->area;
         /* if the window is maximized, its area isn't all that meaningful.
-           save it's premax area instead. */
+           save its premax area instead. */
         if (self->max_horz) {
             self->pre_fullscreen_area.x = self->pre_max_area.x;
             self->pre_fullscreen_area.width = self->pre_max_area.width;
@@ -2957,6 +3120,9 @@ void client_fullscreen(ObClient *self, gboolean fs)
         RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
     }
 
+    ob_debug("Window %s going fullscreen (%d)",
+             self->title, self->fullscreen);
+
     client_setup_decor_and_functions(self, FALSE);
     client_move_resize(self, x, y, w, h);
 
@@ -2978,9 +3144,8 @@ static void client_iconify_recursive(ObClient *self,
     GSList *it;
     gboolean changed = FALSE;
 
-
     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) {
@@ -3000,7 +3165,7 @@ static void client_iconify_recursive(ObClient *self,
 
             if (curdesk && self->desktop != screen_desktop &&
                 self->desktop != DESKTOP_ALL)
-                client_set_desktop(self, screen_desktop, FALSE);
+                client_set_desktop(self, screen_desktop, FALSE, FALSE);
 
             /* this puts it after the current focused window */
             focus_order_remove(self);
@@ -3016,6 +3181,9 @@ static void client_iconify_recursive(ObClient *self,
             frame_begin_iconify_animation(self->frame, iconic);
         /* do this after starting the animation so it doesn't flash */
         client_showhide(self);
+
+        hooks_queue((iconic ? OB_HOOK_WIN_ICONIC : OB_HOOK_WIN_UNICONIC),
+                    self);
     }
 
     /* iconify all direct transients, and deiconify all transients
@@ -3040,9 +3208,9 @@ void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
 void client_maximize(ObClient *self, gboolean max, gint dir)
 {
     gint x, y, w, h;
-     
+
     g_assert(dir == 0 || dir == 1 || dir == 2);
-    if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
+    if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && max) return;/* can't */
 
     /* check if already done */
     if (max) {
@@ -3103,6 +3271,8 @@ void client_maximize(ObClient *self, gboolean max, gint dir)
 
     client_setup_decor_and_functions(self, FALSE);
     client_move_resize(self, x, y, w, h);
+
+    hooks_queue((max ? OB_HOOK_WIN_MAX : OB_HOOK_WIN_UNMAX), self);
 }
 
 void client_shade(ObClient *self, gboolean shade)
@@ -3115,44 +3285,170 @@ void client_shade(ObClient *self, gboolean shade)
     client_change_state(self);
     client_change_wm_state(self); /* the window is being hidden/shown */
     /* resize the frame to just the titlebar */
-    frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
+    frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
+
+    hooks_queue((shade ? OB_HOOK_WIN_SHADE : OB_HOOK_WIN_UNSHADE), self);
 }
 
-void client_close(ObClient *self)
+static void client_ping_event(ObClient *self, gboolean dead)
 {
-    XEvent ce;
+    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(obt_display, self->window);
+    else {
+        /* 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 gboolean client_kill_requested(ObPrompt *p, gint result, gpointer data)
+{
+    ObClient *self = data;
+
+    if (result == OB_KILL_RESULT_YES)
         client_kill(self);
-    
-    /*
-      XXX: itd be cool to do timeouts and shit here for killing the client's
-      process off
-      like... if the window is around after 5 seconds, then the close button
-      turns a nice red, and if this function is called again, the client is
-      explicitly killed.
-    */
+    return TRUE; /* call the cleanup func */
+}
+
+static void client_kill_cleanup(ObPrompt *p, gpointer data)
+{
+    ObClient *self = data;
 
-    ce.xclient.type = ClientMessage;
-    ce.xclient.message_type =  prop_atoms.wm_protocols;
-    ce.xclient.display = ob_display;
-    ce.xclient.window = self->window;
-    ce.xclient.format = 32;
-    ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
-    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);
+    g_assert(p == self->kill_prompt);
+
+    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[] = {
+            { 0, OB_KILL_RESULT_NO },
+            { 0, OB_KILL_RESULT_YES }
+        };
+        gchar *m;
+        const gchar *y, *title;
+
+        title = self->original_title;
+        if (title[0] == '\0') {
+            /* empty string, so use its parent */
+            ObClient *p = client_search_top_direct_parent(self);
+            if (p) title = p->original_title;
+        }
+
+        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?"),
+                 title, sig);
+            y = _("End Process");
+        }
+        else {
+            m = g_strdup_printf
+                (_("The window \"%s\" does not seem to be responding.  Do you want to disconnect it from the X server?"),
+                 title);
+            y = _("Disconnect");
+        }
+        /* set the dialog buttons' text */
+        answers[0].text = _("Cancel");  /* "no" */
+        answers[1].text = y;            /* "yes" */
+
+        self->kill_prompt = prompt_new(m, NULL, answers,
+                                       sizeof(answers)/sizeof(answers[0]),
+                                       OB_KILL_RESULT_NO, /* default = no */
+                                       OB_KILL_RESULT_NO, /* cancel = no */
+                                       client_kill_requested,
+                                       client_kill_cleanup,
+                                       self);
+        g_free(m);
+    }
+
+    prompt_show(self->kill_prompt, self, TRUE);
 }
 
 void client_kill(ObClient *self)
 {
-    XKillClient(ob_display, self->window);
+    /* 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_level == 0) {
+            ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
+                     self->window, self->pid);
+            kill(self->pid, SIGTERM);
+            ++self->kill_level;
+
+            /* show that we're trying to kill it */
+            client_update_title(self);
+        }
+        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);
+    }
 }
 
 void client_hilite(ObClient *self, gboolean hilite)
@@ -3171,45 +3467,56 @@ void client_hilite(ObClient *self, gboolean hilite)
     }
 }
 
-void client_set_desktop_recursive(ObClient *self,
-                                  guint target,
-                                  gboolean donthide)
+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 */
         if (!donthide)
-            client_showhide(self);
+            client_hide(self);
+        client_show(self);
         /* raise if it was not already on the desktop */
-        if (old != DESKTOP_ALL)
+        if (old != DESKTOP_ALL && !dontraise)
             stacking_raise(CLIENT_AS_WINDOW(self));
         if (STRUT_EXISTS(self->strut))
             screen_update_areas();
+        else
+            /* the new desktop's geometry may be different, so we may need to
+               resize, for example if we are maximized */
+            client_reconfigure(self, FALSE);
+
+        if (old != self->desktop)
+            hooks_queue(OB_HOOK_WIN_DESK_CHANGE, self);
     }
 
     /* move all transients */
     for (it = self->transients; it; it = g_slist_next(it))
         if (it->data != self)
             if (client_is_direct_child(self, it->data))
-                client_set_desktop_recursive(it->data, target, donthide);
+                client_set_desktop_recursive(it->data, target,
+                                             donthide, dontraise);
 }
 
-void client_set_desktop(ObClient *self, guint target, gboolean donthide)
+void client_set_desktop(ObClient *self, guint target,
+                        gboolean donthide, gboolean dontraise)
 {
     self = client_search_top_direct_parent(self);
-    client_set_desktop_recursive(self, target, donthide);
+    client_set_desktop_recursive(self, target, donthide, dontraise);
 }
 
 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
@@ -3222,7 +3529,7 @@ ObClient *client_search_modal_child(ObClient *self)
 {
     GSList *it;
     ObClient *ret;
-  
+
     for (it = self->transients; it; it = g_slist_next(it)) {
         ObClient *c = it->data;
         if ((ret = client_search_modal_child(c))) return ret;
@@ -3231,25 +3538,45 @@ ObClient *client_search_modal_child(ObClient *self)
     return NULL;
 }
 
+static gboolean client_validate_unmap(ObClient *self, int n)
+{
+    XEvent e;
+    gboolean ret = TRUE;
+
+    if (XCheckTypedWindowEvent(obt_display, self->window, UnmapNotify, &e)) {
+        if (n < self->ignore_unmaps) // ignore this one, but look for more
+            ret = client_validate_unmap(self, n+1);
+        else
+            ret = FALSE; // the window is going to become unmanaged
+
+        /* put them back on the event stack so they end up in the same order */
+        XPutBackEvent(obt_display, &e);
+    }
+
+    return ret;
+}
+
 gboolean client_validate(ObClient *self)
 {
-    XEvent e; 
+    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)) {
+        XPutBackEvent(obt_display, &e);
         return FALSE;
     }
 
+    if (!client_validate_unmap(self, 0))
+        return FALSE;
+
     return TRUE;
 }
 
 void client_set_wm_state(ObClient *self, glong state)
 {
     if (state == self->wmstate) return; /* no change */
-  
+
     switch (state) {
     case IconicState:
         client_iconify(self, TRUE, TRUE, FALSE);
@@ -3273,118 +3600,81 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
     gboolean above = self->above;
     gboolean below = self->below;
     gint i;
+    gboolean value;
 
-    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; 
+        return;
 
     for (i = 0; i < 2; ++i) {
         Atom state = i == 0 ? data1 : 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)
-                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)
-                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)
-                action = self->iconic ?
-                    prop_atoms.net_wm_state_remove :
-                    prop_atoms.net_wm_state_add;
-            else if (state == prop_atoms.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)
-                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;
+        if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) {
+            if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
+                value = modal;
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
+                value = self->max_vert;
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
+                value = self->max_horz;
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
+                value = shaded;
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
+                value = self->skip_taskbar;
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
+                value = self->skip_pager;
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
+                value = self->iconic;
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
+                value = fullscreen;
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
+                value = self->above;
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
+                value = self->below;
+            else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
+                value = self->demands_attention;
+            else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
+                value = undecorated;
+            action = value ? 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) {
-                modal = TRUE;
-            } else if (state == prop_atoms.net_wm_state_maximized_vert) {
-                max_vert = TRUE;
-            } else if (state == prop_atoms.net_wm_state_maximized_horz) {
-                max_horz = TRUE;
-            } else if (state == prop_atoms.net_wm_state_shaded) {
-                shaded = TRUE;
-            } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
-                self->skip_taskbar = TRUE;
-            } else if (state == prop_atoms.net_wm_state_skip_pager) {
-                self->skip_pager = TRUE;
-            } else if (state == prop_atoms.net_wm_state_hidden) {
-                iconic = TRUE;
-            } else if (state == prop_atoms.net_wm_state_fullscreen) {
-                fullscreen = TRUE;
-            } else if (state == prop_atoms.net_wm_state_above) {
-                above = TRUE;
-                below = FALSE;
-            } else if (state == prop_atoms.net_wm_state_below) {
-                above = FALSE;
-                below = TRUE;
-            } else if (state == prop_atoms.net_wm_state_demands_attention) {
-                demands_attention = TRUE;
-            } else if (state == prop_atoms.ob_wm_state_undecorated) {
-                undecorated = TRUE;
-            }
 
-        } else { /* action == prop_atoms.net_wm_state_remove */
-            if (state == prop_atoms.net_wm_state_modal) {
-                modal = FALSE;
-            } else if (state == prop_atoms.net_wm_state_maximized_vert) {
-                max_vert = FALSE;
-            } else if (state == prop_atoms.net_wm_state_maximized_horz) {
-                max_horz = FALSE;
-            } else if (state == prop_atoms.net_wm_state_shaded) {
-                shaded = FALSE;
-            } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
-                self->skip_taskbar = FALSE;
-            } else if (state == prop_atoms.net_wm_state_skip_pager) {
-                self->skip_pager = FALSE;
-            } else if (state == prop_atoms.net_wm_state_hidden) {
-                iconic = FALSE;
-            } else if (state == prop_atoms.net_wm_state_fullscreen) {
-                fullscreen = FALSE;
-            } else if (state == prop_atoms.net_wm_state_above) {
-                above = FALSE;
-            } else if (state == prop_atoms.net_wm_state_below) {
+        value = action == OBT_PROP_ATOM(NET_WM_STATE_ADD);
+        if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
+            modal = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
+            max_vert = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
+            max_horz = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
+            shaded = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
+            self->skip_taskbar = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
+            self->skip_pager = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
+            iconic = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
+            fullscreen = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
+            above = value;
+            /* only unset below when setting above, otherwise you can't get to
+               the normal layer */
+            if (value)
                 below = FALSE;
-            } else if (state == prop_atoms.net_wm_state_demands_attention) {
-                demands_attention = FALSE;
-            } else if (state == prop_atoms.ob_wm_state_undecorated) {
-                undecorated = FALSE;
-            }
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
+            /* and vice versa */
+            if (value)
+                above = FALSE;
+            below = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
+            demands_attention = value;
+        } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
+            undecorated = value;
         }
     }
 
@@ -3474,65 +3764,54 @@ 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 there is a grab going on, then we need to cancel it. if we move
-       focus during the grab, applications will get NotifyWhileGrabbed events
-       and ignore them !
+    /* if using focus_delay, stop the timer now so that focus doesn't
+       go moving on us */
+    event_halt_focus_delay();
 
-       actions should not rely on being able to move focus during an
-       interactive grab.
-    */
     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;
 }
 
-/*! Present the client to the user.
-  @param raise If the client should be raised or not. You should only set
-               raise to false if you don't care if the window is completely
-               hidden.
-*/
-static void client_present(ObClient *self, gboolean here, gboolean raise)
+static void client_present(ObClient *self, gboolean here, gboolean raise,
+                           gboolean unshade)
 {
-    /* if using focus_delay, stop the timer now so that focus doesn't
-       go moving on us */
-    event_halt_focus_delay();
-
     if (client_normal(self) && screen_showing_desktop)
         screen_show_desktop(FALSE, self);
     if (self->iconic)
@@ -3541,14 +3820,14 @@ static void client_present(ObClient *self, gboolean here, gboolean raise)
         self->desktop != screen_desktop)
     {
         if (here)
-            client_set_desktop(self, screen_desktop, FALSE);
+            client_set_desktop(self, screen_desktop, FALSE, TRUE);
         else
             screen_set_desktop(self->desktop, FALSE);
     } else if (!self->frame->visible)
         /* if its not visible for other reasons, then don't mess
            with it */
         return;
-    if (self->shaded)
+    if (self->shaded && unshade)
         client_shade(self, FALSE);
     if (raise)
         stacking_raise(CLIENT_AS_WINDOW(self));
@@ -3556,33 +3835,14 @@ static void client_present(ObClient *self, gboolean here, gboolean raise)
     client_focus(self);
 }
 
-void client_activate(ObClient *self, gboolean here, gboolean user)
+/* this function exists to map to the net_active_window message in the ewmh */
+void client_activate(ObClient *self, gboolean here, gboolean raise,
+                     gboolean unshade, gboolean user)
 {
-    guint32 last_time = focus_client ? focus_client->user_time : CurrentTime;
-    gboolean allow = FALSE;
-
-    /* if the request came from the user, or if nothing is focused, then grant
-       the request.
-       if the currently focused app doesn't set a user_time, then it can't
-       benefit from any focus stealing prevention.
-    */
-    if (user || !focus_client || !last_time)
-        allow = TRUE;
-    /* otherwise, if they didn't give a time stamp or if it is too old, they
-       don't get focus */
+    if (user || (self->desktop == DESKTOP_ALL ||
+                 self->desktop == screen_desktop))
+        client_present(self, here, raise, unshade);
     else
-        allow = event_curtime && event_time_after(event_curtime, last_time);
-
-    ob_debug_type(OB_DEBUG_FOCUS,
-                  "Want to activate window 0x%x with time %u (last time %u), "
-                  "source=%s allowing? %d\n",
-                  self->window, event_curtime, last_time,
-                  (user ? "user" : "application"), allow);
-
-    if (allow)
-        client_present(self, here, TRUE);
-    else
-        /* don't focus it but tell the user it wants attention */
         client_hilite(self, TRUE);
 }
 
@@ -3606,7 +3866,7 @@ static void client_bring_windows_recursive(ObClient *self,
         if (iconic && self->iconic)
             client_iconify(self, FALSE, TRUE, FALSE);
         else
-            client_set_desktop(self, desktop, FALSE);
+            client_set_desktop(self, desktop, FALSE, FALSE);
     }
 }
 
@@ -3625,53 +3885,19 @@ gboolean client_focused(ObClient *self)
     return self == focus_client;
 }
 
-static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
+RrImage* client_icon(ObClient *self)
 {
-    guint i;
-    gulong min_diff, min_i;
+    RrImage *ret = NULL;
 
-    if (!self->nicons) {
-        ObClientIcon *parent = NULL;
+    if (self->icon_set)
+        ret = self->icon_set;
+    else if (self->parents) {
         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)
-{
-    ObClientIcon *ret;
-    static ObClientIcon deficon;
-
-    if (!(ret = client_icon_recursive(self, w, h))) {
-        deficon.width = deficon.height = 48;
-        deficon.data = ob_rr_theme->def_win_icon;
-        ret = &deficon;
+        for (it = self->parents; it && !ret; it = g_slist_next(it))
+            ret = client_icon(it->data);
     }
+    if (!ret)
+        ret = client_default_icon;
     return ret;
 }
 
@@ -3693,13 +3919,16 @@ void client_set_layer(ObClient *self, gint layer)
 void client_set_undecorated(ObClient *self, gboolean undecorated)
 {
     if (self->undecorated != undecorated &&
-        /* don't let it undecorate if the function is missing, but let 
+        /* don't let it undecorate if the function is missing, but let
            it redecorate */
         (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
     {
         self->undecorated = undecorated;
         client_setup_decor_and_functions(self, TRUE);
         client_change_state(self); /* reflect this in the state hints */
+
+        hooks_queue((undecorated ?
+                     OB_HOOK_WIN_UNDECORATED : OB_HOOK_WIN_DECORATED), self);
     }
 }
 
@@ -3713,7 +3942,7 @@ ObClient *client_direct_parent(ObClient *self)
     if (!self->parents) return NULL;
     if (self->transient_for_group) return NULL;
     return self->parents->data;
-}                        
+}
 
 ObClient *client_search_top_direct_parent(ObClient *self)
 {
@@ -3728,7 +3957,7 @@ static GSList *client_search_all_top_parents_internal(ObClient *self,
 {
     GSList *ret;
     ObClient *p;
-    
+
     /* move up the direct transient chain as far as possible */
     while ((p = client_direct_parent(self)) &&
            (!bylayer || p->layer == layer))
@@ -3762,6 +3991,21 @@ ObClient *client_search_focus_parent(ObClient *self)
     return NULL;
 }
 
+ObClient *client_search_focus_parent_full(ObClient *self)
+{
+    GSList *it;
+    ObClient *ret = NULL;
+
+    for (it = self->parents; it; it = g_slist_next(it)) {
+        if (client_focused(it->data))
+            ret = it->data;
+        else
+            ret = client_search_focus_parent_full(it->data);
+        if (ret) break;
+    }
+    return ret;
+}
+
 ObClient *client_search_parent(ObClient *self, ObClient *search)
 {
     GSList *it;
@@ -3785,188 +4029,331 @@ ObClient *client_search_transient(ObClient *self, ObClient *search)
     return NULL;
 }
 
-#define WANT_EDGE(cur, c) \
-            if (cur == c)                                                     \
-                continue;                                                     \
-            if (c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&  \
-                cur->desktop != screen_desktop)                               \
-                continue;                                                     \
-            if (cur->iconic)                                                  \
-                continue;
+static void detect_edge(Rect area, ObDirection dir,
+                        gint my_head, gint my_size,
+                        gint my_edge_start, gint my_edge_size,
+                        gint *dest, gboolean *near_edge)
+{
+    gint edge_start, edge_size, head, tail;
+    gboolean skip_head = FALSE, skip_tail = FALSE;
+
+    switch (dir) {
+        case OB_DIRECTION_NORTH:
+        case OB_DIRECTION_SOUTH:
+            edge_start = area.x;
+            edge_size = area.width;
+            break;
+        case OB_DIRECTION_EAST:
+        case OB_DIRECTION_WEST:
+            edge_start = area.y;
+            edge_size = area.height;
+            break;
+        default:
+            g_assert_not_reached();
+    }
 
-#define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
-            if ((his_edge_start >= my_edge_start && \
-                 his_edge_start <= my_edge_end) ||  \
-                (my_edge_start >= his_edge_start && \
-                 my_edge_start <= his_edge_end))    \
-                dest = his_offset;
-
-/* finds the nearest edge in the given direction from the current client
- * note to self: the edge is the -frame- edge (the actual one), not the
- * client edge.
- */
-gint client_directional_edge_search(ObClient *c, ObDirection dir, gboolean hang)
-{
-    gint dest, monitor_dest;
-    gint my_edge_start, my_edge_end, my_offset;
-    GList *it;
-    Rect *a, *monitor;
-    
-    if(!client_list)
-        return -1;
+    /* do we collide with this window? */
+    if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
+                edge_start, edge_size))
+        return;
 
-    a = screen_area(c->desktop);
-    monitor = screen_area_monitor(c->desktop, client_monitor(c));
+    switch (dir) {
+        case OB_DIRECTION_NORTH:
+            head = RECT_BOTTOM(area);
+            tail = RECT_TOP(area);
+            break;
+        case OB_DIRECTION_SOUTH:
+            head = RECT_TOP(area);
+            tail = RECT_BOTTOM(area);
+            break;
+        case OB_DIRECTION_WEST:
+            head = RECT_RIGHT(area);
+            tail = RECT_LEFT(area);
+            break;
+        case OB_DIRECTION_EAST:
+            head = RECT_LEFT(area);
+            tail = RECT_RIGHT(area);
+            break;
+        default:
+            g_assert_not_reached();
+    }
+    switch (dir) {
+        case OB_DIRECTION_NORTH:
+        case OB_DIRECTION_WEST:
+            /* check if our window is past the head of this window */
+            if (my_head <= head + 1)
+                skip_head = TRUE;
+            /* check if our window's tail is past the tail of this window */
+            if (my_head + my_size - 1 <= tail)
+                skip_tail = TRUE;
+            /* check if the head of this window is closer than the previously
+               chosen edge (take into account that the previously chosen
+               edge might have been a tail, not a head) */
+            if (head + (*near_edge ? 0 : my_size) <= *dest)
+                skip_head = TRUE;
+            /* check if the tail of this window is closer than the previously
+               chosen edge (take into account that the previously chosen
+               edge might have been a head, not a tail) */
+            if (tail - (!*near_edge ? 0 : my_size) <= *dest)
+                skip_tail = TRUE;
+            break;
+        case OB_DIRECTION_SOUTH:
+        case OB_DIRECTION_EAST:
+            /* check if our window is past the head of this window */
+            if (my_head >= head - 1)
+                skip_head = TRUE;
+            /* check if our window's tail is past the tail of this window */
+            if (my_head - my_size + 1 >= tail)
+                skip_tail = TRUE;
+            /* check if the head of this window is closer than the previously
+               chosen edge (take into account that the previously chosen
+               edge might have been a tail, not a head) */
+            if (head - (*near_edge ? 0 : my_size) >= *dest)
+                skip_head = TRUE;
+            /* check if the tail of this window is closer than the previously
+               chosen edge (take into account that the previously chosen
+               edge might have been a head, not a tail) */
+            if (tail + (!*near_edge ? 0 : my_size) >= *dest)
+                skip_tail = TRUE;
+            break;
+        default:
+            g_assert_not_reached();
+    }
 
-    switch(dir) {
-    case OB_DIRECTION_NORTH:
-        my_edge_start = c->frame->area.x;
-        my_edge_end = c->frame->area.x + c->frame->area.width;
-        my_offset = c->frame->area.y + (hang ? c->frame->area.height : 0);
-        
-        /* default: top of screen */
-        dest = a->y + (hang ? c->frame->area.height : 0);
-        monitor_dest = monitor->y + (hang ? c->frame->area.height : 0);
-        /* if the monitor edge comes before the screen edge, */
-        /* use that as the destination instead. (For xinerama) */
-        if (monitor_dest != dest && my_offset > monitor_dest)
-            dest = monitor_dest; 
-
-        for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
-            gint his_edge_start, his_edge_end, his_offset;
-            ObClient *cur = it->data;
-
-            WANT_EDGE(cur, c)
-
-            his_edge_start = cur->frame->area.x;
-            his_edge_end = cur->frame->area.x + cur->frame->area.width;
-            his_offset = cur->frame->area.y + 
-                         (hang ? 0 : cur->frame->area.height);
-
-            if(his_offset + 1 > my_offset)
-                continue;
+    ob_debug("my head %d size %d", my_head, my_size);
+    ob_debug("head %d tail %d dest %d", head, tail, *dest);
+    if (!skip_head) {
+        ob_debug("using near edge %d", head);
+        *dest = head;
+        *near_edge = TRUE;
+    }
+    else if (!skip_tail) {
+        ob_debug("using far edge %d", tail);
+        *dest = tail;
+        *near_edge = FALSE;
+    }
+}
 
-            if(his_offset < dest)
-                continue;
+void client_find_edge_directional(ObClient *self, ObDirection dir,
+                                  gint my_head, gint my_size,
+                                  gint my_edge_start, gint my_edge_size,
+                                  gint *dest, gboolean *near_edge)
+{
+    GList *it;
+    Rect *a, *mon;
+    Rect dock_area;
+    gint edge;
 
-            HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
-        }
+    a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
+                    &self->frame->area);
+    mon = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR,
+                      &self->frame->area);
+
+    switch (dir) {
+    case OB_DIRECTION_NORTH:
+        if (my_head >= RECT_TOP(*mon) + 1)
+            edge = RECT_TOP(*mon) - 1;
+        else
+            edge = RECT_TOP(*a) - 1;
         break;
     case OB_DIRECTION_SOUTH:
-        my_edge_start = c->frame->area.x;
-        my_edge_end = c->frame->area.x + c->frame->area.width;
-        my_offset = c->frame->area.y + (hang ? 0 : c->frame->area.height);
+        if (my_head <= RECT_BOTTOM(*mon) - 1)
+            edge = RECT_BOTTOM(*mon) + 1;
+        else
+            edge = RECT_BOTTOM(*a) + 1;
+        break;
+    case OB_DIRECTION_EAST:
+        if (my_head <= RECT_RIGHT(*mon) - 1)
+            edge = RECT_RIGHT(*mon) + 1;
+        else
+            edge = RECT_RIGHT(*a) + 1;
+        break;
+    case OB_DIRECTION_WEST:
+        if (my_head >= RECT_LEFT(*mon) + 1)
+            edge = RECT_LEFT(*mon) - 1;
+        else
+            edge = RECT_LEFT(*a) - 1;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    /* default to the far edge, then narrow it down */
+    *dest = edge;
+    *near_edge = TRUE;
 
-        /* default: bottom of screen */
-        dest = a->y + a->height - (hang ? c->frame->area.height : 0);
-        monitor_dest = monitor->y + monitor->height -
-                       (hang ? c->frame->area.height : 0);
-        /* if the monitor edge comes before the screen edge, */
-        /* use that as the destination instead. (For xinerama) */
-        if (monitor_dest != dest && my_offset < monitor_dest)
-            dest = monitor_dest; 
+    for (it = client_list; it; it = g_list_next(it)) {
+        ObClient *cur = it->data;
 
-        for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
-            gint his_edge_start, his_edge_end, his_offset;
-            ObClient *cur = it->data;
+        /* skip windows to not bump into */
+        if (cur == self)
+            continue;
+        if (cur->iconic)
+            continue;
+        if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
+            cur->desktop != screen_desktop)
+            continue;
 
-            WANT_EDGE(cur, c)
+        ob_debug("trying window %s", cur->title);
 
-            his_edge_start = cur->frame->area.x;
-            his_edge_end = cur->frame->area.x + cur->frame->area.width;
-            his_offset = cur->frame->area.y +
-                         (hang ? cur->frame->area.height : 0);
+        detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
+                    my_edge_size, dest, near_edge);
+    }
+    dock_get_area(&dock_area);
+    detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
+                my_edge_size, dest, near_edge);
+    g_free(a);
+    g_free(mon);
+}
 
+void client_find_move_directional(ObClient *self, ObDirection dir,
+                                  gint *x, gint *y)
+{
+    gint head, size;
+    gint e, e_start, e_size;
+    gboolean near;
 
-            if(his_offset - 1 < my_offset)
-                continue;
-            
-            if(his_offset > dest)
-                continue;
+    switch (dir) {
+    case OB_DIRECTION_EAST:
+        head = RECT_RIGHT(self->frame->area);
+        size = self->frame->area.width;
+        e_start = RECT_TOP(self->frame->area);
+        e_size = self->frame->area.height;
+        break;
+    case OB_DIRECTION_WEST:
+        head = RECT_LEFT(self->frame->area);
+        size = self->frame->area.width;
+        e_start = RECT_TOP(self->frame->area);
+        e_size = self->frame->area.height;
+        break;
+    case OB_DIRECTION_NORTH:
+        head = RECT_TOP(self->frame->area);
+        size = self->frame->area.height;
+        e_start = RECT_LEFT(self->frame->area);
+        e_size = self->frame->area.width;
+        break;
+    case OB_DIRECTION_SOUTH:
+        head = RECT_BOTTOM(self->frame->area);
+        size = self->frame->area.height;
+        e_start = RECT_LEFT(self->frame->area);
+        e_size = self->frame->area.width;
+        break;
+    default:
+        g_assert_not_reached();
+    }
 
-            HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
-        }
+    client_find_edge_directional(self, dir, head, size,
+                                 e_start, e_size, &e, &near);
+    *x = self->frame->area.x;
+    *y = self->frame->area.y;
+    switch (dir) {
+    case OB_DIRECTION_EAST:
+        if (near) e -= self->frame->area.width;
+        else      e++;
+        *x = e;
         break;
     case OB_DIRECTION_WEST:
-        my_edge_start = c->frame->area.y;
-        my_edge_end = c->frame->area.y + c->frame->area.height;
-        my_offset = c->frame->area.x + (hang ? c->frame->area.width : 0);
-
-        /* default: leftmost egde of screen */
-        dest = a->x + (hang ? c->frame->area.width : 0);
-        monitor_dest = monitor->x + (hang ? c->frame->area.width : 0);
-        /* if the monitor edge comes before the screen edge, */
-        /* use that as the destination instead. (For xinerama) */
-        if (monitor_dest != dest && my_offset > monitor_dest)
-            dest = monitor_dest;            
-
-        for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
-            gint his_edge_start, his_edge_end, his_offset;
-            ObClient *cur = it->data;
-
-            WANT_EDGE(cur, c)
-
-            his_edge_start = cur->frame->area.y;
-            his_edge_end = cur->frame->area.y + cur->frame->area.height;
-            his_offset = cur->frame->area.x +
-                         (hang ? 0 : cur->frame->area.width);
-
-            if(his_offset + 1 > my_offset)
-                continue;
+        if (near) e++;
+        else      e -= self->frame->area.width;
+        *x = e;
+        break;
+    case OB_DIRECTION_NORTH:
+        if (near) e++;
+        else      e -= self->frame->area.height;
+        *y = e;
+        break;
+    case OB_DIRECTION_SOUTH:
+        if (near) e -= self->frame->area.height;
+        else      e++;
+        *y = e;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    frame_frame_gravity(self->frame, x, y);
+}
 
-            if(his_offset < dest)
-                continue;
+void client_find_resize_directional(ObClient *self, ObDirection side,
+                                    gboolean grow,
+                                    gint *x, gint *y, gint *w, gint *h)
+{
+    gint head;
+    gint e, e_start, e_size, delta;
+    gboolean near;
+    ObDirection dir;
 
-            HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
-        }
-       break;
+    switch (side) {
     case OB_DIRECTION_EAST:
-        my_edge_start = c->frame->area.y;
-        my_edge_end = c->frame->area.y + c->frame->area.height;
-        my_offset = c->frame->area.x + (hang ? 0 : c->frame->area.width);
-        
-        /* default: rightmost edge of screen */
-        dest = a->x + a->width - (hang ? c->frame->area.width : 0);
-        monitor_dest = monitor->x + monitor->width -
-                       (hang ? c->frame->area.width : 0);
-        /* if the monitor edge comes before the screen edge, */
-        /* use that as the destination instead. (For xinerama) */
-        if (monitor_dest != dest && my_offset < monitor_dest)
-            dest = monitor_dest;            
-
-        for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
-            gint his_edge_start, his_edge_end, his_offset;
-            ObClient *cur = it->data;
-
-            WANT_EDGE(cur, c)
-
-            his_edge_start = cur->frame->area.y;
-            his_edge_end = cur->frame->area.y + cur->frame->area.height;
-            his_offset = cur->frame->area.x +
-                         (hang ? cur->frame->area.width : 0);
-
-            if(his_offset - 1 < my_offset)
-                continue;
-            
-            if(his_offset > dest)
-                continue;
+        head = RECT_RIGHT(self->frame->area) +
+            (self->size_inc.width - 1) * (grow ? 1 : 0);
+        e_start = RECT_TOP(self->frame->area);
+        e_size = self->frame->area.height;
+        dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
+        break;
+    case OB_DIRECTION_WEST:
+        head = RECT_LEFT(self->frame->area) -
+            (self->size_inc.width - 1) * (grow ? 1 : 0);
+        e_start = RECT_TOP(self->frame->area);
+        e_size = self->frame->area.height;
+        dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
+        break;
+    case OB_DIRECTION_NORTH:
+        head = RECT_TOP(self->frame->area) -
+            (self->size_inc.height - 1) * (grow ? 1 : 0);
+        e_start = RECT_LEFT(self->frame->area);
+        e_size = self->frame->area.width;
+        dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
+        break;
+    case OB_DIRECTION_SOUTH:
+        head = RECT_BOTTOM(self->frame->area) +
+            (self->size_inc.height - 1) * (grow ? 1 : 0);
+        e_start = RECT_LEFT(self->frame->area);
+        e_size = self->frame->area.width;
+        dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
+        break;
+    default:
+        g_assert_not_reached();
+    }
 
-            HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
-        }
+    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", e);
+    *x = self->frame->area.x;
+    *y = self->frame->area.y;
+    *w = self->frame->area.width;
+    *h = self->frame->area.height;
+    switch (side) {
+    case OB_DIRECTION_EAST:
+        if (grow == near) --e;
+        delta = e - RECT_RIGHT(self->frame->area);
+        *w += delta;
+        break;
+    case OB_DIRECTION_WEST:
+        if (grow == near) ++e;
+        delta = RECT_LEFT(self->frame->area) - e;
+        *x -= delta;
+        *w += delta;
+        break;
+    case OB_DIRECTION_NORTH:
+        if (grow == near) ++e;
+        delta = RECT_TOP(self->frame->area) - e;
+        *y -= delta;
+        *h += delta;
+        break;
+    case OB_DIRECTION_SOUTH:
+        if (grow == near) --e;
+        delta = e - RECT_BOTTOM(self->frame->area);
+        *h += delta;
         break;
-    case OB_DIRECTION_NORTHEAST:
-    case OB_DIRECTION_SOUTHEAST:
-    case OB_DIRECTION_NORTHWEST:
-    case OB_DIRECTION_SOUTHWEST:
-        /* not implemented */
     default:
         g_assert_not_reached();
-        dest = 0; /* suppress warning */
     }
-    return dest;
+    frame_frame_gravity(self->frame, x, y);
+    *w -= self->frame->size.left + self->frame->size.right;
+    *h -= self->frame->size.top + self->frame->size.bottom;
 }
 
-ObClient* client_under_pointer()
+ObClient* client_under_pointer(void)
 {
     gint x, y;
     GList *it;
@@ -3977,6 +4364,11 @@ ObClient* client_under_pointer()
             if (WINDOW_IS_CLIENT(it->data)) {
                 ObClient *c = WINDOW_AS_CLIENT(it->data);
                 if (c->frame->visible &&
+                    /* check the desktop, this is done during desktop
+                       switching and windows are shown/hidden status is not
+                       reliable */
+                    (c->desktop == screen_desktop ||
+                     c->desktop == DESKTOP_ALL) &&
                     /* ignore all animating windows */
                     !frame_iconify_animating(c->frame) &&
                     RECT_CONTAINS(c->frame->area, x, y))
@@ -3994,3 +4386,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;
+}
This page took 0.132239 seconds and 4 git commands to generate.