]> Dogcows Code - chaz/openbox/blobdiff - openbox/client.c
split out edge detecting code a bit so it is easy to add the dock area and do that too
[chaz/openbox] / openbox / client.c
index 0084588c4905e5c258a1a84c76da58e3d6ac38e5..f38a0c01d11a9460642884789b55f44849fd1158 100644 (file)
@@ -32,6 +32,7 @@
 #include "event.h"
 #include "grab.h"
 #include "focus.h"
+#include "propwin.h"
 #include "stacking.h"
 #include "openbox.h"
 #include "group.h"
@@ -63,37 +64,39 @@ typedef struct
 
 GList            *client_list          = NULL;
 
-static GSList *client_destructors      = NULL;
-static GSList *client_desktop_notifies = NULL;
+extern ObDock *dock;
 
-static void client_get_all(ObClient *self);
-static void client_toggle_border(ObClient *self, gboolean show);
+static GSList *client_destroy_notifies = NULL;
+
+static void client_get_all(ObClient *self, gboolean real);
 static void client_get_startup_id(ObClient *self);
 static void client_get_session_ids(ObClient *self);
 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_layer(ObClient *self);
 static void client_get_shaped(ObClient *self);
 static void client_get_mwm_hints(ObClient *self);
-static void client_get_gravity(ObClient *self);
 static void client_get_colormap(ObClient *self);
-static void client_get_transientness(ObClient *self);
 static void client_change_allowed_actions(ObClient *self);
 static void client_change_state(ObClient *self);
 static void client_change_wm_state(ObClient *self);
-static void client_apply_startup_state(ObClient *self, gint x, gint y);
+static void client_apply_startup_state(ObClient *self,
+                                       gint x, gint y, gint w, gint h);
 static void client_restore_session_state(ObClient *self);
 static gboolean client_restore_session_stacking(ObClient *self);
 static ObAppSettings *client_get_settings_state(ObClient *self);
 static void client_update_transient_tree(ObClient *self,
                                          ObGroup *oldgroup, ObGroup *newgroup,
+                                         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);
+
 
 void client_startup(gboolean reconfig)
 {
@@ -104,48 +107,37 @@ void client_startup(gboolean reconfig)
 
 void client_shutdown(gboolean reconfig)
 {
+    if (reconfig) return;
 }
 
-void client_add_destructor(ObClientCallback func, gpointer data)
-{
-    ClientCallback *d = g_new(ClientCallback, 1);
-    d->func = func;
-    d->data = data;
-    client_destructors = g_slist_prepend(client_destructors, d);
-}
-
-void client_remove_destructor(ObClientCallback func)
+static void client_call_notifies(ObClient *self, GSList *list)
 {
     GSList *it;
 
-    for (it = client_destructors; it; it = g_slist_next(it)) {
+    for (it = list; it; it = g_slist_next(it)) {
         ClientCallback *d = it->data;
-        if (d->func == func) {
-            g_free(d);
-            client_destructors = g_slist_delete_link(client_destructors, it);
-            break;
-        }
+        d->func(self, d->data);
     }
 }
 
-void client_add_desktop_notify(ObClientCallback func, gpointer data)
+void client_add_destroy_notify(ObClientCallback func, gpointer data)
 {
     ClientCallback *d = g_new(ClientCallback, 1);
     d->func = func;
     d->data = data;
-    client_desktop_notifies = g_slist_prepend(client_desktop_notifies, d);
+    client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
 }
 
-void client_remove_desktop_notify(ObClientCallback func)
+void client_remove_destroy_notify(ObClientCallback func)
 {
     GSList *it;
 
-    for (it = client_desktop_notifies; it; it = g_slist_next(it)) {
+    for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
         ClientCallback *d = it->data;
         if (d->func == func) {
             g_free(d);
-            client_desktop_notifies =
-                g_slist_delete_link(client_desktop_notifies, it);
+            client_destroy_notifies =
+                g_slist_delete_link(client_destroy_notifies, it);
             break;
         }
     }
@@ -175,37 +167,6 @@ void client_set_list()
     stacking_set_list();
 }
 
-/*
-  void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
-  {
-  GSList *it;
-
-  for (it = self->transients; it; it = g_slist_next(it)) {
-  if (!func(it->data, data)) return;
-  client_foreach_transient(it->data, func, data);
-  }
-  }
-
-  void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
-  {
-  if (self->transient_for) {
-  if (self->transient_for != OB_TRAN_GROUP) {
-  if (!func(self->transient_for, data)) return;
-  client_foreach_ancestor(self->transient_for, func, data);
-  } else {
-  GSList *it;
-
-  for (it = self->group->members; it; it = g_slist_next(it))
-  if (it->data != self &&
-  !((ObClient*)it->data)->transient_for) {
-  if (!func(it->data, data)) return;
-  client_foreach_ancestor(it->data, func, data);
-  }
-  }
-  }
-  }
-*/
-
 void client_manage_all()
 {
     guint i, j, nchild;
@@ -254,12 +215,13 @@ void client_manage(Window window)
     XWMHints *wmhint;
     gboolean activate = FALSE;
     ObAppSettings *settings;
-    gint newx, newy;
+    gint placex, placey, placew, placeh;
+    gboolean transient = FALSE;
 
     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 */
+    /* 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))
     {
@@ -291,7 +253,7 @@ void client_manage(Window window)
         XFree(wmhint);
     }
 
-    ob_debug("Managing window: %lx\n", window);
+    ob_debug("Managing window: 0x%lx\n", window);
 
     /* choose the events we want to receive on the CLIENT window */
     attrib_set.event_mask = CLIENT_EVENTMASK;
@@ -299,7 +261,6 @@ void client_manage(Window window)
     XChangeWindowAttributes(ob_display, window,
                             CWEventMask|CWDontPropagate, &attrib_set);
 
-
     /* create the ObClient struct, and populate it from the hints on the
        window */
     self = g_new0(ObClient, 1);
@@ -308,80 +269,105 @@ void client_manage(Window window)
 
     /* non-zero defaults */
     self->wmstate = WithdrawnState; /* make sure it gets updated first time */
-    self->layer = -1;
+    self->gravity = NorthWestGravity;
     self->desktop = screen_num_desktops; /* always an invalid value */
     self->user_time = focus_client ? focus_client->user_time : CurrentTime;
 
-    client_get_all(self);
-    /* per-app settings override stuff, and return the settings for other
-       uses too */
+    /* 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);
+
+    /* create the decoration frame for the client window */
+    self->frame = frame_new(self);
+
+    frame_grab_client(self->frame);
+
+    /* we've grabbed everything and set everything that we need to at mapping
+       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 */
+    /* the session should get the last say though */
     client_restore_session_state(self);
 
-    client_calc_layer(self);
+    /* 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;
     }
 
-    /* update the focus lists, do this before the call to change_state or
-       it can end up in the list twice! */
-    focus_order_add_new(self);
-
-    /* remove the client's border (and adjust re gravity) */
-    client_toggle_border(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);
-
-    /* create the decoration frame for the client window */
-    self->frame = frame_new(self);
-
-    frame_grab_client(self->frame, self);
-
     /* do this after we have a frame.. it uses the frame to help determine the
        WM_STATE to apply. */
     client_change_state(self);
 
-    grab_server(FALSE);
+    /* add ourselves to the focus order */
+    focus_order_add_new(self);
 
-    stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
+    /* do this to add ourselves to the stacking list in a non-intrusive way */
+    client_calc_layer(self);
 
     /* 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_parent(self)) &&
+         client_search_focus_tree_full(self)) &&
         /* this checks for focus=false for the window */
         (!settings || settings->focus != 0) &&
-        /* note the check against Type_Normal/Dialog, 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_DIALOG))
+        focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE))
     {
         activate = TRUE;
     }
 
-    /* get the current position */
-    newx = self->area.x;
-    newy = self->area.y;
+    /* remove the client's border */
+    XSetWindowBorderWidth(ob_display, self->window, 0);
 
-    /* figure out placement for the window */
-    if (ob_state() == OB_STATE_RUNNING) {
-        gboolean transient;
+    /* adjust the frame to the client's size before showing or placing
+       the window */
+    frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
+    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;
 
-        transient = place_client(self, &newx, &newy, settings);
+    /* figure out placement for the window if the window is new */
+    if (ob_state() == OB_STATE_RUNNING) {
+        ob_debug("Positioned: %s @ %d %d\n",
+                 (!self->positioned ? "no" :
+                  (self->positioned == PPosition ? "program specified" :
+                   (self->positioned == USPosition ? "user specified" :
+                    (self->positioned == (PPosition | USPosition) ?
+                     "program + user specified" :
+                     "BADNESS !?")))), placex, placey);
+
+        ob_debug("Sized: %s @ %d %d\n",
+                 (!self->sized ? "no" :
+                  (self->sized == PSize ? "program specified" :
+                   (self->sized == USSize ? "user specified" :
+                    (self->sized == (PSize | USSize) ?
+                     "program + user specified" :
+                     "BADNESS !?")))), placew, placeh);
+
+        /* splash screens are also returned as TRUE for transient,
+           and so will be forced on screen below */
+        transient = place_client(self, &placex, &placey, settings);
 
         /* make sure the window is visible. */
-        client_find_onscreen(self, &newx, &newy,
-                             self->area.width,
-                             self->area.height,
+        client_find_onscreen(self, &placex, &placey, placew, placeh,
                              /* non-normal clients has less rules, and
                                 windows that are being restored from a
                                 session do also. we can assume you want
@@ -391,27 +377,71 @@ void client_manage(Window window)
                                 place.c or by the user are allowed partially
                                 off-screen and on xinerama divides (ie,
                                 it is up to the placement routines to avoid
-                                the xinerama divides) */
-                             transient ||
-                             (((self->positioned & PPosition) &&
-                               !(self->positioned & USPosition)) &&
-                              client_normal(self) &&
-                              !self->session));
+                                the xinerama divides)
+
+                                splash screens get "transient" set to TRUE by
+                                the place_client call
+                             */
+                             ob_state() == OB_STATE_RUNNING &&
+                             (transient ||
+                              (!(self->positioned & USPosition) &&
+                               client_normal(self) &&
+                               !self->session)));
     }
 
-    /* do this after the window is placed, so the premax/prefullscreen numbers
-       won't be all wacko!!
-       also, this moves the window to the position where it has been placed
+    /* if the window isn't user-sized, then make it fit inside
+       the visible screen area on its monitor. Use basically the same rules
+       for forcing the window on screen in the client_find_onscreen call.
+
+       do this after place_client, it chooses the monitor!
+
+       splash screens get "transient" set to TRUE by
+       the place_client call
     */
-    ob_debug("placing window 0x%x at %d, %d with size %d x %d\n",
-             self->window, newx, newy, self->area.width, self->area.height);
+    if (ob_state() == OB_STATE_RUNNING &&
+        (transient ||
+         (!(self->sized & USSize || self->positioned & USPosition) &&
+          client_normal(self) &&
+          !self->session)))
+    {
+        Rect placer;
+
+        RECT_SET(placer, placex, placey, placew, placeh);
+        frame_rect_to_frame(self->frame, &placer);
+
+        Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &placer);
+
+        /* 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;
+
+        /* 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);
+
+            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);
     if (self->session)
-        ob_debug("session requested %d %d\n",
-                 self->session->x, self->session->y);
+        ob_debug("  but session requested %d, %d  %d x %d instead, "
+                 "overriding\n",
+                 self->session->x, self->session->y,
+                 self->session->w, self->session->h);
 
-    client_apply_startup_state(self, newx, newy);
+    /* do this after the window is placed, so the premax/prefullscreen numbers
+       won't be all wacko!!
 
-    mouse_grab_for_client(self, TRUE);
+       this also places the window
+    */
+    client_apply_startup_state(self, placex, placey, placew, placeh);
 
     if (activate) {
         guint32 last_time = focus_client ?
@@ -434,8 +464,9 @@ void client_manage(Window window)
                           "Not focusing the window because its on another "
                           "desktop\n");
         }
-        /* If something is focused, and it's not our parent... */
-        else if (focus_client && client_search_focus_parent(self) == NULL)
+        /* 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 &&
@@ -446,15 +477,37 @@ void client_manage(Window window)
                               "Not focusing the window because the time is "
                               "too old\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))
+            {
+                activate = FALSE;
+                ob_debug_type(OB_DEBUG_FOCUS,
+                              "Not focusing the window because it is a "
+                              "transient, and the time is very ambiguous\n");
+            }
             /* Don't steal focus from globally active clients.
                I stole this idea from KWin. It seems nice.
              */
-            if (!(focus_client->can_focus || focus_client->focus_notify)) {
+            else if (!(focus_client->can_focus ||
+                       focus_client->focus_notify))
+            {
                 activate = FALSE;
                 ob_debug_type(OB_DEBUG_FOCUS,
                               "Not focusing the window because a globally "
                               "active client has focus\n");
             }
+            /* Don't move focus if it's not going to go to this window
+               anyway */
+            else if (client_focus_target(self) != self) {
+                activate = FALSE;
+                ob_debug_type(OB_DEBUG_FOCUS,
+                              "Not focusing the window because another window "
+                              "would get the focus anyway\n");
+            }
         }
 
         if (!activate) {
@@ -476,22 +529,28 @@ void client_manage(Window window)
            raised to the top. Legacy begets legacy I guess?
         */
         if (!client_restore_session_stacking(self))
-            client_raise(self);
+            stacking_raise(CLIENT_AS_WINDOW(self));
     }
 
+    mouse_grab_for_client(self, TRUE);
+
     /* 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);
+    }
 
-    /* use client_focus instead of client_activate cuz client_activate does
-       stuff like switch desktops etc and I'm not interested in all that when
-       a window maps since its not based on an action from the user like
-       clicking a window to activate it. so keep the new window out of the way
-       but do focus it. */
     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 */
@@ -505,7 +564,47 @@ void client_manage(Window window)
     /* update the list hints */
     client_set_list();
 
-    ob_debug("Managed window 0x%lx (%s)\n", window, self->class);
+    /* free the ObAppSettings shallow copy */
+    g_free(settings);
+
+    ob_debug("Managed window 0x%lx plate 0x%x (%s)\n",
+             window, self->frame->window, self->class);
+
+    return;
+}
+
+
+ObClient *client_fake_manage(Window window)
+{
+    ObClient *self;
+    ObAppSettings *settings;
+
+    ob_debug("Pretend-managing window: %lx\n", window);
+
+    /* do this minimal stuff to figure out the client's decorations */
+
+    self = g_new0(ObClient, 1);
+    self->window = window;
+
+    client_get_all(self, FALSE);
+    /* per-app settings override stuff, and return the settings for other
+       uses too. this returns a shallow copy that needs to be freed */
+    settings = client_get_settings_state(self);
+
+    client_setup_decor_and_functions(self, FALSE);
+
+    /* create the decoration frame for the client window and adjust its size */
+    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, 
+             self->frame->size.top, self->frame->size.bottom);
+
+    /* free the ObAppSettings shallow copy */
+    g_free(settings);
+
+    return self;
 }
 
 void client_unmanage_all()
