move the code for tracking what extensions are available on the display into the...
[dana/openbox.git] / openbox / screen.c
index 9b6a977..0d89163 100644 (file)
 #include "debug.h"
 #include "openbox.h"
 #include "dock.h"
-#include "xerror.h"
 #include "prop.h"
 #include "grab.h"
 #include "startupnotify.h"
 #include "moveresize.h"
 #include "config.h"
+#include "mainloop.h"
 #include "screen.h"
 #include "client.h"
 #include "session.h"
@@ -33,9 +33,9 @@
 #include "event.h"
 #include "focus.h"
 #include "popup.h"
-#include "extensions.h"
 #include "render/render.h"
 #include "gettext.h"
+#include "obt/display.h"
 
 #include <X11/Xlib.h>
 #ifdef HAVE_UNISTD_H
 #define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \
                         EnterWindowMask | LeaveWindowMask | \
                         SubstructureRedirectMask | FocusChangeMask | \
-                        ButtonPressMask | ButtonReleaseMask | ButtonMotionMask)
+                        ButtonPressMask | ButtonReleaseMask)
+
+static gboolean screen_validate_layout(ObDesktopLayout *l);
+static gboolean replace_wm(void);
+static void     screen_tell_ksplash(void);
+static void     screen_fallback_focus(void);
 
 guint    screen_num_desktops;
 guint    screen_num_monitors;
 guint    screen_desktop;
-guint    screen_last_desktop;
-Size     screen_physical_size;
+guint    screen_last_desktop = 1;
 gboolean screen_showing_desktop;
-DesktopLayout screen_desktop_layout;
+ObDesktopLayout screen_desktop_layout;
 gchar  **screen_desktop_names;
 Window   screen_support_win;
 Time     screen_desktop_user_time = CurrentTime;
 
-static Rect  **area; /* array of desktop holding array of xinerama areas */
-static Rect  *monitor_area;
+static Size     screen_physical_size;
+static guint    screen_old_desktop;
+static gboolean screen_desktop_timeout = TRUE;
+/*! An array of desktops, holding array of areas per monitor */
+static Rect  *monitor_area = NULL;
+/*! An array of desktops, holding an array of struts */
+static GSList *struts_top = NULL;
+static GSList *struts_left = NULL;
+static GSList *struts_right = NULL;
+static GSList *struts_bottom = NULL;
+
+static ObPagerPopup *desktop_popup;
 
-static ObPagerPopup *desktop_cycle_popup;
+/*! The number of microseconds that you need to be on a desktop before it will
+  replace the remembered "last desktop" */
+#define REMEMBER_LAST_DESKTOP_TIME 750000
 
-static gboolean replace_wm()
+static gboolean replace_wm(void)
 {
     gchar *wm_sn;
     Atom wm_sn_atom;
@@ -86,34 +102,18 @@ static gboolean replace_wm()
                       ob_screen);
             return FALSE;
         }
-        xerror_set_ignore(TRUE);
-        xerror_occured = FALSE;
+        obt_display_ignore_errors(ob_display, TRUE);
 
         /* We want to find out when the current selection owner dies */
         XSelectInput(ob_display, current_wm_sn_owner, StructureNotifyMask);
         XSync(ob_display, FALSE);
 
-        xerror_set_ignore(FALSE);
-        if (xerror_occured)
+        obt_display_ignore_errors(ob_display, FALSE);
+        if (obt_display_error_occured)
             current_wm_sn_owner = None;
     }
 
-    {
-        /* Generate a timestamp */
-        XEvent event;
-
-        XSelectInput(ob_display, screen_support_win, PropertyChangeMask);
-
-        XChangeProperty(ob_display, screen_support_win,
-                        prop_atoms.wm_class, prop_atoms.string,
-                        8, PropModeAppend, NULL, 0);
-        XWindowEvent(ob_display, screen_support_win,
-                     PropertyChangeMask, &event);
-
-        XSelectInput(ob_display, screen_support_win, NoEventMask);
-
-        timestamp = event.xproperty.time;
-    }
+    timestamp = event_get_server_time();
 
     XSetSelectionOwner(ob_display, wm_sn_atom, screen_support_win,
                        timestamp);
@@ -153,7 +153,7 @@ static gboolean replace_wm()
     return TRUE;
 }
 
-gboolean screen_annex(const gchar *program_name)
+gboolean screen_annex(void)
 {
     XSetWindowAttributes attrib;
     pid_t pid;
@@ -163,12 +163,14 @@ gboolean screen_annex(const gchar *program_name)
 
     /* create the netwm support window */
     attrib.override_redirect = TRUE;
+    attrib.event_mask = PropertyChangeMask;
     screen_support_win = XCreateWindow(ob_display,
                                        RootWindow(ob_display, ob_screen),
                                        -100, -100, 1, 1, 0,
                                        CopyFromParent, InputOutput,
                                        CopyFromParent,
-                                       CWOverrideRedirect, &attrib);
+                                       CWEventMask | CWOverrideRedirect,
+                                       &attrib);
     XMapWindow(ob_display, screen_support_win);
     XLowerWindow(ob_display, screen_support_win);
 
@@ -177,12 +179,11 @@ gboolean screen_annex(const gchar *program_name)
         return FALSE;
     }
 
-    xerror_set_ignore(TRUE);
-    xerror_occured = FALSE;
+    obt_display_ignore_errors(ob_display, TRUE);
     XSelectInput(ob_display, RootWindow(ob_display, ob_screen),
                  ROOT_EVENTMASK);
-    xerror_set_ignore(FALSE);
-    if (xerror_occured) {
+    obt_display_ignore_errors(ob_display, FALSE);
+    if (obt_display_error_occured) {
         g_message(_("A window manager is already running on screen %d"),
                   ob_screen);
 
@@ -190,7 +191,6 @@ gboolean screen_annex(const gchar *program_name)
         return FALSE;
     }
 
-
     screen_set_root_cursor();
 
     /* set the OPENBOX_PID hint */
@@ -203,7 +203,7 @@ gboolean screen_annex(const gchar *program_name)
                net_supporting_wm_check, window, screen_support_win);
 
     /* set properties on the supporting window */
-    PROP_SETS(screen_support_win, net_wm_name, program_name);
+    PROP_SETS(screen_support_win, net_wm_name, "Openbox");
     PROP_SET32(screen_support_win, net_supporting_wm_check,
                window, screen_support_win);
 
@@ -258,6 +258,8 @@ gboolean screen_annex(const gchar *program_name)
     supported[i++] = prop_atoms.net_wm_action_fullscreen;
     supported[i++] = prop_atoms.net_wm_action_change_desktop;
     supported[i++] = prop_atoms.net_wm_action_close;
+    supported[i++] = prop_atoms.net_wm_action_above;
+    supported[i++] = prop_atoms.net_wm_action_below;
     supported[i++] = prop_atoms.net_wm_state;
     supported[i++] = prop_atoms.net_wm_state_modal;
     supported[i++] = prop_atoms.net_wm_state_maximized_vert;
@@ -273,7 +275,9 @@ gboolean screen_annex(const gchar *program_name)
     supported[i++] = prop_atoms.net_moveresize_window;
     supported[i++] = prop_atoms.net_wm_moveresize;
     supported[i++] = prop_atoms.net_wm_user_time;
+/*
     supported[i++] = prop_atoms.net_wm_user_time_window;
+*/
     supported[i++] = prop_atoms.net_frame_extents;
     supported[i++] = prop_atoms.net_request_frame_extents;
     supported[i++] = prop_atoms.net_restack_window;
@@ -282,56 +286,127 @@ gboolean screen_annex(const gchar *program_name)
     supported[i++] = prop_atoms.net_wm_sync_request;
     supported[i++] = prop_atoms.net_wm_sync_request_counter;
 #endif
+    supported[i++] = prop_atoms.net_wm_pid;
+    supported[i++] = prop_atoms.net_wm_ping;
 
     supported[i++] = prop_atoms.kde_wm_change_state;
     supported[i++] = prop_atoms.kde_net_wm_frame_strut;
     supported[i++] = prop_atoms.kde_net_wm_window_type_override;
 
-    supported[i++] = prop_atoms.openbox_wm_state_undecorated;
+    supported[i++] = prop_atoms.ob_wm_action_undecorate;
+    supported[i++] = prop_atoms.ob_wm_state_undecorated;
     supported[i++] = prop_atoms.openbox_pid;
-    supported[i++] = prop_atoms.openbox_config;
-    supported[i++] = prop_atoms.openbox_control;
+    supported[i++] = prop_atoms.ob_theme;
+    supported[i++] = prop_atoms.ob_control;
     g_assert(i == num_support);
 
     PROP_SETA32(RootWindow(ob_display, ob_screen),
                 net_supported, atom, supported, num_support);
     g_free(supported);
 
