Make a pending ReplayPointer happen before moving/showing/hiding a window in an action.
[mikachu/openbox.git] / openbox / client.c
index 250b665..43a2f55 100644 (file)
@@ -24,6 +24,7 @@
 #include "xerror.h"
 #include "screen.h"
 #include "moveresize.h"
+#include "ping.h"
 #include "place.h"
 #include "prop.h"
 #include "extensions.h"
@@ -32,7 +33,6 @@
 #include "event.h"
 #include "grab.h"
 #include "focus.h"
-#include "propwin.h"
 #include "stacking.h"
 #include "openbox.h"
 #include "group.h"
 #include "keyboard.h"
 #include "mouse.h"
 #include "render/render.h"
+#include "gettext.h"
 
 #ifdef HAVE_UNISTD_H
 #  include <unistd.h>
 #endif
 
+#ifdef HAVE_SIGNAL_H
+#  include <signal.h> /* for kill() */
+#endif
+
 #include <glib.h>
 #include <X11/Xutil.h>
 
@@ -75,6 +80,10 @@ static void client_get_state(ObClient *self);
 static void client_get_shaped(ObClient *self);
 static void client_get_mwm_hints(ObClient *self);
 static void client_get_colormap(ObClient *self);
+static void client_set_desktop_recursive(ObClient *self,
+                                         guint target,
+                                         gboolean donthide,
+                                         gboolean dontraise);
 static void client_change_allowed_actions(ObClient *self);
 static void client_change_state(ObClient *self);
 static void client_change_wm_state(ObClient *self);
@@ -94,6 +103,7 @@ static GSList *client_search_all_top_parents_internal(ObClient *self,
                                                       gboolean bylayer,
                                                       ObStackingLayer layer);
 static void client_call_notifies(ObClient *self, GSList *list);
+static void client_ping_event(ObClient *self, gboolean dead);
 
 
 void client_startup(gboolean reconfig)
@@ -141,7 +151,7 @@ void client_remove_destroy_notify(ObClientCallback func)
     }
 }
 
-void client_set_list()
+void client_set_list(void)
 {
     Window *windows, *win_it;
     GList *it;
@@ -165,7 +175,7 @@ void client_set_list()
     stacking_set_list();
 }
 
-void client_manage_all()
+void client_manage_all(void)
 {
     guint i, j, nchild;
     Window w, *children;
@@ -191,6 +201,9 @@ void client_manage_all()
         }
     }
 
