Let ObPrompts be modal dialogs, and when they are transient for a window, make it...
[mikachu/openbox.git] / openbox / client.c
index 22eb840..32daf66 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"
@@ -31,6 +32,7 @@
 #include "session.h"
 #include "event.h"
 #include "grab.h"
+#include "prompt.h"
 #include "focus.h"
 #include "stacking.h"
 #include "openbox.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>
 
@@ -61,9 +68,10 @@ typedef struct
     gpointer data;
 } ClientCallback;
 
-GList            *client_list          = NULL;
+GList            *client_list           = NULL;
 
-static GSList *client_destroy_notifies = NULL;
+static GSList  *client_destroy_notifies = NULL;
+static RrImage *client_default_icon     = NULL;
 
 static void client_get_all(ObClient *self, gboolean real);
 static void client_get_startup_id(ObClient *self);
@@ -74,6 +82,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);
@@ -93,10 +105,25 @@ 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);
+static void client_prompt_kill(ObClient *self);
 
 
 void client_startup(gboolean reconfig)
 {
+    if ((client_default_icon = RrImageCacheFind(ob_rr_icons,
+                                                ob_rr_theme->def_win_icon,
+                                                ob_rr_theme->def_win_icon_w,
+                                                ob_rr_theme->def_win_icon_h)))
+        RrImageRef(client_default_icon);
+    else {
+        client_default_icon = RrImageNew(ob_rr_icons);
+        RrImageAddPicture(client_default_icon,
+                          ob_rr_theme->def_win_icon,
+                          ob_rr_theme->def_win_icon_w,
+                          ob_rr_theme->def_win_icon_h);
+    }
+
     if (reconfig) return;
 
     client_set_list();
@@ -104,6 +131,9 @@ void client_startup(gboolean reconfig)
 
 void client_shutdown(gboolean reconfig)
 {
+    RrImageUnref(client_default_icon);
+    client_default_icon = NULL;
+
     if (reconfig) return;
 }
 
@@ -190,6 +220,9 @@ void client_manage_all(void)
         }
     }
 
+    /* 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;
@@ -197,13 +230,13 @@ void client_manage_all(void)
             if (attrib.override_redirect) continue;
 
             if (attrib.map_state != IsUnmapped)
-                client_manage(children[i]);
+                client_manage(children[i], NULL);
         }
     }
     XFree(children);
 }
 
-void client_manage(Window window)
+void client_manage(Window window, ObPrompt *prompt)
 {
     ObClient *self;
     XEvent e;
@@ -255,8 +288,10 @@ void client_manage(Window window)
 
     map_time = event_get_server_time();
 
-    /* choose the events we want to receive on the CLIENT window */
-    attrib_set.event_mask = CLIENT_EVENTMASK;
+    /* choose the events we want to receive on the CLIENT window
+       (ObPrompt windows can request events too) */
+    attrib_set.event_mask = CLIENT_EVENTMASK |
+        (prompt ? prompt->event_mask : 0);
     attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
     XChangeWindowAttributes(ob_display, window,
                             CWEventMask|CWDontPropagate, &attrib_set);
@@ -266,6 +301,7 @@ void client_manage(Window window)
     self = g_new0(ObClient, 1);
     self->obwin.type = Window_Client;
     self->window = window;
+    self->prompt = prompt;
 
     /* non-zero defaults */
     self->wmstate = WithdrawnState; /* make sure it gets updated first time */
@@ -278,9 +314,16 @@ 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);
+       should be reparented back to root automatically, unless we are managing
+       an internal ObPrompt window  */
+    if (!self->prompt)
+        XChangeSaveSet(ob_display, window, SetModeInsert);
 
     /* create the decoration frame for the client window */
     self->frame = frame_new(self);
@@ -298,11 +341,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);
-
     /* tell startup notification that this app started */