+    screen_tell_ksplash();
+
     return TRUE;
 }
 
+static void screen_tell_ksplash(void)
+{
+    XEvent e;
+    char **argv;
+
+    argv = g_new(gchar*, 6);
+    argv[0] = g_strdup("dcop");
+    argv[1] = g_strdup("ksplash");
+    argv[2] = g_strdup("ksplash");
+    argv[3] = g_strdup("upAndRunning(QString)");
+    argv[4] = g_strdup("wm started");
+    argv[5] = NULL;
+
+    /* tell ksplash through the dcop server command line interface */
+    g_spawn_async(NULL, argv, NULL,
+                  G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD |
+                  G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL,
+                  NULL, NULL, NULL, NULL);
+    g_strfreev(argv);
+
+    /* i'm not sure why we do this, kwin does it, but ksplash doesn't seem to
+       hear it anyways. perhaps it is for old ksplash. or new ksplash. or
+       something. oh well. */
+    e.xclient.type = ClientMessage;
+    e.xclient.display = ob_display;
+    e.xclient.window = RootWindow(ob_display, ob_screen);
+    e.xclient.message_type =
+        XInternAtom(ob_display, "_KDE_SPLASH_PROGRESS", False );
+    e.xclient.format = 8;
+    strcpy(e.xclient.data.b, "wm started");
+    XSendEvent(ob_display, RootWindow(ob_display, ob_screen),
+               False, SubstructureNotifyMask, &e );
+}
+
 void screen_startup(gboolean reconfig)
 {
-    guint i, numnames;
-    gchar **names;
-    GSList *it;
+    gchar **names = NULL;
     guint32 d;
+    gboolean namesexist = FALSE;
 
-    desktop_cycle_popup = pager_popup_new(FALSE);
-    pager_popup_height(desktop_cycle_popup, POPUP_HEIGHT);
+    desktop_popup = pager_popup_new();
+    pager_popup_height(desktop_popup, POPUP_HEIGHT);
 
-    if (reconfig)
+    if (reconfig) {
+        /* update the pager popup's width */
+        pager_popup_text_width_to_strings(desktop_popup,
+                                          screen_desktop_names,
+                                          screen_num_desktops);
         return;
+    }
 
     /* get the initial size */
     screen_resize();
 
-    /* get the desktop names */
-    numnames = g_slist_length(config_desktops_names);
-    names = g_new(gchar*, numnames + 1);
-    names[numnames] = NULL;
-    for (i = 0, it = config_desktops_names; it; ++i, it = g_slist_next(it))
-        names[i] = g_strdup(it->data);
+    /* have names already been set for the desktops? */
+    if (PROP_GETSS(RootWindow(ob_display, ob_screen),
+                   net_desktop_names, utf8, &names))
+    {
+        g_strfreev(names);
+        namesexist = TRUE;
+    }
+
+    /* if names don't exist and we have session names, set those.
+       do this stuff BEFORE setting the number of desktops, because that
+       will create default names for them
+    */
+    if (!namesexist && session_desktop_names != NULL) {
+        guint i, numnames;
+        GSList *it;
 
-    /* set the root window property */
-    PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,names);
+        /* get the desktop names */
+        numnames = g_slist_length(session_desktop_names);
+        names = g_new(gchar*, numnames + 1);
+        names[numnames] = NULL;
+        for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it))
+            names[i] = g_strdup(it->data);
 
-    g_strfreev(names);
+        /* set the root window property */
+        PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,names);
 
-    /* set the number of desktops */
+        g_strfreev(names);
+    }
+
+    /* set the number of desktops, if it's not already set.
+
+       this will also set the default names from the config file up for
+       desktops that don't have names yet */
     screen_num_desktops = 0;
-    screen_set_num_desktops(config_desktops_num);
+    if (PROP_GET32(RootWindow(ob_display, ob_screen),
+                   net_number_of_desktops, cardinal, &d))
+        screen_set_num_desktops(d);
+    /* restore from session if possible */
+    else if (session_num_desktops)
+        screen_set_num_desktops(session_num_desktops);
+    else
+        screen_set_num_desktops(config_desktops_num);
 
+    screen_desktop = screen_num_desktops;  /* something invalid */
     /* start on the current desktop when a wm was already running */
     if (PROP_GET32(RootWindow(ob_display, ob_screen),
                    net_current_desktop, cardinal, &d) &&
@@ -344,20 +419,25 @@ void screen_startup(gboolean reconfig)
     else
         screen_set_desktop(MIN(config_screen_firstdesk,
                                screen_num_desktops) - 1, FALSE);
+    screen_last_desktop = screen_desktop;
 
     /* don't start in showing-desktop mode */
     screen_showing_desktop = FALSE;
     PROP_SET32(RootWindow(ob_display, ob_screen),
                net_showing_desktop, cardinal, screen_showing_desktop);
 
-    screen_update_layout();
+    if (session_desktop_layout_present &&
+        screen_validate_layout(&session_desktop_layout))
+    {
+        screen_desktop_layout = session_desktop_layout;
+    }
+    else
+        screen_update_layout();
 }
 
 void screen_shutdown(gboolean reconfig)
 {
-    Rect **r;
-
-    pager_popup_free(desktop_cycle_popup);
+    pager_popup_free(desktop_popup);
 
     if (reconfig)
         return;
@@ -376,14 +456,9 @@ void screen_shutdown(gboolean reconfig)
 
     g_strfreev(screen_desktop_names);
     screen_desktop_names = NULL;
-
-    for (r = area; *r; ++r)
-        g_free(*r);
-    g_free(area);
-    area = NULL;
 }
 