@@ -518,9 +617,11 @@ void client_unmanage(ObClient *self)
 {
     guint j;
     GSList *it;
+    gulong ignore_start;
 
-    ob_debug("Unmanaging window: %lx (%s) (%s)\n", self->window, self->class,
-             self->title ? self->title : "");
+    ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)\n",
+             self->window, self->frame->window,
+             self->class, self->title ? self->title : "");
 
     g_assert(self != NULL);
 
@@ -528,18 +629,25 @@ void client_unmanage(ObClient *self)
        don't generate more events */
     XSelectInput(ob_display, self->window, NoEventMask);
 
+    /* ignore enter events from the unmap so it doesnt mess with the focus */
+    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);
 
-    /* ignore enter events from the unmap so it doesnt mess with the focus */
-    event_ignore_queued_enters();
+    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);
 
+    /* kill the property windows */
+    propwin_remove(self->user_time_window, OB_PROPWIN_USER_TIME, self);
+
     /* update the focus lists */
     focus_order_remove(self);
     if (client_focused(self)) {
@@ -556,28 +664,19 @@ void client_unmanage(ObClient *self)
     if (STRUT_EXISTS(self->strut))
         screen_update_areas();
 
-    for (it = client_destructors; it; it = g_slist_next(it)) {
-        ClientCallback *d = it->data;
-        d->func(self, d->data);
-    }
+    client_call_notifies(self, client_destroy_notifies);
 
     /* tell our parent(s) that we're gone */
-    if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
-        for (it = self->group->members; it; it = g_slist_next(it))
-            if (it->data != self)
-                ((ObClient*)it->data)->transients =
-                    g_slist_remove(((ObClient*)it->data)->transients, self);
-    } else if (self->transient_for) {        /* transient of window */
-        self->transient_for->transients =
-            g_slist_remove(self->transient_for->transients, self);
-    }
+    for (it = self->parents; it; it = g_slist_next(it))
+        ((ObClient*)it->data)->transients =
+            g_slist_remove(((ObClient*)it->data)->transients,self);
 
     /* tell our transients that we're gone */
     for (it = self->transients; it; it = g_slist_next(it)) {
-        if (((ObClient*)it->data)->transient_for != OB_TRAN_GROUP) {
-            ((ObClient*)it->data)->transient_for = NULL;
-            client_calc_layer(it->data);
-        }
+        ((ObClient*)it->data)->parents =
+            g_slist_remove(((ObClient*)it->data)->parents, self);
+        /* we could be keeping our children in a higher layer */
+        client_calc_layer(it->data);
     }
 
     /* remove from its group */
@@ -588,7 +687,9 @@ void client_unmanage(ObClient *self)
 
     /* restore the window's original geometry so it is not lost */
     {
-        Rect a = self->area;
+        Rect a;
+
+        a = self->area;
 
         if (self->fullscreen)
             a = self->pre_fullscreen_area;
@@ -603,17 +704,20 @@ void client_unmanage(ObClient *self)
             }
         }
 
-        /* give the client its border back */
-        client_toggle_border(self, TRUE);
-
         self->fullscreen = self->max_horz = self->max_vert = FALSE;
+        /* let it be moved and resized no matter what */
+        self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE;
         self->decorations = 0; /* unmanaged windows have no decor */
 
+        /* give the client its border back */
+        XSetWindowBorderWidth(ob_display, self->window, self->border_width);
+
         client_move_resize(self, a.x, a.y, a.width, a.height);
     }
 
     /* reparent the window out of the frame, and free the frame */
-    frame_release_client(self->frame, self);
+    frame_release_client(self->frame);
+    frame_free(self->frame);
     self->frame = NULL;
 
     if (ob_state() != OB_STATE_EXITING) {
@@ -623,11 +727,15 @@ void client_unmanage(ObClient *self)
         PROP_ERASE(self->window, net_wm_state);
         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 */
+        /* 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);
     }
 
+    /* update the list hints */
+    client_set_list();
+
     ob_debug("Unmanaged window 0x%lx\n", self->window);
 
     /* free all data allocated in the client struct */
@@ -645,78 +753,92 @@ void client_unmanage(ObClient *self)
     g_free(self->client_machine);
     g_free(self->sm_client_id);
     g_free(self);
-     
-    /* update the list hints */
-    client_set_list();
 }
 
+void client_fake_unmanage(ObClient *self)
+{
+    /* this is all that got allocated to get the decorations */
+
+    frame_free(self->frame);
+    g_free(self);
+}
+
+/*! Returns a new structure containing the per-app settings for this client.
+  The returned structure needs to be freed with g_free. */
 static ObAppSettings *client_get_settings_state(ObClient *self)
 {
-    ObAppSettings *settings = NULL;
+    ObAppSettings *settings;
     GSList *it;
 
+    settings = config_create_app_settings();
+
     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
         ObAppSettings *app = it->data;
-        
-        if ((app->name && !app->class && !strcmp(app->name, self->name))
-            || (app->class && !app->name && !strcmp(app->class, self->class))
-            || (app->class && app->name && !strcmp(app->class, self->class)
-                && !strcmp(app->name, self->name)))
-        {
-            /* Match if no role was specified in the per app setting, or if the
-             * string matches the beginning of the role, since apps like to set
-             * the role to things like browser-window-23c4b2f */
-            if (!app->role
-                || !strncmp(app->role, self->role, strlen(app->role)))
-            {
-                ob_debug("Window matching: %s\n", app->name);
-                /* use this one */
-                settings = app;
-                break;
-            }
+        gboolean match = TRUE;
+
+        g_assert(app->name != NULL || app->class != NULL);
+
+        /* we know that either name or class is not NULL so it will have to
+           match to use the rule */
+        if (app->name &&
+            !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))
+            match = FALSE;
+        else if (app->role &&
+                 !g_pattern_match(app->role,
+                                  strlen(self->role), self->role, NULL))
+            match = FALSE;
+
+        if (match) {
+            ob_debug("Window matching: %s\n", app->name);
+
+            /* copy the settings to our struct, overriding the existing
+               settings if they are not defaults */
+            config_app_settings_copy_non_defaults(app, settings);
         }
     }
 
-    if (settings) {
-        if (settings->shade != -1)
-            self->shaded = !!settings->shade;
-        if (settings->decor != -1)
-            self->undecorated = !settings->decor;
-        if (settings->iconic != -1)
-            self->iconic = !!settings->iconic;
-        if (settings->skip_pager != -1)
-            self->skip_pager = !!settings->skip_pager;
-        if (settings->skip_taskbar != -1)
-            self->skip_taskbar = !!settings->skip_taskbar;
-
-        if (settings->max_vert != -1)
-            self->max_vert = !!settings->max_vert;
-        if (settings->max_horz != -1)
-            self->max_horz = !!settings->max_horz;
-
-        if (settings->fullscreen != -1)
-            self->fullscreen = !!settings->fullscreen;
-
-        if (settings->desktop) {
-            if (settings->desktop == DESKTOP_ALL)
-                self->desktop = settings->desktop;
-            else if (settings->desktop > 0 &&
-                     settings->desktop <= screen_num_desktops)
-                self->desktop = settings->desktop - 1;
-        }
+    if (settings->shade != -1)
+        self->shaded = !!settings->shade;
+    if (settings->decor != -1)
+        self->undecorated = !settings->decor;
+    if (settings->iconic != -1)
+        self->iconic = !!settings->iconic;
+    if (settings->skip_pager != -1)
+        self->skip_pager = !!settings->skip_pager;
+    if (settings->skip_taskbar != -1)
+        self->skip_taskbar = !!settings->skip_taskbar;
+
+    if (settings->max_vert != -1)
+        self->max_vert = !!settings->max_vert;
+    if (settings->max_horz != -1)
+        self->max_horz = !!settings->max_horz;
+
+    if (settings->fullscreen != -1)
+        self->fullscreen = !!settings->fullscreen;
+
+    if (settings->desktop) {
+        if (settings->desktop == DESKTOP_ALL)
+            self->desktop = settings->desktop;
+        else if (settings->desktop > 0 &&
+                 settings->desktop <= screen_num_desktops)
+            self->desktop = settings->desktop - 1;
+    }
 
-        if (settings->layer == -1) {
-            self->below = TRUE;
-            self->above = FALSE;
-        }
-        else if (settings->layer == 0) {
-            self->below = FALSE;
-            self->above = FALSE;
-        }
-        else if (settings->layer == 1) {
-            self->below = FALSE;
-            self->above = TRUE;
-        }
+    if (settings->layer == -1) {
+        self->below = TRUE;
+        self->above = FALSE;
+    }
+    else if (settings->layer == 0) {
+        self->below = FALSE;
+        self->above = FALSE;
+    }
+    else if (settings->layer == 1) {
+        self->below = FALSE;
+        self->above = TRUE;
     }
     return settings;
 }
@@ -741,6 +863,7 @@ static void client_restore_session_state(ObClient *self)
 
     RECT_SET_POINT(self->area, self->session->x, self->session->y);
     self->positioned = USPosition;
+    self->sized = USSize;
     if (self->session->w > 0)
         self->area.width = self->session->w;
     if (self->session->h > 0)
@@ -805,45 +928,24 @@ 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;
 
