Merge branch 'backport' into work
[mikachu/openbox.git] / openbox / client.c
index 66006a4..a055e63 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"
@@ -413,6 +414,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 +429,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 focuesed: %d  relative focused: %d",
+                      parent_focused, relative_focused);
 
         if (menu_frame_visible || moveresize_in_progress) {
             activate = FALSE;
@@ -443,13 +458,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 +472,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 it's 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 +503,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;
@@ -560,6 +579,8 @@ 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);
 }
 
 
@@ -635,6 +656,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)) {
@@ -2519,6 +2543,8 @@ gboolean client_show(ObClient *self)
            desktop!
         */
         client_change_wm_state(self);
+
+        hooks_queue(OB_HOOK_WIN_VISIBLE, self);
     }
     return show;
 }
@@ -2557,6 +2583,8 @@ gboolean client_hide(ObClient *self)
            desktop!
         */
         client_change_wm_state(self);
+
+        hooks_queue(OB_HOOK_WIN_INVISIBLE, self);
     }
     return hide;
 }
@@ -2796,8 +2824,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 preferred 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;
@@ -3150,6 +3180,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
@@ -3176,7 +3209,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) {
@@ -3237,6 +3270,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)
@@ -3250,6 +3285,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)
@@ -3312,12 +3349,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;
@@ -3332,7 +3377,14 @@ static void client_prompt_kill(ObClient *self)
             { 0, OB_KILL_RESULT_YES }
         };
         gchar *m;
-        const gchar *y;
+        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;
@@ -3344,24 +3396,26 @@ static void client_prompt_kill(ObClient *self)
 
             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);
+                 title, sig);
             y = _("End Process");
         }
         else {
             m = g_strdup_printf
                 (_("The window \"%s\" does not seem to be responding.  Do you want to disconnect it from the X server?"),
-                 self->original_title);
+                 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);
     }
 
@@ -3444,6 +3498,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 */
@@ -3523,6 +3580,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) ||
@@ -3538,103 +3596,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)) {
+        } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
+            /* and vice versa */
+            if (value)
                 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)) {
-                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;
         }
     }
 
@@ -3883,6 +3903,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);
     }
 }
 
@@ -3945,6 +3968,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;
@@ -4028,12 +4066,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:
@@ -4047,12 +4085,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:
@@ -4060,7 +4098,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;
@@ -4223,28 +4261,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;