-void screen_resize()
+void screen_resize(void)
 {
     static gint oldw = 0, oldh = 0;
     gint w, h;
@@ -417,7 +492,7 @@ void screen_set_num_desktops(guint num)
 {
     guint old;
     gulong *viewport;
-    GList *it;
+    GList *it, *stacking_copy;
 
     g_assert(num > 0);
 
@@ -437,13 +512,23 @@ void screen_set_num_desktops(guint num)
     /* the number of rows/columns will differ */
     screen_update_layout();
 
-    /* move windows on desktops that will no longer exist! */
-    for (it = client_list; it; it = g_list_next(it)) {
-        ObClient *c = it->data;
-        if (c->desktop >= num && c->desktop != DESKTOP_ALL)
-            client_set_desktop(c, num - 1, FALSE);
+    /* move windows on desktops that will no longer exist!
+       make a copy of the list cuz we're changing it */
+    stacking_copy = g_list_copy(stacking_list);
+    for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
+        if (WINDOW_IS_CLIENT(it->data)) {
+            ObClient *c = it->data;
+            if (c->desktop != DESKTOP_ALL && c->desktop >= num)
+                client_set_desktop(c, num - 1, FALSE, TRUE);
+            /* raise all the windows that are on the current desktop which
+               is being merged */
+            else if (screen_desktop == num - 1 &&
+                     (c->desktop == DESKTOP_ALL ||
+                      c->desktop == screen_desktop))
+                stacking_raise(CLIENT_AS_WINDOW(c));
+        }
     }
+
     /* change our struts/area to match (after moving windows) */
     screen_update_areas();
 
@@ -456,27 +541,139 @@ void screen_set_num_desktops(guint num)
         screen_set_desktop(num - 1, TRUE);
 }
 
-void screen_set_desktop(guint num, gboolean dofocus)
+static void screen_fallback_focus(void)
 {
     ObClient *c;
+    gboolean allow_omni;
+
+    /* only allow omnipresent windows to get focus on desktop change if
+       an omnipresent window is already focused (it'll keep focus probably, but
+       maybe not depending on mouse-focus options) */
+    allow_omni = focus_client && (client_normal(focus_client) &&
+                                  focus_client->desktop == DESKTOP_ALL);
+
+    /* the client moved there already so don't move focus. prevent flicker
+       on sendtodesktop + follow */
+    if (focus_client && focus_client->desktop == screen_desktop)
+        return;
+
+    /* have to try focus here because when you leave an empty desktop
+       there is no focus out to watch for. also, we have different rules
+       here. we always allow it to look under the mouse pointer if
+       config_focus_last is FALSE
+
+       do this before hiding the windows so if helper windows are coming
+       with us, they don't get hidden
+    */
+    if ((c = focus_fallback(TRUE, !config_focus_last, allow_omni,
+                            !allow_omni)))
+    {
+        /* only do the flicker reducing stuff ahead of time if we are going
+           to call xsetinputfocus on the window ourselves. otherwise there is
+           no guarantee the window will actually take focus.. */
+        if (c->can_focus) {
+            /* reduce flicker by hiliting now rather than waiting for the
+               server FocusIn event */
+            frame_adjust_focus(c->frame, TRUE);
+            /* do this here so that if you switch desktops to a window with
+               helper windows then the helper windows won't flash */
+            client_bring_helper_windows(c);
+        }
+    }
+}
+
+static gboolean last_desktop_func(gpointer data)
+{
+    screen_desktop_timeout = TRUE;
+    return FALSE;
+}
+
+void screen_set_desktop(guint num, gboolean dofocus)
+{
     GList *it;
-    guint old;
-     
+    guint previous;
+    gulong ignore_start;
+
     g_assert(num < screen_num_desktops);
 
-    old = screen_desktop;
+    previous = screen_desktop;
     screen_desktop = num;
+
+    if (previous == num) return;
+
     PROP_SET32(RootWindow(ob_display, ob_screen),
                net_current_desktop, cardinal, num);
 
-    if (old == num) return;
+    /* This whole thing decides when/how to save the screen_last_desktop so
+       that it can be restored later if you want */
+    if (screen_desktop_timeout) {
+        /* If screen_desktop_timeout is true, then we've been on this desktop
+           long enough and we can save it as the last desktop. */
 
-    screen_last_desktop = old;
+        /* save the "last desktop" as the "old desktop" */
+        screen_old_desktop = screen_last_desktop;
+        /* save the desktop we're coming from as the "last desktop" */
+        screen_last_desktop = previous;
+    }
+    else {
+        /* If screen_desktop_timeout is false, then we just got to this desktop
+           and we are moving away again. */
+
+        if (screen_desktop == screen_last_desktop) {
+            /* If we are moving to the "last desktop" .. */
+            if (previous == screen_old_desktop) {
+                /* .. from the "old desktop", change the last desktop to
+                   be where we are coming from */
+                screen_last_desktop = screen_old_desktop;
+            }
+            else if (screen_last_desktop == screen_old_desktop) {
+                /* .. and also to the "old desktop", change the "last
+                   desktop" to be where we are coming from */
+                screen_last_desktop = previous;
+            }
+            else {
+                /* .. from some other desktop, then set the "last desktop" to
+                   be the saved "old desktop", i.e. where we were before the
+                   "last desktop" */
+                screen_last_desktop = screen_old_desktop;
+            }
+        }
+        else {
+            /* If we are moving to any desktop besides the "last desktop"..
+               (this is the normal case) */
+            if (screen_desktop == screen_old_desktop) {
+                /* If moving to the "old desktop", which is not the
+                   "last desktop", don't save anything */
+            }
+            else if (previous == screen_old_desktop) {
+                /* If moving from the "old desktop", and not to the
+                   "last desktop", don't save anything */
+            }
+            else if (screen_last_desktop == screen_old_desktop) {
+                /* If the "last desktop" is the same as "old desktop" and
+                   you're not moving to the "last desktop" then save where
+                   we're coming from as the "last desktop" */
+                screen_last_desktop = previous;
+            }
+            else {
+                /* If the "last desktop" is different from the "old desktop"
+                   and you're not moving to the "last desktop", then don't save
+                   anything */
+            }
+        }
+    }
+    screen_desktop_timeout = FALSE;
+    ob_main_loop_timeout_remove(ob_main_loop, last_desktop_func);
+    ob_main_loop_timeout_add(ob_main_loop, REMEMBER_LAST_DESKTOP_TIME,
+                             last_desktop_func, NULL, NULL, NULL);
 
     ob_debug("Moving to desktop %d\n", num+1);
 
+    /* ignore enter events caused by the move */
+    ignore_start = event_start_ignore_all_enters();
+
     if (moveresize_client)
-        client_set_desktop(moveresize_client, num, TRUE);
+        client_set_desktop(moveresize_client, num, TRUE, FALSE);
 
     /* show windows before hiding the rest to lessen the enter/leave events */
 
@@ -488,26 +685,7 @@ void screen_set_desktop(guint num, gboolean dofocus)
         }
     }
 
-    /* have to try focus here because when you leave an empty desktop
-       there is no focus out to watch for
-
-       do this before hiding the windows so if helper windows are coming
-       with us, they don't get hidden
-    */
-    if (dofocus && (c = focus_fallback_target(TRUE, focus_client))) {
-        /* only do the flicker reducing stuff ahead of time if we are going
-           to call xsetinputfocus on the window ourselves. otherwise there is
-           no guarantee the window will actually take focus.. */
-        if (c->can_focus) {
-            /* do this here so that if you switch desktops to a window with
-               helper windows then the helper windows won't flash */
-            client_bring_helper_windows(c);
-            /* reduce flicker by hiliting now rather than waiting for the
-               server FocusIn event */
-            frame_adjust_focus(c->frame, TRUE);
-        }
-        client_focus(c);
-    }
+    if (dofocus) screen_fallback_focus();
 
     /* hide windows from bottom to top */
     for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
@@ -517,10 +695,100 @@ void screen_set_desktop(guint num, gboolean dofocus)
         }
     }
 
-    event_ignore_queued_enters();
+    event_end_ignore_all_enters(ignore_start);
 
     if (event_curtime != CurrentTime)
         screen_desktop_user_time = event_curtime;
+
+    if (ob_state() == OB_STATE_RUNNING)
+        screen_show_desktop_popup(screen_desktop);
+}
+
+void screen_add_desktop(gboolean current)
+{
+    gulong ignore_start;
+
+    /* ignore enter events caused by this */
+    ignore_start = event_start_ignore_all_enters();
+
+    screen_set_num_desktops(screen_num_desktops+1);
+
+    /* move all the clients over */
+    if (current) {
+        GList *it;
+
+        for (it = client_list; it; it = g_list_next(it)) {
+            ObClient *c = it->data;
+            if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop &&
+                /* don't move direct children, they'll be moved with their
+                   parent - which will have to be on the same desktop */
+                !client_direct_parent(c))
+            {
+                ob_debug("moving window %s\n", c->title);
+                client_set_desktop(c, c->desktop+1, FALSE, TRUE);
+            }
+        }
+    }
+
+    event_end_ignore_all_enters(ignore_start);
+}
+
+void screen_remove_desktop(gboolean current)
+{
+    guint rmdesktop, movedesktop;
+    GList *it, *stacking_copy;
+    gulong ignore_start;
+
+    if (screen_num_desktops <= 1) return;
+
+    /* ignore enter events caused by this */
+    ignore_start = event_start_ignore_all_enters();
+
+    /* what desktop are we removing and moving to? */
+    if (current)
+        rmdesktop = screen_desktop;
+    else
+        rmdesktop = screen_num_desktops - 1;
+    if (rmdesktop < screen_num_desktops - 1)
+        movedesktop = rmdesktop + 1;
+    else
+        movedesktop = rmdesktop;
+
+    /* make a copy of the list cuz we're changing it */
+    stacking_copy = g_list_copy(stacking_list);
+    for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
+        if (WINDOW_IS_CLIENT(it->data)) {
+            ObClient *c = it->data;
+            guint d = c->desktop;
+            if (d != DESKTOP_ALL && d >= movedesktop &&
+                /* don't move direct children, they'll be moved with their
+                   parent - which will have to be on the same desktop */
+                !client_direct_parent(c))
+            {
+                ob_debug("moving window %s\n", c->title);
+                client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
+            }
+            /* raise all the windows that are on the current desktop which
+               is being merged */
+            if ((screen_desktop == rmdesktop - 1 ||
+                 screen_desktop == rmdesktop) &&
+                (d == DESKTOP_ALL || d == screen_desktop))
+            {
+                stacking_raise(CLIENT_AS_WINDOW(c));
+                ob_debug("raising window %s\n", c->title);
+            }
+        }
+    }
+
+    /* fallback focus like we're changing desktops */
+    if (screen_desktop < screen_num_desktops - 1) {
+        screen_fallback_focus();
+        ob_debug("fake desktop change\n");
+    }
+
+    screen_set_num_desktops(screen_num_desktops-1);
+
+    event_end_ignore_all_enters(ignore_start);
 }
 
 static void get_row_col(guint d, guint *r, guint *c)
@@ -631,42 +899,51 @@ static guint translate_row_col(guint r, guint c)
     return 0;
 }
 