-    all_a = screen_area(self->desktop);
-    mon_a = screen_area_monitor(self->desktop, client_monitor(self));
+    RECT_SET(desired, *x, *y, w, 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;
     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;
@@ -861,10 +963,10 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
         POINT_SET(newbl, newtl.x, newbr.y);
 
         /* is it moving or just resizing from some corner? */
-        stationary_l = oldtl.x == oldtl.x;
-        stationary_r = oldtr.x == oldtr.x;
-        stationary_t = oldtl.y == oldtl.y;
-        stationary_b = oldbl.y == oldbl.y;
+        stationary_l = oldtl.x == newtl.x;
+        stationary_r = oldtr.x == newtr.x;
+        stationary_t = oldtl.y == newtl.y;
+        stationary_b = oldbl.y == newbl.y;
 
         /* if left edge is growing and didnt move right edge */
         if (stationary_r && newtl.x < oldtl.x)
@@ -880,123 +982,91 @@ 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 (fw <= mon_a->width) {
-        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 + mon_a->width - fw;
-    }
-    if (fh <= mon_a->height) {
-        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 + mon_a->height - fh;
-    }
+    for (i = 0; i < screen_num_monitors; ++i) {
+        Rect *a;
 
-    /* get where the client should be */
-    frame_frame_gravity(self->frame, x, y, w, h);
+        if (!screen_physical_area_monitor_contains(i, &desired))
+            continue;
 
-    return ox != *x || oy != *y;
-}
+        a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
 
-static void client_toggle_border(ObClient *self, gboolean show)
-{
-    /* adjust our idea of where the client is, based on its border. When the
-       border is removed, the client should now be considered to be in a
-       different position.
-       when re-adding the border to the client, the same operation needs to be
-       reversed. */
-    gint oldx = self->area.x, oldy = self->area.y;
-    gint x = oldx, y = oldy;
-    switch(self->gravity) {
-    default:
-    case NorthWestGravity:
-    case WestGravity:
-    case SouthWestGravity:
-        break;
-    case NorthEastGravity:
-    case EastGravity:
-    case SouthEastGravity:
-        if (show) x -= self->border_width * 2;
-        else      x += self->border_width * 2;
-        break;
-    case NorthGravity:
-    case SouthGravity:
-    case CenterGravity:
-    case ForgetGravity:
-    case StaticGravity:
-        if (show) x -= self->border_width;
-        else      x += self->border_width;
-        break;
-    }
-    switch(self->gravity) {
-    default:
-    case NorthWestGravity:
-    case NorthGravity:
-    case NorthEastGravity:
-        break;
-    case SouthWestGravity:
-    case SouthGravity:
-    case SouthEastGravity:
-        if (show) y -= self->border_width * 2;
-        else      y += self->border_width * 2;
-        break;
-    case WestGravity:
-    case EastGravity:
-    case CenterGravity:
-    case ForgetGravity:
-    case StaticGravity:
-        if (show) y -= self->border_width;
-        else      y += self->border_width;
-        break;
+        /* 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.
+        */
+        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 - fw*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);
     }
-    self->area.x = x;
-    self->area.y = y;
 
-    if (show) {
-        XSetWindowBorderWidth(ob_display, self->window, self->border_width);
+    /* get where the client should be */
+    frame_frame_gravity(self->frame, x, y);
 
-        /* set border_width to 0 because there is no border to add into
-           calculations anymore */
-        self->border_width = 0;
-    } else
-        XSetWindowBorderWidth(ob_display, self->window, 0);
+    return ox != *x || oy != *y;
 }
 
-
-static void client_get_all(ObClient *self)
+static void client_get_all(ObClient *self, gboolean real)
 {
+    /* this is needed for the frame to set itself up */
     client_get_area(self);
+
+    /* these things can change the decor and functions of the window */
+
     client_get_mwm_hints(self);
+    /* this can change the mwmhints for special cases */
+    client_get_type_and_transientness(self);
+    client_get_state(self);
+    client_update_normal_hints(self);
 
-    /* The transient-ness of a window is used to pick a type, but the type can
-       also affect transiency.
+    /* get the session related properties, these can change decorations
+       from per-app settings */
+    client_get_session_ids(self);
 
-       Dialogs are always made transients for their group if they have one.
+    /* now we got everything that can affect the decorations */
+    if (!real)
+        return;
 
-       I also have made non-application type windows be transients for their
-       group (eg utility windows).
-    */
-    client_get_transientness(self);
-    client_get_type(self);/* this can change the mwmhints for special cases */
-    client_get_state(self);
+    /* get this early so we have it for debugging */
+    client_update_title(self);
+
+    client_update_protocols(self);
 
     client_update_wmhints(self);
     /* this may have already been called from client_update_wmhints */
-    if (self->transient_for == NULL)
+    if (!self->parents && !self->transient_for_group)
         client_update_transient_for(self);
+
     client_get_startup_id(self);
     client_get_desktop(self);/* uses transient data/group/startup id if a
                                 desktop is not specified */
     client_get_shaped(self);
 
-    client_get_layer(self); /* if layer hasn't been specified, get it from
-                               other sources if possible */
-
     {
         /* a couple type-based defaults for new windows */
 
@@ -1004,29 +1074,17 @@ static void client_get_all(ObClient *self)
         if (self->type == OB_CLIENT_TYPE_DESKTOP)
             self->desktop = DESKTOP_ALL;
     }
-
-    client_update_protocols(self);
-
-    client_get_gravity(self); /* get the attribute gravity */
-    client_update_normal_hints(self); /* this may override the attribute
-                                         gravity */
-
-    /* got the type, the mwmhints, the protocols, and the normal hints
-       (min/max sizes), so we're ready to set up the decorations/functions */
-    client_setup_decor_and_functions(self);
   
 #ifdef SYNC
     client_update_sync_request_counter(self);
 #endif
 
-    /* get the session related properties */
-    client_get_session_ids(self);
-
     client_get_colormap(self);
-    client_update_title(self);
     client_update_strut(self);
     client_update_icons(self);
-    client_update_user_time(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);
 }
 
@@ -1050,8 +1108,8 @@ static void client_get_area(ObClient *self)
     POINT_SET(self->root_pos, wattrib.x, wattrib.y);
     self->border_width = wattrib.border_width;
 
-    ob_debug("client area: %d %d  %d %d\n", wattrib.x, wattrib.y,
-             wattrib.width, wattrib.height);
+    ob_debug("client area: %d %d  %d %d  bw %d\n", wattrib.x, wattrib.y,
+             wattrib.width, wattrib.height, wattrib.border_width);
 }
 
 static void client_get_desktop(ObClient *self)
@@ -1063,69 +1121,45 @@ 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); 
     } else {
-        gboolean trdesk = FALSE;
+        GSList *it;
+        gboolean first = TRUE;
+        guint all = screen_num_desktops; /* not a valid value */
 
-        if (self->transient_for) {
-            if (self->transient_for != OB_TRAN_GROUP) {
-                self->desktop = self->transient_for->desktop;
-                trdesk = TRUE;
-            } else {
-                GSList *it;
+        /* if they are all on one desktop, then open it on the
+           same desktop */
+        for (it = self->parents; it; it = g_slist_next(it)) {
+            ObClient *c = it->data;
 
-                for (it = self->group->members; it; it = g_slist_next(it))
-                    if (it->data != self &&
-                        !((ObClient*)it->data)->transient_for) {
-                        self->desktop = ((ObClient*)it->data)->desktop;
-                        trdesk = TRUE;
-                        break;
-                    }
+            if (c->desktop == DESKTOP_ALL) continue;
+
+            if (first) {
+                all = c->desktop;
+                first = FALSE;
             }
+            else if (all != c->desktop)
+                all = screen_num_desktops; /* make it invalid */
         }
-        if (!trdesk) {
-            /* try get from the startup-notification protocol */
-            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;
-            } else
-                /* defaults to the current desktop */
-                self->desktop = screen_desktop;
-        }
-    }
-}
-
-static void client_get_layer(ObClient *self)
-{
-    if (!(self->above || self->below)) {
-        if (self->group) {
-            /* apply stuff from the group */
-            GSList *it;
-            gint layer = -2;
+        if (all != screen_num_desktops) {
+            self->desktop = all;
 
-            for (it = self->group->members; it; it = g_slist_next(it)) {
-                ObClient *c = it->data;
-                if (c != self && !client_search_transient(self, c) &&
-                    client_normal(self) && client_normal(c))
-                {
-                    layer = MAX(layer,
-                                (c->above ? 1 : (c->below ? -1 : 0)));
-                }
-            }
-            switch (layer) {
-            case -1:
-                self->below = TRUE;
-                break;
-            case -2:
-            case 0:
-                break;
-            case 1:
-                self->above = TRUE;
-                break;
-            default:
-                g_assert_not_reached();
-                break;
-            }
+            ob_debug("client desktop set from parents: 0x%x\n",
+                     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\n",
+                     self->desktop);
+        }
+        /* defaults to the current desktop */
+        else {
+            self->desktop = screen_desktop;
+            ob_debug("client desktop set to the current desktop: %d\n",
+                     self->desktop);
         }
     }
 }
@@ -1160,7 +1194,7 @@ static void client_get_state(ObClient *self)
                 self->below = TRUE;
             else if (state[i] == prop_atoms.net_wm_state_demands_attention)
                 self->demands_attention = TRUE;
-            else if (state[i] == prop_atoms.openbox_wm_state_undecorated)
+            else if (state[i] == prop_atoms.ob_wm_state_undecorated)
                 self->undecorated = TRUE;
         }
 
@@ -1187,20 +1221,13 @@ static void client_get_shaped(ObClient *self)
 #endif
 }
 
-void client_get_transientness(ObClient *self)
-{
-    Window t;
-    if (XGetTransientForHint(ob_display, self->window, &t))
-        self->transient = TRUE;
-}
-
 void client_update_transient_for(ObClient *self)
 {
     Window t = None;
     ObClient *target = NULL;
+    gboolean trangroup = FALSE;
 
     if (XGetTransientForHint(ob_display, self->window, &t)) {
-        self->transient = TRUE;
         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*/
@@ -1210,157 +1237,138 @@ void client_update_transient_for(ObClient *self)
                    a dockapp, for example */
                 target = NULL;
             }
-
-            /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
-
-               Setting the transient_for to Root is actually illegal, however
-               applications from time have done this to specify transient for
-               their group.
-
-               Now you can do that by being a TYPE_DIALOG and not setting
-               the transient_for hint at all on your window. But people still
-               use Root, and Kwin is very strange in this regard.
-
-               KWin 3.0 will not consider windows with transient_for set to
-               Root as transient for their group *UNLESS* they are also modal.
-               In that case, it will make them transient for the group. This
-               leads to all sorts of weird behavior from KDE apps which are
-               only tested in KWin. I'd like to follow their behavior just to
-               make this work right with KDE stuff, but that seems wrong.
-            */
-            if (!target && self->group) {
-                /* not transient to a client, see if it is transient for a
-                   group */
-                if (t == RootWindow(ob_display, ob_screen)) {
-                    /* window is a transient for its group! */
-                    target = OB_TRAN_GROUP;
-                }
-            }
         }
-    } else if (self->type == OB_CLIENT_TYPE_DIALOG ||
-               self->type == OB_CLIENT_TYPE_TOOLBAR ||
-               self->type == OB_CLIENT_TYPE_MENU ||
-               self->type == OB_CLIENT_TYPE_UTILITY)
-    {
-        self->transient = TRUE;
-        if (self->group)
-            target = OB_TRAN_GROUP;
-    } else
-        self->transient = FALSE;
+
+        /* 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))
+            trangroup = TRUE;
+    } else if (self->group && self->transient)
+        trangroup = TRUE;
 
     client_update_transient_tree(self, self->group, self->group,
-                                 self->transient_for, target);
-    self->transient_for = target;
+                                 self->transient_for_group, trangroup,
+                                 client_direct_parent(self), target);
+    self->transient_for_group = trangroup;
                           
 }
 
 static void client_update_transient_tree(ObClient *self,
                                          ObGroup *oldgroup, ObGroup *newgroup,
+                                         gboolean oldgtran, gboolean newgtran,
                                          ObClient* oldparent,
                                          ObClient *newparent)
 {
     GSList *it, *next;
     ObClient *c;
 
-    /* No change has occured */
-    if (oldgroup == newgroup && oldparent == newparent) return;
+    g_assert(!oldgtran || oldgroup);
+    g_assert(!newgtran || newgroup);
+    g_assert((!oldgtran && !oldparent) ||
+             (oldgtran && !oldparent) ||
+             (!oldgtran && oldparent));
+    g_assert((!newgtran && !newparent) ||
+             (newgtran && !newparent) ||
+             (!newgtran && newparent));
 
-    /** Remove the client from the transient tree wherever it has changed **/
+    /* * *
+      Group transient windows are not allowed to have other group
+      transient windows as their children.
+      * * */
 