+    /* manage windows in reverse order from how they were originally mapped.
+       this is an attempt to manage children windows before their parents, so
+       that when the parent is mapped, it can find the child */
     for (i = 0; i < nchild; ++i) {
         if (children[i] == None)
             continue;
@@ -215,6 +228,7 @@ void client_manage(Window window)
     ObAppSettings *settings;
     gboolean transient = FALSE;
     Rect place, *monitor;
+    Time launch_time, map_time;
 
     grab_server(TRUE);
 
@@ -253,6 +267,8 @@ void client_manage(Window window)
 
     ob_debug("Managing window: 0x%lx\n", window);
 
+    map_time = event_get_server_time();
+
     /* choose the events we want to receive on the CLIENT window */
     attrib_set.event_mask = CLIENT_EVENTMASK;
     attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
@@ -269,7 +285,6 @@ void client_manage(Window window)
     self->wmstate = WithdrawnState; /* make sure it gets updated first time */
     self->gravity = NorthWestGravity;
     self->desktop = screen_num_desktops; /* always an invalid value */
-    self->user_time = focus_client ? focus_client->user_time : CurrentTime;
 
     /* get all the stuff off the window */
     client_get_all(self, TRUE);
@@ -277,6 +292,11 @@ void client_manage(Window window)
     ob_debug("Window type: %d\n", self->type);
     ob_debug("Window group: 0x%x\n", self->group?self->group->leader:0);
 
+    /* now we have all of the window's information so we can set this up.
+       do this before creating the frame, so it can tell that we are still
+       mapping and doesn't go applying things right away */
+    client_setup_decor_and_functions(self, FALSE);
+
     /* specify that if we exit, the window should not be destroyed and
        should be reparented back to root automatically */
     XChangeSaveSet(ob_display, window, SetModeInsert);
@@ -297,13 +317,8 @@ void client_manage(Window window)
     /* the session should get the last say though */
     client_restore_session_state(self);
 
-    /* now we have all of the window's information so we can set this up */
-    client_setup_decor_and_functions(self, FALSE);
-
-    {
-        Time t = sn_app_started(self->startup_id, self->class);
-        if (t) self->user_time = t;
-    }
+    /* tell startup notification that this app started */
+    launch_time = sn_app_started(self->startup_id, self->class, self->name);
 
     /* do this after we have a frame.. it uses the frame to help determine the
        WM_STATE to apply. */
@@ -451,16 +466,17 @@ void client_manage(Window window)
     g_free(monitor);
     monitor = NULL;
 
+    ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s\n",
+                  activate ? "yes" : "no");
     if (activate) {
         gboolean raise = FALSE;
-        guint32 last_time = focus_client ?
-            focus_client->user_time : CurrentTime;
 
         /* This is focus stealing prevention */
         ob_debug_type(OB_DEBUG_FOCUS,
-                      "Want to focus new window 0x%x with time %u "
-                      "(last time %u)\n",
-                      self->window, self->user_time, last_time);
+                      "Want to focus new window 0x%x at time %u "
+                      "launched at %u (last user interaction time %u)\n",
+                      self->window, map_time, launch_time,
+                      event_last_user_time);
 
         if (menu_frame_visible || moveresize_in_progress) {
             activate = FALSE;
@@ -473,10 +489,10 @@ void client_manage(Window window)
 
         /* if it's on another desktop */
         else if (!(self->desktop == screen_desktop ||
-              self->desktop == DESKTOP_ALL) &&
-            /* the timestamp is from before you changed desktops */
-            self->user_time && screen_desktop_user_time &&
-            !event_time_after(self->user_time, screen_desktop_user_time))
+                   self->desktop == DESKTOP_ALL) &&
+                 /* the timestamp is from before you changed desktops */
+                 launch_time && screen_desktop_user_time &&
+                 !event_time_after(launch_time, screen_desktop_user_time))
         {
             activate = FALSE;
             raise = TRUE;
@@ -488,26 +504,25 @@ void client_manage(Window window)
         else if (focus_client && client_search_focus_tree_full(self) == NULL &&
                  client_search_focus_group_full(self) == NULL)
         {
-            /* If time stamp is old, don't steal focus */
-            if (self->user_time && last_time &&
-                !event_time_after(self->user_time, last_time))
+            /* If the user is working in another window right now, then don't
+               steal focus */
+            if (event_last_user_time && launch_time &&
+                event_time_after(event_last_user_time, launch_time) &&
+                event_last_user_time != launch_time &&
+                event_time_after(event_last_user_time,
+                                 map_time - OB_EVENT_USER_TIME_DELAY))
             {
                 activate = FALSE;
                 ob_debug_type(OB_DEBUG_FOCUS,
-                              "Not focusing the window because the time is "
-                              "too old\n");
+                              "Not focusing the window because the user is "
+                              "working in another window\n");
             }
-            /* If its a transient (and parents aren't focused) and the time
-               is ambiguous (either the current focus target doesn't have
-               a timestamp, or they are the same (we probably inherited it
-               from them) */
-            else if (client_has_parent(self) &&
-                     (!last_time || self->user_time == last_time))
-            {
+            /* If its a transient (and its parents aren't focused) */
+            else if (client_has_parent(self)) {
                 activate = FALSE;
                 ob_debug_type(OB_DEBUG_FOCUS,
                               "Not focusing the window because it is a "
-                              "transient, and the time is very ambiguous\n");
+                              "transient, and its relatives aren't focused\n");
             }
             /* Don't steal focus from globally active clients.
                I stole this idea from KWin. It seems nice.
@@ -529,13 +544,22 @@ void client_manage(Window window)
                               "Not focusing the window because another window "
                               "would get the focus anyway\n");
             }
+            else if (!(self->desktop == screen_desktop ||
+                       self->desktop == DESKTOP_ALL))
+            {
+                activate = FALSE;
+                raise = TRUE;
+                ob_debug_type(OB_DEBUG_FOCUS,
+                              "Not focusing the window because it is on "
+                              "another desktop and no relatives are focused ");
+            }
         }
 
         if (!activate) {
             ob_debug_type(OB_DEBUG_FOCUS,
-                          "Focus stealing prevention activated for %s with "
-                          "time %u (last time %u)\n",
-                          self->title, self->user_time, last_time);
+                          "Focus stealing prevention activated for %s at "
+                          "time %u (last user interactioon time %u)\n",
+                          self->title, map_time, event_last_user_time);
             /* if the client isn't focused, then hilite it so the user
                knows it is there */
             client_hilite(self, TRUE);
@@ -588,6 +612,15 @@ void client_manage(Window window)
     /* update the list hints */
     client_set_list();
 
+    /* watch for when the application stops responding.  only do this for
+       normal windows, i.e. windows which have titlebars and close buttons 
+       and things like that.
+       we don't need to stop pinging on unmanage, because it will be handled
+       automatically by the destroy callback!
+    */
+    if (self->ping && client_normal(self))
+        ping_start(self, client_ping_event);
+
     /* free the ObAppSettings shallow copy */
     g_free(settings);
 
@@ -631,7 +664,7 @@ ObClient *client_fake_manage(Window window)
     return self;
 }
 
