Various fixes for sparse warnings.
[mikachu/openbox.git] / openbox / client.c
index d65ce4a..6a6ec2b 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
@@ -24,6 +24,7 @@
 #include "xerror.h"
 #include "screen.h"
 #include "moveresize.h"
+#include "ping.h"
 #include "place.h"
 #include "prop.h"
 #include "extensions.h"
@@ -32,7 +33,6 @@
 #include "event.h"
 #include "grab.h"
 #include "focus.h"
-#include "propwin.h"
 #include "stacking.h"
 #include "openbox.h"
 #include "group.h"
 #include "keyboard.h"
 #include "mouse.h"
 #include "render/render.h"
+#include "gettext.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>
 
@@ -75,6 +80,10 @@ static void client_get_state(ObClient *self);
 static void client_get_shaped(ObClient *self);
 static void client_get_mwm_hints(ObClient *self);
 static void client_get_colormap(ObClient *self);
+static void client_set_desktop_recursive(ObClient *self,
+                                         guint target,
+                                         gboolean donthide,
+                                         gboolean dontraise);
 static void client_change_allowed_actions(ObClient *self);
 static void client_change_state(ObClient *self);
 static void client_change_wm_state(ObClient *self);
@@ -94,6 +103,7 @@ 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);
 
 
 void client_startup(gboolean reconfig)
@@ -141,7 +151,7 @@ void client_remove_destroy_notify(ObClientCallback func)
     }
 }
 
-void client_set_list()
+void client_set_list(void)
 {
     Window *windows, *win_it;
     GList *it;
@@ -165,7 +175,7 @@ void client_set_list()
     stacking_set_list();
 }
 
-void client_manage_all()
+void client_manage_all(void)
 {
     guint i, j, nchild;
     Window w, *children;
@@ -213,8 +223,9 @@ void client_manage(Window window)
     XWMHints *wmhint;
     gboolean activate = FALSE;
     ObAppSettings *settings;
-    gint placex, placey, placew, placeh;
     gboolean transient = FALSE;
+    Rect place, *monitor;
+    Time launch_time, map_time;
 
     grab_server(TRUE);
 
@@ -237,7 +248,7 @@ void client_manage(Window window)
         grab_server(FALSE);
         return; /* don't manage it */
     }
-  
+
     /* is the window a docking app */
     if ((wmhint = XGetWMHints(ob_display, window))) {
         if ((wmhint->flags & StateHint) &&
@@ -253,6 +264,8 @@ void client_manage(Window 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;
     attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
@@ -269,11 +282,13 @@ void client_manage(Window window)
     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\n", self->type);
+    ob_debug("Window group: 0x%x\n", self->group?self->group->leader:0);
+
     /* specify that if we exit, the window should not be destroyed and
        should be reparented back to root automatically */
     XChangeSaveSet(ob_display, window, SetModeInsert);
@@ -297,10 +312,8 @@ void client_manage(Window window)
     /* now we have all of the window's information so we can set this up */
     client_setup_decor_and_functions(self, FALSE);
 
-    {
-        Time t = sn_app_started(self->startup_id, self->class);
-        if (t) self->user_time = t;
-    }
+    /* tell startup notification that this app started */
+    launch_time = sn_app_started(self->startup_id, self->class);
 
     /* do this after we have a frame.. it uses the frame to help determine the
        WM_STATE to apply. */
@@ -320,7 +333,7 @@ void client_manage(Window window)
          client_search_focus_tree_full(self)) &&
         /* this checks for focus=false for the window */
         (!settings || settings->focus != 0) &&
-        focus_valid_target(self, FALSE, TRUE, FALSE, FALSE))
+        focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE))
     {
         activate = TRUE;
     }