-    launch_time = sn_app_started(self->startup_id, self->class);
+    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. */
@@ -647,7 +687,6 @@ void client_unmanage_all(void)
 
 void client_unmanage(ObClient *self)
 {
-    guint j;
     GSList *it;
     gulong ignore_start;
 
@@ -674,8 +713,10 @@ void client_unmanage(ObClient *self)
 
     mouse_grab_for_client(self, FALSE);
 
-    /* remove the window from our save set */
-    XChangeSaveSet(ob_display, self->window, SetModeDelete);
+    /* remove the window from our save set, unless we are managing an internal
+       ObPrompt window */
+    if (!self->prompt)
+        XChangeSaveSet(ob_display, self->window, SetModeDelete);
 
     /* update the focus lists */
     focus_order_remove(self);
@@ -684,6 +725,10 @@ void client_unmanage(ObClient *self)
         focus_client = NULL;
     }
 
+    /* if we're prompting to kill the client, close that */
+    prompt_unref(self->kill_prompt);
+    self->kill_prompt = NULL;
+
     client_list = g_list_remove(client_list, self);
     stacking_remove(self);
     g_hash_table_remove(window_map, &self->window);
@@ -762,20 +807,24 @@ 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();
 
     ob_debug("Unmanaged window 0x%lx\n", self->window);
 
     /* free all data allocated in the client struct */
+    RrImageUnref(self->icon_set);
     g_slist_free(self->transients);
-    for (j = 0; j < self->nicons; ++j)
-        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);
+    g_free(self->original_title);
     g_free(self->name);
     g_free(self->class);
     g_free(self->role);
@@ -813,13 +862,15 @@ static ObAppSettings *client_get_settings_state(ObClient *self)
             !g_pattern_match(app->name, strlen(self->name), self->name, NULL))
             match = FALSE;
         else if (app->class &&
-                !g_pattern_match(app->class,
-                                 strlen(self->class), self->class, NULL))
+                 !g_pattern_match(app->class,
+                                  strlen(self->class), self->class, NULL))
             match = FALSE;
         else if (app->role &&
                  !g_pattern_match(app->role,
                                   strlen(self->role), self->role, NULL))
             match = FALSE;
+        else if ((signed)app->type >= 0 && app->type != self->type)
+            match = FALSE;
 
         if (match) {
             ob_debug("Window matching: %s\n", app->name);
@@ -962,6 +1013,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);
@@ -1011,18 +1063,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.
@@ -1502,6 +1562,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
@@ -1526,7 +1590,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;
 
@@ -1801,16 +1865,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;
@@ -1915,6 +1979,7 @@ void client_update_title(ObClient *self)
     gchar *visible = NULL;
 
     g_free(self->title);
+    g_free(self->original_title);
 
     /* try netwm */
     if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) {
@@ -1931,6 +1996,7 @@ void client_update_title(ObClient *self)
                 data = g_strdup("Unnamed Window");
         }
     }
+    self->original_title = g_strdup(data);
 
     if (self->client_machine) {
         visible = g_strdup_printf("%s (%s)", data, self->client_machine);
@@ -1938,6 +2004,15 @@ void client_update_title(ObClient *self)
     } else
         visible = data;
 
+    if (self->not_responding) {
+        data = visible;
+        if (self->kill_level > 0)
+            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;
 
@@ -1961,6 +2036,15 @@ void client_update_title(ObClient *self)
     } else
         visible = data;
 
+    if (self->not_responding) {
+        data = visible;
+        if (self->kill_level > 0)
+            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;
 }
@@ -2024,101 +2108,129 @@ void client_update_icons(ObClient *self)
     guint num;
     guint32 *data;
     guint w, h, i, j;
+    guint num_seen;  /* number of icons present */
+    RrImage *img;
+
+    img = NULL;
 