-void client_unmanage_all()
+void client_unmanage_all(void)
 {
     while (client_list != NULL)
         client_unmanage(client_list->data);
@@ -669,9 +702,6 @@ void client_unmanage(ObClient *self)
     /* remove the window from our save set */
     XChangeSaveSet(ob_display, self->window, SetModeDelete);
 
-    /* kill the property windows */
-    propwin_remove(self->user_time_window, OB_PROPWIN_USER_TIME, self);
-
     /* update the focus lists */
     focus_order_remove(self);
     if (client_focused(self)) {
@@ -757,6 +787,11 @@ void client_unmanage(ObClient *self)
         XMapWindow(ob_display, self->window);
     }
 
+    /* these should not be left on the window ever.  other window managers
+       don't necessarily use them and it will mess them up (like compiz) */
+    PROP_ERASE(self->window, net_wm_visible_name);
+    PROP_ERASE(self->window, net_wm_visible_icon_name);
+
     /* update the list hints */
     client_set_list();
 
@@ -768,6 +803,7 @@ void client_unmanage(ObClient *self)
         g_free(self->icons[j].data);
     if (self->nicons > 0)
         g_free(self->icons);
+    g_free(self->startup_id);
     g_free(self->wm_command);
     g_free(self->title);
     g_free(self->icon_title);
@@ -957,6 +993,7 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
     gint fw, fh;
     Rect desired;
     guint i;
+    gboolean found_mon;
 
     RECT_SET(desired, *x, *y, w, h);
     frame_rect_to_frame(self->frame, &desired);
@@ -1006,18 +1043,26 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
             rudeb = TRUE;
     }
 
+    /* we iterate through every monitor that the window is at least partially
+       on, to make sure it is obeying the rules on them all
+
+       if the window does not appear on any monitors, then use the first one
+    */
+    found_mon = FALSE;
     for (i = 0; i < screen_num_monitors; ++i) {
         Rect *a;
 
         if (!screen_physical_area_monitor_contains(i, &desired)) {
-            if (i < screen_num_monitors - 1)
+            if (i < screen_num_monitors - 1 || found_mon)
                 continue;
 
             /* the window is not inside any monitor! so just use the first
                one */
             a = screen_area(self->desktop, 0, NULL);
-        } else
+        } else {
+            found_mon = TRUE;
             a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
+        }
 
         /* This makes sure windows aren't entirely outside of the screen so you
            can't see them at all.
@@ -1111,9 +1156,6 @@ static void client_get_all(ObClient *self, gboolean real)
     client_get_colormap(self);
     client_update_strut(self);
     client_update_icons(self);
-    client_update_user_time_window(self);
-    if (!self->user_time_window) /* check if this would have been called */
-        client_update_user_time(self);
     client_update_icon_geometry(self);
 }
 
@@ -1500,6 +1542,10 @@ void client_update_protocols(ObClient *self)
                 /* if this protocol is requested, then the window will be
                    notified whenever we want it to receive focus */
                 self->focus_notify = TRUE;
+            else if (proto[i] == prop_atoms.net_wm_ping)
+                /* if this protocol is requested, then the window will allow
+                   pings to determine if it is still alive */
+                self->ping = TRUE;
 #ifdef SYNC
             else if (proto[i] == prop_atoms.net_wm_sync_request)
                 /* if this protocol is requested, then resizing the
@@ -1524,7 +1570,7 @@ void client_update_sync_request_counter(ObClient *self)
 }
 #endif
 
-void client_get_colormap(ObClient *self)
+static void client_get_colormap(ObClient *self)
 {
     XWindowAttributes wa;
 
@@ -1634,11 +1680,16 @@ void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
     switch (self->type) {
     case OB_CLIENT_TYPE_NORMAL:
         /* normal windows retain all of the possible decorations and
-           functionality, and are the only windows that you can fullscreen */
+           functionality, and can be fullscreen */
         self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
         break;
 
     case OB_CLIENT_TYPE_DIALOG:
+        /* sometimes apps make dialog windows fullscreen for some reason (for
+           e.g. kpdf does this..) */
+        self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
+        break;
+
     case OB_CLIENT_TYPE_UTILITY:
         /* these windows don't have anything added or removed by default */
         break;
@@ -1794,16 +1845,16 @@ static void client_change_allowed_actions(ObClient *self)
 
     PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
 
-    /* make sure the window isn't breaking any rules now */
+   /* make sure the window isn't breaking any rules now
+
+   don't check ICONIFY here.  just cuz a window can't iconify doesnt mean
+   it can't be iconified with its parent
+   */
 
     if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
         if (self->frame) client_shade(self, FALSE);
         else self->shaded = FALSE;
     }
-    if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
-        if (self->frame) client_iconify(self, FALSE, TRUE, FALSE);
-        else self->iconic = FALSE;
-    }
     if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
         if (self->frame) client_fullscreen(self, FALSE);
         else self->fullscreen = FALSE;
@@ -1931,6 +1982,15 @@ void client_update_title(ObClient *self)
     } else
         visible = data;
 
+    if (self->not_responding) {
+        data = visible;
+        if (self->close_tried_term)
+            visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
+        else
+            visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
+        g_free(data);
+    }
+
     PROP_SETS(self->window, net_wm_visible_name, visible);
     self->title = visible;
 
@@ -1954,6 +2014,15 @@ void client_update_title(ObClient *self)
     } else
         visible = data;
 
+    if (self->not_responding) {
+        data = visible;
+        if (self->close_tried_term)
+            visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
+        else
+            visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
+        g_free(data);
+    }
+
     PROP_SETS(self->window, net_wm_visible_icon_name, visible);
     self->icon_title = visible;
 }
@@ -2012,11 +2081,17 @@ void client_update_strut(ObClient *self)
     }
 }
 