-void screen_desktop_popup(guint d, gboolean show)
+static gboolean hide_desktop_popup_func(gpointer data)
+{
+    pager_popup_hide(desktop_popup);
+    return FALSE; /* don't repeat */
+}
+
+void screen_show_desktop_popup(guint d)
 {
     Rect *a;
 
-    if (!show) {
-        pager_popup_hide(desktop_cycle_popup);
-    } else {
-        a = screen_physical_area_monitor(0);
-        pager_popup_position(desktop_cycle_popup, CenterGravity,
-                             a->x + a->width / 2, a->y + a->height / 2);
-        pager_popup_icon_size_multiplier(desktop_cycle_popup,
-                                         screen_desktop_layout.columns /
-                                         screen_desktop_layout.rows / 2,
-                                         screen_desktop_layout.rows/
-                                         screen_desktop_layout.columns / 2);
-        pager_popup_max_width(desktop_cycle_popup,
-                              MAX(a->width/3, POPUP_WIDTH));
-        pager_popup_show(desktop_cycle_popup, screen_desktop_names[d], d);
-    }
+    /* 0 means don't show the popup */
+    if (!config_desktop_popup_time) return;
+
+    a = screen_physical_area_active();
+    pager_popup_position(desktop_popup, CenterGravity,
+                         a->x + a->width / 2, a->y + a->height / 2);
+    pager_popup_icon_size_multiplier(desktop_popup,
+                                     (screen_desktop_layout.columns /
+                                      screen_desktop_layout.rows) / 2,
+                                     (screen_desktop_layout.rows/
+                                      screen_desktop_layout.columns) / 2);
+    pager_popup_max_width(desktop_popup,
+                          MAX(a->width/3, POPUP_WIDTH));
+    pager_popup_show(desktop_popup, screen_desktop_names[d], d);
+
+    ob_main_loop_timeout_remove(ob_main_loop, hide_desktop_popup_func);
+    ob_main_loop_timeout_add(ob_main_loop, config_desktop_popup_time * 1000,
+                             hide_desktop_popup_func, NULL, NULL, NULL);
+    g_free(a);
 }
 
-guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
-                           gboolean dialog, gboolean done, gboolean cancel)
+void screen_hide_desktop_popup(void)
 {
-    guint r, c;
-    static guint d = (guint)-1;
-    guint ret;
-
-    if (d == (guint)-1)
-        d = screen_desktop;
+    ob_main_loop_timeout_remove(ob_main_loop, hide_desktop_popup_func);
+    pager_popup_hide(desktop_popup);
+}
 
-    if ((cancel || done) && dialog)
-        goto show_cycle_dialog;
+guint screen_find_desktop(guint from, ObDirection dir,
+                          gboolean wrap, gboolean linear)
+{
+    guint r, c;
+    guint d;
 
+    d = from;
     get_row_col(d, &r, &c);
-
     if (linear) {
         switch (dir) {
         case OB_DIRECTION_EAST:
@@ -674,212 +951,184 @@ guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
                 ++d;
             else if (wrap)
                 d = 0;
+            else
+                return from;
             break;
         case OB_DIRECTION_WEST:
             if (d > 0)
                 --d;
             else if (wrap)
                 d = screen_num_desktops - 1;
+            else
+                return from;
             break;
         default:
-            assert(0);
-            return screen_desktop;
+            g_assert_not_reached();
+            return from;
         }
     } else {
         switch (dir) {
         case OB_DIRECTION_EAST:
             ++c;
             if (c >= screen_desktop_layout.columns) {
-                if (wrap) {
+                if (wrap)
                     c = 0;
-                } else {
-                    d = screen_desktop;
-                    goto show_cycle_dialog;
-                }
+                else
+                    return from;
             }
             d = translate_row_col(r, c);
             if (d >= screen_num_desktops) {
-                if (wrap) {
+                if (wrap)
                     ++c;
-                } else {
-                    d = screen_desktop;
-                    goto show_cycle_dialog;
-                }
+                else
+                    return from;
             }
             break;
         case OB_DIRECTION_WEST:
             --c;
             if (c >= screen_desktop_layout.columns) {
-                if (wrap) {
+                if (wrap)
                     c = screen_desktop_layout.columns - 1;
-                } else {
-                    d = screen_desktop;
-                    goto show_cycle_dialog;
-                }
+                else
+                    return from;
             }
             d = translate_row_col(r, c);
             if (d >= screen_num_desktops) {
-                if (wrap) {
+                if (wrap)
                     --c;
-                } else {
-                    d = screen_desktop;
-                    goto show_cycle_dialog;
-                }
+                else
+                    return from;
             }
             break;
         case OB_DIRECTION_SOUTH:
             ++r;
             if (r >= screen_desktop_layout.rows) {
-                if (wrap) {
+                if (wrap)
                     r = 0;
-                } else {
-                    d = screen_desktop;
-                    goto show_cycle_dialog;
-                }
+                else
+                    return from;
             }
             d = translate_row_col(r, c);
             if (d >= screen_num_desktops) {
-                if (wrap) {
+                if (wrap)
                     ++r;
-                } else {
-                    d = screen_desktop;
-                    goto show_cycle_dialog;
-                }
+                else
+                    return from;
             }
             break;
         case OB_DIRECTION_NORTH:
             --r;
             if (r >= screen_desktop_layout.rows) {
-                if (wrap) {
+                if (wrap)
                     r = screen_desktop_layout.rows - 1;
-                } else {
-                    d = screen_desktop;
-                    goto show_cycle_dialog;
-                }
+                else
+                    return from;
             }
             d = translate_row_col(r, c);
             if (d >= screen_num_desktops) {
-                if (wrap) {
+                if (wrap)
                     --r;
-                } else {
-                    d = screen_desktop;
-                    goto show_cycle_dialog;
-                }
+                else
+                    return from;
             }
             break;
         default:
-            assert(0);
-            return d = screen_desktop;
+            g_assert_not_reached();
+            return from;
         }
 
         d = translate_row_col(r, c);
     }
+    return d;
+}
 
-show_cycle_dialog:
-    if (dialog && !cancel && !done) {
-        screen_desktop_popup(d, TRUE);
-    } else
-        screen_desktop_popup(0, FALSE);
-    ret = d;
+static gboolean screen_validate_layout(ObDesktopLayout *l)
+{
+    if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
+        return FALSE;
 
-    if (!dialog || cancel || done)
-        d = (guint)-1;
+    /* fill in a zero rows/columns */
+    if (l->columns == 0) {
+        l->columns = screen_num_desktops / l->rows;
+        if (l->rows * l->columns < screen_num_desktops)
+            l->columns++;
+        if (l->rows * l->columns >= screen_num_desktops + l->columns)
+            l->rows--;
+    } else if (l->rows == 0) {
+        l->rows = screen_num_desktops / l->columns;
+        if (l->columns * l->rows < screen_num_desktops)
+            l->rows++;
+        if (l->columns * l->rows >= screen_num_desktops + l->rows)
+            l->columns--;
+    }
 
-    return ret;
+    /* bounds checking */
+    if (l->orientation == OB_ORIENTATION_HORZ) {
+        l->columns = MIN(screen_num_desktops, l->columns);
+        l->rows = MIN(l->rows,
+                      (screen_num_desktops + l->columns - 1) / l->columns);
+        l->columns = screen_num_desktops / l->rows +
+            !!(screen_num_desktops % l->rows);
+    } else {
+        l->rows = MIN(screen_num_desktops, l->rows);
+        l->columns = MIN(l->columns,
+                         (screen_num_desktops + l->rows - 1) / l->rows);
+        l->rows = screen_num_desktops / l->columns +
+            !!(screen_num_desktops % l->columns);
+    }
+    return TRUE;
 }
 