@@ -334,10 +347,8 @@ 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) {
@@ -347,7 +358,7 @@ void client_manage(Window window)
                    (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",
                  (!self->sized ? "no" :
@@ -355,14 +366,15 @@ void client_manage(Window window)
                    (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
@@ -379,9 +391,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
@@ -397,34 +416,34 @@ void client_manage(Window window)
         (transient ||
          (!(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)))))
     {
-        Rect placer;
+        Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
 
-        RECT_SET(placer, placex, placey, placew, placeh);
-        frame_rect_to_frame(self->frame, &placer);
+        /* 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;
 
-        Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &placer);
+        /* fit the window inside the area */
+        place.width = MIN(place.width, a->width);
+        place.height = MIN(place.height, a->height);
 
-        /* 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;
+        ob_debug("setting window size to %dx%d\n", place.width, place.height);
 
-        /* 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);
+        /* 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;
 
-            ob_debug("setting window size to %dx%d\n",
-                     self->area.width, self->area.height);
-        }
         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);
+             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",
@@ -436,25 +455,42 @@ 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\n",
+                  activate ? "yes" : "no");
     if (activate) {
-        guint32 last_time = focus_client ?
-            focus_client->user_time : CurrentTime;
+        gboolean raise = FALSE;
 
         /* 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)\n",
+                      self->window, map_time, launch_time,
+                      event_last_user_time);
+
+        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\n");
+        }
 
         /* 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");
@@ -463,26 +499,25 @@ void client_manage(Window window)
         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 the user is working in another window right now, then don't
+               steal focus */
+            if (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\n");
             }
-            /* 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 its a transient (and its parents aren't focused) */
+            else if (client_has_parent(self)) {
                 activate = FALSE;
                 ob_debug_type(OB_DEBUG_FOCUS,
                               "Not focusing the window because it is a "
-                              "transient, and the time is very ambiguous\n");
+                              "transient, and its relatives aren't focused\n");
             }
             /* Don't steal focus from globally active clients.
                I stole this idea from KWin. It seems nice.
@@ -499,20 +534,33 @@ void client_manage(Window 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");
             }
+            else if (!(self->desktop == screen_desktop ||
+                       self->desktop == DESKTOP_ALL))
+            {
+                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 interactioon time %u)\n",
+                          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 {
@@ -559,6 +607,12 @@ void client_manage(Window window)
     /* update the list hints */
     client_set_list();
 
+    /* watch for when the application stops responding.  only do this for
+       normal windows, i.e. windows which have titlebars and close buttons 
+       and things like that */
+    if (self->ping && client_normal(self))
+        ping_start(self, client_ping_event);
+
     /* free the ObAppSettings shallow copy */
     g_free(settings);
 
@@ -593,7 +647,7 @@ ObClient *client_fake_manage(Window window)
     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, 
+             self->frame->size.left, self->frame->size.right,
              self->frame->size.top, self->frame->size.bottom);
 
     /* free the ObAppSettings shallow copy */
@@ -602,7 +656,7 @@ ObClient *client_fake_manage(Window window)
     return self;
 }
 
-void client_unmanage_all()
+void client_unmanage_all(void)
 {
     while (client_list != NULL)
         client_unmanage(client_list->data);
@@ -640,8 +694,9 @@ void client_unmanage(ObClient *self)
     /* remove the window from our save set */
     XChangeSaveSet(ob_display, self->window, SetModeDelete);
 
-    /* kill the property windows */
-    propwin_remove(self->user_time_window, OB_PROPWIN_USER_TIME, self);
+    /* stop pinging the window */
+    if (self->ping && client_normal(self))
+        ping_stop(self);
 
     /* update the focus lists */
     focus_order_remove(self);
@@ -728,6 +783,11 @@ void client_unmanage(ObClient *self)
         XMapWindow(ob_display, self->window);
     }
 
+    /* these should not be left on the window ever.  other window managers
+       don't necessarily use them and it will mess them up (like compiz) */
+    PROP_ERASE(self->window, net_wm_visible_name);
+    PROP_ERASE(self->window, net_wm_visible_icon_name);
+
     /* update the list hints */
     client_set_list();
 
@@ -933,7 +993,7 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
     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;