+/* Avoid storing icons above this size if possible */
+#define AVOID_ABOVE 64
+
 void client_update_icons(ObClient *self)
 {
     guint num;
     guint32 *data;
     guint w, h, i, j;
+    guint num_seen;  /* number of icons present */
+    guint num_small_seen;  /* number of icons small enough present */
+    guint smallest, smallest_area;
 
     for (i = 0; i < self->nicons; ++i)
         g_free(self->icons[i].data);
@@ -2027,25 +2102,54 @@ void client_update_icons(ObClient *self)
     if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
         /* figure out how many valid icons are in here */
         i = 0;
-        while (num - i > 2) {
-            w = data[i++];
-            h = data[i++];
-            i += w * h;
-            if (i > num || w*h == 0) break;
-            ++self->nicons;
-        }
+        num_seen = num_small_seen = 0;
+        smallest = smallest_area = 0;
+        if (num > 2)
+            while (i < num) {
+                w = data[i++];
+                h = data[i++];
+                i += w * h;
+                /* watch for it being too small for the specified size, or for
+                   zero sized icons. */
+                if (i > num || w == 0 || h == 0) break;
+
+                if (!smallest_area || w*h < smallest_area) {
+                    smallest = num_seen;
+                    smallest_area = w*h;
+                }
+                ++num_seen;
+                if (w <= AVOID_ABOVE && h <= AVOID_ABOVE)
+                    ++num_small_seen;
+            }
+        if (num_small_seen > 0)
+            self->nicons = num_small_seen;
+        else if (num_seen)
+            self->nicons = 1;
 
         self->icons = g_new(ObClientIcon, self->nicons);
 
         /* store the icons */
         i = 0;
-        for (j = 0; j < self->nicons; ++j) {
+        for (j = 0; j < self->nicons;) {
             guint x, y, t;
 
             w = self->icons[j].width = data[i++];
             h = self->icons[j].height = data[i++];
 
-            if (w*h == 0) continue;
+            /* if there are some icons smaller than the threshold, we're
+               skipping all the ones above */
+            if (num_small_seen > 0) {
+                if (w > AVOID_ABOVE || h > AVOID_ABOVE) {
+                    i += w*h;
+                    continue;
+                }
+            }
+            /* if there were no icons smaller than the threshold, then we are
+               only taking the smallest available one we saw */
+            else if (j != smallest) {
+                i += w*h;
+                continue;
+            }
 
             self->icons[j].data = g_new(RrPixel32, w * h);
             for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
@@ -2060,6 +2164,8 @@ void client_update_icons(ObClient *self)
                     (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
             }
             g_assert(i <= num);
+
+            ++j;
         }
 
         g_free(data);
@@ -2114,62 +2220,6 @@ void client_update_icons(ObClient *self)
         frame_adjust_icon(self->frame);
 }
 
-void client_update_user_time(ObClient *self)
-{
-    guint32 time;
-    gboolean got = FALSE;
-
-    if (self->user_time_window)
-        got = PROP_GET32(self->user_time_window,
-                         net_wm_user_time, cardinal, &time);
-    if (!got)
-        got = PROP_GET32(self->window, net_wm_user_time, cardinal, &time);
-
-    if (got) {
-        /* we set this every time, not just when it grows, because in practice
-           sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
-           backward we don't want all windows to stop focusing. we'll just
-           assume noone is setting times older than the last one, cuz that
-           would be pretty stupid anyways
-        */
-        self->user_time = time;
-
-        /*ob_debug("window %s user time %u\n", self->title, time);*/
-    }
-}
-
-void client_update_user_time_window(ObClient *self)
-{
-    guint32 w;
-
-    if (!PROP_GET32(self->window, net_wm_user_time_window, window, &w))
-        w = None;
-
-    if (w != self->user_time_window) {
-        /* remove the old window */
-        propwin_remove(self->user_time_window, OB_PROPWIN_USER_TIME, self);
-        self->user_time_window = None;
-
-        if (self->group && self->group->leader == w) {
-            ob_debug_type(OB_DEBUG_APP_BUGS, "Window is setting its "
-                          "_NET_WM_USER_TYPE_WINDOW to its group leader\n");
-            /* do it anyways..? */
-        }
-        else if (w == self->window) {
-            ob_debug_type(OB_DEBUG_APP_BUGS, "Window is setting its "
-                          "_NET_WM_USER_TIME_WINDOW to itself\n");
-            w = None; /* don't do it */
-        }
-
-        /* add the new window */
-        propwin_add(w, OB_PROPWIN_USER_TIME, self);
-        self->user_time_window = w;
-
-        /* and update from it */
-        client_update_user_time(self);
-    }
-}
-
 void client_update_icon_geometry(ObClient *self)
 {
     guint num;
@@ -2177,12 +2227,13 @@ void client_update_icon_geometry(ObClient *self)
 
     RECT_SET(self->icon_geometry, 0, 0, 0, 0);
 
-    if (PROP_GETA32(self->window, net_wm_icon_geometry, cardinal, &data, &num)
-        && num == 4)
+    if (PROP_GETA32(self->window, net_wm_icon_geometry, cardinal, &data, &num))
     {
-        /* don't let them set it with an area < 0 */
-        RECT_SET(self->icon_geometry, data[0], data[1],
-                 MAX(data[2],0), MAX(data[3],0));
+        if (num == 4)
+            /* don't let them set it with an area < 0 */
+            RECT_SET(self->icon_geometry, data[0], data[1],
+                     MAX(data[2],0), MAX(data[3],0));
+        g_free(data);
     }
 }
 
@@ -2270,6 +2321,7 @@ static void client_get_session_ids(ObClient *self)
 
     if (got) {
         gchar localhost[128];
+        guint32 pid;
 
         gethostname(localhost, 127);
         localhost[127] = '\0';
@@ -2277,6 +2329,11 @@ static void client_get_session_ids(ObClient *self)
             self->client_machine = s;
         else
             g_free(s);
+
+        /* see if it has the PID set too (the PID requires that the
+           WM_CLIENT_MACHINE be set) */
+        if (PROP_GET32(self->window, net_wm_pid, cardinal, &pid))
+            self->pid = pid;
     }
 }
 
@@ -2414,7 +2471,15 @@ static ObStackingLayer calc_layer(ObClient *self)
               (self->decorations == 0 &&
                !(self->max_horz && self->max_vert) &&
                RECT_EQUAL(self->area, *monitor))) &&
-             (client_focused(self) || client_search_focus_tree(self)))
+             /* you are fullscreen while you or your children are focused.. */
+             (client_focused(self) || client_search_focus_tree(self) ||
+              /* you can be fullscreen if you're on another desktop */
+              (self->desktop != screen_desktop &&
+               self->desktop != DESKTOP_ALL) ||
+              /* and you can also be fullscreen if the focused client is on
+                 another monitor, or nothing else is focused */
+              (!focus_client ||
+               client_monitor(focus_client) != client_monitor(self))))
         l = OB_STACKING_LAYER_FULLSCREEN;
     else if (self->above) l = OB_STACKING_LAYER_ABOVE;
     else if (self->below) l = OB_STACKING_LAYER_BELOW;