-void screen_update_layout()
+void screen_update_layout(void)
+
 {
-    ObOrientation orient;
-    ObCorner corner;
-    guint rows;
-    guint cols;
+    ObDesktopLayout l;
     guint32 *data;
     guint num;
-    gboolean valid = FALSE;
+
+    screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
+    screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
+    screen_desktop_layout.rows = 1;
+    screen_desktop_layout.columns = screen_num_desktops;
 
     if (PROP_GETA32(RootWindow(ob_display, ob_screen),
                     net_desktop_layout, cardinal, &data, &num)) {
         if (num == 3 || num == 4) {
 
             if (data[0] == prop_atoms.net_wm_orientation_vert)
-                orient = OB_ORIENTATION_VERT;
+                l.orientation = OB_ORIENTATION_VERT;
             else if (data[0] == prop_atoms.net_wm_orientation_horz)
-                orient = OB_ORIENTATION_HORZ;
+                l.orientation = OB_ORIENTATION_HORZ;
             else
-                goto screen_update_layout_bail;
+                return;
 
             if (num < 4)
-                corner = OB_CORNER_TOPLEFT;
+                l.start_corner = OB_CORNER_TOPLEFT;
             else {
                 if (data[3] == prop_atoms.net_wm_topleft)
-                    corner = OB_CORNER_TOPLEFT;
+                    l.start_corner = OB_CORNER_TOPLEFT;
                 else if (data[3] == prop_atoms.net_wm_topright)
-                    corner = OB_CORNER_TOPRIGHT;
+                    l.start_corner = OB_CORNER_TOPRIGHT;
                 else if (data[3] == prop_atoms.net_wm_bottomright)
-                    corner = OB_CORNER_BOTTOMRIGHT;
+                    l.start_corner = OB_CORNER_BOTTOMRIGHT;
                 else if (data[3] == prop_atoms.net_wm_bottomleft)
-                    corner = OB_CORNER_BOTTOMLEFT;
+                    l.start_corner = OB_CORNER_BOTTOMLEFT;
                 else
-                    goto screen_update_layout_bail;
+                    return;
             }
 
-            cols = data[1];
-            rows = data[2];
-
-            /* fill in a zero rows/columns */
-            if ((cols == 0 && rows == 0)) { /* both 0's is bad data.. */
-                goto screen_update_layout_bail;
-            } else {
-                if (cols == 0) {
-                    cols = screen_num_desktops / rows;
-                    if (rows * cols < screen_num_desktops)
-                        cols++;
-                    if (rows * cols >= screen_num_desktops + cols)
-                        rows--;
-                } else if (rows == 0) {
-                    rows = screen_num_desktops / cols;
-                    if (cols * rows < screen_num_desktops)
-                        rows++;
-                    if (cols * rows >= screen_num_desktops + rows)
-                        cols--;
-                }
-            }
+            l.columns = data[1];
+            l.rows = data[2];
 
-            /* bounds checking */
-            if (orient == OB_ORIENTATION_HORZ) {
-                cols = MIN(screen_num_desktops, cols);
-                rows = MIN(rows, (screen_num_desktops + cols - 1) / cols);
-                cols = screen_num_desktops / rows +
-                    !!(screen_num_desktops % rows);
-            } else {
-                rows = MIN(screen_num_desktops, rows);
-                cols = MIN(cols, (screen_num_desktops + rows - 1) / rows);
-                rows = screen_num_desktops / cols +
-                    !!(screen_num_desktops % cols);
-            }
+            if (screen_validate_layout(&l))
+                screen_desktop_layout = l;
 
-            valid = TRUE;
+            g_free(data);
         }
-    screen_update_layout_bail:
-        g_free(data);
     }
-
-    if (!valid) {
-        /* defaults */
-        orient = OB_ORIENTATION_HORZ;
-        corner = OB_CORNER_TOPLEFT;
-        rows = 1;
-        cols = screen_num_desktops;
-    }
-
-    screen_desktop_layout.orientation = orient;
-    screen_desktop_layout.start_corner = corner;
-    screen_desktop_layout.rows = rows;
-    screen_desktop_layout.columns = cols;
 }
 
-void screen_update_desktop_names()
+void screen_update_desktop_names(void)
 {
     guint i;
 
@@ -893,47 +1142,71 @@ void screen_update_desktop_names()
     else
         i = 0;
     if (i < screen_num_desktops) {
+        GSList *it;
+
         screen_desktop_names = g_renew(gchar*, screen_desktop_names,
                                        screen_num_desktops + 1);
         screen_desktop_names[screen_num_desktops] = NULL;
-        for (; i < screen_num_desktops; ++i)
-            screen_desktop_names[i] = g_strdup_printf("desktop %i", i + 1);
+
+        it = g_slist_nth(config_desktops_names, i);
+
+        for (; i < screen_num_desktops; ++i) {
+            if (it && ((char*)it->data)[0]) /* not empty */
+                /* use the names from the config file when possible */
+                screen_desktop_names[i] = g_strdup(it->data);
+            else
+                /* make up a nice name if it's not though */
+                screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
+                                                          i + 1);
+            if (it) it = g_slist_next(it);
+        }
+
+        /* if we changed any names, then set the root property so we can
+           all agree on the names */
+        PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,
+                   screen_desktop_names);
     }
 
     /* resize the pager for these names */
-    pager_popup_text_width_to_strings(desktop_cycle_popup,
+    pager_popup_text_width_to_strings(desktop_popup,
                                       screen_desktop_names,
                                       screen_num_desktops);
 }
 
-void screen_show_desktop(gboolean show, gboolean restore_focus)
+void screen_show_desktop(gboolean show, ObClient *show_only)
 {
     GList *it;
-     
+
     if (show == screen_showing_desktop) return; /* no change */
 
     screen_showing_desktop = show;
 
     if (show) {
-        /* bottom to top */
+        /* hide windows bottom to top */
         for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
             if (WINDOW_IS_CLIENT(it->data)) {
                 ObClient *client = it->data;
                 client_showhide(client);
             }
         }
-    } else {
-        /* top to bottom */
+    }
+    else {
+        /* restore windows top to bottom */
         for (it = stacking_list; it; it = g_list_next(it)) {
             if (WINDOW_IS_CLIENT(it->data)) {
                 ObClient *client = it->data;
-                client_showhide(client);
+                if (client_should_show(client)) {
+                    if (!show_only || client == show_only)
+                        client_show(client);
+                    else
+                        client_iconify(client, TRUE, FALSE, TRUE);
+                }
             }
         }
     }
 
     if (show) {
-        /* focus desktop */
+        /* focus the desktop */
         for (it = focus_order; it; it = g_list_next(it)) {
             ObClient *c = it->data;
             if (c->type == OB_CLIENT_TYPE_DESKTOP &&
@@ -941,13 +1214,20 @@ void screen_show_desktop(gboolean show, gboolean restore_focus)
                 client_focus(it->data))
                 break;
         }
-    } else if (restore_focus) {
+    }
+    else if (!show_only) {
         ObClient *c;
 
-        /* use NULL for the "old" argument because the desktop was focused
-           and we don't want to fallback to the desktop by default */
-        if ((c = focus_fallback_target(TRUE, NULL)))
-            client_focus(c);
+        if ((c = focus_fallback(TRUE, FALSE, TRUE, FALSE))) {
+            /* only do the flicker reducing stuff ahead of time if we are going
+               to call xsetinputfocus on the window ourselves. otherwise there
+               is no guarantee the window will actually take focus.. */
+            if (c->can_focus) {
+                /* reduce flicker by hiliting now rather than waiting for the
+                   server FocusIn event */
+                frame_adjust_focus(c->frame, TRUE);
+            }
+        }
     }
 
     show = !!show; /* make it boolean */
@@ -957,271 +1237,401 @@ void screen_show_desktop(gboolean show, gboolean restore_focus)
 
 void screen_install_colormap(ObClient *client, gboolean install)
 {
-    if (client == NULL) {
+    if (client == NULL || client->colormap == None) {
         if (install)
             XInstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
         else
             XUninstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
     } else {
-        xerror_set_ignore(TRUE);
-        if (install) {
-            if (client->colormap != None)
-                XInstallColormap(RrDisplay(ob_rr_inst), client->colormap);
-        } else
+        obt_display_ignore_errors(ob_display, TRUE);
+        if (install)
+            XInstallColormap(RrDisplay(ob_rr_inst), client->colormap);
+        else
             XUninstallColormap(RrDisplay(ob_rr_inst), client->colormap);
-        xerror_set_ignore(FALSE);
+        obt_display_ignore_errors(ob_display, FALSE);
     }
 }
 
-static inline void
-screen_area_add_strut_left(const StrutPartial *s, const Rect *monitor_area,
-                           gint edge, Strut *ret)
-{
-    if (s->left &&
-        ((s->left_end <= s->left_start) ||
-         (RECT_TOP(*monitor_area) < s->left_end &&
-          RECT_BOTTOM(*monitor_area) > s->left_start)))
-        ret->left = MAX(ret->left, edge);
+#define STRUT_LEFT_ON_MONITOR(s, i) \
+    (RANGES_INTERSECT(s->left_start, s->left_end - s->left_start + 1, \
+                      monitor_area[i].y, monitor_area[i].height))
+#define STRUT_RIGHT_ON_MONITOR(s, i) \
+    (RANGES_INTERSECT(s->right_start, s->right_end - s->right_start + 1, \
+                      monitor_area[i].y, monitor_area[i].height))
+#define STRUT_TOP_ON_MONITOR(s, i) \
+    (RANGES_INTERSECT(s->top_start, s->top_end - s->top_start + 1, \
+                      monitor_area[i].x, monitor_area[i].width))
+#define STRUT_BOTTOM_ON_MONITOR(s, i) \
+    (RANGES_INTERSECT(s->bottom_start, s->bottom_end - s->bottom_start + 1, \
+                      monitor_area[i].x, monitor_area[i].width))
+
+typedef struct {
+    guint desktop;
+    StrutPartial *strut;
+} ObScreenStrut;
+
+#define RESET_STRUT_LIST(sl) \
+    (g_slist_free(sl), sl = NULL)
+
+#define ADD_STRUT_TO_LIST(sl, d, s) \
+{ \
+    ObScreenStrut *ss = g_new(ObScreenStrut, 1); \
+    ss->desktop = d; \
+    ss->strut = s;  \
+    sl = g_slist_prepend(sl, ss); \
 }
 
-static inline void
-screen_area_add_strut_top(const StrutPartial *s, const Rect *monitor_area,
-                          gint edge, Strut *ret)
-{
-    if (s->top &&
-        ((s->top_end <= s->top_start) ||
-         (RECT_LEFT(*monitor_area) < s->top_end &&
-          RECT_RIGHT(*monitor_area) > s->top_start)))
-        ret->top = MAX(ret->top, edge);
+#define VALIDATE_STRUTS(sl, side, max) \
+{ \
+    GSList *it; \
+    for (it = sl; it; it = g_slist_next(it)) { \
+      ObScreenStrut *ss = it->data; \
+      ss->strut->side = MIN(max, ss->strut->side); \
+    } \
 }
 
-static inline void
-screen_area_add_strut_right(const StrutPartial *s, const Rect *monitor_area,
-                            gint edge, Strut *ret)
+static void get_xinerama_screens(Rect **xin_areas, guint *nxin)
 {
-    if (s->right &&
-        ((s->right_end <= s->right_start) ||
-         (RECT_TOP(*monitor_area) < s->right_end &&
-          RECT_BOTTOM(*monitor_area) > s->right_start)))
-        ret->right = MAX(ret->right, edge);
-}
+    guint i;
+    gint l, r, t, b;
+
+    if (ob_debug_xinerama) {
+        g_print("Using fake xinerama !\n");
+        gint w = WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen));
+        gint h = HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen));
+        *nxin = 2;
+        *xin_areas = g_new(Rect, *nxin + 1);
+        RECT_SET((*xin_areas)[0], 0, 0, w/2, h);
+        RECT_SET((*xin_areas)[1], w/2, 0, w-(w/2), h);
+    }
+#ifdef XINERAMA
+    else if (obt_display_extension_xinerama) {
+        guint i;
+        gint n;
+        XineramaScreenInfo *info = XineramaQueryScreens(ob_display, &n);
+        *nxin = n;
+        *xin_areas = g_new(Rect, *nxin + 1);
+        for (i = 0; i < *nxin; ++i)
+            RECT_SET((*xin_areas)[i], info[i].x_org, info[i].y_org,
+                     info[i].width, info[i].height);
+        XFree(info);
+    }
+#endif
+    else {
+        *nxin = 1;
+        *xin_areas = g_new(Rect, *nxin + 1);
+        RECT_SET((*xin_areas)[0], 0, 0,
+                 WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen)),
+                 HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen)));
+    }
 