-    /* If the window is becoming a direct transient for a window in its group
-       then that window can't be a child of this window anymore */
-    if (oldparent != newparent &&
-        newparent != NULL && newparent != OB_TRAN_GROUP &&
-        newparent->transient_for == OB_TRAN_GROUP &&
-        newgroup != NULL && newgroup == oldgroup)
-    {
-        self->transients = g_slist_remove(self->transients, newparent);
-    }
-            
 
-    /* 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 ||
-         (newparent == OB_TRAN_GROUP && oldparent != newparent)) &&
-        oldgroup != NULL && oldparent != OB_TRAN_GROUP)
-    {
-        for (it = self->transients; it; it = next) {
-            next = g_slist_next(it);
-            c = it->data;
-            if (c->group == oldgroup)
-                self->transients = g_slist_delete_link(self->transients, it);
-        }
-    }
+    /* No change has occured */
+    if (oldgroup == newgroup &&
+        oldgtran == newgtran &&
+        oldparent == newparent) return;
 
-    /* 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 (oldparent == OB_TRAN_GROUP && (oldgroup != newgroup ||
-                                       oldparent != newparent))
-    {
-        for (it = oldgroup->members; it; it = g_slist_next(it)) {
-            c = it->data;
-            if (c != self && (!c->transient_for ||
-                              c->transient_for != OB_TRAN_GROUP))
-                c->transients = g_slist_remove(c->transients, 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 != NULL && oldparent != OB_TRAN_GROUP &&
-             oldparent != newparent)
-        oldparent->transients = g_slist_remove(oldparent->transients, self);
+    /** Remove the client from the transient tree **/
 
+    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);
+    }
+    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 (newparent == OB_TRAN_GROUP && (oldgroup != newgroup ||
-                                       oldparent != newparent))
-    {
-        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 ||
-                              c->transient_for != OB_TRAN_GROUP))
+            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 != NULL && newparent != OB_TRAN_GROUP &&
-             newparent != oldparent &&
+    else if (newparent &&
              /* don't make ourself its child if it is already our child */
-             !client_is_direct_child(self, newparent))
+             !client_is_direct_child(self, newparent) &&
+             client_normal(newparent))
+    {
         newparent->transients = g_slist_prepend(newparent->transients, 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
-       B is a member of the group and transient for A
+       B is transient for A
+       C is transient for B
+       A can't be transient for C or we have a cycle
     */
-    if (oldgroup != newgroup && newgroup != NULL &&
-        newparent != OB_TRAN_GROUP)
+    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)) {
             c = it->data;
-            if (c != self && c->transient_for == OB_TRAN_GROUP &&
+            if (c != self && c->transient_for_group &&
                 /* Don't make it our child if it is already our parent */
                 !client_is_direct_child(c, self))
             {
                 self->transients = g_slist_prepend(self->transients, c);
+                c->parents = g_slist_prepend(c->parents, 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)
@@ -1381,12 +1389,14 @@ static void client_get_mwm_hints(ObClient *self)
     }
 }
 
-void client_get_type(ObClient *self)
+void client_get_type_and_transientness(ObClient *self)
 {
     guint num, i;
     guint32 *val;
+    Window t;
 
     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 */
@@ -1420,7 +1430,10 @@ void client_get_type(ObClient *self)
         }
         g_free(val);
     }
-    
+
+    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
@@ -1430,6 +1443,15 @@ void client_get_type(ObClient *self)
         else
             self->type = OB_CLIENT_TYPE_NORMAL;
     }
+
+    /* then, based on our type, we can update our transientness.. */
+    if (self->type == OB_CLIENT_TYPE_DIALOG ||
+        self->type == OB_CLIENT_TYPE_TOOLBAR ||
+        self->type == OB_CLIENT_TYPE_MENU ||
+        self->type == OB_CLIENT_TYPE_UTILITY)
+    {
+        self->transient = TRUE;
+    }
 }
 
 void client_update_protocols(ObClient *self)
@@ -1473,16 +1495,6 @@ void client_update_sync_request_counter(ObClient *self)
 }
 #endif
 
-static void client_get_gravity(ObClient *self)
-{
-    XWindowAttributes wattrib;
-    Status ret;
-
-    ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
-    g_assert(ret != BadWindow);
-    self->gravity = wattrib.win_gravity;
-}
-
 void client_get_colormap(ObClient *self)
 {
     XWindowAttributes wa;
@@ -1493,14 +1505,22 @@ void client_get_colormap(ObClient *self)
 
 void client_update_colormap(ObClient *self, Colormap colormap)
 {
-    self->colormap = colormap;
+    if (colormap == self->colormap) return;
+
+    ob_debug("Setting client %s colormap: 0x%x\n", 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 */
+    } else
+        self->colormap = colormap;
 }
 
 void client_update_normal_hints(ObClient *self)
 {
     XSizeHints size;
     glong ret;
-    gint oldgravity = self->gravity;
 
     /* defaults */
     self->min_ratio = 0.0f;
@@ -1516,20 +1536,10 @@ void client_update_normal_hints(ObClient *self)
         if (!client_normal(self))
         */
         self->positioned = (size.flags & (PPosition|USPosition));
+        self->sized = (size.flags & (PSize|USSize));
 
-        if (size.flags & PWinGravity) {
+        if (size.flags & PWinGravity)
             self->gravity = size.win_gravity;
-      
-            /* if the client has a frame, i.e. has already been mapped and
-               is changing its gravity */
-            if (self->frame && self->gravity != oldgravity) {
-                /* move our idea of the client's position based on its new
-                   gravity */
-                client_convert_gravity(self, oldgravity,
-                                       &self->area.x, &self->area.y,
-                                       self->area.width, self->area.height);
-            }
-        }
 
         if (size.flags & PAspect) {
             if (size.min_aspect.y)
@@ -1551,10 +1561,19 @@ void client_update_normal_hints(ObClient *self)
     
         if (size.flags & PResizeInc && size.width_inc && size.height_inc)
             SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
+
+        ob_debug("Normal hints: min size (%d %d) max size (%d %d)\n   "
+                 "size inc (%d %d) base size (%d %d)\n",
+                 self->min_size.width, self->min_size.height,
+                 self->max_size.width, self->max_size.height,
+                 self->size_inc.width, self->size_inc.height,
+                 self->base_size.width, self->base_size.height);
     }
+    else
+        ob_debug("Normal hints: not set\n");
 }
 
-void client_setup_decor_and_functions(ObClient *self)
+void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
 {
     /* start with everything (cept fullscreen) */
     self->decorations =
@@ -1574,7 +1593,10 @@ void client_setup_decor_and_functions(ObClient *self)
          OB_CLIENT_FUNC_ICONIFY |
          OB_CLIENT_FUNC_MAXIMIZE |
          OB_CLIENT_FUNC_SHADE |
-         OB_CLIENT_FUNC_CLOSE);
+         OB_CLIENT_FUNC_CLOSE |
+         OB_CLIENT_FUNC_BELOW |
+         OB_CLIENT_FUNC_ABOVE |
+         OB_CLIENT_FUNC_UNDECORATE);
 
     if (!(self->min_size.width < self->max_size.width ||
           self->min_size.height < self->max_size.height))
@@ -1589,23 +1611,37 @@ void client_setup_decor_and_functions(ObClient *self)
 
     case OB_CLIENT_TYPE_DIALOG:
     case OB_CLIENT_TYPE_UTILITY:
-        /* these windows cannot be maximized */
-        self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
+        /* these windows don't have anything added or removed by default */
         break;
 
     case OB_CLIENT_TYPE_MENU:
     case OB_CLIENT_TYPE_TOOLBAR:
-        /* these windows get less functionality */
-        self->functions &= ~(OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_RESIZE);
+        /* these windows can't iconify or maximize */
+        self->decorations &= ~(OB_FRAME_DECOR_ICONIFY |
+                               OB_FRAME_DECOR_MAXIMIZE);
+        self->functions &= ~(OB_CLIENT_FUNC_ICONIFY |
+                             OB_CLIENT_FUNC_MAXIMIZE);
         break;
 
-    case OB_CLIENT_TYPE_DESKTOP:
-    case OB_CLIENT_TYPE_DOCK:
     case OB_CLIENT_TYPE_SPLASH:
-        /* none of these windows are manipulated by the window manager */
+        /* these don't get get any decorations, and the only thing you can
+           do with them is move them */
+        self->decorations = 0;
+        self->functions = OB_CLIENT_FUNC_MOVE;
+        break;
+
+    case OB_CLIENT_TYPE_DESKTOP:
+        /* these windows are not manipulated by the window manager */
         self->decorations = 0;
         self->functions = 0;
         break;
+
+    case OB_CLIENT_TYPE_DOCK:
+        /* these windows are not manipulated by the window manager, but they
+           can set below layer which has a special meaning */
+        self->decorations = 0;
+        self->functions = OB_CLIENT_FUNC_BELOW;
+        break;
     }
 
     /* Mwm Hints are applied subtractively to what has already been chosen for
@@ -1649,7 +1685,7 @@ void client_setup_decor_and_functions(ObClient *self)
     if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
         self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
     if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
-        self->decorations &= ~OB_FRAME_DECOR_GRIPS;
+        self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE);
 
     /* can't maximize without moving/resizing */
     if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
@@ -1659,18 +1695,22 @@ void client_setup_decor_and_functions(ObClient *self)
         self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
     }
 
-    /* kill the handle on fully maxed windows */
-    if (self->max_vert && self->max_horz)
-        self->decorations &= ~OB_FRAME_DECOR_HANDLE;
+    if (self->max_horz && self->max_vert) {
+        /* you can't resize fully maximized windows */
+        self->functions &= ~OB_CLIENT_FUNC_RESIZE;
+        /* kill the handle on fully maxed windows */
+        self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
+    }
+
+    /* If there are no decorations to remove, don't allow the user to try
+       toggle the state */
+    if (self->decorations == 0)
+        self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
 
     /* 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))
@@ -1686,15 +1726,14 @@ void client_setup_decor_and_functions(ObClient *self)
 
     client_change_allowed_actions(self);
 
-    if (self->frame) {
-        /* adjust the client's decorations, etc. */
-        client_reconfigure(self);
-    }
+    if (reconfig)
+        /* force reconfigure to make sure decorations are updated */
+        client_reconfigure(self, TRUE);
 }
 
 static void client_change_allowed_actions(ObClient *self)
 {
-    gulong actions[9];
+    gulong actions[12];
     gint num = 0;
 
     /* desktop windows are kept on all desktops */
@@ -1717,6 +1756,12 @@ static void client_change_allowed_actions(ObClient *self)
         actions[num++] = prop_atoms.net_wm_action_maximize_horz;
         actions[num++] = prop_atoms.net_wm_action_maximize_vert;
     }
+    if (self->functions & OB_CLIENT_FUNC_ABOVE)
+        actions[num++] = prop_atoms.net_wm_action_above;
+    if (self->functions & OB_CLIENT_FUNC_BELOW)
+        actions[num++] = prop_atoms.net_wm_action_below;
+    if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
+        actions[num++] = prop_atoms.ob_wm_action_undecorate;
 
     PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
 
@@ -1727,7 +1772,7 @@ static void client_change_allowed_actions(ObClient *self)
         else self->shaded = FALSE;
     }
     if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
-        if (self->frame) client_iconify(self, FALSE, TRUE);
+        if (self->frame) client_iconify(self, FALSE, TRUE, FALSE);
         else self->iconic = FALSE;
     }
     if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
@@ -1741,15 +1786,6 @@ static void client_change_allowed_actions(ObClient *self)
     }
 }
 