@@ -2440,23 +2505,54 @@ static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
         stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
     }
 
+    /* we've been restacked */
+    self->visited = TRUE;
+
     for (it = self->transients; it; it = g_slist_next(it))
         client_calc_layer_recursive(it->data, orig,
                                     self->layer);
 }
 
+static void client_calc_layer_internal(ObClient *self)
+{
+    GSList *sit;
+
+    /* transients take on the layer of their parents */
+    sit = client_search_all_top_parents(self);
+
+    for (; sit; sit = g_slist_next(sit))
+        client_calc_layer_recursive(sit->data, self, 0);
+}
+
 void client_calc_layer(ObClient *self)
 {
-    ObClient *orig;
-    GSList *it;
+    GList *it;
 
-    orig = self;
+    /* skip over stuff above fullscreen layer */
+    for (it = stacking_list; it; it = g_list_next(it))
+        if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
 
-    /* transients take on the layer of their parents */
-    it = client_search_all_top_parents(self);
+    /* find the windows in the fullscreen layer, and mark them not-visited */
+    for (; it; it = g_list_next(it)) {
+        if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
+        else if (WINDOW_IS_CLIENT(it->data))
+            WINDOW_AS_CLIENT(it->data)->visited = FALSE;
+    }
+
+    client_calc_layer_internal(self);
 
-    for (; it; it = g_slist_next(it))
-        client_calc_layer_recursive(it->data, orig, 0);
+    /* skip over stuff above fullscreen layer */
+    for (it = stacking_list; it; it = g_list_next(it))
+        if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
+
+    /* now recalc any windows in the fullscreen layer which have not
+       had their layer recalced already */
+    for (; it; it = g_list_next(it)) {
+        if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
+        else if (WINDOW_IS_CLIENT(it->data) &&
+                 !WINDOW_AS_CLIENT(it->data)->visited)
+            client_calc_layer_internal(it->data);
+    }
 }
 
 gboolean client_should_show(ObClient *self)
@@ -2476,6 +2572,10 @@ gboolean client_show(ObClient *self)
     gboolean show = FALSE;
 
     if (client_should_show(self)) {
+        /* replay pending pointer event before showing the window, in case it
+           should be going to something under the window */
+        mouse_replay_pointer();
+
         frame_show(self->frame);
         show = TRUE;
 
@@ -2517,6 +2617,10 @@ gboolean client_hide(ObClient *self)
            so trying to ignore them is futile in case 3 anyways
         */
 
+        /* replay pending pointer event before hiding the window, in case it
+           should be going to the window */
+        mouse_replay_pointer();
+
         frame_hide(self->frame);
         hide = TRUE;
 
@@ -2874,6 +2978,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
 void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
                       gboolean user, gboolean final, gboolean force_reply)
 {
+    Rect oldframe;
     gint oldw, oldh;
     gboolean send_resize_client;
     gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
@@ -2896,6 +3001,7 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
 
     oldw = self->area.width;
     oldh = self->area.height;
+    oldframe = self->frame->area;
     RECT_SET(self->area, x, y, w, h);
 
     /* for app-requested resizes, always resize if 'resized' is true.
@@ -2930,6 +3036,10 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
         if (!user)
             ignore_start = event_start_ignore_all_enters();
 
+        /* replay pending pointer event before move the window, in case it
+           would change what window gets the event */
+        mouse_replay_pointer();
+
         frame_adjust_area(self->frame, fmoved, fresized, FALSE);
 
         if (!user)
@@ -3000,6 +3110,14 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
     }
 
     XFlush(ob_display);
+
+    /* if it moved between monitors, then this can affect the stacking
+       layer of this window or others - for fullscreen windows */
+    if (screen_find_monitor(&self->frame->area) !=
+        screen_find_monitor(&oldframe))
+    {
+        client_calc_layer(self);
+    }
 }
 
 void client_fullscreen(ObClient *self, gboolean fs)
@@ -3206,41 +3324,58 @@ void client_shade(ObClient *self, gboolean shade)
     frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
 }
 