@@ -980,10 +1040,15 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
     for (i = 0; i < screen_num_monitors; ++i) {
         Rect *a;
 
-        if (!screen_physical_area_monitor_contains(i, &desired))
-            continue;
+        if (!screen_physical_area_monitor_contains(i, &desired)) {
+            if (i < screen_num_monitors - 1)
+                continue;
 
-        a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
+            /* the window is not inside any monitor! so just use the first
+               one */
+            a = screen_area(self->desktop, 0, NULL);
+        } else
+            a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
 
         /* This makes sure windows aren't entirely outside of the screen so you
            can't see them at all.
@@ -1000,7 +1065,7 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
             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 - fw*9/10;
+                *y = a->y - fh*9/10;
         }
 
         /* This here doesn't let windows even a pixel outside the
@@ -1021,7 +1086,7 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
     }
 
     /* 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;
 }
@@ -1069,7 +1134,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
@@ -1077,9 +1142,6 @@ 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);
 }
 
@@ -1095,7 +1157,7 @@ static void client_get_area(ObClient *self)
 {
     XWindowAttributes wattrib;
     Status ret;
-  
+
     ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
     g_assert(ret != BadWindow);
 
@@ -1116,7 +1178,7 @@ static void client_get_desktop(ObClient *self)
             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\n", self->desktop);
     } else {
         GSList *it;
         gboolean first = TRUE;
@@ -1163,7 +1225,7 @@ static void client_get_state(ObClient *self)
 {
     guint32 *state;
     guint num;
-  
+
     if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
         gulong i;
         for (i = 0; i < num; ++i) {
@@ -1246,7 +1308,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,
@@ -1278,88 +1340,45 @@ static void client_update_transient_tree(ObClient *self,
         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);
-            }
-        }
+    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 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);
-            }
-        }
-    }
-    /* 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
+
+    /* 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))
@@ -1368,9 +1387,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
@@ -1378,7 +1396,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)) {
@@ -1392,6 +1412,20 @@ static void client_update_transient_tree(ObClient *self,
             }
         }
     }
+
+    /** If we change our group transient-ness, our children change their
+        effect 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)
@@ -1420,7 +1454,7 @@ 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)) {
         /* use the first value that we know about in the array */
         for (i = 0; i < num; ++i) {
@@ -1456,7 +1490,7 @@ void client_get_type_and_transientness(ObClient *self)
 
     if (XGetTransientForHint(ob_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
@@ -1494,8 +1528,12 @@ void client_update_protocols(ObClient *self)
                 /* if this protocol is requested, then the window will be
                    notified whenever we want it to receive focus */
                 self->focus_notify = TRUE;
+            else if (proto[i] == prop_atoms.net_wm_ping)
+                /* 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] == prop_atoms.net_wm_sync_request)
                 /* if this protocol is requested, then resizing the
                    window will be synchronized between the frame and the
                    client */
@@ -1518,7 +1556,7 @@ void client_update_sync_request_counter(ObClient *self)
 }
 #endif
 
-void client_get_colormap(ObClient *self)
+static void client_get_colormap(ObClient *self)
 {
     XWindowAttributes wa;
 
@@ -1575,13 +1613,13 @@ 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);
 
@@ -1628,11 +1666,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;
@@ -1815,7 +1858,7 @@ void client_update_wmhints(ObClient *self)
 
     /* assume a window takes input if it doesnt specify */
     self->can_focus = TRUE;
-  
+
     if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
         gboolean ur;
 
@@ -1902,17 +1945,17 @@ void client_update_title(ObClient *self)
     gchar *visible = NULL;
 
     g_free(self->title);
-     
+
     /* try netwm */
     if (!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 (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");
@@ -1925,6 +1968,15 @@ void client_update_title(ObClient *self)
     } else
         visible = data;
 
+    if (self->not_responding) {
+        data = visible;
+        if (self->close_tried_term)
+            visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
+        else
+            visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
+        g_free(data);
+    }
+
     PROP_SETS(self->window, net_wm_visible_name, visible);
     self->title = visible;
 
@@ -1948,6 +2000,15 @@ void client_update_title(ObClient *self)
     } else
         visible = data;
 
+    if (self->not_responding) {
+        data = visible;
+        if (self->close_tried_term)
+            visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
+        else
+            visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
+        g_free(data);
+    }
+
     PROP_SETS(self->window, net_wm_visible_icon_name, visible);
     self->icon_title = visible;
 }
@@ -2030,7 +2091,7 @@ void client_update_icons(ObClient *self)
         }
 
         self->icons = g_new(ObClientIcon, self->nicons);
-    
+
         /* store the icons */
         i = 0;
         for (j = 0; j < self->nicons; ++j) {
@@ -2087,7 +2148,7 @@ void client_update_icons(ObClient *self)
        or removes it entirely, it's not very likely it is going to set one
        right away afterwards
 
-       if it has parents, then one of them will have an icon already 
+       if it has parents, then one of them will have an icon already
     */
     if (self->nicons == 0 && !self->parents) {
         RrPixel32 *icon = ob_rr_theme->def_win_icon;
@@ -2108,62 +2169,6 @@ void client_update_icons(ObClient *self)
         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);
-    }
-}
-
 void client_update_icon_geometry(ObClient *self)
 {
     guint num;
@@ -2264,6 +2269,7 @@ static void client_get_session_ids(ObClient *self)
 
     if (got) {
         gchar localhost[128];
+        guint32 pid;
 
         gethostname(localhost, 127);
         localhost[127] = '\0';
@@ -2271,6 +2277,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 (PROP_GET32(self->window, net_wm_pid, cardinal, &pid))
+            self->pid = pid;
     }
 }
 