-void client_reconfigure(ObClient *self)
-{
-    /* by making this pass FALSE for user, we avoid the emacs event storm where
-       every configurenotify causes an update in its normal hints, i think this
-       is generally what we want anyways... */
-    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;
@@ -1799,8 +1835,10 @@ void client_update_wmhints(ObClient *self)
             /* Put ourselves into the new group's transient tree, and remove
                ourselves from the old group's */
             client_update_transient_tree(self, oldgroup, self->group,
-                                         self->transient_for,
-                                         self->transient_for);
+                                         self->transient_for_group,
+                                         self->transient_for_group,
+                                         client_direct_parent(self),
+                                         client_direct_parent(self));
 
             /* Lastly, being in a group, or not, can change if the window is
                transient for anything.
@@ -1810,11 +1848,11 @@ void client_update_wmhints(ObClient *self)
                transient for something, even if transient_for was NULL because
                it wasn't in a group before.
 
-               If transient_for was NULL and oldgroup was NULL we can assume
+               If parents was NULL and oldgroup was NULL we can assume
                that when we add the new group, it will become transient for
                something.
 
-               If transient_for was OB_TRAN_GROUP, then it must have already
+               If transient_for_group is TRUE, then it must have already
                had a group. If it is getting a new group, the above call to
                client_update_transient_tree has already taken care of
                everything ! If it is losing all group status then it will
@@ -1822,13 +1860,14 @@ void client_update_wmhints(ObClient *self)
                updated.
             */
             if (self->transient &&
-                ((self->transient_for == NULL && oldgroup == NULL) ||
-                 (self->transient_for == OB_TRAN_GROUP && !self->group)))
+                ((self->parents == NULL && oldgroup == NULL) ||
+                 (self->transient_for_group && !self->group)))
                 client_update_transient_for(self);
         }
 
         /* the WM_HINTS can contain an icon */
-        client_update_icons(self);
+        if (hints->flags & IconPixmapHint)
+            client_update_icons(self);
 
         XFree(hints);
     }
@@ -1880,8 +1919,14 @@ void client_update_title(ObClient *self)
               PROP_GETS(self->window, wm_icon_name, utf8, &data)))
             data = g_strdup(self->title);
 
-    PROP_SETS(self->window, net_wm_visible_icon_name, data);
-    self->icon_title = data;
+    if (self->client_machine) {
+        visible = g_strdup_printf("%s (%s)", data, self->client_machine);
+        g_free(data);
+    } else
+        visible = data;
+
+    PROP_SETS(self->window, net_wm_visible_icon_name, visible);
+    self->icon_title = visible;
 }
 
 void client_update_strut(ObClient *self)
@@ -1906,12 +1951,12 @@ void client_update_strut(ObClient *self)
     if (!got &&
         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],
@@ -1919,6 +1964,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);
     }
@@ -1993,18 +2039,19 @@ void client_update_icons(ObClient *self)
 
         if ((hints = XGetWMHints(ob_display, self->window))) {
             if (hints->flags & IconPixmapHint) {
-                self->nicons++;
+                self->nicons = 1;
                 self->icons = g_new(ObClientIcon, self->nicons);
                 xerror_set_ignore(TRUE);
                 if (!RrPixmapToRGBA(ob_rr_inst,
                                     hints->icon_pixmap,
                                     (hints->flags & IconMaskHint ?
                                      hints->icon_mask : None),
-                                    &self->icons[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--;
+                                    &self->icons[0].width,
+                                    &self->icons[0].height,
+                                    &self->icons[0].data))
+                {
+                    g_free(self->icons);
+                    self->nicons = 0;
                 }
                 xerror_set_ignore(FALSE);
             }
@@ -2015,8 +2062,11 @@ void client_update_icons(ObClient *self)
     /* 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) {
+       right away afterwards
+
+       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;
         gulong *data;
 
@@ -2038,8 +2088,15 @@ void client_update_icons(ObClient *self)
 void client_update_user_time(ObClient *self)
 {
     guint32 time;
+    gboolean got = FALSE;
 
-    if (PROP_GET32(self->window, net_wm_user_time, cardinal, &time)) {
+    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
@@ -2048,9 +2105,39 @@ void client_update_user_time(ObClient *self)
         */
         self->user_time = time;
 
-        /*
-        ob_debug("window %s user time %u\n", self->title, 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);
     }
 }
 
@@ -2159,6 +2246,8 @@ static void client_get_session_ids(ObClient *self)
         localhost[127] = '\0';
         if (strcmp(localhost, s) != 0)
             self->client_machine = s;
+        else
+            g_free(s);
     }
 }
 
@@ -2188,7 +2277,7 @@ static void client_change_wm_state(ObClient *self)
 
 static void client_change_state(ObClient *self)
 {
-    gulong netstate[11];
+    gulong netstate[12];
     guint num;
 
     num = 0;
@@ -2215,7 +2304,7 @@ static void client_change_state(ObClient *self)
     if (self->demands_attention)
         netstate[num++] = prop_atoms.net_wm_state_demands_attention;
     if (self->undecorated)
-        netstate[num++] = prop_atoms.openbox_wm_state_undecorated;
+        netstate[num++] = prop_atoms.ob_wm_state_undecorated;
     PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
 
     if (self->frame)
@@ -2236,35 +2325,52 @@ ObClient *client_search_focus_tree(ObClient *self)
 
 ObClient *client_search_focus_tree_full(ObClient *self)
 {
-    if (self->transient_for) {
-        if (self->transient_for != OB_TRAN_GROUP) {
-            return client_search_focus_tree_full(self->transient_for);
-        } else {
-            GSList *it;
-            gboolean recursed = FALSE;
-        
-            for (it = self->group->members; it; it = g_slist_next(it))
-                if (!((ObClient*)it->data)->transient_for) {
-                    ObClient *c;
-                    if ((c = client_search_focus_tree_full(it->data)))
-                        return c;
-                    recursed = TRUE;
-                }
-            if (recursed)
-                return NULL;
+    if (self->parents) {
+        GSList *it;
+
+        for (it = self->parents; it; it = g_slist_next(it)) {
+            ObClient *c = it->data;
+            if ((c = client_search_focus_tree_full(it->data))) return c;
         }
+
+        return NULL;
+    }
+    else {
+        /* this function checks the whole tree, the client_search_focus_tree
+           does not, so we need to check this window */
+        if (client_focused(self))
+            return self;
+        return client_search_focus_tree(self);
     }
+}
+
+ObClient *client_search_focus_group_full(ObClient *self)
+{
+    GSList *it;
 
-    /* this function checks the whole tree, the client_search_focus_tree~
-       does not, so we need to check this window */
-    if (client_focused(self))
-        return self;
-    return client_search_focus_tree(self);
+    if (self->group) {
+        for (it = self->group->members; it; it = g_slist_next(it)) {
+            ObClient *c = it->data;
+
+            if (client_focused(c)) return c;
+            if ((c = client_search_focus_tree(it->data))) return c;
+        }
+    } else
+        if (client_focused(self)) return self;
+    return NULL;
+}
+
+gboolean client_has_parent(ObClient *self)
+{
+    return self->parents != NULL;
 }
 
 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;
@@ -2273,24 +2379,25 @@ static ObStackingLayer calc_layer(ObClient *self)
         else l = OB_STACKING_LAYER_ABOVE;
     }
     else if ((self->fullscreen ||
-              /* no decorations and fills the monitor = oldskool fullscreen */
-              (self->frame != NULL &&
-               (self->frame->size.right == 0 && self->frame->size.left == 0 &&
-                self->frame->size.bottom == 0 && self->frame->size.top == 0 &&
-                RECT_EQUAL(self->area,
-                           *screen_physical_area_monitor
-                           (client_monitor(self)))))) &&
+              /* No decorations and fills the monitor = oldskool fullscreen.
+                 But not for maximized windows.
+              */
+              (self->decorations == 0 &&
+               !(self->max_horz && self->max_vert) &&
+               RECT_EQUAL(self->area, *monitor))) &&
              (client_focused(self) || client_search_focus_tree(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;
 }
 
 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
-                                        ObStackingLayer min, gboolean raised)
+                                        ObStackingLayer min)
 {
     ObStackingLayer old, own;
     GSList *it;
@@ -2299,16 +2406,14 @@ static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
     own = calc_layer(self);
     self->layer = MAX(own, min);
 
+    if (self->layer != old) {
+        stacking_remove(CLIENT_AS_WINDOW(self));
+        stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
+    }
+
     for (it = self->transients; it; it = g_slist_next(it))
         client_calc_layer_recursive(it->data, orig,
-                                    self->layer,
-                                    raised ? raised : self->layer != old);
-
-    if (!raised && self->layer != old)
-        if (orig->frame) { /* only restack if the original window is managed */
-            stacking_remove(CLIENT_AS_WINDOW(self));
-            stacking_add(CLIENT_AS_WINDOW(self));
-        }
+                                    self->layer);
 }
 
 void client_calc_layer(ObClient *self)
@@ -2322,7 +2427,7 @@ void client_calc_layer(ObClient *self)
     it = client_search_all_top_parents(self);
 
     for (; it; it = g_slist_next(it))
-        client_calc_layer_recursive(it->data, orig, 0, FALSE);
+        client_calc_layer_recursive(it->data, orig, 0);
 }
 
 gboolean client_should_show(ObClient *self)
@@ -2337,48 +2442,68 @@ gboolean client_should_show(ObClient *self)
     return FALSE;
 }
 
-void client_show(ObClient *self)
+gboolean client_show(ObClient *self)
 {
+    gboolean show = FALSE;
 
     if (client_should_show(self)) {
         frame_show(self->frame);
-    }
+        show = TRUE;
 
-    /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
-       needs to be in IconicState. This includes when it is on another
-       desktop!
-    */
-    client_change_wm_state(self);
+        /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
+           it needs to be in IconicState. This includes when it is on another
+           desktop!
+        */
+        client_change_wm_state(self);
+    }
+    return show;
 }
 
-void client_hide(ObClient *self)
+gboolean client_hide(ObClient *self)
 {
+    gboolean hide = FALSE;
+
     if (!client_should_show(self)) {
-        frame_hide(self->frame);
-    }
+        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 !
 
-    /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
-       needs to be in IconicState. This includes when it is on another
-       desktop!
-    */
-    client_change_wm_state(self);
-}
+               actions should not rely on being able to move focus during an
+               interactive grab.
+            */
+            event_cancel_all_key_grabs();
+        }
 
-void client_showhide(ObClient *self)
-{
+        /* 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
+        */
 
-    if (client_should_show(self)) {
-        frame_show(self->frame);
-    }
-    else {
         frame_hide(self->frame);
+        hide = TRUE;
+
+        /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
+           it needs to be in IconicState. This includes when it is on another
+           desktop!
+        */
+        client_change_wm_state(self);
     }
+    return hide;
+}
 
-    /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
-       needs to be in IconicState. This includes when it is on another
-       desktop!
-    */
-    client_change_wm_state(self);
+void client_showhide(ObClient *self)
+{
+    if (!client_show(self))
+        client_hide(self);
 }
 
 gboolean client_normal(ObClient *self) {
@@ -2410,69 +2535,76 @@ gboolean client_enter_focusable(ObClient *self)
 }
 
 
-static void client_apply_startup_state(ObClient *self, gint x, gint y)
+static void client_apply_startup_state(ObClient *self,
+                                       gint x, gint y, gint w, gint h)
 {
-    gboolean pos = FALSE; /* has the window's position been configured? */
-    gint ox, oy;
+    /* save the states that we are going to apply */
+    gboolean iconic = self->iconic;
+    gboolean fullscreen = self->fullscreen;
+    gboolean undecorated = self->undecorated;
+    gboolean shaded = self->shaded;
+    gboolean demands_attention = self->demands_attention;
+    gboolean max_horz = self->max_horz;
+    gboolean max_vert = self->max_vert;
+    Rect oldarea;
+    gint l;
 
-    /* save the position, and set self->area for these to use */
-    ox = self->area.x;
-    oy = self->area.y;
-    self->area.x = x;
-    self->area.y = y;
+    /* turn them all off in the client, so they won't affect the window
+       being placed */
+    self->iconic = self->fullscreen = self->undecorated = self->shaded =
+        self->demands_attention = self->max_horz = self->max_vert = FALSE;
 
-    /* set the desktop hint, to make sure that it always exists */
-    PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
+    /* move the client to its placed position, or it it's already there,
+       generate a ConfigureNotify telling the client where it is.
 
-    /* these are in a carefully crafted order.. */
+       do this after adjusting the frame. otherwise it gets all weird and
+       clients don't work right
 
-    if (self->iconic) {
-        self->iconic = FALSE;
-        client_iconify(self, TRUE, FALSE);
-    }
-    if (self->fullscreen) {
-        self->fullscreen = FALSE;
+       do this before applying the states so they have the correct
+       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, 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.. */
+
+    if (iconic)
+        client_iconify(self, TRUE, FALSE, TRUE);
+    if (fullscreen)
         client_fullscreen(self, TRUE);
-        pos = TRUE;
-    }
-    if (self->undecorated) {
-        self->undecorated = FALSE;
+    if (undecorated)
         client_set_undecorated(self, TRUE);
-    }
-    if (self->shaded) {
-        self->shaded = FALSE;
+    if (shaded)
         client_shade(self, TRUE);
-    }
-    if (self->demands_attention) {
-        self->demands_attention = FALSE;
+    if (demands_attention)
         client_hilite(self, TRUE);
-    }
   
-    if (self->max_vert && self->max_horz) {
-        self->max_vert = self->max_horz = FALSE;
+    if (max_vert && max_horz)
         client_maximize(self, TRUE, 0);
-        pos = TRUE;
-    } else if (self->max_vert) {
-        self->max_vert = FALSE;
+    else if (max_vert)
         client_maximize(self, TRUE, 2);
-        pos = TRUE;
-    } else if (self->max_horz) {
-        self->max_horz = FALSE;
+    else if (max_horz)
         client_maximize(self, TRUE, 1);
-        pos = TRUE;
-    }
 
-    /* if the client didn't get positioned yet, then do so now
-       call client_move even if the window is not being moved anywhere, because
-       when we reparent it and decorate it, it is getting moved and we need to
-       be telling it so with a ConfigureNotify event.
+    /* 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
     */
-    if (!pos) {
-        /* use the saved position */
-        self->area.x = ox;
-        self->area.y = oy;
-        client_move(self, x, y);
-    }
+    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);
 
     /* nothing to do for the other states:
        skip_taskbar
@@ -2483,34 +2615,138 @@ static void client_apply_startup_state(ObClient *self, gint x, gint y)
     */
 }
 