-void client_close(ObClient *self)
+static void client_ping_event(ObClient *self, gboolean dead)
 {
-    XEvent ce;
+    self->not_responding = dead;
+    client_update_title(self);
+
+    if (!dead) {
+        /* try kill it nicely the first time again, if it started responding
+           at some point */
+        self->close_tried_term = FALSE;
+    }
+}
 
+void client_close(ObClient *self)
+{
     if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
 
     /* in the case that the client provides no means to requesting that it
        close, we just kill it */
     if (!self->delete_window)
+        /* don't use client_kill(), we should only kill based on PID in
+           response to a lack of PING replies */
+        XKillClient(ob_display, self->window);
+    else if (self->not_responding)
         client_kill(self);
-
-    /*
-      XXX: itd be cool to do timeouts and shit here for killing the client's
-      process off
-      like... if the window is around after 5 seconds, then the close button
-      turns a nice red, and if this function is called again, the client is
-      explicitly killed.
-    */
-
-    ce.xclient.type = ClientMessage;
-    ce.xclient.message_type =  prop_atoms.wm_protocols;
-    ce.xclient.display = ob_display;
-    ce.xclient.window = self->window;
-    ce.xclient.format = 32;
-    ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
-    ce.xclient.data.l[1] = event_curtime;
-    ce.xclient.data.l[2] = 0l;
-    ce.xclient.data.l[3] = 0l;
-    ce.xclient.data.l[4] = 0l;
-    XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
+    else
+        /* request the client to close with WM_DELETE_WINDOW */
+        PROP_MSG_TO(self->window, self->window, wm_protocols,
+                    prop_atoms.wm_delete_window, event_curtime, 0, 0, 0,
+                    NoEventMask);
 }
 
 void client_kill(ObClient *self)
 {
-    XKillClient(ob_display, self->window);
+    if (!self->client_machine && self->pid) {
+        /* running on the local host */
+        if (!self->close_tried_term) {
+            ob_debug("killing window 0x%x with pid %lu, with SIGTERM\n",
+                     self->window, self->pid);
+            kill(self->pid, SIGTERM);
+            self->close_tried_term = TRUE;
+
+            /* show that we're trying to kill it */
+            client_update_title(self);
+        }
+        else {
+            ob_debug("killing window 0x%x with pid %lu, with SIGKILL\n",
+                     self->window, self->pid);
+            kill(self->pid, SIGKILL); /* kill -9 */
+        }
+    }
+    else
+        XKillClient(ob_display, self->window);
 }
 
 void client_hilite(ObClient *self, gboolean hilite)
@@ -3259,10 +3394,10 @@ void client_hilite(ObClient *self, gboolean hilite)
     }
 }
 