@@ -2461,7 +2472,7 @@ gboolean client_should_show(ObClient *self)
         return FALSE;
     if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
         return TRUE;
-    
+
     return FALSE;
 }
 
@@ -2605,7 +2616,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)
@@ -2613,11 +2624,18 @@ 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, FALSE);
-    }
+    /* 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);
@@ -2702,7 +2720,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
     frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
 
     /* gets the frame's position */
-    frame_client_gravity(self->frame, x, y, *w, *h);
+    frame_client_gravity(self->frame, x, y);
 
     /* these positions are frame positions, not client positions */
 
@@ -2749,7 +2767,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
     }
 
     /* gets the client's position */
-    frame_frame_gravity(self->frame, x, y, *w, *h);
+    frame_frame_gravity(self->frame, x, y);
 
     /* work within the prefered sizes given by the window */
     if (!(*w == self->area.width && *h == self->area.height)) {
@@ -2800,7 +2818,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
         /* 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;
@@ -3029,6 +3047,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)\n",
+             self->title, self->fullscreen);
+
     client_setup_decor_and_functions(self, FALSE);
     client_move_resize(self, x, y, w, h);
 
@@ -3112,7 +3133,7 @@ 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 */
 
@@ -3190,41 +3211,58 @@ void client_shade(ObClient *self, gboolean shade)
     frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
 }
 
-void client_close(ObClient *self)
+static void client_ping_event(ObClient *self, gboolean dead)
 {
-    XEvent ce;
+    self->not_responding = dead;
+    client_update_title(self);
+
+    if (!dead) {
+        /* try kill it nicely the first time again, if it started responding
+           at some point */
+        self->close_tried_term = FALSE;
+    }
+}
 
+void client_close(ObClient *self)
+{
     if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
 
     /* in the case that the client provides no means to requesting that it
        close, we just kill it */
     if (!self->delete_window)
+        /* don't use client_kill(), we should only kill based on PID in
+           response to a lack of PING replies */
+        XKillClient(ob_display, self->window);
+    else if (self->not_responding)
         client_kill(self);
-    
-    /*
-      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.
-    */
-
-    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);
+    else
+        /* request the client to close with WM_DELETE_WINDOW */
+        PROP_MSG_TO(self->window, self->window, wm_protocols,
+                    prop_atoms.wm_delete_window, event_curtime, 0, 0, 0,
+                    NoEventMask);
 }
 
 void client_kill(ObClient *self)
 {
-    XKillClient(ob_display, self->window);
+    if (!self->client_machine && self->pid) {
+        /* running on the local host */
+        if (!self->close_tried_term) {
+            ob_debug("killing window 0x%x with pid %lu, with SIGTERM\n",
+                     self->window, self->pid);
+            kill(self->pid, SIGTERM);
+            self->close_tried_term = TRUE;
+
+            /* show that we're trying to kill it */
+            client_update_title(self);
+        }
+        else {
+            ob_debug("killing window 0x%x with pid %lu, with SIGKILL\n",
+                     self->window, self->pid);
+            kill(self->pid, SIGKILL); /* kill -9 */
+        }
+    }
+    else
+        XKillClient(ob_display, self->window);
 }
 
 void client_hilite(ObClient *self, gboolean hilite)
@@ -3243,10 +3281,10 @@ void client_hilite(ObClient *self, gboolean hilite)
     }
 }
 
-void client_set_desktop_recursive(ObClient *self,
-                                  guint target,
-                                  gboolean donthide,
-                                  gboolean dontraise)
+static void client_set_desktop_recursive(ObClient *self,
+                                         guint target,
+                                         gboolean donthide,
+                                         gboolean dontraise)
 {
     guint old;
     GSList *it;
@@ -3302,7 +3340,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;
@@ -3313,7 +3351,7 @@ ObClient *client_search_modal_child(ObClient *self)
 
 gboolean client_validate(ObClient *self)
 {
-    XEvent e; 
+    XEvent e;
 
     XSync(ob_display, FALSE); /* get all events on the server */
 
@@ -3329,7 +3367,7 @@ gboolean client_validate(ObClient *self)
 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);
@@ -3358,11 +3396,11 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
           action == prop_atoms.net_wm_state_remove ||
           action == prop_atoms.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 */
@@ -3409,7 +3447,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
                 action = undecorated ? prop_atoms.net_wm_state_remove :
                     prop_atoms.net_wm_state_add;
         }