-    for (i = 0; i < self->nicons; ++i)
-        g_free(self->icons[i].data);
-    if (self->nicons > 0)
-        g_free(self->icons);
-    self->nicons = 0;
+    /* grab the server, because we might be setting the window's icon and
+       we don't want them to set it in between and we overwrite their own
+       icon */
+    grab_server(TRUE);
 
     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) {
+        num_seen = 0;
+        while (i + 2 < num) { /* +2 is to make sure there is a w and h */
             w = data[i++];
             h = data[i++];
-            i += w * h;
-            if (i > num || w*h == 0) break;
-            ++self->nicons;
+            /* watch for the data being too small for the specified size,
+               or for zero sized icons. */
+            if (i + w*h > num || w == 0 || h == 0) break;
+
+            /* convert it to the right bit order for ObRender */
+            for (j = 0; j < w*h; ++j)
+                data[i+j] =
+                    (((data[i+j] >> 24) & 0xff) << RrDefaultAlphaOffset) +
+                    (((data[i+j] >> 16) & 0xff) << RrDefaultRedOffset)   +
+                    (((data[i+j] >>  8) & 0xff) << RrDefaultGreenOffset) +
+                    (((data[i+j] >>  0) & 0xff) << RrDefaultBlueOffset);
+
+            /* is it in the cache? */
+            img = RrImageCacheFind(ob_rr_icons, &data[i], w, h);
+            if (img) RrImageRef(img); /* own it */
+
+            i += w*h;
+            ++num_seen;
+
+            /* don't bother looping anymore if we already found it in the cache
+               since we'll just use that! */
+            if (img) break;
         }
 
-        self->icons = g_new(ObClientIcon, self->nicons);
-
-        /* store the icons */
-        i = 0;
-        for (j = 0; j < self->nicons; ++j) {
-            guint x, y, t;
-
-            w = self->icons[j].width = data[i++];
-            h = self->icons[j].height = data[i++];
-
-            if (w*h == 0) continue;
-
-            self->icons[j].data = g_new(RrPixel32, w * h);
-            for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
-                if (x >= w) {
-                    x = 0;
-                    ++y;
-                }
-                self->icons[j].data[t] =
-                    (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
-                    (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
-                    (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
-                    (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
+        /* if it's not in the cache yet, then add it to the cache now.
+           we have already converted it to the correct bit order above */
+        if (!img && num_seen > 0) {
+            img = RrImageNew(ob_rr_icons);
+            i = 0;
+            for (j = 0; j < num_seen; ++j) {
+                w = data[i++];
+                h = data[i++];
+                RrImageAddPicture(img, &data[i], w, h);
+                i += w*h;
             }
-            g_assert(i <= num);
         }
 
         g_free(data);
-    } else {
+    }
+
+    /* if we didn't find an image from the NET_WM_ICON stuff, then try the
+       legacy X hints */
+    if (!img) {
         XWMHints *hints;
 
         if ((hints = XGetWMHints(ob_display, self->window))) {
             if (hints->flags & IconPixmapHint) {
-                self->nicons = 1;
-                self->icons = g_new(ObClientIcon, self->nicons);
+                gboolean xicon;
                 xerror_set_ignore(TRUE);
-                if (!RrPixmapToRGBA(ob_rr_inst,
-                                    hints->icon_pixmap,
-                                    (hints->flags & IconMaskHint ?
-                                     hints->icon_mask : None),
-                                    &self->icons[0].width,
-                                    &self->icons[0].height,
-                                    &self->icons[0].data))
-                {
-                    g_free(self->icons);
-                    self->nicons = 0;
-                }
+                xicon = RrPixmapToRGBA(ob_rr_inst,
+                                       hints->icon_pixmap,
+                                       (hints->flags & IconMaskHint ?
+                                        hints->icon_mask : None),
+                                       (gint*)&w, (gint*)&h, &data);
                 xerror_set_ignore(FALSE);
+
+
+                if (xicon) {
+                    if (w > 0 && h > 0) {
+                        /* is this icon in the cache yet? */
+                        img = RrImageCacheFind(ob_rr_icons, data, w, h);
+                        if (img) RrImageRef(img); /* own it */
+
+                        /* if not, then add it */
+                        if (!img) {
+                            img = RrImageNew(ob_rr_icons);
+                            RrImageAddPicture(img, data, w, h);
+                        }
+                    }
+
+                    g_free(data);
+                }
             }
             XFree(hints);
         }
     }
 
-    /* set the default icon onto the window
-       in theory, this could be a race, but if a window doesn't set an icon
-       or removes it entirely, it's not very likely it is going to set one
-       right away afterwards
+    /* set the client's icons to be whatever we found */
+    RrImageUnref(self->icon_set);
+    self->icon_set = img;
 
-       if it has parents, then one of them will have an icon already
+    /* if the client has no icon at all, then we set a default icon onto it.
+       but, if it has parents, then one of them will have an icon already
     */
-    if (self->nicons == 0 && !self->parents) {
+    if (!self->icon_set && !self->parents) {
         RrPixel32 *icon = ob_rr_theme->def_win_icon;
-        gulong *data;
-
-        data = g_new(gulong, 48*48+2);
-        data[0] = data[1] =  48;
-        for (i = 0; i < 48*48; ++i)
-            data[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
+        gulong *ldata; /* use a long here to satisfy OBT_PROP_SETA32 */
+
+        w = ob_rr_theme->def_win_icon_w;
+        h = ob_rr_theme->def_win_icon_h;
+        ldata = g_new(gulong, w*h+2);
+        ldata[0] = w;
+        ldata[1] = h;
+        for (i = 0; i < w*h; ++i)
+            ldata[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
                 (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
                 (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
                 (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
-        PROP_SETA32(self->window, net_wm_icon, cardinal, data, 48*48+2);
-        g_free(data);
+        PROP_SETA32(self->window, net_wm_icon, cardinal, ldata, w*h+2);
+        g_free(ldata);
     } else if (self->frame)
         /* don't draw the icon empty if we're just setting one now anyways,
            we'll get the property change any second */
         frame_adjust_icon(self->frame);
+
+    grab_server(FALSE);
 }
 
 void client_update_icon_geometry(ObClient *self)
@@ -2128,12 +2240,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);
     }
 }
 
@@ -2221,6 +2334,7 @@ static void client_get_session_ids(ObClient *self)
 
     if (got) {
         gchar localhost[128];
+        guint32 pid;
 
         gethostname(localhost, 127);
         localhost[127] = '\0';
@@ -2228,6 +2342,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;
     }
 }
 
@@ -2365,7 +2484,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;
@@ -2391,23 +2518,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)
@@ -2427,6 +2585,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;
 
@@ -2468,6 +2630,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;
 
@@ -2825,6 +2991,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;
@@ -2847,6 +3014,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.
@@ -2881,6 +3049,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)
@@ -2951,6 +3123,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)
@@ -3157,41 +3337,140 @@ 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;
+    if (self->not_responding != dead) {
+        self->not_responding = dead;
+        client_update_title(self);
 
+        if (dead)
+            /* the client isn't responding, so ask to kill it */
+            client_prompt_kill(self);
+        else {
+            /* it came back to life ! */
+
+            if (self->kill_prompt) {
+                prompt_unref(self->kill_prompt);
+                self->kill_prompt = NULL;
+            }
+
+            self->kill_level = 0;
+        }
+    }
+}
+
+void client_close(ObClient *self)
+{
     if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
 
+    /* if closing an internal obprompt, that is just cancelling it */
+    if (self->prompt) {
+        prompt_cancel(self->prompt);
+        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 {
+        /* 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);
+
+        /* we're trying to close the window, so see if it is responding. if it
+           is not, then we will let them kill the window */
+        if (self->ping)
+            ping_start(self, client_ping_event);
+
+        /* if we already know the window isn't responding (maybe they clicked
+           no in the kill dialog but it hasn't come back to life), then show
+           the kill dialog */
+        if (self->not_responding)
+            client_prompt_kill(self);
+    }
+}
+
+#define OB_KILL_RESULT_NO 0
+#define OB_KILL_RESULT_YES 1
+
+static void client_kill_requested(ObPrompt *p, gint result, gpointer data)
+{
+    ObClient *self = data;
+
+    if (result == OB_KILL_RESULT_YES)
         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.
-    */
+    prompt_unref(self->kill_prompt);
+    self->kill_prompt = NULL;
+}
+
+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 }
+        };
+        gchar *m;
+
+        if (client_on_localhost(self)) {
+            const gchar *sig;
+
+            if (self->kill_level == 0)
+                sig = "terminate";
+            else
+                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);
+        }
+        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);
+
 