-static inline void
-screen_area_add_strut_bottom(const StrutPartial *s, const Rect *monitor_area,
-                             gint edge, Strut *ret)
-{
-    if (s->bottom &&
-        ((s->bottom_end <= s->bottom_start) ||
-         (RECT_LEFT(*monitor_area) < s->bottom_end &&
-          RECT_RIGHT(*monitor_area) > s->bottom_start)))
-        ret->bottom = MAX(ret->bottom, edge);
+    /* returns one extra with the total area in it */
+    l = (*xin_areas)[0].x;
+    t = (*xin_areas)[0].y;
+    r = (*xin_areas)[0].x + (*xin_areas)[0].width - 1;
+    b = (*xin_areas)[0].y + (*xin_areas)[0].height - 1;
+    for (i = 1; i < *nxin; ++i) {
+        l = MIN(l, (*xin_areas)[i].x);
+        t = MIN(l, (*xin_areas)[i].y);
+        r = MAX(r, (*xin_areas)[i].x + (*xin_areas)[i].width - 1);
+        b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1);
+    }
+    RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1);
 }
 
-void screen_update_areas()
+void screen_update_areas(void)
 {
-    guint i, x;
+    guint i, j;
     gulong *dims;
     GList *it;
-    gint o;
+    GSList *sit;
 
     g_free(monitor_area);
-    extensions_xinerama_screens(&monitor_area, &screen_num_monitors);
-
-    if (area) {
-        for (i = 0; area[i]; ++i)
-            g_free(area[i]);
-        g_free(area);
+    get_xinerama_screens(&monitor_area, &screen_num_monitors);
+
+    /* set up the user-specified margins */
+    config_margins.top_start = RECT_LEFT(monitor_area[screen_num_monitors]);
+    config_margins.top_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
+    config_margins.bottom_start = RECT_LEFT(monitor_area[screen_num_monitors]);
+    config_margins.bottom_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
+    config_margins.left_start = RECT_TOP(monitor_area[screen_num_monitors]);
+    config_margins.left_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
+    config_margins.right_start = RECT_TOP(monitor_area[screen_num_monitors]);
+    config_margins.right_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
+
+    dims = g_new(gulong, 4 * screen_num_desktops * screen_num_monitors);
+
+    RESET_STRUT_LIST(struts_left);
+    RESET_STRUT_LIST(struts_top);
+    RESET_STRUT_LIST(struts_right);
+    RESET_STRUT_LIST(struts_bottom);
+
+    /* collect the struts */
+    for (it = client_list; it; it = g_list_next(it)) {
+        ObClient *c = it->data;
+        if (c->strut.left)
+            ADD_STRUT_TO_LIST(struts_left, c->desktop, &c->strut);
+        if (c->strut.top)
+            ADD_STRUT_TO_LIST(struts_top, c->desktop, &c->strut);
+        if (c->strut.right)
+            ADD_STRUT_TO_LIST(struts_right, c->desktop, &c->strut);
+        if (c->strut.bottom)
+            ADD_STRUT_TO_LIST(struts_bottom, c->desktop, &c->strut);
     }
+    if (dock_strut.left)
+        ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &dock_strut);
+    if (dock_strut.top)
+        ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &dock_strut);
+    if (dock_strut.right)
+        ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &dock_strut);
+    if (dock_strut.bottom)
+        ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &dock_strut);
+
+    if (config_margins.left)
+        ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &config_margins);
+    if (config_margins.top)
+        ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &config_margins);
+    if (config_margins.right)
+        ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &config_margins);
+    if (config_margins.bottom)
+        ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &config_margins);
+
+    VALIDATE_STRUTS(struts_left, left,
+                    monitor_area[screen_num_monitors].width / 2);
+    VALIDATE_STRUTS(struts_right, right,
+                    monitor_area[screen_num_monitors].width / 2);
+    VALIDATE_STRUTS(struts_top, top,
+                    monitor_area[screen_num_monitors].height / 2);
+    VALIDATE_STRUTS(struts_bottom, bottom,
+                    monitor_area[screen_num_monitors].height / 2);
+
+    /* set up the work areas to be full screen */
+    for (i = 0; i < screen_num_monitors; ++i)
+        for (j = 0; j < screen_num_desktops; ++j) {
+            dims[(i * screen_num_desktops + j) * 4+0] = monitor_area[i].x;
+            dims[(i * screen_num_desktops + j) * 4+1] = monitor_area[i].y;
+            dims[(i * screen_num_desktops + j) * 4+2] = monitor_area[i].width;
+            dims[(i * screen_num_desktops + j) * 4+3] = monitor_area[i].height;
+        }
 