-void client_convert_gravity(ObClient *self, gint gravity, gint *x, gint *y,
-                            gint w, gint h)
+void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww)
 {
-    gint oldg = self->gravity;
+    /* these should be the current values. this is for when you're not moving,
+       just resizing */
+    g_assert(*x == self->area.x);
+    g_assert(oldw == self->area.width);
+
+    /* horizontal */
+    switch (self->gravity) {
+    default:
+    case NorthWestGravity:
+    case WestGravity:
+    case SouthWestGravity:
+    case StaticGravity:
+    case ForgetGravity:
+        break;
+    case NorthGravity:
+    case CenterGravity:
+    case SouthGravity:
+        *x -= (neww - oldw) / 2;
+        break;
+    case NorthEastGravity:
+    case EastGravity:
+    case SouthEastGravity:
+        *x -= neww - oldw;
+        break;
+    }
+}
 
-    /* get the frame's position from the requested stuff */
-    self->gravity = gravity;
-    frame_client_gravity(self->frame, x, y, w, h);
-    self->gravity = oldg;
+void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh)
+{
+    /* these should be the current values. this is for when you're not moving,
+       just resizing */
+    g_assert(*y == self->area.y);
+    g_assert(oldh == self->area.height);
 
-    /* get the client's position in its true gravity from that */
-    frame_frame_gravity(self->frame, x, y, w, h);
+    /* vertical */
+    switch (self->gravity) {
+    default:
+    case NorthWestGravity:
+    case NorthGravity:
+    case NorthEastGravity:
+    case StaticGravity:
+    case ForgetGravity:
+        break;
+    case WestGravity:
+    case CenterGravity:
+    case EastGravity:
+        *y -= (newh - oldh) / 2;
+        break;
+    case SouthWestGravity:
+    case SouthGravity:
+    case SouthEastGravity:
+        *y -= newh - oldh;
+        break;
+    }
 }
 
 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
        anything visible for real, this way the constraints below can work with
        the updated frame dimensions. */
-    frame_adjust_area(self->frame, TRUE, TRUE, TRUE);
+    frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
+
+    /* gets the frame's position */
+    frame_client_gravity(self->frame, x, y);
+
+    /* 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);
+        a = screen_physical_area_monitor(i);
+
+        *x = a->x;
+        *y = a->y;
+        *w = a->width;
+        *h = a->height;
+
+        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;
+
+        /* 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) {
+            *x = a->x;
+            *w = a->width - self->frame->size.left - self->frame->size.right;
+        }
+        if (self->max_vert) {
+            *y = a->y;
+            *h = a->height - self->frame->size.top - self->frame->size.bottom;
+        }
+
+        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);
 
     /* work within the prefered sizes given by the window */
     if (!(*w == self->area.width && *h == self->area.height)) {
         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 || self->base_size.height) {
@@ -2542,99 +2778,53 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
         *h -= baseh;
 
         /* keep to the increments */
-        *w /= self->size_inc.width;
-        *h /= self->size_inc.height;
+        *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 = 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;
+        /* save the logical size */
+        *logicalw = incw > 1 ? *w : *w + basew;
+        *logicalh = inch > 1 ? *h : *h + baseh;
 
-        i = screen_find_monitor(&desired_area);
-        a = screen_physical_area_monitor(i);
+        *w *= incw;
+        *h *= inch;
 
-        *x = a->x;
-        *y = a->y;
-        *w = a->width;
-        *h = a->height;
+        *w += basew;
+        *h += baseh;
 
-        user = FALSE; /* ignore if the client can't be moved/resized when it
-                         is entering fullscreen */
-    } else if (self->max_horz || self->max_vert) {
-        Rect *a;
-        guint i;
+        /* 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;
 
-        i = screen_find_monitor(&desired_area);
-        a = screen_area_monitor(self->desktop, i);
+        if (minratio)
+            if (*h * minratio > *w) {
+                *h = (gint)(*w / minratio);
 
-        /* set the size and position if maximized */
-        if (self->max_horz) {
-            *x = a->x;
-            *w = a->width - self->frame->size.left - self->frame->size.right;
-        }
-        if (self->max_vert) {
-            *y = a->y;
-            *h = a->height - self->frame->size.top - self->frame->size.bottom;
-        }
+                /* 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);
+                }
+            }
 
-        /* maximizing is not allowed if the user can't move+resize the window
-         */
+        *w += self->base_size.width;
+        *h += self->base_size.height;
     }
 
-    /* gets the client's position */
-    frame_frame_gravity(self->frame, x, y, *w, *h);
-
     /* these override the above states! if you cant move you can't move! */
     if (user) {
         if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
@@ -2652,15 +2842,16 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
 }
 
 
-void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h,
-                           gboolean user, gboolean final,
-                           gboolean force_reply)
+void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
+                      gboolean user, gboolean final, gboolean force_reply)
 {
-    gint oldw, oldh, oldrx, oldry;
+    gint oldw, oldh;
     gboolean send_resize_client;
     gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
+    gboolean fmoved, fresized;
     guint fdecor = self->frame->decorations;
     gboolean fhorz = self->frame->max_horz;
+    gboolean fvert = self->frame->max_vert;
     gint logicalw, logicalh;
 
     /* find the new x, y, width, and height (and logical size) */
@@ -2671,8 +2862,8 @@ void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h,
         SIZE_SET(self->logical_size, logicalw, logicalh);
 
     /* figure out if we moved or resized or what */
-    moved = x != self->area.x || y != self->area.y;
-    resized = w != self->area.width || h != self->area.height;
+    moved = (x != self->area.x || y != self->area.y);
+    resized = (w != self->area.width || h != self->area.height);
 
     oldw = self->area.width;
     oldh = self->area.height;
@@ -2686,61 +2877,97 @@ void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h,
                                     (resized && config_resize_redraw))));
 
     /* if the client is enlarging, then resize the client before the frame */
-    if (send_resize_client && user && (w > oldw || h > oldh)) {
-        XResizeWindow(ob_display, self->window, MAX(w, oldw), MAX(h, oldh));
-        /* resize the plate to show the client padding color underneath */
+    if (send_resize_client && (w > oldw || h > oldh)) {
+        XMoveResizeWindow(ob_display, self->window,
+                          self->frame->size.left, self->frame->size.top,
+                          MAX(w, oldw), MAX(h, oldh));
         frame_adjust_client_area(self->frame);
     }
 
     /* find the frame's dimensions and move/resize it */
-    if (self->decorations != fdecor || self->max_horz != fhorz)
-        moved = resized = TRUE;
-    if (moved || resized)
-        frame_adjust_area(self->frame, moved, resized, FALSE);
-
-    /* find the client's position relative to the root window */
-    oldrx = self->root_pos.x;
-    oldry = self->root_pos.y;
-    rootmoved = (oldrx != (signed)(self->frame->area.x +
-                                   self->frame->size.left -
-                                   self->border_width) ||
-                 oldry != (signed)(self->frame->area.y +
-                                   self->frame->size.top -
-                                   self->border_width));
-
-    if (force_reply || ((!user || (user && final)) && rootmoved))
+    fmoved = moved;
+    fresized = resized;
+
+    /* if decorations changed, then readjust everything for the frame */
+    if (self->decorations != fdecor ||
+        self->max_horz != fhorz || self->max_vert != fvert)
     {
-        XEvent event;
+        fmoved = fresized = TRUE;
+    }
+
+    /* adjust the frame */
+    if (fmoved || fresized) {
+        gulong ignore_start;
+        if (!user)
+            ignore_start = event_start_ignore_all_enters();
+
+        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 (if force_reply is true)
+
+       When user = TRUE, then the request is coming from "us", like when 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 && (rootmoved || force_reply)) ||
+        (user && final && rootmoved))
+    {
+        XEvent event;
 
         event.type = ConfigureNotify;
         event.xconfigure.display = ob_display;
         event.xconfigure.event = self->window;
         event.xconfigure.window = self->window;
 
+        ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d\n",
+                 self->title, self->root_pos.x, self->root_pos.y, w, h);
+
         /* root window real coords */
         event.xconfigure.x = self->root_pos.x;
         event.xconfigure.y = self->root_pos.y;
         event.xconfigure.width = w;
         event.xconfigure.height = h;
-        event.xconfigure.border_width = 0;
-        event.xconfigure.above = self->frame->plate;
+        event.xconfigure.border_width = self->border_width;
+        event.xconfigure.above = None;
         event.xconfigure.override_redirect = FALSE;
         XSendEvent(event.xconfigure.display, event.xconfigure.window,
                    FALSE, StructureNotifyMask, &event);
     }
 
-    /* if the client is shrinking, then resize the frame before the client */
-    if (send_resize_client && (!user || (w <= oldw || h <= oldh))) {
-        /* resize the plate to show the client padding color underneath */
-        frame_adjust_client_area(self->frame);
+    /* if the client is shrinking, then resize the frame before the client.
 
-        XResizeWindow(ob_display, self->window, w, h);
+       both of these resize sections may run, because the top one only resizes
+       in the direction that is growing
+     */
+    if (send_resize_client && (w <= oldw || h <= oldh)) {
+        frame_adjust_client_area(self->frame);
+        XMoveResizeWindow(ob_display, self->window,
+                          self->frame->size.left, self->frame->size.top, w, h);
     }
 
     XFlush(ob_display);
@@ -2755,7 +2982,6 @@ void client_fullscreen(ObClient *self, gboolean fs)
 
     self->fullscreen = fs;
     client_change_state(self); /* change the state hints on the client */
-    client_calc_layer(self);   /* and adjust out layer/stacking */
 
     if (fs) {
         self->pre_fullscreen_area = self->area;
@@ -2770,40 +2996,43 @@ void client_fullscreen(ObClient *self, gboolean fs)
             self->pre_fullscreen_area.height = self->pre_max_area.height;
         }
 
-        /* these are not actually used cuz client_configure will set them
-           as appropriate when the window is fullscreened */
-        x = y = w = h = 0;
+        /* these will help configure_full figure out where to fullscreen
+           the window */
+        x = self->area.x;
+        y = self->area.y;
+        w = self->area.width;
+        h = self->area.height;
     } else {
-        Rect *a;
-
-        if (self->pre_fullscreen_area.width > 0 &&
-            self->pre_fullscreen_area.height > 0)
-        {
-            x = self->pre_fullscreen_area.x;
-            y = self->pre_fullscreen_area.y;
-            w = self->pre_fullscreen_area.width;
-            h = self->pre_fullscreen_area.height;
-            RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
-        } else {
-            /* pick some fallbacks... */
-            a = screen_area_monitor(self->desktop, 0);
-            x = a->x + a->width / 4;
-            y = a->y + a->height / 4;
-            w = a->width / 2;
-            h = a->height / 2;
-        }
+        g_assert(self->pre_fullscreen_area.width > 0 &&
+                 self->pre_fullscreen_area.height > 0);
+
+        x = self->pre_fullscreen_area.x;
+        y = self->pre_fullscreen_area.y;
+        w = self->pre_fullscreen_area.width;
+        h = self->pre_fullscreen_area.height;
+        RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
     }
 
