Merge branch 'backport' into work
[mikachu/openbox.git] / openbox / client.c
index 946e80d..a62aa5b 100644 (file)
@@ -33,6 +33,7 @@
 #include "focus.h"
 #include "stacking.h"
 #include "openbox.h"
+#include "hooks.h"
 #include "group.h"
 #include "config.h"
 #include "menuframe.h"
@@ -67,7 +68,7 @@ typedef struct
     gpointer data;
 } ClientCallback;
 
-GList            *client_list           = NULL;
+GList          *client_list             = NULL;
 
 static GSList  *client_destroy_notifies = NULL;
 static RrImage *client_default_icon     = NULL;
@@ -79,7 +80,6 @@ static void client_get_area(ObClient *self);
 static void client_get_desktop(ObClient *self);
 static void client_get_state(ObClient *self);
 static void client_get_shaped(ObClient *self);
-static void client_get_mwm_hints(ObClient *self);
 static void client_get_colormap(ObClient *self);
 static void client_set_desktop_recursive(ObClient *self,
                                          guint target,
@@ -107,7 +107,6 @@ static void client_call_notifies(ObClient *self, GSList *list);
 static void client_ping_event(ObClient *self, gboolean dead);
 static void client_prompt_kill(ObClient *self);
 
-
 void client_startup(gboolean reconfig)
 {
     if ((client_default_icon = RrImageCacheFind(ob_rr_icons,
@@ -202,6 +201,7 @@ void client_manage(Window window, ObPrompt *prompt)
     gboolean transient = FALSE;
     Rect place, *monitor;
     Time launch_time, map_time;
+    guint32 user_time;
 
     ob_debug("Managing window: 0x%lx", window);
 
@@ -232,6 +232,12 @@ void client_manage(Window window, ObPrompt *prompt)
 
     ob_debug("Window type: %d", self->type);
     ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
+    ob_debug("Window name: %s class: %s role: %s", self->name, self->class, self->role);
+
+    /* per-app settings override stuff from client_get_all, and return the
+       settings for other uses too. the returned settings is a shallow copy,
+       that needs to be freed with g_free(). */
+    settings = client_get_settings_state(self);
 
     /* now we have all of the window's information so we can set this up.
        do this before creating the frame, so it can tell that we are still
@@ -253,16 +259,15 @@ void client_manage(Window window, ObPrompt *prompt)
        time now */
     grab_server(FALSE);
 
-    /* per-app settings override stuff from client_get_all, and return the
-       settings for other uses too. the returned settings is a shallow copy,
-       that needs to be freed with g_free(). */
-    settings = client_get_settings_state(self);
     /* the session should get the last say though */
     client_restore_session_state(self);
 
     /* tell startup notification that this app started */
     launch_time = sn_app_started(self->startup_id, self->class, self->name);
 
+    if (!OBT_PROP_GET32(self->window, NET_WM_USER_TIME, CARDINAL, &user_time))
+        user_time = map_time;
+
     /* do this after we have a frame.. it uses the frame to help determine the
        WM_STATE to apply. */
     client_change_state(self);
@@ -279,6 +284,8 @@ void client_manage(Window window, ObPrompt *prompt)
         /* this means focus=true for window is same as config_focus_new=true */
         ((config_focus_new || (settings && settings->focus == 1)) ||
          client_search_focus_tree_full(self)) &&
+        /* NET_WM_USER_TIME 0 when mapping means don't focus */
+        (user_time != 0) &&
         /* this checks for focus=false for the window */
         (!settings || settings->focus != 0) &&
         focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE))
@@ -413,6 +420,14 @@ void client_manage(Window window, ObPrompt *prompt)
                   activate ? "yes" : "no");
     if (activate) {
         gboolean raise = FALSE;
+        gboolean relative_focused;
+        gboolean parent_focused;
+
+        parent_focused = (focus_client != NULL &&
+                          client_search_focus_parent(self));
+        relative_focused = (focus_client != NULL &&
+                            (client_search_focus_tree_full(self) != NULL ||
+                             client_search_focus_group_full(self) != NULL));
 
         /* This is focus stealing prevention */
         ob_debug_type(OB_DEBUG_FOCUS,
@@ -420,6 +435,12 @@ void client_manage(Window window, ObPrompt *prompt)
                       "launched at %u (last user interaction time %u)",
                       self->window, map_time, launch_time,
                       event_last_user_time);
+        ob_debug_type(OB_DEBUG_FOCUS,
+                      "Current focus_client: %s",
+                      (focus_client ? focus_client->title : "(none)"));
+        ob_debug_type(OB_DEBUG_FOCUS,
+                      "parent focused: %d  relative focused: %d",
+                      parent_focused, relative_focused);
 
         if (menu_frame_visible || moveresize_in_progress) {
             activate = FALSE;
@@ -443,13 +464,12 @@ void client_manage(Window window, ObPrompt *prompt)
                           "Not focusing the window because its on another "
                           "desktop");
         }
-        /* If something is focused, and it's not our relative... */
-        else if (focus_client && client_search_focus_tree_full(self) == NULL &&
-                 client_search_focus_group_full(self) == NULL)
-        {
+        /* If something is focused... */
+        else if (focus_client) {
             /* If the user is working in another window right now, then don't
                steal focus */
-            if (event_last_user_time && launch_time &&
+            if (!parent_focused &&
+                event_last_user_time && launch_time &&
                 event_time_after(event_last_user_time, launch_time) &&
                 event_last_user_time != launch_time &&
                 event_time_after(event_last_user_time,
@@ -458,10 +478,12 @@ void client_manage(Window window, ObPrompt *prompt)
                 activate = FALSE;
                 ob_debug_type(OB_DEBUG_FOCUS,
                               "Not focusing the window because the user is "
-                              "working in another window");
+                              "working in another window that is not "
+                              "its parent");
             }
-            /* If its a transient (and its parents aren't focused) */
-            else if (client_has_parent(self)) {
+            /* If the new window is a transient (and its relatives aren't
+               focused) */
+            else if (client_has_parent(self) && !relative_focused) {
                 activate = FALSE;
                 ob_debug_type(OB_DEBUG_FOCUS,
                               "Not focusing the window because it is a "
@@ -487,8 +509,11 @@ void client_manage(Window window, ObPrompt *prompt)
                               "Not focusing the window because another window "
                               "would get the focus anyway");
             }
+            /* Don't move focus if the window is not visible on the current
+               desktop and none of its relatives are focused */
             else if (!(self->desktop == screen_desktop ||
-                       self->desktop == DESKTOP_ALL))
+                       self->desktop == DESKTOP_ALL) &&
+                     !relative_focused)
             {
                 activate = FALSE;
                 raise = TRUE;
@@ -501,7 +526,7 @@ void client_manage(Window window, ObPrompt *prompt)
         if (!activate) {
             ob_debug_type(OB_DEBUG_FOCUS,
                           "Focus stealing prevention activated for %s at "
-                          "time %u (last user interactioon time %u)",
+                          "time %u (last user interaction time %u)",
                           self->title, map_time, event_last_user_time);
             /* if the client isn't focused, then hilite it so the user
                knows it is there */
@@ -560,8 +585,9 @@ void client_manage(Window window, ObPrompt *prompt)
 
     ob_debug("Managed window 0x%lx plate 0x%x (%s)",
              window, self->frame->window, self->class);
-}
 
+    hooks_queue(OB_HOOK_WIN_NEW, self);
+}
 
 ObClient *client_fake_manage(Window window)
 {
@@ -598,7 +624,7 @@ ObClient *client_fake_manage(Window window)
 
 void client_unmanage_all(void)
 {
-    while (client_list != NULL)
+    while (client_list)
         client_unmanage(client_list->data);
 }
 
@@ -635,6 +661,9 @@ void client_unmanage(ObClient *self)
     if (!self->prompt)
         XChangeSaveSet(obt_display, self->window, SetModeDelete);
 
+    /* this can't be queued to run later */
+    hooks_run(OB_HOOK_WIN_CLOSE, self);
+
     /* update the focus lists */
     focus_order_remove(self);
     if (client_focused(self)) {
@@ -1003,7 +1032,7 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
 
         /* 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
+           It makes sure 10% of the window is on the screen at least. And don't
            let it move itself off the top of the screen, which would hide the
            titlebar on you. (The user can still do this if they want too, it's
            only limiting the application.
@@ -1214,7 +1243,7 @@ static void client_get_state(ObClient *self)
 static void client_get_shaped(ObClient *self)
 {
     self->shaped = FALSE;
-#ifdef   SHAPE
+#ifdef SHAPE
     if (obt_display_extension_shape) {
         gint foo;
         guint ufoo;
@@ -1225,7 +1254,7 @@ static void client_get_shaped(ObClient *self)
         XShapeQueryExtents(obt_display, self->window, &s, &foo,
                            &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
                            &ufoo);
-        self->shaped = (s != 0);
+        self->shaped = !!s;
     }
 #endif
 }
@@ -1237,9 +1266,9 @@ void client_update_transient_for(ObClient *self)
     gboolean trangroup = FALSE;
 
     if (XGetTransientForHint(obt_display, self->window, &t)) {
-        if (t != self->window) { /* cant be transient to itself! */
+        if (t != self->window) { /* can't be transient to itself! */
             ObWindow *tw = window_find(t);
-            /* if this happens then we need to check for it*/
+            /* if this happens then we need to check for it */
             g_assert(tw != CLIENT_AS_WINDOW(self));
             if (tw && WINDOW_IS_CLIENT(tw)) {
                 /* watch out for windows with a parent that is something
@@ -1286,7 +1315,6 @@ static void client_update_transient_tree(ObClient *self,
       transient windows as their children.
       * * */
 
-
     /* No change has occured */
     if (oldgroup == newgroup &&
         oldgtran == newgtran &&
@@ -1327,7 +1355,7 @@ static void client_update_transient_tree(ObClient *self,
     /* 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
+       WARNING: Cyclical transient-ness is possible if two windows are
        transient for eachother.
     */
     else if (newparent &&
@@ -1366,7 +1394,7 @@ static void client_update_transient_tree(ObClient *self,
     }
 
     /** If we change our group transient-ness, our children change their
-        effect group transient-ness, which affects how they relate to other
+        effective group transient-ness, which affects how they relate to other
         group windows **/
 
     for (it = self->transients; it; it = g_slist_next(it)) {
@@ -1380,7 +1408,7 @@ static void client_update_transient_tree(ObClient *self,
     }
 }
 
-static void client_get_mwm_hints(ObClient *self)
+void client_get_mwm_hints(ObClient *self)
 {
     guint num;
     guint32 *hints;
@@ -1527,7 +1555,7 @@ void client_update_colormap(ObClient *self, Colormap colormap)
     if (client_focused(self)) {
         screen_install_colormap(self, FALSE); /* uninstall old one */
         self->colormap = colormap;
-        screen_install_colormap(self, FALSE); /* install new one */
+        screen_install_colormap(self, TRUE); /* install new one */
     } else
         self->colormap = colormap;
 }
@@ -1541,7 +1569,7 @@ void client_update_normal_hints(ObClient *self)
     self->min_ratio = 0.0f;
     self->max_ratio = 0.0f;
     SIZE_SET(self->size_inc, 1, 1);
-    SIZE_SET(self->base_size, 0, 0);
+    SIZE_SET(self->base_size, -1, -1);
     SIZE_SET(self->min_size, 0, 0);
     SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
 
@@ -1810,7 +1838,7 @@ void client_update_wmhints(ObClient *self)
 {
     XWMHints *hints;
 
-    /* assume a window takes input if it doesnt specify */
+    /* assume a window takes input if it doesn't specify */
     self->can_focus = TRUE;
 
     if ((hints = XGetWMHints(obt_display, self->window)) != NULL) {
@@ -1842,7 +1870,7 @@ void client_update_wmhints(ObClient *self)
             ObGroup *oldgroup = self->group;
 
             /* remove from the old group if there was one */
-            if (self->group != NULL) {
+            if (self->group) {
                 group_remove(self->group, self);
                 self->group = NULL;
             }
@@ -1913,7 +1941,7 @@ void client_update_title(ObClient *self)
     */
                 data = g_strdup("");
             } else
-                data = g_strdup("Unnamed Window");
+                data = g_strdup(_("Unnamed Window"));
         }
     }
     self->original_title = g_strdup(data);
@@ -2102,7 +2130,6 @@ void client_update_icons(ObClient *self)
                                        (gint*)&w, (gint*)&h, &data);
                 obt_display_ignore_errors(FALSE);
 
-
                 if (xicon) {
                     if (w > 0 && h > 0) {
                         /* is this icon in the cache yet? */
@@ -2519,6 +2546,8 @@ gboolean client_show(ObClient *self)
            desktop!
         */
         client_change_wm_state(self);
+
+        hooks_queue(OB_HOOK_WIN_VISIBLE, self);
     }
     return show;
 }
@@ -2529,13 +2558,6 @@ gboolean client_hide(ObClient *self)
 
     if (!client_should_show(self)) {
         if (self == focus_client) {
-            /* if there is a grab going on, then we need to cancel it. if we
-               move focus during the grab, applications will get
-               NotifyWhileGrabbed events and ignore them !
-
-               actions should not rely on being able to move focus during an
-               interactive grab.
-            */
             event_cancel_all_key_grabs();
         }
 
@@ -2564,6 +2586,8 @@ gboolean client_hide(ObClient *self)
            desktop!
         */
         client_change_wm_state(self);
+
+        hooks_queue(OB_HOOK_WIN_INVISIBLE, self);
     }
     return hide;
 }
@@ -2602,7 +2626,6 @@ gboolean client_enter_focusable(ObClient *self)
             self->type != OB_CLIENT_TYPE_DESKTOP);
 }
 
-
 static void client_apply_startup_state(ObClient *self,
                                        gint x, gint y, gint w, gint h)
 {
@@ -2748,7 +2771,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
     Rect desired = {*x, *y, *w, *h};
     frame_rect_to_frame(self->frame, &desired);
 
-    /* make the frame recalculate its dimentions n shit without changing
+    /* make the frame recalculate its dimensions n shit without changing
        anything visible for real, this way the constraints below can work with
        the updated frame dimensions. */
     frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
@@ -2803,8 +2826,10 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
     /* gets the client's position */
     frame_frame_gravity(self->frame, x, y);
 
-    /* work within the prefered sizes given by the window */
-    if (!(*w == self->area.width && *h == self->area.height)) {
+    /* work within the preferred sizes given by the window, these may have
+       changed rather than it's requested width and height, so always run
+       through this code */
+    {
         gint basew, baseh, minw, minh;
         gint incw, inch;
         gfloat minratio, maxratio;
@@ -2817,7 +2842,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
             0 : self->max_ratio;
 
         /* base size is substituted with min size if not specified */
-        if (self->base_size.width || self->base_size.height) {
+        if (self->base_size.width >= 0 || self->base_size.height >= 0) {
             basew = self->base_size.width;
             baseh = self->base_size.height;
         } else {
@@ -2833,6 +2858,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
             minh = self->base_size.height;
         }
 
+        /* This comment is no longer true */
         /* if this is a user-requested resize, then check against min/max
            sizes */
 
@@ -2909,7 +2935,6 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
     g_assert(*h > 0);
 }
 
-
 void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
                       gboolean user, gboolean final, gboolean force_reply)
 {
@@ -3068,7 +3093,7 @@ void client_fullscreen(ObClient *self, gboolean fs)
     if (fs) {
         self->pre_fullscreen_area = self->area;
         /* if the window is maximized, its area isn't all that meaningful.
-           save it's premax area instead. */
+           save its premax area instead. */
         if (self->max_horz) {
             self->pre_fullscreen_area.x = self->pre_max_area.x;
             self->pre_fullscreen_area.width = self->pre_max_area.width;
@@ -3119,7 +3144,6 @@ static void client_iconify_recursive(ObClient *self,
     GSList *it;
     gboolean changed = FALSE;
 
-
     if (self->iconic != iconic) {
         ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
                  self->window);
@@ -3157,6 +3181,9 @@ static void client_iconify_recursive(ObClient *self,
             frame_begin_iconify_animation(self->frame, iconic);
         /* do this after starting the animation so it doesn't flash */
         client_showhide(self);
+
+        hooks_queue((iconic ? OB_HOOK_WIN_ICONIC : OB_HOOK_WIN_UNICONIC),
+                    self);
     }
 
     /* iconify all direct transients, and deiconify all transients
@@ -3183,7 +3210,7 @@ void client_maximize(ObClient *self, gboolean max, gint dir)
     gint x, y, w, h;
 
     g_assert(dir == 0 || dir == 1 || dir == 2);
-    if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
+    if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && max) return;/* can't */
 
     /* check if already done */
     if (max) {
@@ -3244,6 +3271,8 @@ void client_maximize(ObClient *self, gboolean max, gint dir)
 
     client_setup_decor_and_functions(self, FALSE);
     client_move_resize(self, x, y, w, h);
+
+    hooks_queue((max ? OB_HOOK_WIN_MAX : OB_HOOK_WIN_UNMAX), self);
 }
 
 void client_shade(ObClient *self, gboolean shade)
@@ -3257,6 +3286,8 @@ void client_shade(ObClient *self, gboolean shade)
     client_change_wm_state(self); /* the window is being hidden/shown */
     /* resize the frame to just the titlebar */
     frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
+
+    hooks_queue((shade ? OB_HOOK_WIN_SHADE : OB_HOOK_WIN_UNSHADE), self);
 }
 
 static void client_ping_event(ObClient *self, gboolean dead)
@@ -3319,12 +3350,20 @@ void client_close(ObClient *self)
 #define OB_KILL_RESULT_NO 0
 #define OB_KILL_RESULT_YES 1
 
-static void client_kill_requested(ObPrompt *p, gint result, gpointer data)
+static gboolean client_kill_requested(ObPrompt *p, gint result, gpointer data)
 {
     ObClient *self = data;
 
     if (result == OB_KILL_RESULT_YES)
         client_kill(self);
+    return TRUE; /* call the cleanup func */
+}
+
+static void client_kill_cleanup(ObPrompt *p, gpointer data)
+{
+    ObClient *self = data;
+
+    g_assert(p == self->kill_prompt);
 
     prompt_unref(self->kill_prompt);
     self->kill_prompt = NULL;
@@ -3335,10 +3374,18 @@ static void client_prompt_kill(ObClient *self)
     /* check if we're already prompting */
     if (!self->kill_prompt) {
         ObPromptAnswer answers[] = {
-            { _("No"), OB_KILL_RESULT_NO },
-            { _("Yes"), OB_KILL_RESULT_YES }
+            { 0, OB_KILL_RESULT_NO },
+            { 0, OB_KILL_RESULT_YES }
         };
         gchar *m;
+        const gchar *y, *title;
+
+        title = self->original_title;
+        if (title[0] == '\0') {
+            /* empty string, so use its parent */
+            ObClient *p = client_search_top_direct_parent(self);
+            if (p) title = p->original_title;
+        }
 
         if (client_on_localhost(self)) {
             const gchar *sig;
@@ -3349,22 +3396,31 @@ static void client_prompt_kill(ObClient *self)
                 sig = "kill";
 
             m = g_strdup_printf
-                (_("The window \"%s\" does not seem to be responding.  Do you want to force it to exit by sending the %s signal?"), self->original_title, sig);
+                (_("The window \"%s\" does not seem to be responding.  Do you want to force it to exit by sending the %s signal?"),
+                 title, sig);
+            y = _("End Process");
         }
-        else
+        else {
             m = g_strdup_printf
-                (_("The window \"%s\" does not seem to be responding.  Do you want to disconnect it from the X server?"), self->original_title);
-
+                (_("The window \"%s\" does not seem to be responding.  Do you want to disconnect it from the X server?"),
+                 title);
+            y = _("Disconnect");
+        }
+        /* set the dialog buttons' text */
+        answers[0].text = _("Cancel");  /* "no" */
+        answers[1].text = y;            /* "yes" */
 
-        self->kill_prompt = prompt_new(m, answers,
+        self->kill_prompt = prompt_new(m, NULL, answers,
                                        sizeof(answers)/sizeof(answers[0]),
                                        OB_KILL_RESULT_NO, /* default = no */
                                        OB_KILL_RESULT_NO, /* cancel = no */
-                                       client_kill_requested, self);
+                                       client_kill_requested,
+                                       client_kill_cleanup,
+                                       self);
         g_free(m);
     }
 
-    prompt_show(self->kill_prompt, self);
+    prompt_show(self->kill_prompt, self, TRUE);
 }
 
 void client_kill(ObClient *self)
@@ -3443,6 +3499,9 @@ static void client_set_desktop_recursive(ObClient *self,
             /* the new desktop's geometry may be different, so we may need to
                resize, for example if we are maximized */
             client_reconfigure(self, FALSE);
+
+        if (old != self->desktop)
+            hooks_queue(OB_HOOK_WIN_DESK_CHANGE, self);
     }
 
     /* move all transients */
@@ -3479,19 +3538,38 @@ ObClient *client_search_modal_child(ObClient *self)
     return NULL;
 }
 
+static gboolean client_validate_unmap(ObClient *self, int n)
+{
+    XEvent e;
+    gboolean ret = TRUE;
+
+    if (XCheckTypedWindowEvent(obt_display, self->window, UnmapNotify, &e)) {
+        if (n < self->ignore_unmaps) // ignore this one, but look for more
+            ret = client_validate_unmap(self, n+1);
+        else
+            ret = FALSE; // the window is going to become unmanaged
+
+        /* put them back on the event stack so they end up in the same order */
+        XPutBackEvent(obt_display, &e);
+    }
+
+    return ret;
+}
+
 gboolean client_validate(ObClient *self)
 {
     XEvent e;
 
     XSync(obt_display, FALSE); /* get all events on the server */
 
-    if (XCheckTypedWindowEvent(obt_display, self->window, DestroyNotify, &e) ||
-        XCheckTypedWindowEvent(obt_display, self->window, UnmapNotify, &e))
-    {
+    if (XCheckTypedWindowEvent(obt_display, self->window, DestroyNotify, &e)) {
         XPutBackEvent(obt_display, &e);
         return FALSE;
     }
 
+    if (!client_validate_unmap(self, 0))
+        return FALSE;
+
     return TRUE;
 }
 
@@ -3522,6 +3600,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
     gboolean above = self->above;
     gboolean below = self->below;
     gint i;
+    gboolean value;
 
     if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) ||
           action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) ||
@@ -3537,103 +3616,65 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
         /* if toggling, then pick whether we're adding or removing */
         if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) {
             if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
-                action = modal ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
-                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+                value = modal;
             else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
-                action = self->max_vert ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
-                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+                value = self->max_vert;
             else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
-                action = self->max_horz ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
-                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+                value = self->max_horz;
             else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
-                action = shaded ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
-                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+                value = shaded;
             else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
-                action = self->skip_taskbar ?
-                    OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
-                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+                value = self->skip_taskbar;
             else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
-                action = self->skip_pager ?
-                    OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
-                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+                value = self->skip_pager;
             else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
-                action = self->iconic ?
-                    OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
-                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+                value = self->iconic;
             else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
-                action = fullscreen ?
-                    OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
-                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+                value = fullscreen;
             else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
-                action = self->above ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
-                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+                value = self->above;
             else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
-                action = self->below ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
-                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+                value = self->below;
             else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
-                action = self->demands_attention ?
-                    OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
-                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+                value = self->demands_attention;
             else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
-                action = undecorated ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
-                    OBT_PROP_ATOM(NET_WM_STATE_ADD);
+                value = undecorated;
+            action = value ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
+                             OBT_PROP_ATOM(NET_WM_STATE_ADD);
         }
 
-        if (action == OBT_PROP_ATOM(NET_WM_STATE_ADD)) {
-            if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
-                modal = TRUE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
-                max_vert = TRUE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
-                max_horz = TRUE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
-                shaded = TRUE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
-                self->skip_taskbar = TRUE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
-                self->skip_pager = TRUE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
-                iconic = TRUE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
-                fullscreen = TRUE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
-                above = TRUE;
+        value = action == OBT_PROP_ATOM(NET_WM_STATE_ADD);
+        if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
+            modal = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
+            max_vert = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
+            max_horz = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
+            shaded = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
+            self->skip_taskbar = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
+            self->skip_pager = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
+            iconic = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
+            fullscreen = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
+            above = value;
+            /* only unset below when setting above, otherwise you can't get to
+               the normal layer */
+            if (value)
                 below = FALSE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
-                above = FALSE;
-                below = TRUE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
-                demands_attention = TRUE;
-            } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
-                undecorated = TRUE;
-            }
-
-        } else { /* action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) */
-            if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
-                modal = FALSE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
-                max_vert = FALSE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
-                max_horz = FALSE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
-                shaded = FALSE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
-                self->skip_taskbar = FALSE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
-                self->skip_pager = FALSE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
-                iconic = FALSE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
-                fullscreen = FALSE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
+            /* and vice versa */
+            if (value)
                 above = FALSE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
-                below = FALSE;
-            } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
-                demands_attention = FALSE;
-            } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
-                undecorated = FALSE;
-            }
+            below = value;
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
+            demands_attention = value;
+        } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
+            undecorated = value;
         }
     }
 
@@ -3735,13 +3776,6 @@ gboolean client_focus(ObClient *self)
        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();
 
     obt_display_ignore_errors(TRUE);
@@ -3801,10 +3835,15 @@ static void client_present(ObClient *self, gboolean here, gboolean raise,
     client_focus(self);
 }
 
+/* this function exists to map to the net_active_window message in the ewmh */
 void client_activate(ObClient *self, gboolean here, gboolean raise,
                      gboolean unshade, gboolean user)
 {
-    client_present(self, here, raise, unshade);
+    if (user || (self->desktop == DESKTOP_ALL ||
+                 self->desktop == screen_desktop))
+        client_present(self, here, raise, unshade);
+    else
+        client_hilite(self, TRUE);
 }
 
 static void client_bring_windows_recursive(ObClient *self,
@@ -3846,8 +3885,6 @@ gboolean client_focused(ObClient *self)
     return self == focus_client;
 }
 
-
-
 RrImage* client_icon(ObClient *self)
 {
     RrImage *ret = NULL;
@@ -3889,6 +3926,9 @@ void client_set_undecorated(ObClient *self, gboolean undecorated)
         self->undecorated = undecorated;
         client_setup_decor_and_functions(self, TRUE);
         client_change_state(self); /* reflect this in the state hints */
+
+        hooks_queue((undecorated ?
+                     OB_HOOK_WIN_UNDECORATED : OB_HOOK_WIN_DECORATED), self);
     }
 }
 
@@ -3951,6 +3991,21 @@ ObClient *client_search_focus_parent(ObClient *self)
     return NULL;
 }
 