-    area = g_new(Rect*, screen_num_desktops + 2);
-    for (i = 0; i < screen_num_desktops + 1; ++i)
-        area[i] = g_new0(Rect, screen_num_monitors + 1);
-    area[i] = NULL;
-     
-    dims = g_new(gulong, 4 * screen_num_desktops);
+    /* calculate the work areas from the struts */
+    for (i = 0; i < screen_num_monitors; ++i)
+        for (j = 0; j < screen_num_desktops; ++j) {
+            gint l = 0, r = 0, t = 0, b = 0;
 
-    for (i = 0; i < screen_num_desktops + 1; ++i) {
-        Strut *struts;
-        gint l, r, t, b;
+            /* only add the strut to the area if it touches the monitor */
 
-        struts = g_new0(Strut, screen_num_monitors);
-
-        /* calc the xinerama areas */
-        for (x = 0; x < screen_num_monitors; ++x) {
-            area[i][x] = monitor_area[x];
-            if (x == 0) {
-                l = monitor_area[x].x;
-                t = monitor_area[x].y;
-                r = monitor_area[x].x + monitor_area[x].width - 1;
-                b = monitor_area[x].y + monitor_area[x].height - 1;
-            } else {
-                l = MIN(l, monitor_area[x].x);
-                t = MIN(t, monitor_area[x].y);
-                r = MAX(r, monitor_area[x].x + monitor_area[x].width - 1);
-                b = MAX(b, monitor_area[x].y + monitor_area[x].height - 1);
+            for (sit = struts_left; sit; sit = g_slist_next(sit)) {
+                ObScreenStrut *s = sit->data;
+                if ((s->desktop == j || s->desktop == DESKTOP_ALL) &&
+                    STRUT_LEFT_ON_MONITOR(s->strut, i))
+                    l = MAX(l, s->strut->left);
             }
-        }
-        RECT_SET(area[i][x], l, t, r - l + 1, b - t + 1);
-
-        /* apply the struts */
-
-        /* find the left-most xin heads, i do this in 2 loops :| */
-        o = area[i][0].x;
-        for (x = 1; x < screen_num_monitors; ++x)
-            o = MIN(o, area[i][x].x);
-
-        for (x = 0; x < screen_num_monitors; ++x) {
-            for (it = client_list; it; it = g_list_next(it)) {
-                ObClient *c = it->data;
-                screen_area_add_strut_left(&c->strut,
-                                           &monitor_area[x],
-                                           o + c->strut.left - area[i][x].x,
-                                           &struts[x]);
+            for (sit = struts_top; sit; sit = g_slist_next(sit)) {
+                ObScreenStrut *s = sit->data;
+                if ((s->desktop == j || s->desktop == DESKTOP_ALL) &&
+                    STRUT_TOP_ON_MONITOR(s->strut, i))
+                    t = MAX(t, s->strut->top);
+            }
+            for (sit = struts_right; sit; sit = g_slist_next(sit)) {
+                ObScreenStrut *s = sit->data;
+                if ((s->desktop == j || s->desktop == DESKTOP_ALL) &&
+                    STRUT_RIGHT_ON_MONITOR(s->strut, i))
+                    r = MAX(r, s->strut->right);
+            }
+            for (sit = struts_bottom; sit; sit = g_slist_next(sit)) {
+                ObScreenStrut *s = sit->data;
+                if ((s->desktop == j || s->desktop == DESKTOP_ALL) &&
+                    STRUT_BOTTOM_ON_MONITOR(s->strut, i))
+                    b = MAX(b, s->strut->bottom);
             }
-            screen_area_add_strut_left(&dock_strut,
-                                       &monitor_area[x],
-                                       o + dock_strut.left - area[i][x].x,
-                                       &struts[x]);
 
-            area[i][x].x += struts[x].left;
-            area[i][x].width -= struts[x].left;
+            /* based on these margins, set the work area for the
+               monitor/desktop */
+            dims[(i * screen_num_desktops + j) * 4 + 0] += l;
+            dims[(i * screen_num_desktops + j) * 4 + 1] += t;
+            dims[(i * screen_num_desktops + j) * 4 + 2] -= l + r;
+            dims[(i * screen_num_desktops + j) * 4 + 3] -= t + b;
         }
 
-        /* find the top-most xin heads, i do this in 2 loops :| */
-        o = area[i][0].y;
-        for (x = 1; x < screen_num_monitors; ++x)
-            o = MIN(o, area[i][x].y);
-
-        for (x = 0; x < screen_num_monitors; ++x) {
-            for (it = client_list; it; it = g_list_next(it)) {
-                ObClient *c = it->data;
-                screen_area_add_strut_top(&c->strut,
-                                           &monitor_area[x],
-                                           o + c->strut.top - area[i][x].y,
-                                           &struts[x]);
-            }
-            screen_area_add_strut_top(&dock_strut,
-                                      &monitor_area[x],
-                                      o + dock_strut.top - area[i][x].y,
-                                      &struts[x]);
+    /* all the work areas are not used here, only the ones for the first
+       monitor are */
+    PROP_SETA32(RootWindow(ob_display, ob_screen), net_workarea, cardinal,
+                dims, 4 * screen_num_desktops);
 
-            area[i][x].y += struts[x].top;
-            area[i][x].height -= struts[x].top;
-        }
+    /* the area has changed, adjust all the windows if they need it */
+    for (it = client_list; it; it = g_list_next(it))
+        client_reconfigure(it->data, FALSE);
 
-        /* find the right-most xin heads, i do this in 2 loops :| */
-        o = area[i][0].x + area[i][0].width - 1;
-        for (x = 1; x < screen_num_monitors; ++x)
-            o = MAX(o, area[i][x].x + area[i][x].width - 1);
-
-        for (x = 0; x < screen_num_monitors; ++x) {
-            for (it = client_list; it; it = g_list_next(it)) {
-                ObClient *c = it->data;
-                screen_area_add_strut_right(&c->strut,
-                                           &monitor_area[x],
-                                           (area[i][x].x +
-                                            area[i][x].width - 1) -
-                                            (o - c->strut.right),
-                                            &struts[x]);
-            }
-            screen_area_add_strut_right(&dock_strut,
-                                        &monitor_area[x],
-                                        (area[i][x].x +
-                                         area[i][x].width - 1) -
-                                        (o - dock_strut.right),
-                                        &struts[x]);
-
-            area[i][x].width -= struts[x].right;
-        }
+    g_free(dims);
+}
 
-        /* find the bottom-most xin heads, i do this in 2 loops :| */
-        o = area[i][0].y + area[i][0].height - 1;
-        for (x = 1; x < screen_num_monitors; ++x)
-            o = MAX(o, area[i][x].y + area[i][x].height - 1);
-
-        for (x = 0; x < screen_num_monitors; ++x) {
-            for (it = client_list; it; it = g_list_next(it)) {
-                ObClient *c = it->data;
-                screen_area_add_strut_bottom(&c->strut,
-                                             &monitor_area[x],
-                                             (area[i][x].y +
-                                              area[i][x].height - 1) - \
-                                             (o - c->strut.bottom),
-                                             &struts[x]);
-            }
-            screen_area_add_strut_bottom(&dock_strut,
-                                         &monitor_area[x],
-                                         (area[i][x].y +
-                                          area[i][x].height - 1) - \
-                                         (o - dock_strut.bottom),
-                                         &struts[x]);
-
-            area[i][x].height -= struts[x].bottom;
-        }
+#if 0
+Rect* screen_area_all_monitors(guint desktop)
+{
+    guint i;
+    Rect *a;
 
-        l = RECT_LEFT(area[i][0]);
-        t = RECT_TOP(area[i][0]);
-        r = RECT_RIGHT(area[i][0]);
-        b = RECT_BOTTOM(area[i][0]);
-        for (x = 1; x < screen_num_monitors; ++x) {
-            l = MIN(l, RECT_LEFT(area[i][x]));
-            t = MIN(l, RECT_TOP(area[i][x]));
-            r = MAX(r, RECT_RIGHT(area[i][x]));
-            b = MAX(b, RECT_BOTTOM(area[i][x]));
-        }
-        RECT_SET(area[i][screen_num_monitors], l, t,
-                 r - l + 1, b - t + 1);
+    a = screen_area_monitor(desktop, 0);
 
-        /* XXX optimize when this is run? */
+    /* combine all the monitors together */
+    for (i = 1; i < screen_num_monitors; ++i) {
+        Rect *m = screen_area_monitor(desktop, i);
+        gint l, r, t, b;
 
-        /* the area has changed, adjust all the maximized 
-           windows */
-        for (it = client_list; it; it = g_list_next(it)) {
-            ObClient *c = it->data; 
-            if (i < screen_num_desktops) {
-                if (c->desktop == i)
-                    client_reconfigure(c);
-            } else if (c->desktop == DESKTOP_ALL)
-                client_reconfigure(c);
-        }
-        if (i < screen_num_desktops) {
-            /* don't set these for the 'all desktops' area */
-            dims[(i * 4) + 0] = area[i][screen_num_monitors].x;
-            dims[(i * 4) + 1] = area[i][screen_num_monitors].y;
-            dims[(i * 4) + 2] = area[i][screen_num_monitors].width;
-            dims[(i * 4) + 3] = area[i][screen_num_monitors].height;
-        }
+        l = MIN(RECT_LEFT(*a), RECT_LEFT(*m));
+        t = MIN(RECT_TOP(*a), RECT_TOP(*m));
+        r = MAX(RECT_RIGHT(*a), RECT_RIGHT(*m));
+        b = MAX(RECT_BOTTOM(*a), RECT_BOTTOM(*m));
 
-        g_free(struts);
-    }
+        RECT_SET(*a, l, t, r - l + 1, b - t + 1);
 
-    PROP_SETA32(RootWindow(ob_display, ob_screen), net_workarea, cardinal,
-                dims, 4 * screen_num_desktops);
+        g_free(m);
+    }
 
-    g_free(dims);
+    return a;
 }
+#endif
 
-Rect *screen_area(guint desktop)
+#define STRUT_LEFT_IN_SEARCH(s, search) \
+    (RANGES_INTERSECT(search->y, search->height, \
+                      s->left_start, s->left_end - s->left_start + 1))
+#define STRUT_RIGHT_IN_SEARCH(s, search) \
+    (RANGES_INTERSECT(search->y, search->height, \
+                      s->right_start, s->right_end - s->right_start + 1))
+#define STRUT_TOP_IN_SEARCH(s, search) \
+    (RANGES_INTERSECT(search->x, search->width, \
+                      s->top_start, s->top_end - s->top_start + 1))
+#define STRUT_BOTTOM_IN_SEARCH(s, search) \
+    (RANGES_INTERSECT(search->x, search->width, \
+                      s->bottom_start, s->bottom_end - s->bottom_start + 1))
+
+#define STRUT_LEFT_IGNORE(s, us, search) \
+    (head == SCREEN_AREA_ALL_MONITORS && us && \
+     RECT_LEFT(monitor_area[i]) + s->left > RECT_LEFT(*search))
+#define STRUT_RIGHT_IGNORE(s, us, search) \
+    (head == SCREEN_AREA_ALL_MONITORS && us && \
+     RECT_RIGHT(monitor_area[i]) - s->right < RECT_RIGHT(*search))
+#define STRUT_TOP_IGNORE(s, us, search) \
+    (head == SCREEN_AREA_ALL_MONITORS && us && \
+     RECT_TOP(monitor_area[i]) + s->top > RECT_TOP(*search))
+#define STRUT_BOTTOM_IGNORE(s, us, search) \
+    (head == SCREEN_AREA_ALL_MONITORS && us && \
+     RECT_BOTTOM(monitor_area[i]) - s->bottom < RECT_BOTTOM(*search))
+
+Rect* screen_area(guint desktop, guint head, Rect *search)
 {
-    return screen_area_monitor(desktop, screen_num_monitors);
-}
+    Rect *a;
+    GSList *it;
+    gint l, r, t, b, al, ar, at, ab;
+    guint i, d;
+    gboolean us = search != NULL; /* user provided search */
 
-Rect *screen_area_monitor(guint desktop, guint head)
-{
-    if (head > screen_num_monitors)
-        return NULL;
-    if (desktop >= screen_num_desktops) {
-        if (desktop == DESKTOP_ALL)
-            return &area[screen_num_desktops][head];
-        return NULL;
+    g_assert(desktop < screen_num_desktops || desktop == DESKTOP_ALL);
+    g_assert(head < screen_num_monitors || head == SCREEN_AREA_ONE_MONITOR ||
+             head == SCREEN_AREA_ALL_MONITORS);
+    g_assert(!(head == SCREEN_AREA_ONE_MONITOR && search == NULL));
+
+    /* find any struts for this monitor
+       which will be affecting the search area.
+    */
+
+    /* search everything if search is null */
+    if (!search) {
+        if (head < screen_num_monitors) search = &monitor_area[head];
+        else search = &monitor_area[screen_num_monitors];
+    }
+    if (head == SCREEN_AREA_ONE_MONITOR) head = screen_find_monitor(search);
+
+    /* al is "all left" meaning the furthest left you can get, l is our
+       "working left" meaning our current strut edge which we're calculating
+    */
+
+    /* only include monitors which the search area lines up with */
+    if (RECT_INTERSECTS_RECT(monitor_area[screen_num_monitors], *search)) {
+        al = l = RECT_RIGHT(monitor_area[screen_num_monitors]);
+        at = t = RECT_BOTTOM(monitor_area[screen_num_monitors]);
+        ar = r = RECT_LEFT(monitor_area[screen_num_monitors]);
+        ab = b = RECT_TOP(monitor_area[screen_num_monitors]);
+        for (i = 0; i < screen_num_monitors; ++i) {
+            /* add the monitor if applicable */
+            if (RANGES_INTERSECT(search->x, search->width,
+                                 monitor_area[i].x, monitor_area[i].width))
+            {
+                at = t = MIN(t, RECT_TOP(monitor_area[i]));
+                ab = b = MAX(b, RECT_BOTTOM(monitor_area[i]));
+            }
+            if (RANGES_INTERSECT(search->y, search->height,
+                                 monitor_area[i].y, monitor_area[i].height))
+            {
+                al = l = MIN(l, RECT_LEFT(monitor_area[i]));
+                ar = r = MAX(r, RECT_RIGHT(monitor_area[i]));
+            }
+        }
+    } else {
+        al = l = RECT_LEFT(monitor_area[screen_num_monitors]);
+        at = t = RECT_TOP(monitor_area[screen_num_monitors]);
+        ar = r = RECT_RIGHT(monitor_area[screen_num_monitors]);
+        ab = b = RECT_BOTTOM(monitor_area[screen_num_monitors]);
     }
-    return &area[desktop][head];
+
+    for (d = 0; d < screen_num_desktops; ++d) {
+        if (d != desktop && desktop != DESKTOP_ALL) continue;
+
+        for (i = 0; i < screen_num_monitors; ++i) {
+            if (head != SCREEN_AREA_ALL_MONITORS && head != i) continue;
+
+            for (it = struts_left; it; it = g_slist_next(it)) {
+                ObScreenStrut *s = it->data;
+                if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
+                    STRUT_LEFT_IN_SEARCH(s->strut, search) &&
+                    !STRUT_LEFT_IGNORE(s->strut, us, search))
+                    l = MAX(l, al + s->strut->left);
+            }
+            for (it = struts_top; it; it = g_slist_next(it)) {
+                ObScreenStrut *s = it->data;
+                if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
+                    STRUT_TOP_IN_SEARCH(s->strut, search) &&
+                    !STRUT_TOP_IGNORE(s->strut, us, search))
+                    t = MAX(t, at + s->strut->top);
+            }
+            for (it = struts_right; it; it = g_slist_next(it)) {
+                ObScreenStrut *s = it->data;
+                if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
+                    STRUT_RIGHT_IN_SEARCH(s->strut, search) &&
+                    !STRUT_RIGHT_IGNORE(s->strut, us, search))
+                    r = MIN(r, ar - s->strut->right);
+            }
+            for (it = struts_bottom; it; it = g_slist_next(it)) {
+                ObScreenStrut *s = it->data;
+                if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
+                    STRUT_BOTTOM_IN_SEARCH(s->strut, search) &&
+                    !STRUT_BOTTOM_IGNORE(s->strut, us, search))
+                    b = MIN(b, ab - s->strut->bottom);
+            }
+
+            /* limit to this monitor */
+            if (head == i) {
+                l = MAX(l, RECT_LEFT(monitor_area[i]));
+                t = MAX(t, RECT_TOP(monitor_area[i]));
+                r = MIN(r, RECT_RIGHT(monitor_area[i]));
+                b = MIN(b, RECT_BOTTOM(monitor_area[i]));
+            }
+        }
+    }
+
+    a = g_new(Rect, 1);
+    a->x = l;
+    a->y = t;
+    a->width = r - l + 1;
+    a->height = b - t + 1;
+    return a;
 }
 
 guint screen_find_monitor(Rect *search)
 {
     guint i;
-    guint most = 0;
+    guint most = screen_num_monitors;
     guint mostv = 0;
 
     for (i = 0; i < screen_num_monitors; ++i) {
@@ -1238,27 +1648,58 @@ guint screen_find_monitor(Rect *search)
                 most = i;
             }
         }