-    client_setup_decor_and_functions(self);
+    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);
 
-    /* try focus us when we go into fullscreen mode */
-    client_focus(self);
+    /* and adjust our layer/stacking. do this after resizing the window,
+       and applying decorations, because windows which fill the screen are
+       considered "fullscreen" and it affects their layer */
+    client_calc_layer(self);
+
+    if (fs) {
+        /* try focus us when we go into fullscreen mode */
+        client_focus(self);
+    }
 }
 
 static void client_iconify_recursive(ObClient *self,
-                                     gboolean iconic, gboolean curdesk)
+                                     gboolean iconic, gboolean curdesk,
+                                     gboolean hide_animation)
 {
     GSList *it;
     gboolean changed = FALSE;
@@ -2820,9 +3049,8 @@ static void client_iconify_recursive(ObClient *self,
                 self->iconic = iconic;
 
                 /* update the focus lists.. iconic windows go to the bottom of
-                   the list, put the new iconic window at the 'top of the
-                   bottom'. */
-                focus_order_to_top(self);
+                   the list */
+                focus_order_to_bottom(self);
 
                 changed = TRUE;
             }
@@ -2831,7 +3059,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);
@@ -2843,7 +3071,7 @@ static void client_iconify_recursive(ObClient *self,
 
     if (changed) {
         client_change_state(self);
-        if (ob_state() != OB_STATE_STARTING && config_animate_iconify)
+        if (config_animate_iconify && !hide_animation)
             frame_begin_iconify_animation(self->frame, iconic);
         /* do this after starting the animation so it doesn't flash */
         client_showhide(self);
@@ -2854,15 +3082,17 @@ static void client_iconify_recursive(ObClient *self,
     for (it = self->transients; it; it = g_slist_next(it))
         if (it->data != self)
             if (client_is_direct_child(self, it->data) || !iconic)
-                client_iconify_recursive(it->data, iconic, curdesk);
+                client_iconify_recursive(it->data, iconic, curdesk,
+                                         hide_animation);
 }
 
-void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
+void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
+                    gboolean hide_animation)
 {
     if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
         /* move up the transient chain as far as possible first */
-        self = client_search_top_normal_parent(self);
-        client_iconify_recursive(self, iconic, curdesk);
+        self = client_search_top_direct_parent(self);
+        client_iconify_recursive(self, iconic, curdesk, hide_animation);
     }
 }
 
@@ -2884,8 +3114,8 @@ void client_maximize(ObClient *self, gboolean max, gint dir)
         if (dir == 2 && !self->max_vert) return;
     }
 
-    /* we just tell it to configure in the same place and client_configure
-       worries about filling the screen with the window */
+    /* these will help configure_full figure out which screen to fill with
+       the window */
     x = self->area.x;
     y = self->area.y;
     w = self->area.width;
@@ -2903,34 +3133,23 @@ void client_maximize(ObClient *self, gboolean max, gint dir)
                      self->pre_max_area.width, self->area.height);
         }
     } else {
-        Rect *a;
-
-        a = screen_area_monitor(self->desktop, 0);
         if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
-            if (self->pre_max_area.width > 0) {
-                x = self->pre_max_area.x;
-                w = self->pre_max_area.width;
+            g_assert(self->pre_max_area.width > 0);
 
-                RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
-                         0, self->pre_max_area.height);
-            } else {
-                /* pick some fallbacks... */
-                x = a->x + a->width / 4;
-                w = a->width / 2;
-            }
+            x = self->pre_max_area.x;
+            w = self->pre_max_area.width;
+
+            RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
+                     0, self->pre_max_area.height);
         }
         if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
-            if (self->pre_max_area.height > 0) {
-                y = self->pre_max_area.y;
-                h = self->pre_max_area.height;
+            g_assert(self->pre_max_area.height > 0);
 
-                RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
-                         self->pre_max_area.width, 0);
-            } else {
-                /* pick some fallbacks... */
-                y = a->y + a->height / 4;
-                h = a->height / 2;
-            }
+            y = self->pre_max_area.y;
+            h = self->pre_max_area.height;
+
+            RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
+                     self->pre_max_area.width, 0);
         }
     }
 
@@ -2941,8 +3160,7 @@ void client_maximize(ObClient *self, gboolean max, gint dir)
 
     client_change_state(self); /* change the state hints on the client */
 
-    client_setup_decor_and_functions(self);
-
+    client_setup_decor_and_functions(self, FALSE);
     client_move_resize(self, x, y, w, h);
 }
 
@@ -2956,7 +3174,7 @@ 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);
 }
 
 void client_close(ObClient *self)
@@ -3014,12 +3232,13 @@ void client_hilite(ObClient *self, gboolean hilite)
 
 void client_set_desktop_recursive(ObClient *self,
                                   guint target,
-                                  gboolean donthide)
+                                  gboolean donthide,
+                                  gboolean dontraise)
 {
     guint old;
     GSList *it;
 
-    if (target != self->desktop) {
+    if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
 
         ob_debug("Setting desktop %u\n", target+1);
 
@@ -3032,40 +3251,37 @@ void client_set_desktop_recursive(ObClient *self,
         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)
-            client_raise(self);
+        if (old != DESKTOP_ALL && !dontraise)
+            stacking_raise(CLIENT_AS_WINDOW(self));
         if (STRUT_EXISTS(self->strut))
             screen_update_areas();
-
-        /* call the notifies */
-        GSList *it;
-        for (it = client_desktop_notifies; it; it = g_slist_next(it)) {
-            ClientCallback *d = it->data;
-            d->func(self, d->data);
-        }
+        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);
     }
 
     /* 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)
+                        gboolean donthide, gboolean dontraise)
 {
-    self = client_search_top_normal_parent(self);
-    client_set_desktop_recursive(self, target, donthide);
+    self = client_search_top_direct_parent(self);
+    client_set_desktop_recursive(self, target, donthide, dontraise);
 }
 
 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
 {
-    while (child != parent &&
-           child->transient_for && child->transient_for != OB_TRAN_GROUP)
-        child = child->transient_for;
+    while (child != parent && (child = client_direct_parent(child)));
     return child == parent;
 }
 
@@ -3103,10 +3319,10 @@ void client_set_wm_state(ObClient *self, glong state)
   
     switch (state) {
     case IconicState:
-        client_iconify(self, TRUE, TRUE);
+        client_iconify(self, TRUE, TRUE, FALSE);
         break;
     case NormalState:
-        client_iconify(self, FALSE, TRUE);
+        client_iconify(self, FALSE, TRUE, FALSE);
         break;
     }
 }
@@ -3176,7 +3392,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
                 action = self->demands_attention ?
                     prop_atoms.net_wm_state_remove :
                     prop_atoms.net_wm_state_add;
-            else if (state == prop_atoms.openbox_wm_state_undecorated)
+            else if (state == prop_atoms.ob_wm_state_undecorated)
                 action = undecorated ? prop_atoms.net_wm_state_remove :
                     prop_atoms.net_wm_state_add;
         }
@@ -3206,7 +3422,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
                 below = TRUE;
             } else if (state == prop_atoms.net_wm_state_demands_attention) {
                 demands_attention = TRUE;
-            } else if (state == prop_atoms.openbox_wm_state_undecorated) {
+            } else if (state == prop_atoms.ob_wm_state_undecorated) {
                 undecorated = TRUE;
             }
 
@@ -3233,11 +3449,12 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
                 below = FALSE;
             } else if (state == prop_atoms.net_wm_state_demands_attention) {
                 demands_attention = FALSE;
-            } else if (state == prop_atoms.openbox_wm_state_undecorated) {
+            } else if (state == prop_atoms.ob_wm_state_undecorated) {
                 undecorated = FALSE;
             }
         }
     }
+
     if (max_horz != self->max_horz || max_vert != self->max_vert) {
         if (max_horz != self->max_horz && max_vert != self->max_vert) {
             /* toggling both */
@@ -3263,24 +3480,30 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
         client_shade(self, shaded);
     if (undecorated != self->undecorated)
         client_set_undecorated(self, undecorated);
+    if (above != self->above || below != self->below) {
+        self->above = above;
+        self->below = below;
+        client_calc_layer(self);
+    }
+
     if (modal != self->modal) {
         self->modal = modal;
         /* when a window changes modality, then its stacking order with its
            transients needs to change */
-        client_raise(self);
+        stacking_raise(CLIENT_AS_WINDOW(self));
+
+        /* it also may get focused. if something is focused that shouldn't
+           be focused anymore, then move the focus */
+        if (focus_client && client_focus_target(focus_client) != focus_client)
+            client_focus(focus_client);
     }
+
     if (iconic != self->iconic)
-        client_iconify(self, iconic, FALSE);
+        client_iconify(self, iconic, FALSE, FALSE);
 
     if (demands_attention != self->demands_attention)
         client_hilite(self, demands_attention);
 
-    if (above != self->above || below != self->below) {
-        self->above = above;
-        self->below = below;
-        client_calc_layer(self);
-    }
-
     client_change_state(self); /* change the hint to reflect these changes */
 }
 
@@ -3295,8 +3518,6 @@ ObClient *client_focus_target(ObClient *self)
 
 gboolean client_can_focus(ObClient *self)
 {
-    XEvent ev;
-
     /* choose the correct target */
     self = client_focus_target(self);
 
@@ -3306,51 +3527,49 @@ gboolean client_can_focus(ObClient *self)
     if (!(self->can_focus || self->focus_notify))
         return FALSE;
 
-    /* do a check to see if the window has already been unmapped or destroyed
-       do this intelligently while watching out for unmaps we've generated
-       (ignore_unmaps > 0) */
-    if (XCheckTypedWindowEvent(ob_display, self->window,
-                               DestroyNotify, &ev)) {
-        XPutBackEvent(ob_display, &ev);
-        return FALSE;
-    }
-    while (XCheckTypedWindowEvent(ob_display, self->window,
-                                  UnmapNotify, &ev)) {
-        if (self->ignore_unmaps) {
-            self->ignore_unmaps--;
-        } else {
-            XPutBackEvent(ob_display, &ev);
-            return FALSE;
-        }
-    }
-
     return TRUE;
 }
 
 gboolean client_focus(ObClient *self)
 {
+    /* we might not focus this window, so if we have modal children which would
+       be focused instead, bring them to this desktop */
+    client_bring_modal_windows(self);
+
     /* choose the correct target */
     self = client_focus_target(self);
 
     if (!client_can_focus(self)) {
-        if (!self->frame->visible) {
-            /* update the focus lists */
-            focus_order_to_top(self);
-        }
+        ob_debug_type(OB_DEBUG_FOCUS,
+                      "Client %s can't be focused\n", self->title);
         return FALSE;
     }
 
     ob_debug_type(OB_DEBUG_FOCUS,
-                  "Focusing client \"%s\" at time %u\n",
-                  self->title, event_curtime);
+                  "Focusing client \"%s\" (0x%x) at time %u\n",
+                  self->title, self->window, event_curtime);
+
+    /* if using focus_delay, stop the timer now so that focus doesn't
+       go moving on us */
+    event_halt_focus_delay();
+
+    /* 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();
+
+    xerror_set_ignore(TRUE);
+    xerror_occured = FALSE;
 
     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. */
-        xerror_set_ignore(TRUE);
         XSetInputFocus(ob_display, self->window, RevertToPointerRoot,
                        event_curtime);
-        xerror_set_ignore(FALSE);
     }
 
     if (self->focus_notify) {
@@ -3368,76 +3587,57 @@ gboolean client_focus(ObClient *self)
         XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
     }
 
-#ifdef DEBUG_FOCUS
-    ob_debug("%sively focusing %lx at %d\n",
-             (self->can_focus ? "act" : "pass"),
-             self->window, (gint) event_curtime);
-#endif
+    xerror_set_ignore(FALSE);
 
-    /* Cause the FocusIn to come back to us. Important for desktop switches,
-       since otherwise we'll have no FocusIn on the queue and send it off to
-       the focus_backup. */
-    XSync(ob_display, FALSE);
-    return TRUE;
+    ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d\n", xerror_occured);
+    return !xerror_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, FALSE);
+        screen_show_desktop(FALSE, self);
     if (self->iconic)