-    
+
         if (action == prop_atoms.net_wm_state_add) {
             if (state == prop_atoms.net_wm_state_modal) {
                 modal = TRUE;
@@ -3635,34 +3673,7 @@ static void client_present(ObClient *self, gboolean here, gboolean raise,
 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 currently focused app doesn't set a user_time, then it can't
-       benefit from any focus stealing prevention.
-
-       if the timestamp is missing in the request then let it go through
-       even if it is source=app, because EVERY APPLICATION DOES THIS because
-       GTK IS VERY BUGGY AND HARDCODES source=application... WHY!?
-    */
-    if (!last_time || !event_curtime)
-        allow = TRUE;
-    /* otherwise, if they didn't give a time stamp or if it is too old, they
-       don't get focus */
-    else
-        allow = 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, raise, unshade);
-    else
-        /* don't focus it but tell the user it wants attention */
-        client_hilite(self, TRUE);
+    client_present(self, here, raise, unshade);
 }
 
 static void client_bring_windows_recursive(ObClient *self,
@@ -3718,7 +3729,7 @@ static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
             if ((parent = client_icon_recursive(c, w, h)))
                 break;
         }
-        
+
         return parent;
     }
 
@@ -3772,7 +3783,7 @@ 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))
     {
@@ -3792,7 +3803,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)
 {
@@ -3807,7 +3818,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))
@@ -3864,191 +3875,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();
+    }
+
+    /* do we collide with this window? */
+    if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
+                edge_start, edge_size))
+        return;
+
+    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();
+    }
+
+    ob_debug("my head %d size %d\n", my_head, my_size);
+    ob_debug("head %d tail %d deest %d\n", head, tail, *dest);
+    if (!skip_head) {
+        ob_debug("using near edge %d\n", head);
+        *dest = head;
+        *near_edge = TRUE;
+    }
+    else if (!skip_tail) {
+        ob_debug("using far edge %d\n", tail);
+        *dest = tail;
+        *near_edge = FALSE;
+    }
+}
 
-#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)
+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)
 {
-    gint dest, monitor_dest;
-    gint my_edge_start, my_edge_end, my_offset;
     GList *it;
     Rect *a, *mon;
-    
-    if(!client_list)
-        return -1;
+    Rect dock_area;
+    gint edge;
 
-    a = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS, &c->frame->area);
-    mon = screen_area(c->desktop, SCREEN_AREA_ONE_MONITOR, &c->frame->area);
+    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) {
+    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 = mon->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;
-
-            if(his_offset < dest)
-                continue;
-
-            HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
-        }
+        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 = mon->y + mon->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\n", 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 = mon->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 = mon->x + mon->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;
-
-            HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
-        }
+        head = RECT_RIGHT(self->frame->area) +
+            (self->size_inc.width - 1) * (grow ? 1 : -1);
+        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 : -1);
+        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 : -1);
+        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 : -1);
+        e_start = RECT_LEFT(self->frame->area);
+        e_size = self->frame->area.width;
+        dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
         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 */
     }
 
-    g_free(a);
-    g_free(mon);
-    return dest;
+    ob_debug("head %d dir %d\n", head, dir);
+    client_find_edge_directional(self, dir, head, 1,
+                                 e_start, e_size, &e, &near);
+    ob_debug("edge %d\n", 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;
+    default:
+        g_assert_not_reached();
+    }
+    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;
@@ -4081,30 +4232,3 @@ gboolean client_has_group_siblings(ObClient *self)
 {
     return self->group && self->group->members->next;
 }
-
-ObClientIcon *client_thumbnail(ObClient *self, gint wantw, gint wanth)
-{
-    ObClientIcon *ret;
-    RrPixel32 *data;
-    gint w, h;
-
-    if (!self->frame->pixmap) return NULL;
-    if (!RrPixmapToRGBA(ob_rr_inst, self->frame->pixmap, None, &w, &h, &data))
-        return NULL;
-
-    /* resize the thumbnail (within aspect ratio) to the given sizes */
-
-    ret = g_new(ObClientIcon, 1);
-    ret->data = data;
-    ret->width = w;
-    ret->height = h;
-    return ret;
-}
-
-void clienticon_free(ObClientIcon *ci)
-{
-    if (ci) {
-        g_free(ci->data);
-        g_free(ci);
-    }
-}