-void client_set_desktop_recursive(ObClient *self,
-                                  guint target,
-                                  gboolean donthide,
-                                  gboolean dontraise)
+static void client_set_desktop_recursive(ObClient *self,
+                                         guint target,
+                                         gboolean donthide,
+                                         gboolean dontraise)
 {
     guint old;
     GSList *it;
@@ -3651,34 +3786,7 @@ static void client_present(ObClient *self, gboolean here, gboolean raise,
 void client_activate(ObClient *self, gboolean here, gboolean raise,
                      gboolean unshade, gboolean user)
 {
-    guint32 last_time = focus_client ? focus_client->user_time : CurrentTime;
-    gboolean allow = FALSE;
-
-    /* if the currently focused app doesn't set a user_time, then it can't
-       benefit from any focus stealing prevention.
-
-       if the timestamp is missing in the request then let it go through
-       even if it is source=app, because EVERY APPLICATION DOES THIS because
-       GTK IS VERY BUGGY AND HARDCODES source=application... WHY!?
-    */
-    if (!last_time || !event_curtime)
-        allow = TRUE;
-    /* otherwise, if they didn't give a time stamp or if it is too old, they
-       don't get focus */
-    else
-        allow = event_time_after(event_curtime, last_time);
-
-    ob_debug_type(OB_DEBUG_FOCUS,
-                  "Want to activate window 0x%x with time %u (last time %u), "
-                  "source=%s allowing? %d\n",
-                  self->window, event_curtime, last_time,
-                  (user ? "user" : "application"), allow);
-
-    if (allow)
-        client_present(self, here, raise, unshade);
-    else
-        /* don't focus it but tell the user it wants attention */
-        client_hilite(self, TRUE);
+    client_present(self, here, raise, unshade);
 }
 
 static void client_bring_windows_recursive(ObClient *self,
@@ -3888,7 +3996,7 @@ static void detect_edge(Rect area, ObDirection dir,
     gint edge_start, edge_size, head, tail;
     gboolean skip_head = FALSE, skip_tail = FALSE;
 
-    switch(dir) {
+    switch (dir) {
         case OB_DIRECTION_NORTH:
         case OB_DIRECTION_SOUTH:
             edge_start = area.x;
@@ -3908,7 +4016,7 @@ static void detect_edge(Rect area, ObDirection dir,
                 edge_start, edge_size))
         return;
 
-    switch(dir) {
+    switch (dir) {
         case OB_DIRECTION_NORTH:
             head = RECT_BOTTOM(area);
             tail = RECT_TOP(area);
@@ -3917,38 +4025,54 @@ static void detect_edge(Rect area, ObDirection dir,
             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;
+        case OB_DIRECTION_EAST:
+            head = RECT_LEFT(area);
+            tail = RECT_RIGHT(area);
+            break;
         default:
             g_assert_not_reached();
     }
-    switch(dir) {
+    switch (dir) {
         case OB_DIRECTION_NORTH:
         case OB_DIRECTION_WEST:
+            /* check if our window is past the head of this window */
             if (my_head <= head + 1)
                 skip_head = TRUE;
+            /* check if our window's tail is past the tail of this window */
             if (my_head + my_size - 1 <= tail)
                 skip_tail = TRUE;
-            if (head < *dest)
+            /* check if the head of this window is closer than the previously
+               chosen edge (take into account that the previously chosen
+               edge might have been a tail, not a head) */
+            if (head + (*near_edge ? 0 : my_size) < *dest)
                 skip_head = TRUE;
-            if (tail - my_size < *dest)
+            /* check if the tail of this window is closer than the previously
+               chosen edge (take into account that the previously chosen
+               edge might have been a head, not a tail) */
+            if (tail - (!*near_edge ? 0 : my_size) < *dest)
                 skip_tail = TRUE;
             break;
         case OB_DIRECTION_SOUTH:
         case OB_DIRECTION_EAST:
+            /* check if our window is past the head of this window */
             if (my_head >= head - 1)
                 skip_head = TRUE;
+            /* check if our window's tail is past the tail of this window */
             if (my_head - my_size + 1 >= tail)
                 skip_tail = TRUE;
-            if (head > *dest)
+            /* check if the head of this window is closer than the previously
+               chosen edge (take into account that the previously chosen
+               edge might have been a tail, not a head) */
+            if (head - (*near_edge ? 0 : my_size) > *dest)
                 skip_head = TRUE;
-            if (tail + my_size > *dest)
+            /* check if the tail of this window is closer than the previously
+               chosen edge (take into account that the previously chosen
+               edge might have been a head, not a tail) */
+            if (tail + (!*near_edge ? 0 : my_size) > *dest)
                 skip_tail = TRUE;
             break;
         default:
@@ -3967,7 +4091,6 @@ static void detect_edge(Rect area, ObDirection dir,
         *dest = tail;
         *near_edge = FALSE;
     }
-
 }
 
 void client_find_edge_directional(ObClient *self, ObDirection dir,
@@ -3985,7 +4108,7 @@ void client_find_edge_directional(ObClient *self, ObDirection dir,
     mon = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR,
                       &self->frame->area);
 
-    switch(dir) {
+    switch (dir) {
     case OB_DIRECTION_NORTH:
         if (my_head >= RECT_TOP(*mon) + 1)
             edge = RECT_TOP(*mon) - 1;
@@ -4017,7 +4140,7 @@ void client_find_edge_directional(ObClient *self, ObDirection dir,
     *dest = edge;
     *near_edge = TRUE;
 
-    for(it = client_list; it; it = g_list_next(it)) {
+    for (it = client_list; it; it = g_list_next(it)) {
         ObClient *cur = it->data;
 
         /* skip windows to not bump into */
@@ -4037,6 +4160,8 @@ void client_find_edge_directional(ObClient *self, ObDirection dir,
     dock_get_area(&dock_area);
     detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
                 my_edge_size, dest, near_edge);
+    g_free(a);
+    g_free(mon);
 }
 
 void client_find_move_directional(ObClient *self, ObDirection dir,
@@ -4187,7 +4312,7 @@ void client_find_resize_directional(ObClient *self, ObDirection side,
     *h -= self->frame->size.top + self->frame->size.bottom;
 }
 
-ObClient* client_under_pointer()
+ObClient* client_under_pointer(void)
 {
     gint x, y;
     GList *it;