-        client_iconify(self, FALSE, here);
+        client_iconify(self, FALSE, here, FALSE);
     if (self->desktop != DESKTOP_ALL &&
         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));
 
     client_focus(self);
-
-    if (raise) {
-        /* we do this as an action here. this is rather important. this is
-           because we want the results from the focus change to take place 
-           BEFORE we go about raising the window. when a fullscreen window 
-           loses focus, we need this or else the raise wont be able to raise 
-           above the to-lose-focus fullscreen window. */
-        client_raise(self);
-    }
 }
 
-void client_activate(ObClient *self, gboolean here, gboolean user)
+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
+    /* 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 (user || !focus_client || !last_time)
+    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_curtime && event_time_after(event_curtime, last_time);
+        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), "
@@ -3445,44 +3645,45 @@ void client_activate(ObClient *self, gboolean here, gboolean user)
                   self->window, event_curtime, last_time,
                   (user ? "user" : "application"), allow);
 
-    if (allow) {
-        if (event_curtime != CurrentTime)
-            self->user_time = event_curtime;
-
-        client_present(self, here, TRUE);
-    } else
+    if (allow)
+        client_present(self, here, raise, unshade);
+    else
         /* don't focus it but tell the user it wants attention */
         client_hilite(self, TRUE);
 }
 
-static void client_bring_helper_windows_recursive(ObClient *self,
-                                                  guint desktop)
+static void client_bring_windows_recursive(ObClient *self,
+                                           guint desktop,
+                                           gboolean helpers,
+                                           gboolean modals,
+                                           gboolean iconic)
 {
     GSList *it;
 
     for (it = self->transients; it; it = g_slist_next(it))
-        client_bring_helper_windows_recursive(it->data, desktop);
+        client_bring_windows_recursive(it->data, desktop,
+                                       helpers, modals, iconic);
 
-    if (client_helper(self) &&
-        self->desktop != desktop && self->desktop != DESKTOP_ALL)
+    if (((helpers && client_helper(self)) ||
+         (modals && self->modal)) &&
+        ((self->desktop != desktop && self->desktop != DESKTOP_ALL) ||
+         (iconic && self->iconic)))
     {
-        client_set_desktop(self, desktop, FALSE);
+        if (iconic && self->iconic)
+            client_iconify(self, FALSE, TRUE, FALSE);
+        else
+            client_set_desktop(self, desktop, FALSE, FALSE);
     }
 }
 
 void client_bring_helper_windows(ObClient *self)
 {
-    client_bring_helper_windows_recursive(self, self->desktop);
-}
-
-void client_raise(ObClient *self)
-{
-    action_run_string("Raise", self, CurrentTime);
+    client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE);
 }
 
-void client_lower(ObClient *self)
+void client_bring_modal_windows(ObClient *self)
 {
-    action_run_string("Lower", self, CurrentTime);
+    client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE);
 }
 
 gboolean client_focused(ObClient *self)
@@ -3493,45 +3694,38 @@ gboolean client_focused(ObClient *self)
 static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
 {
     guint i;
-    /* si is the smallest image >= req */
-    /* li is the largest image < req */
-    gulong size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
+    gulong min_diff, min_i;
 
     if (!self->nicons) {
         ObClientIcon *parent = NULL;
+        GSList *it;
 
-        if (self->transient_for) {
-            if (self->transient_for != OB_TRAN_GROUP)
-                parent = client_icon_recursive(self->transient_for, w, h);
-            else {
-                GSList *it;
-                for (it = self->group->members; it; it = g_slist_next(it)) {
-                    ObClient *c = it->data;
-                    if (c != self && !c->transient_for) {
-                        if ((parent = client_icon_recursive(c, w, h)))
-                            break;
-                    }
-                }
-            }
+        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;
     }
 
-    for (i = 0; i < self->nicons; ++i) {
-        size = self->icons[i].width * self->icons[i].height;
-        if (size < smallest && size >= (unsigned)(w * h)) {
-            smallest = size;
-            si = i;
-        }
-        if (size > largest && size <= (unsigned)(w * h)) {
-            largest = size;
-            li = i;
+    /* 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;
         }
     }
-    if (largest == 0) /* didnt find one smaller than the requested size */
-        return &self->icons[si];
-    return &self->icons[li];
+    return &self->icons[min_i];
 }
 
 const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
@@ -3564,16 +3758,13 @@ void client_set_layer(ObClient *self, gint layer)
 
 void client_set_undecorated(ObClient *self, gboolean undecorated)
 {
-    if (self->undecorated != undecorated) {
+    if (self->undecorated != undecorated &&
+        /* 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);
-        /* Make sure the client knows it might have moved. Maybe there is a
-         * better way of doing this so only one client_configure is sent, but
-         * since 125 of these are sent per second when moving the window (with
-         * user = FALSE) i doubt it matters much.
-         */
-        client_configure(self, self->area.x, self->area.y,
-                         self->area.width, self->area.height, TRUE, TRUE);
+        client_setup_decor_and_functions(self, TRUE);
         client_change_state(self); /* reflect this in the state hints */
     }
 }
@@ -3583,11 +3774,17 @@ guint client_monitor(ObClient *self)
     return screen_find_monitor(&self->frame->area);
 }
 
-ObClient *client_search_top_normal_parent(ObClient *self)
+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)
 {
-    while (self->transient_for && self->transient_for != OB_TRAN_GROUP &&
-           client_normal(self->transient_for))
-        self = self->transient_for;
+    ObClient *p;
+    while ((p = client_direct_parent(self))) self = p;
     return self;
 }
 
@@ -3595,34 +3792,18 @@ static GSList *client_search_all_top_parents_internal(ObClient *self,
                                                       gboolean bylayer,
                                                       ObStackingLayer layer)
 {
-    GSList *ret = NULL;
+    GSList *ret;
+    ObClient *p;
     
     /* move up the direct transient chain as far as possible */
-    while (self->transient_for && self->transient_for != OB_TRAN_GROUP &&
-           (!bylayer || self->transient_for->layer == layer) &&
-           client_normal(self->transient_for))
-        self = self->transient_for;
-
-    if (!self->transient_for)
-        ret = g_slist_prepend(ret, self);
-    else {
-            GSList *it;
-
-            g_assert(self->group);
-
-            for (it = self->group->members; it; it = g_slist_next(it)) {
-                ObClient *c = it->data;
-
-                if (!c->transient_for && client_normal(c) &&
-                    (!bylayer || c->layer == layer))
-                {
-                    ret = g_slist_prepend(ret, c);
-                }
-            }
+    while ((p = client_direct_parent(self)) &&
+           (!bylayer || p->layer == layer))
+        self = p;
 
-            if (ret == NULL) /* no group parents */
-                ret = g_slist_prepend(ret, self);
-    }
+    if (!self->parents)
+        ret = g_slist_prepend(NULL, self);
+    else
+        ret = g_slist_copy(self->parents);
 
     return ret;
 }
@@ -3639,46 +3820,20 @@ GSList *client_search_all_top_parents_layer(ObClient *self)
 
 ObClient *client_search_focus_parent(ObClient *self)
 {
-    if (self->transient_for) {
-        if (self->transient_for != OB_TRAN_GROUP) {
-            if (client_focused(self->transient_for))
-                return self->transient_for;
-        } else {
-            GSList *it;
-
-            for (it = self->group->members; it; it = g_slist_next(it)) {
-                ObClient *c = it->data;
+    GSList *it;
 
-                /* checking transient_for prevents infinate loops! */
-                if (c != self && !c->transient_for)
-                    if (client_focused(c))
-                        return c;
-            }
-        }
-    }
+    for (it = self->parents; it; it = g_slist_next(it))
+        if (client_focused(it->data)) return it->data;
 
     return NULL;
 }
 
 ObClient *client_search_parent(ObClient *self, ObClient *search)
 {
-    if (self->transient_for) {
-        if (self->transient_for != OB_TRAN_GROUP) {
-            if (self->transient_for == search)
-                return search;
-        } else {
-            GSList *it;
-
-            for (it = self->group->members; it; it = g_slist_next(it)) {
-                ObClient *c = it->data;
+    GSList *it;
 
-                /* checking transient_for prevents infinate loops! */
-                if (c != self && !c->transient_for)
-                    if (c == search)
-                        return search;
-            }
-        }
-    }
+    for (it = self->parents; it; it = g_slist_next(it))
+        if (it->data == search) return search;
 
     return NULL;
 }
@@ -3696,188 +3851,309 @@ ObClient *client_search_transient(ObClient *self, ObClient *search)
     return NULL;
 }
 
-#define WANT_EDGE(cur, c) \
-            if(cur == c)                                                      \
-                continue;                                                     \
-            if(!client_normal(cur))                                           \
-                continue;                                                     \
-            if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
-                continue;                                                     \
-            if(cur->iconic)                                                   \
-                continue;                                                     \
-            if(cur->layer < c->layer && !config_resist_layers_below)          \
-                continue;
-
-#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;
-
-    a = screen_area(c->desktop);
-    monitor = screen_area_monitor(c->desktop, client_monitor(c));
+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:
-        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; 
+        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();
+    }
 
-        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;
+    /* 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_EAST:
+            head = RECT_LEFT(area);
+            tail = RECT_RIGHT(area);
+            break;
+        case OB_DIRECTION_WEST:
+            head = RECT_RIGHT(area);
+            tail = RECT_LEFT(area);
+            break;
+        default:
+            g_assert_not_reached();
+    }
+    switch(dir) {
+        case OB_DIRECTION_NORTH:
+        case OB_DIRECTION_WEST:
+            if (my_head <= head + 1)
+                skip_head = TRUE;
+            if (my_head + my_size - 1 <= tail)
+                skip_tail = TRUE;
+            if (head < *dest)
+                skip_head = TRUE;
+            if (tail - my_size < *dest)
+                skip_tail = TRUE;
+            break;
+        case OB_DIRECTION_SOUTH:
+        case OB_DIRECTION_EAST:
+            if (my_head >= head - 1)
+                skip_head = TRUE;
+            if (my_head - my_size + 1 >= tail)
+                skip_tail = TRUE;
+            if (head > *dest)
+                skip_head = TRUE;
+            if (tail + my_size > *dest)
+                skip_tail = TRUE;
+            break;
+        default:
+            g_assert_not_reached();
+    }
 
-            WANT_EDGE(cur, c)
+    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;
+    }
 
-            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;
+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;
+    gint edge;
 
-            if(his_offset < dest)
-                continue;
+    a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
+                    &self->frame->area);
+    mon = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR,
+                      &self->frame->area);
 
-            HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
-        }
+    switch(dir) {
+    case OB_DIRECTION_NORTH:
+        if (my_head >= RECT_TOP(*mon))
+            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);
-
-        /* 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; 
+        if (my_head <= RECT_BOTTOM(*mon))
+            edge = RECT_BOTTOM(*mon) + 1;
+        else
+            edge = RECT_BOTTOM(*a) + 1;
+        break;
+    case OB_DIRECTION_EAST:
+        if (my_head <= RECT_RIGHT(*mon))
+            edge = RECT_RIGHT(*mon) + 1;
+        else
+            edge = RECT_RIGHT(*a) + 1;
+        break;
+    case OB_DIRECTION_WEST:
+        if (my_head >= RECT_LEFT(*mon))
+            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;
 
-        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;
+    for(it = client_list; it; it = g_list_next(it)) {
+        ObClient *cur = it->data;
 
-            WANT_EDGE(cur, c)
+        /* 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;
 
-            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);
+        ob_debug("trying window %s\n", cur->title);
 
+        detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
+                    my_edge_size, dest, near_edge);
+    }
+    detect_edge(dock->area, dir, my_head, my_size, my_edge_start,
+                my_edge_size, dest, near_edge);
+}
 
-            if(his_offset - 1 < my_offset)
-                continue;
-            
-            if(his_offset > dest)
-                continue;
+void client_find_move_directional(ObClient *self, ObDirection dir,
+                                  gint *x, gint *y)
+{
+    gint head, size;
+    gint e, e_start, e_size;
+    gboolean near;
 
-            HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
-        }
+    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:
-        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);
+        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();
+    }
 
-            if(his_offset + 1 > my_offset)
-                continue;
+    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:
+        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 : -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;
+    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\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;
-    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()
@@ -3891,6 +4167,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))
This page took 0.117572 seconds and 4 git commands to generate.