+        g_free(area);
     }
     return most;
 }
 
-Rect *screen_physical_area()
+Rect* screen_physical_area_all_monitors(void)
 {
     return screen_physical_area_monitor(screen_num_monitors);
 }
 
-Rect *screen_physical_area_monitor(guint head)
+Rect* screen_physical_area_monitor(guint head)
+{
+    Rect *a;
+    g_assert(head <= screen_num_monitors);
+
+    a = g_new(Rect, 1);
+    *a = monitor_area[head];
+    return a;
+}
+
+gboolean screen_physical_area_monitor_contains(guint head, Rect *search)
 {
-    if (head > screen_num_monitors)
-        return NULL;
-    return &monitor_area[head];
+    g_assert(head <= screen_num_monitors);
+    g_assert(search);
+    return RECT_INTERSECTS_RECT(monitor_area[head], *search);
+}
+
+Rect* screen_physical_area_active(void)
+{
+    Rect *a;
+    gint x, y;
+
+    if (moveresize_client)
+        a = screen_physical_area_monitor(client_monitor(focus_client));
+    else if (focus_client)
+        a = screen_physical_area_monitor(client_monitor(focus_client));
+    else {
+        Rect mon;
+        if (screen_pointer_pos(&x, &y))
+            RECT_SET(mon, x, y, 1, 1);
+        else
+            RECT_SET(mon, 0, 0, 1, 1);
+        a = screen_physical_area_monitor(screen_find_monitor(&mon));
+    }
+    return a;
 }
 
-void screen_set_root_cursor()
+void screen_set_root_cursor(void)
 {
     if (sn_app_starting())
         XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
-                      ob_cursor(OB_CURSOR_BUSY));
+                      ob_cursor(OB_CURSOR_BUSYPOINTER));
     else
         XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
                       ob_cursor(OB_CURSOR_POINTER));