move the code for tracking what extensions are available on the display into the...
[dana/openbox.git] / openbox / screen.c
index 08b2d74..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();
+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;
 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_cycle_popup;
+static ObPagerPopup *desktop_popup;
 
-static gboolean replace_wm()
+/*! 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(void)
 {
     gchar *wm_sn;
     Atom wm_sn_atom;
@@ -89,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);
@@ -156,7 +153,7 @@ static gboolean replace_wm()
     return TRUE;
 }
 
-gboolean screen_annex()
+gboolean screen_annex(void)
 {
     XSetWindowAttributes attrib;
     pid_t pid;
@@ -166,12 +163,14 @@ gboolean screen_annex()
 
     /* 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);
 
@@ -180,12 +179,11 @@ gboolean screen_annex()
         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);
 
@@ -277,7 +275,9 @@ gboolean screen_annex()
     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;
@@ -286,6 +286,8 @@ gboolean screen_annex()
     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;
@@ -294,7 +296,7 @@ gboolean screen_annex()
     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.ob_config;
+    supported[i++] = prop_atoms.ob_theme;
     supported[i++] = prop_atoms.ob_control;
     g_assert(i == num_support);
 
@@ -302,21 +304,57 @@ gboolean screen_annex()
                 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)
 {
     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) {
         /* update the pager popup's width */
-        pager_popup_text_width_to_strings(desktop_cycle_popup,
+        pager_popup_text_width_to_strings(desktop_popup,
                                           screen_desktop_names,
                                           screen_num_desktops);
         return;
@@ -399,9 +437,7 @@ void screen_startup(gboolean reconfig)
 
 void screen_shutdown(gboolean reconfig)
 {
-    Rect **r;
-
-    pager_popup_free(desktop_cycle_popup);
+    pager_popup_free(desktop_popup);
 
     if (reconfig)
         return;
@@ -420,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;
@@ -461,7 +492,7 @@ void screen_set_num_desktops(guint num)
 {
     guint old;
     gulong *viewport;
-    GList *it;
+    GList *it, *stacking_copy;
 
     g_assert(num > 0);
 
@@ -481,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();
 
@@ -500,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;
-     
-    g_assert(num < screen_num_desktops);
+    guint previous;
+    gulong ignore_start;
 
-    if (old == num) return;
+    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);
 
-    screen_last_desktop = old;
+    /* 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. */
+
+        /* 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 */
 
@@ -532,30 +685,7 @@ void screen_set_desktop(guint num, gboolean dofocus)
         }
     }
 
-    if (focus_client && (focus_client->desktop == DESKTOP_ALL ||
-                         focus_client->desktop == screen_desktop))
-        dofocus = FALSE;
-
-    /* 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(TRUE)))
-    {
-        /* 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);
-        }
-    }
+    if (dofocus) screen_fallback_focus();
 
     /* hide windows from bottom to top */
     for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
@@ -565,10 +695,100 @@ void screen_set_desktop(guint num, gboolean dofocus)
         }
     }
 
-    event_ignore_all_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)
@@ -679,43 +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, oldd;
-
-    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;
 
-    oldd = d;
+    d = from;
     get_row_col(d, &r, &c);
-
     if (linear) {
         switch (dir) {
         case OB_DIRECTION_EAST:
@@ -723,16 +951,20 @@ 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) {
@@ -742,16 +974,14 @@ guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
                 if (wrap)
                     c = 0;
                 else
-                    goto show_cycle_dialog;
+                    return from;
             }
             d = translate_row_col(r, c);
             if (d >= screen_num_desktops) {
-                if (wrap) {
+                if (wrap)
                     ++c;
-                } else {
-                    d = oldd;
-                    goto show_cycle_dialog;
-                }
+                else
+                    return from;
             }
             break;
         case OB_DIRECTION_WEST:
@@ -760,16 +990,14 @@ guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
                 if (wrap)
                     c = screen_desktop_layout.columns - 1;
                 else
-                    goto show_cycle_dialog;
+                    return from;
             }
             d = translate_row_col(r, c);
             if (d >= screen_num_desktops) {
-                if (wrap) {
+                if (wrap)
                     --c;
-                } else {
-                    d = oldd;
-                    goto show_cycle_dialog;
-                }
+                else
+                    return from;
             }
             break;
         case OB_DIRECTION_SOUTH:
@@ -778,16 +1006,14 @@ guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
                 if (wrap)
                     r = 0;
                 else
-                    goto show_cycle_dialog;
+                    return from;
             }
             d = translate_row_col(r, c);
             if (d >= screen_num_desktops) {
-                if (wrap) {
+                if (wrap)
                     ++r;
-                } else {
-                    d = oldd;
-                    goto show_cycle_dialog;
-                }
+                else
+                    return from;
             }
             break;
         case OB_DIRECTION_NORTH:
@@ -796,37 +1022,24 @@ guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
                 if (wrap)
                     r = screen_desktop_layout.rows - 1;
                 else
-                    goto show_cycle_dialog;
+                    return from;
             }
             d = translate_row_col(r, c);
             if (d >= screen_num_desktops) {
-                if (wrap) {
+                if (wrap)
                     --r;
-                } else {
-                    d = oldd;
-                    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);
     }
-
-show_cycle_dialog:
-    if (dialog && !cancel && !done) {
-        screen_desktop_popup(d, TRUE);
-    } else
-        screen_desktop_popup(0, FALSE);
-    ret = d;
-
-    if (!dialog || cancel || done)
-        d = (guint)-1;
-
-    return ret;
+    return d;
 }
 
 static gboolean screen_validate_layout(ObDesktopLayout *l)
@@ -866,7 +1079,7 @@ static gboolean screen_validate_layout(ObDesktopLayout *l)
     return TRUE;
 }
 
-void screen_update_layout()
+void screen_update_layout(void)
 
 {
     ObDesktopLayout l;
@@ -881,7 +1094,7 @@ void screen_update_layout()
     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)
                 l.orientation = OB_ORIENTATION_VERT;
             else if (data[0] == prop_atoms.net_wm_orientation_horz)
@@ -915,7 +1128,7 @@ void screen_update_layout()
     }
 }
 
-void screen_update_desktop_names()
+void screen_update_desktop_names(void)
 {
     guint i;
 
@@ -955,7 +1168,7 @@ void screen_update_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);
 }
@@ -963,7 +1176,7 @@ void screen_update_desktop_names()
 void screen_show_desktop(gboolean show, ObClient *show_only)
 {
     GList *it;
-     
+
     if (show == screen_showing_desktop) return; /* no change */
 
     screen_showing_desktop = show;
@@ -1005,7 +1218,7 @@ void screen_show_desktop(gboolean show, ObClient *show_only)
     else if (!show_only) {
         ObClient *c;
 
-        if ((c = focus_fallback(TRUE))) {
+        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.. */
@@ -1024,271 +1237,401 @@ void screen_show_desktop(gboolean show, ObClient *show_only)
 
 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 */
+
+    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.
+    */
 
-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;
+    /* 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) {
@@ -1305,23 +1648,54 @@ 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),