+ObClient *client_search_focus_parent_full(ObClient *self)
+{
+    GSList *it;
+    ObClient *ret = NULL;
+
+    for (it = self->parents; it; it = g_slist_next(it)) {
+        if (client_focused(it->data))
+            ret = it->data;
+        else
+            ret = client_search_focus_parent_full(it->data);
+        if (ret) break;
+    }
+    return ret;
+}
+
 ObClient *client_search_parent(ObClient *self, ObClient *search)
 {
     GSList *it;
@@ -4034,12 +4089,12 @@ static void detect_edge(Rect area, ObDirection dir,
             /* check if the head of this window is closer than the previously
                chosen edge (take into account that the previously chosen
                edge might have been a tail, not a head) */
-            if (head + (*near_edge ? 0 : my_size) < *dest)
+            if (head + (*near_edge ? 0 : my_size) <= *dest)
                 skip_head = TRUE;
             /* check if the tail of this window is closer than the previously
                chosen edge (take into account that the previously chosen
                edge might have been a head, not a tail) */
-            if (tail - (!*near_edge ? 0 : my_size) < *dest)
+            if (tail - (!*near_edge ? 0 : my_size) <= *dest)
                 skip_tail = TRUE;
             break;
         case OB_DIRECTION_SOUTH:
@@ -4053,12 +4108,12 @@ static void detect_edge(Rect area, ObDirection dir,
             /* check if the head of this window is closer than the previously
                chosen edge (take into account that the previously chosen
                edge might have been a tail, not a head) */
-            if (head - (*near_edge ? 0 : my_size) > *dest)
+            if (head - (*near_edge ? 0 : my_size) >= *dest)
                 skip_head = TRUE;
             /* check if the tail of this window is closer than the previously
                chosen edge (take into account that the previously chosen
                edge might have been a head, not a tail) */
-            if (tail + (!*near_edge ? 0 : my_size) > *dest)
+            if (tail + (!*near_edge ? 0 : my_size) >= *dest)
                 skip_tail = TRUE;
             break;
         default:
@@ -4066,7 +4121,7 @@ static void detect_edge(Rect area, ObDirection dir,
     }
 
     ob_debug("my head %d size %d", my_head, my_size);
-    ob_debug("head %d tail %d deest %d", head, tail, *dest);
+    ob_debug("head %d tail %d dest %d", head, tail, *dest);
     if (!skip_head) {
         ob_debug("using near edge %d", head);
         *dest = head;
@@ -4229,28 +4284,28 @@ void client_find_resize_directional(ObClient *self, ObDirection side,
     switch (side) {
     case OB_DIRECTION_EAST:
         head = RECT_RIGHT(self->frame->area) +
-            (self->size_inc.width - 1) * (grow ? 1 : -1);
+            (self->size_inc.width - 1) * (grow ? 1 : 0);
         e_start = RECT_TOP(self->frame->area);
         e_size = self->frame->area.height;
         dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
         break;
     case OB_DIRECTION_WEST:
         head = RECT_LEFT(self->frame->area) -
-            (self->size_inc.width - 1) * (grow ? 1 : -1);
+            (self->size_inc.width - 1) * (grow ? 1 : 0);
         e_start = RECT_TOP(self->frame->area);
         e_size = self->frame->area.height;
         dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
         break;
     case OB_DIRECTION_NORTH:
         head = RECT_TOP(self->frame->area) -
-            (self->size_inc.height - 1) * (grow ? 1 : -1);
+            (self->size_inc.height - 1) * (grow ? 1 : 0);
         e_start = RECT_LEFT(self->frame->area);
         e_size = self->frame->area.width;
         dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
         break;
     case OB_DIRECTION_SOUTH:
         head = RECT_BOTTOM(self->frame->area) +
-            (self->size_inc.height - 1) * (grow ? 1 : -1);
+            (self->size_inc.height - 1) * (grow ? 1 : 0);
         e_start = RECT_LEFT(self->frame->area);
         e_size = self->frame->area.width;
         dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;