-    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);
+        self->kill_prompt = prompt_new(m, answers,
+                                       sizeof(answers)/sizeof(answers[0]),
+                                       OB_KILL_RESULT_NO, /* default = no */
+                                       OB_KILL_RESULT_NO, /* cancel = no */
+                                       client_kill_requested, self);
+        g_free(m);
+    }
+
+    prompt_show(self->kill_prompt, self, TRUE);
 }
 
 void client_kill(ObClient *self)
 {
-    XKillClient(ob_display, self->window);
+    /* don't kill our own windows */
+    if (self->prompt) return;
+
+    if (client_on_localhost(self) && self->pid) {
+        /* running on the local host */
+        if (self->kill_level == 0) {
+            ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
+                     self->window, self->pid);
+            kill(self->pid, SIGTERM);
+            ++self->kill_level;
+
+            /* 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 {
+        /* running on a remote host */
+        XKillClient(ob_display, self->window);
+    }
 }
 
 void client_hilite(ObClient *self, gboolean hilite)
@@ -3210,10 +3489,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;
@@ -3644,53 +3923,21 @@ gboolean client_focused(ObClient *self)
     return self == focus_client;
 }
 
-static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
-{
-    guint i;
-    gulong min_diff, min_i;
 
-    if (!self->nicons) {
-        ObClientIcon *parent = NULL;
-        GSList *it;
 
-        for (it = self->parents; it; it = g_slist_next(it)) {
-            ObClient *c = it->data;
-            if ((parent = client_icon_recursive(c, w, h)))
-                break;
-        }
-
-        return parent;
-    }
-
-    /* some kind of crappy approximation to find the icon closest in size to
-       what we requested, but icons are generally all the same ratio as
-       eachother so it's good enough. */
-
-    min_diff = ABS(self->icons[0].width - w) + ABS(self->icons[0].height - h);
-    min_i = 0;
-
-    for (i = 1; i < self->nicons; ++i) {
-        gulong diff;
-
-        diff = ABS(self->icons[i].width - w) + ABS(self->icons[i].height - h);
-        if (diff < min_diff) {
-            min_diff = diff;
-            min_i = i;
-        }
-    }
-    return &self->icons[min_i];
-}
-
-const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
+RrImage* client_icon(ObClient *self)
 {
-    ObClientIcon *ret;
-    static ObClientIcon deficon;
+    RrImage *ret = NULL;
 
-    if (!(ret = client_icon_recursive(self, w, h))) {
-        deficon.width = deficon.height = 48;
-        deficon.data = ob_rr_theme->def_win_icon;
-        ret = &deficon;
+    if (self->icon_set)
+        ret = self->icon_set;
+    else if (self->parents) {
+        GSList *it;
+        for (it = self->parents; it && !ret; it = g_slist_next(it))
+            ret = client_icon(it->data);
     }
+    if (!ret)
+        ret = client_default_icon;
     return ret;
 }
 
@@ -4161,3 +4408,9 @@ gboolean client_has_group_siblings(ObClient *self)
 {
     return self->group && self->group->members->next;
 }
+
+/*! Returns TRUE if the client is running on the same machine as Openbox */
+gboolean client_on_localhost(ObClient *self)
+{
+    return self->client_machine == NULL;
+}