add restart action
[dana/openbox-history.git] / openbox / action.c
index 804fe0b..0c241b4 100644 (file)
@@ -20,6 +20,7 @@
 #include "debug.h"
 #include "client.h"
 #include "focus.h"
+#include "focus_cycle.h"
 #include "moveresize.h"
 #include "menu.h"
 #include "prop.h"
 
 #include <glib.h>
 
-inline void client_action_start(union ActionData *data)
+static gulong ignore_start = 0;
+
+static void client_action_start(union ActionData *data)
 {
-    if (config_focus_follow)
-        if (data->any.context != OB_FRAME_CONTEXT_CLIENT && !data->any.button)
-            grab_pointer(TRUE, FALSE, OB_CURSOR_NONE);
+    ignore_start = event_start_ignore_all_enters();
 }
 
-inline void client_action_end(union ActionData *data)
+static void client_action_end(union ActionData *data, gboolean allow_enters)
 {
     if (config_focus_follow)
-        if (data->any.context != OB_FRAME_CONTEXT_CLIENT && !data->any.button)
-            grab_pointer(FALSE, FALSE, OB_CURSOR_NONE);
+        if (data->any.context != OB_FRAME_CONTEXT_CLIENT) {
+            if (!data->any.button && data->any.c && !allow_enters) {
+                event_end_ignore_all_enters(ignore_start);
+            } else {
+                ObClient *c;
+
+                /* usually this is sorta redundant, but with a press action
+                   that moves windows our from under the cursor, the enter
+                   event will come as a GrabNotify which is ignored, so this
+                   makes a fake enter event
+                */
+                if ((c = client_under_pointer()) && c != data->any.c) {
+                    ob_debug_type(OB_DEBUG_FOCUS,
+                                  "Generating fake enter because we did a "
+                                  "mouse-event action");
+                    event_enter_client(c);
+                }
+            }
+        }
 }
 
 typedef struct
@@ -82,6 +100,8 @@ void action_unref(ObAction *a)
     /* deal with pointers */
     if (a->func == action_execute || a->func == action_restart)
         g_free(a->data.execute.path);
+    else if (a->func == action_debug)
+        g_free(a->data.debug.string);
     else if (a->func == action_showmenu)
         g_free(a->data.showmenu.name);
 
@@ -97,6 +117,8 @@ ObAction* action_copy(const ObAction *src)
     /* deal with pointers */
     if (a->func == action_execute || a->func == action_restart)
         a->data.execute.path = g_strdup(a->data.execute.path);
+    else if (a->func == action_debug)
+        a->data.debug.string = g_strdup(a->data.debug.string);
     else if (a->func == action_showmenu)
         a->data.showmenu.name = g_strdup(a->data.showmenu.name);
 
@@ -109,6 +131,7 @@ void setup_action_directional_focus_north(ObAction **a, ObUserAction uact)
     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTH;
     (*a)->data.interdiraction.dialog = TRUE;
     (*a)->data.interdiraction.dock_windows = FALSE;
+    (*a)->data.interdiraction.desktop_windows = FALSE;
 }
 
 void setup_action_directional_focus_east(ObAction **a, ObUserAction uact)
@@ -117,6 +140,7 @@ void setup_action_directional_focus_east(ObAction **a, ObUserAction uact)
     (*a)->data.interdiraction.direction = OB_DIRECTION_EAST;
     (*a)->data.interdiraction.dialog = TRUE;
     (*a)->data.interdiraction.dock_windows = FALSE;
+    (*a)->data.interdiraction.desktop_windows = FALSE;
 }
 
 void setup_action_directional_focus_south(ObAction **a, ObUserAction uact)
@@ -125,6 +149,7 @@ void setup_action_directional_focus_south(ObAction **a, ObUserAction uact)
     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTH;
     (*a)->data.interdiraction.dialog = TRUE;
     (*a)->data.interdiraction.dock_windows = FALSE;
+    (*a)->data.interdiraction.desktop_windows = FALSE;
 }
 
 void setup_action_directional_focus_west(ObAction **a, ObUserAction uact)
@@ -133,6 +158,7 @@ void setup_action_directional_focus_west(ObAction **a, ObUserAction uact)
     (*a)->data.interdiraction.direction = OB_DIRECTION_WEST;
     (*a)->data.interdiraction.dialog = TRUE;
     (*a)->data.interdiraction.dock_windows = FALSE;
+    (*a)->data.interdiraction.desktop_windows = FALSE;
 }
 
 void setup_action_directional_focus_northeast(ObAction **a, ObUserAction uact)
@@ -141,6 +167,7 @@ void setup_action_directional_focus_northeast(ObAction **a, ObUserAction uact)
     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHEAST;
     (*a)->data.interdiraction.dialog = TRUE;
     (*a)->data.interdiraction.dock_windows = FALSE;
+    (*a)->data.interdiraction.desktop_windows = FALSE;
 }
 
 void setup_action_directional_focus_southeast(ObAction **a, ObUserAction uact)
@@ -149,6 +176,7 @@ void setup_action_directional_focus_southeast(ObAction **a, ObUserAction uact)
     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHEAST;
     (*a)->data.interdiraction.dialog = TRUE;
     (*a)->data.interdiraction.dock_windows = FALSE;
+    (*a)->data.interdiraction.desktop_windows = FALSE;
 }
 
 void setup_action_directional_focus_southwest(ObAction **a, ObUserAction uact)
@@ -157,6 +185,7 @@ void setup_action_directional_focus_southwest(ObAction **a, ObUserAction uact)
     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHWEST;
     (*a)->data.interdiraction.dialog = TRUE;
     (*a)->data.interdiraction.dock_windows = FALSE;
+    (*a)->data.interdiraction.desktop_windows = FALSE;
 }
 
 void setup_action_directional_focus_northwest(ObAction **a, ObUserAction uact)
@@ -165,6 +194,7 @@ void setup_action_directional_focus_northwest(ObAction **a, ObUserAction uact)
     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHWEST;
     (*a)->data.interdiraction.dialog = TRUE;
     (*a)->data.interdiraction.dock_windows = FALSE;
+    (*a)->data.interdiraction.desktop_windows = FALSE;
 }
 
 void setup_action_send_to_desktop(ObAction **a, ObUserAction uact)
@@ -235,7 +265,9 @@ void setup_action_send_to_desktop_down(ObAction **a, ObUserAction uact)
 
 void setup_action_desktop(ObAction **a, ObUserAction uact)
 {
+/*
     (*a)->data.desktop.inter.any.interactive = FALSE;
+*/
 }
 
 void setup_action_desktop_prev(ObAction **a, ObUserAction uact)
@@ -293,6 +325,7 @@ void setup_action_cycle_windows_next(ObAction **a, ObUserAction uact)
     (*a)->data.cycle.forward = TRUE;
     (*a)->data.cycle.dialog = TRUE;
     (*a)->data.cycle.dock_windows = FALSE;
+    (*a)->data.cycle.desktop_windows = FALSE;
     (*a)->data.cycle.all_desktops = FALSE;
 }
 
@@ -303,6 +336,7 @@ void setup_action_cycle_windows_previous(ObAction **a, ObUserAction uact)
     (*a)->data.cycle.forward = FALSE;
     (*a)->data.cycle.dialog = TRUE;
     (*a)->data.cycle.dock_windows = FALSE;
+    (*a)->data.cycle.desktop_windows = FALSE;
     (*a)->data.cycle.all_desktops = FALSE;
 }
 
@@ -407,33 +441,31 @@ void setup_action_bottom_layer(ObAction **a, ObUserAction uact)
 void setup_action_move(ObAction **a, ObUserAction uact)
 {
     (*a)->data.moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS;
-    (*a)->data.moveresize.move = TRUE;
     (*a)->data.moveresize.keyboard =
         (uact == OB_USER_ACTION_NONE ||
          uact == OB_USER_ACTION_KEYBOARD_KEY ||
          uact == OB_USER_ACTION_MENU_SELECTION);
+    (*a)->data.moveresize.corner = 0;
 }
 
 void setup_action_resize(ObAction **a, ObUserAction uact)
 {
     (*a)->data.moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS;
-    (*a)->data.moveresize.move = FALSE;
     (*a)->data.moveresize.keyboard =
         (uact == OB_USER_ACTION_NONE ||
          uact == OB_USER_ACTION_KEYBOARD_KEY ||
          uact == OB_USER_ACTION_MENU_SELECTION);
+    (*a)->data.moveresize.corner = 0;
 }
 
-void setup_action_showmenu(ObAction **a, ObUserAction uact)
+void setup_action_addremove_desktop_current(ObAction **a, ObUserAction uact)
 {
-    (*a)->data.showmenu.any.client_action = OB_CLIENT_ACTION_OPTIONAL;
-    /* you cannot call ShowMenu from inside a menu, cuz the menu code makes
-       assumptions that there is only one menu (and submenus) open at
-       a time! */
-    if (uact == OB_USER_ACTION_MENU_SELECTION) {
-        action_unref(*a);
-        *a = NULL;
-    }
+    (*a)->data.addremovedesktop.current = TRUE;
+}
+
+void setup_action_addremove_desktop_last(ObAction **a, ObUserAction uact)
+{
+    (*a)->data.addremovedesktop.current = FALSE;
 }
 
 void setup_action_focus(ObAction **a, ObUserAction uact)
@@ -449,11 +481,6 @@ void setup_client_action(ObAction **a, ObUserAction uact)
 ActionString actionstrings[] =
 {
     {
-        "execute", 
-        action_execute, 
-        NULL
-    },
-    {
         "directionalfocusnorth", 
         action_directional_focus, 
         setup_action_directional_focus_north
@@ -735,12 +762,12 @@ ActionString actionstrings[] =
     },
     {
         "move",
-        action_moveresize,
+        action_move,
         setup_action_move
     },
     {
         "resize",
-        action_moveresize,
+        action_resize,
         setup_action_resize
     },
     {
@@ -749,46 +776,11 @@ ActionString actionstrings[] =
         NULL
     },
     {
-        "toggleshowdesktop",
-        action_toggle_show_desktop,
-        NULL
-    },
-    {
-        "showdesktop",
-        action_show_desktop,
-        NULL
-    },
-    {
-        "unshowdesktop",
-        action_unshow_desktop,
-        NULL
-    },
-    {
         "desktoplast",
         action_desktop_last,
         NULL
     },
     {
-        "reconfigure",
-        action_reconfigure,
-        NULL
-    },
-    {
-        "restart",
-        action_restart,
-        NULL
-    },
-    {
-        "exit",
-        action_exit,
-        NULL
-    },
-    {
-        "showmenu",
-        action_showmenu,
-        setup_action_showmenu
-    },
-    {
         "sendtotoplayer",
         action_send_to_layer,
         setup_action_top_layer
@@ -889,6 +881,26 @@ ActionString actionstrings[] =
         NULL
     },
     {
+        "adddesktoplast",
+        action_add_desktop,
+        setup_action_addremove_desktop_last
+    },
+    {
+        "removedesktoplast",
+        action_remove_desktop,
+        setup_action_addremove_desktop_last
+    },
+    {
+        "adddesktopcurrent",
+        action_add_desktop,
+        setup_action_addremove_desktop_current
+    },
+    {
+        "removedesktopcurrent",
+        action_remove_desktop,
+        setup_action_addremove_desktop_current
+    },
+    {
         NULL,
         NULL,
         NULL
@@ -936,24 +948,6 @@ ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
 
     if (parse_attr_string("name", node, &actname)) {
         if ((act = action_from_string(actname, uact))) {
-            if (act->func == action_execute || act->func == action_restart) {
-                if ((n = parse_find_node("execute", node->xmlChildrenNode))) {
-                    gchar *s = parse_string(doc, n);
-                    act->data.execute.path = parse_expand_tilde(s);
-                    g_free(s);
-                }
-                if ((n = parse_find_node("startupnotify", node->xmlChildrenNode))) {
-                    xmlNodePtr m;
-                    if ((m = parse_find_node("enabled", n->xmlChildrenNode)))
-                        act->data.execute.startupnotify = parse_bool(doc, m);
-                    if ((m = parse_find_node("name", n->xmlChildrenNode)))
-                        act->data.execute.name = parse_string(doc, m);
-                    if ((m = parse_find_node("icon", n->xmlChildrenNode)))
-                        act->data.execute.icon_name = parse_string(doc, m);
-                }
-            } else if (act->func == action_showmenu) {
-                if ((n = parse_find_node("menu", node->xmlChildrenNode)))
-                    act->data.showmenu.name = parse_string(doc, n);
             } else if (act->func == action_move_relative_horz ||
                        act->func == action_move_relative_vert ||
                        act->func == action_resize_relative_horz ||
@@ -978,9 +972,11 @@ ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
                     act->data.desktop.desk = parse_int(doc, n);
                 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
+/*
                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
                     act->data.desktop.inter.any.interactive =
                         parse_bool(doc, n);
+*/
            } else if (act->func == action_send_to_desktop) {
                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
                     act->data.sendto.desk = parse_int(doc, n);
@@ -1011,6 +1007,8 @@ ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
                     act->data.cycle.dialog = parse_bool(doc, n);
                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
                     act->data.cycle.dock_windows = parse_bool(doc, n);
+                if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
+                    act->data.cycle.desktop_windows = parse_bool(doc, n);
                 if ((n = parse_find_node("allDesktops",
                                          node->xmlChildrenNode)))
                     act->data.cycle.all_desktops = parse_bool(doc, n);
@@ -1019,6 +1017,38 @@ ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
                     act->data.interdiraction.dialog = parse_bool(doc, n);
                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
                     act->data.interdiraction.dock_windows = parse_bool(doc, n);
+                if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
+                    act->data.interdiraction.desktop_windows =
+                        parse_bool(doc, n);
+            } else if (act->func == action_resize) {
+                if ((n = parse_find_node("edge", node->xmlChildrenNode))) {
+                    gchar *s = parse_string(doc, n);
+                    if (!g_ascii_strcasecmp(s, "top"))
+                        act->data.moveresize.corner =
+                            prop_atoms.net_wm_moveresize_size_top;
+                    else if (!g_ascii_strcasecmp(s, "bottom"))
+                        act->data.moveresize.corner =
+                            prop_atoms.net_wm_moveresize_size_bottom;
+                    else if (!g_ascii_strcasecmp(s, "left"))
+                        act->data.moveresize.corner =
+                            prop_atoms.net_wm_moveresize_size_left;
+                    else if (!g_ascii_strcasecmp(s, "right"))
+                        act->data.moveresize.corner =
+                            prop_atoms.net_wm_moveresize_size_right;
+                    else if (!g_ascii_strcasecmp(s, "topleft"))
+                        act->data.moveresize.corner =
+                            prop_atoms.net_wm_moveresize_size_topleft;
+                    else if (!g_ascii_strcasecmp(s, "topright"))
+                        act->data.moveresize.corner =
+                            prop_atoms.net_wm_moveresize_size_topright;
+                    else if (!g_ascii_strcasecmp(s, "bottomleft"))
+                        act->data.moveresize.corner =
+                            prop_atoms.net_wm_moveresize_size_bottomleft;
+                    else if (!g_ascii_strcasecmp(s, "bottomright"))
+                        act->data.moveresize.corner =
+                            prop_atoms.net_wm_moveresize_size_bottomright;
+                    g_free(s);
+                }
             } else if (act->func == action_raise ||
                        act->func == action_lower ||
                        act->func == action_raiselower ||
@@ -1038,7 +1068,6 @@ void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
 {
     GSList *it;
     ObAction *a;
-    gboolean inter = FALSE;
 
     if (!acts)
         return;
@@ -1046,25 +1075,6 @@ void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
     if (x < 0 && y < 0)
         screen_pointer_pos(&x, &y);
 
-    if (grab_on_keyboard())
-        inter = TRUE;
-    else
-        for (it = acts; it; it = g_slist_next(it)) {
-            a = it->data;
-            if (a->data.any.interactive) {
-                inter = TRUE;
-                break;
-            }
-        }
-
-    if (!inter) {
-        /* sometimes when we execute another app as an action,
-           it won't work right unless we XUngrabKeyboard first,
-           even though we grabbed the key/button Asychronously.
-           e.g. "gnome-panel-control --main-menu" */
-        grab_keyboard(FALSE);
-    }
-
     for (it = acts; it; it = g_slist_next(it)) {
         a = it->data;
 
@@ -1088,16 +1098,21 @@ void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
 
             /* XXX UGLY HACK race with motion event starting a move and the
                button release gettnig processed first. answer: don't queue
-               moveresize starts. UGLY HACK XXX */
-            if (a->data.any.interactive || a->func == action_moveresize) {
+               moveresize starts. UGLY HACK XXX
+
+               XXX ALSO don't queue showmenu events, because on button press
+               events we need to know if a mouse grab is going to take place,
+               and set the button to 0, so that later motion events don't think
+               that a drag is going on. since showmenu grabs the pointer..
+            */
+            if (a->data.any.interactive || a->func == action_move ||
+                a->func == action_resize || a->func == action_showmenu)
+            {
                 /* interactive actions are not queued */
                 a->func(&a->data);
-            } else if ((context == OB_FRAME_CONTEXT_CLIENT ||
-                        (c && c->type == OB_CLIENT_TYPE_DESKTOP &&
-                         context == OB_FRAME_CONTEXT_DESKTOP)) &&
-                       (a->func == action_focus ||
-                        a->func == action_activate ||
-                        a->func == action_showmenu))
+            } else if (a->func == action_focus ||
+                       a->func == action_activate ||
+                       a->func == action_showmenu)
             {
                 /* XXX MORE UGLY HACK
                    actions from clicks on client windows are NOT queued.
@@ -1117,11 +1132,15 @@ void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
                    pointer. ugh.
 
                    also with the menus, there is a race going on. if the
-                   desktop wants to pop up a menu, and we do to, we send them
+                   desktop wants to pop up a menu, and we do too, we send them
                    the button before we pop up the menu, so they pop up their
                    menu first. but not always. if we pop up our menu before
                    sending them the button press, then the result is
                    deterministic. yay.
+
+                   XXX further more. focus actions are not queued at all,
+                   because if you bind focus->showmenu, the menu will get
+                   hidden to do the focusing
                 */
                 a->func(&a->data);
             } else
@@ -1143,64 +1162,12 @@ void action_run_string(const gchar *name, struct _ObClient *c, Time time)
     action_run(l, c, 0, time);
 }
 
-void action_execute(union ActionData *data)
-{
-    GError *e = NULL;
-    gchar *cmd, **argv = 0;
-    if (data->execute.path) {
-        cmd = g_filename_from_utf8(data->execute.path, -1, NULL, NULL, NULL);
-        if (cmd) {
-            if (!g_shell_parse_argv (cmd, NULL, &argv, &e)) {
-                g_message(_("Failed to execute '%s': %s"),
-                          cmd, e->message);
-                g_error_free(e);
-            } else if (data->execute.startupnotify) {
-                gchar *program;
-                
-                program = g_path_get_basename(argv[0]);
-                /* sets up the environment */
-                sn_setup_spawn_environment(program,
-                                           data->execute.name,
-                                           data->execute.icon_name,
-                                           /* launch it on the current
-                                              desktop */
-                                           screen_desktop,
-                                           data->execute.any.time);
-                if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH |
-                                   G_SPAWN_DO_NOT_REAP_CHILD,
-                                   NULL, NULL, NULL, &e)) {
-                    g_message(_("Failed to execute '%s': %s"),
-                              cmd, e->message);
-                    g_error_free(e);
-                    sn_spawn_cancel();
-                }
-                unsetenv("DESKTOP_STARTUP_ID");
-                g_free(program);
-                g_strfreev(argv);
-            } else {
-                if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH |
-                                   G_SPAWN_DO_NOT_REAP_CHILD,
-                                   NULL, NULL, NULL, &e))
-                {
-                    g_message(_("Failed to execute '%s': %s"),
-                              cmd, e->message);
-                    g_error_free(e);
-                }
-                g_strfreev(argv);
-            }
-            g_free(cmd);
-        } else {
-            g_message(_("Failed to convert the path '%s' from utf8"),
-                      data->execute.path);
-        }
-    }
-}
-
 void action_activate(union ActionData *data)
 {
     if (data->client.any.c) {
         if (!data->any.button || client_mouse_focusable(data->client.any.c) ||
-            data->any.context != OB_FRAME_CONTEXT_CLIENT)
+            (data->any.context != OB_FRAME_CONTEXT_CLIENT &&
+             data->any.context != OB_FRAME_CONTEXT_FRAME))
         {
             /* if using focus_delay, stop the timer now so that focus doesn't
                go moving on us */
@@ -1220,7 +1187,8 @@ void action_focus(union ActionData *data)
 {
     if (data->client.any.c) {
         if (!data->any.button || client_mouse_focusable(data->client.any.c) ||
-            data->any.context != OB_FRAME_CONTEXT_CLIENT)
+            (data->any.context != OB_FRAME_CONTEXT_CLIENT &&
+             data->any.context != OB_FRAME_CONTEXT_FRAME))
         {
             /* if using focus_delay, stop the timer now so that focus doesn't
                go moving on us */
@@ -1239,14 +1207,14 @@ void action_focus(union ActionData *data)
 void action_unfocus (union ActionData *data)
 {
     if (data->client.any.c == focus_client)
-        focus_fallback(FALSE);
+        focus_fallback(FALSE, FALSE, TRUE);
 }
 
 void action_iconify(union ActionData *data)
 {
     client_action_start(data);
     client_iconify(data->client.any.c, TRUE, TRUE, FALSE);
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_focus_order_to_bottom(union ActionData *data)
@@ -1257,38 +1225,17 @@ void action_focus_order_to_bottom(union ActionData *data)
 void action_raiselower(union ActionData *data)
 {
     ObClient *c = data->client.any.c;
-    GList *it;
-    gboolean raise = FALSE;
 
-    for (it = stacking_list; it; it = g_list_next(it)) {
-        if (WINDOW_IS_CLIENT(it->data)) {
-            ObClient *cit = it->data;
-
-            if (cit == c) break;
-            if (client_normal(cit) == client_normal(c) &&
-                cit->layer == c->layer &&
-                cit->frame->visible &&
-                !client_search_transient(c, cit))
-            {
-                if (RECT_INTERSECTS_RECT(cit->frame->area, c->frame->area)) {
-                    raise = TRUE;
-                    break;
-                }
-            }
-        }
-    }
-
-    if (raise)
-        action_raise(data);
-    else
-        action_lower(data);
+    client_action_start(data);
+    stacking_restack_request(c, NULL, Opposite);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_raise(union ActionData *data)
 {
     client_action_start(data);
     stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_unshaderaise(union ActionData *data)
@@ -1311,7 +1258,7 @@ void action_lower(union ActionData *data)
 {
     client_action_start(data);
     stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_close(union ActionData *data)
@@ -1328,28 +1275,28 @@ void action_shade(union ActionData *data)
 {
     client_action_start(data);
     client_shade(data->client.any.c, TRUE);
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_unshade(union ActionData *data)
 {
     client_action_start(data);
     client_shade(data->client.any.c, FALSE);
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_toggle_shade(union ActionData *data)
 {
     client_action_start(data);
     client_shade(data->client.any.c, !data->client.any.c->shaded);
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_toggle_omnipresent(union ActionData *data)
 { 
     client_set_desktop(data->client.any.c,
                        data->client.any.c->desktop == DESKTOP_ALL ?
-                       screen_desktop : DESKTOP_ALL, FALSE);
+                       screen_desktop : DESKTOP_ALL, FALSE, TRUE);
 }
 
 void action_move_relative_horz(union ActionData *data)
@@ -1357,7 +1304,7 @@ void action_move_relative_horz(union ActionData *data)
     ObClient *c = data->relative.any.c;
     client_action_start(data);
     client_move(c, c->area.x + data->relative.deltax, c->area.y);
-    client_action_end(data);
+    client_action_end(data, FALSE);
 }
 
 void action_move_relative_vert(union ActionData *data)
@@ -1365,18 +1312,19 @@ void action_move_relative_vert(union ActionData *data)
     ObClient *c = data->relative.any.c;
     client_action_start(data);
     client_move(c, c->area.x, c->area.y + data->relative.deltax);
-    client_action_end(data);
+    client_action_end(data, FALSE);
 }
 
 void action_move_to_center(union ActionData *data)
 {
     ObClient *c = data->client.any.c;
     Rect *area;
-    area = screen_area_monitor(c->desktop, 0);
+    area = screen_area(c->desktop, client_monitor(c), NULL);
     client_action_start(data);
-    client_move(c, area->width / 2 - c->area.width / 2,
-                area->height / 2 - c->area.height / 2);
-    client_action_end(data);
+    client_move(c, area->x + area->width / 2 - c->area.width / 2,
+                area->y + area->height / 2 - c->area.height / 2);
+    client_action_end(data, FALSE);
+    g_free(area);
 }
 
 void action_resize_relative_horz(union ActionData *data)
@@ -1386,7 +1334,7 @@ void action_resize_relative_horz(union ActionData *data)
     client_resize(c,
                   c->area.width + data->relative.deltax * c->size_inc.width,
                   c->area.height);
-    client_action_end(data);
+    client_action_end(data, FALSE);
 }
 
 void action_resize_relative_vert(union ActionData *data)
@@ -1396,7 +1344,7 @@ void action_resize_relative_vert(union ActionData *data)
         client_action_start(data);
         client_resize(c, c->area.width, c->area.height +
                       data->relative.deltax * c->size_inc.height);
-        client_action_end(data);
+        client_action_end(data, FALSE);
     }
 }
 
@@ -1406,42 +1354,51 @@ void action_move_relative(union ActionData *data)
     client_action_start(data);
     client_move(c, c->area.x + data->relative.deltax, c->area.y +
                 data->relative.deltay);
-    client_action_end(data);
+    client_action_end(data, FALSE);
 }
 
 void action_resize_relative(union ActionData *data)
 {
     ObClient *c = data->relative.any.c;
-    gint x, y, ow, w, oh, h, lw, lh;
+    gint x, y, ow, xoff, nw, oh, yoff, nh, lw, lh;
 
     client_action_start(data);
 
     x = c->area.x;
     y = c->area.y;
     ow = c->area.width;
-    w = ow + data->relative.deltax * c->size_inc.width
+    xoff = -data->relative.deltaxl * c->size_inc.width;
+    nw = ow + data->relative.deltax * c->size_inc.width
         + data->relative.deltaxl * c->size_inc.width;
     oh = c->area.height;
-    h = oh + data->relative.deltay * c->size_inc.height
+    yoff = -data->relative.deltayu * c->size_inc.height;
+    nh = oh + data->relative.deltay * c->size_inc.height
         + data->relative.deltayu * c->size_inc.height;
+
+    g_print("deltax %d %d x %d ow %d xoff %d nw %d\n",
+            data->relative.deltax, 
+            data->relative.deltaxl, 
+            x, ow, xoff, nw);
     
-    client_try_configure(c, &x, &y, &w, &h, &lw, &lh, TRUE);
-    client_move_resize(c, x + (ow - w), y + (oh - h), w, h);
-    client_action_end(data);
+    client_try_configure(c, &x, &y, &nw, &nh, &lw, &lh, TRUE);
+    xoff = xoff == 0 ? 0 : (xoff < 0 ? MAX(xoff, ow-nw) : MIN(xoff, ow-nw));
+    yoff = yoff == 0 ? 0 : (yoff < 0 ? MAX(yoff, oh-nh) : MIN(yoff, oh-nh));
+    client_move_resize(c, x + xoff, y + yoff, nw, nh);
+    client_action_end(data, FALSE);
 }
 
 void action_maximize_full(union ActionData *data)
 {
     client_action_start(data);
     client_maximize(data->client.any.c, TRUE, 0);
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_unmaximize_full(union ActionData *data)
 {
     client_action_start(data);
     client_maximize(data->client.any.c, FALSE, 0);
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_toggle_maximize_full(union ActionData *data)
@@ -1451,21 +1408,21 @@ void action_toggle_maximize_full(union ActionData *data)
                     !(data->client.any.c->max_horz ||
                       data->client.any.c->max_vert),
                     0);
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_maximize_horz(union ActionData *data)
 {
     client_action_start(data);
     client_maximize(data->client.any.c, TRUE, 1);
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_unmaximize_horz(union ActionData *data)
 {
     client_action_start(data);
     client_maximize(data->client.any.c, FALSE, 1);
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_toggle_maximize_horz(union ActionData *data)
@@ -1473,21 +1430,21 @@ void action_toggle_maximize_horz(union ActionData *data)
     client_action_start(data);
     client_maximize(data->client.any.c,
                     !data->client.any.c->max_horz, 1);
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_maximize_vert(union ActionData *data)
 {
     client_action_start(data);
     client_maximize(data->client.any.c, TRUE, 2);
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_unmaximize_vert(union ActionData *data)
 {
     client_action_start(data);
     client_maximize(data->client.any.c, FALSE, 2);
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_toggle_maximize_vert(union ActionData *data)
@@ -1495,14 +1452,14 @@ void action_toggle_maximize_vert(union ActionData *data)
     client_action_start(data);
     client_maximize(data->client.any.c,
                     !data->client.any.c->max_vert, 2);
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_toggle_fullscreen(union ActionData *data)
 {
     client_action_start(data);
     client_fullscreen(data->client.any.c, !(data->client.any.c->fullscreen));
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_send_to_desktop(union ActionData *data)
@@ -1513,26 +1470,23 @@ void action_send_to_desktop(union ActionData *data)
 
     if (data->sendto.desk < screen_num_desktops ||
         data->sendto.desk == DESKTOP_ALL) {
-        client_set_desktop(c, data->sendto.desk, data->sendto.follow);
-        if (data->sendto.follow)
+        client_set_desktop(c, data->sendto.desk, data->sendto.follow, FALSE);
+        if (data->sendto.follow && data->sendto.desk != screen_desktop)
             screen_set_desktop(data->sendto.desk, TRUE);
     }
 }
 
 void action_desktop(union ActionData *data)
 {
-    if (!data->inter.any.interactive ||
-        (!data->inter.cancel && !data->inter.final))
+    /* XXX add the interactive/dialog option back again once the dialog
+       has been made to not use grabs */
+    if (data->desktop.desk < screen_num_desktops ||
+        data->desktop.desk == DESKTOP_ALL)
     {
-        if (data->desktop.desk < screen_num_desktops ||
-            data->desktop.desk == DESKTOP_ALL)
-        {
-            screen_set_desktop(data->desktop.desk, TRUE);
-            if (data->inter.any.interactive)
-                screen_desktop_popup(data->desktop.desk, TRUE);
-        }
-    } else
-        screen_desktop_popup(0, FALSE);
+        screen_set_desktop(data->desktop.desk, TRUE);
+        if (data->inter.any.interactive)
+            screen_desktop_popup(data->desktop.desk, TRUE);
+    }
 }
 
 void action_desktop_dir(union ActionData *data)
@@ -1551,7 +1505,8 @@ void action_desktop_dir(union ActionData *data)
     if (!data->sendtodir.inter.any.interactive ||
         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
     {
-        if (d != screen_desktop) screen_set_desktop(d, TRUE);
+        if (d != screen_desktop)
+            screen_set_desktop(d, TRUE);
     }
 }
 
@@ -1573,7 +1528,7 @@ void action_send_to_desktop_dir(union ActionData *data)
     if (!data->sendtodir.inter.any.interactive ||
         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
     {
-        client_set_desktop(c, d, data->sendtodir.follow);
+        client_set_desktop(c, d, data->sendtodir.follow, FALSE);
         if (data->sendtodir.follow && d != screen_desktop)
             screen_set_desktop(d, TRUE);
     }
@@ -1581,7 +1536,8 @@ void action_send_to_desktop_dir(union ActionData *data)
 
 void action_desktop_last(union ActionData *data)
 {
-    screen_set_desktop(screen_last_desktop, TRUE);
+    if (screen_last_desktop < screen_num_desktops)
+        screen_set_desktop(screen_last_desktop, TRUE);
 }
 
 void action_toggle_decorations(union ActionData *data)
@@ -1590,7 +1546,7 @@ void action_toggle_decorations(union ActionData *data)
 
     client_action_start(data);
     client_set_undecorated(c, !c->undecorated);
-    client_action_end(data);
+    client_action_end(data, FALSE);
 }
 
 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch,
@@ -1698,54 +1654,41 @@ static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch,
 #undef d
 }
 
-void action_moveresize(union ActionData *data)
+void action_move(union ActionData *data)
 {
     ObClient *c = data->moveresize.any.c;
     guint32 corner;
 
-    if (data->moveresize.keyboard) {
-        corner = (data->moveresize.move ?
-                  prop_atoms.net_wm_moveresize_move_keyboard :
-                  prop_atoms.net_wm_moveresize_size_keyboard);
-    } else {
-        corner = (data->moveresize.move ?
-                  prop_atoms.net_wm_moveresize_move :
-                  pick_corner(data->any.x, data->any.y,
-                              c->frame->area.x, c->frame->area.y,
-                              /* use the client size because the frame
-                                 can be differently sized (shaded
-                                 windows) and we want this based on the
-                                 clients size */
-                              c->area.width + c->frame->size.left +
-                              c->frame->size.right,
-                              c->area.height + c->frame->size.top +
-                              c->frame->size.bottom, c->shaded));
-    }
+    if (data->moveresize.keyboard)
+        corner = prop_atoms.net_wm_moveresize_move_keyboard;
+    else
+        corner = prop_atoms.net_wm_moveresize_move;
 
     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
 }
 
-void action_reconfigure(union ActionData *data)
+void action_resize(union ActionData *data)
 {
-    ob_reconfigure();
-}
-
-void action_restart(union ActionData *data)
-{
-    ob_restart_other(data->execute.path);
-}
+    ObClient *c = data->moveresize.any.c;
+    guint32 corner;
 
-void action_exit(union ActionData *data)
-{
-    ob_exit(0);
-}
+    if (data->moveresize.keyboard)
+        corner = prop_atoms.net_wm_moveresize_size_keyboard;
+    else if (data->moveresize.corner)
+        corner = data->moveresize.corner; /* it was specified in the binding */
+    else
+        corner = pick_corner(data->any.x, data->any.y,
+                             c->frame->area.x, c->frame->area.y,
+                             /* use the client size because the frame
+                                can be differently sized (shaded
+                                windows) and we want this based on the
+                                clients size */
+                             c->area.width + c->frame->size.left +
+                             c->frame->size.right,
+                             c->area.height + c->frame->size.top +
+                             c->frame->size.bottom, c->shaded);
 
-void action_showmenu(union ActionData *data)
-{
-    if (data->showmenu.name) {
-        menu_show(data->showmenu.name, data->any.x, data->any.y,
-                  data->any.button, data->showmenu.any.c);
-    }
+    moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
 }
 
 void action_cycle_windows(union ActionData *data)
@@ -1757,6 +1700,7 @@ void action_cycle_windows(union ActionData *data)
     focus_cycle(data->cycle.forward,
                 data->cycle.all_desktops,
                 data->cycle.dock_windows,
+                data->cycle.desktop_windows,
                 data->cycle.linear, data->any.interactive,
                 data->cycle.dialog,
                 data->cycle.inter.final, data->cycle.inter.cancel);
@@ -1770,6 +1714,7 @@ void action_directional_focus(union ActionData *data)
 
     focus_directional_cycle(data->interdiraction.direction,
                             data->interdiraction.dock_windows,
+                            data->interdiraction.desktop_windows,
                             data->any.interactive,
                             data->interdiraction.dialog,
                             data->interdiraction.inter.final,
@@ -1811,7 +1756,7 @@ void action_movetoedge(union ActionData *data)
     frame_frame_gravity(c->frame, &x, &y, c->area.width, c->area.height);
     client_action_start(data);
     client_move(c, x, y);
-    client_action_end(data);
+    client_action_end(data, FALSE);
 }
 
 void action_growtoedge(union ActionData *data)
@@ -1820,7 +1765,7 @@ void action_growtoedge(union ActionData *data)
     ObClient *c = data->diraction.any.c;
     Rect *a;
 
-    a = screen_area(c->desktop);
+    a = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS, &c->frame->area);
     x = c->frame->area.x;
     y = c->frame->area.y;
     /* get the unshaded frame's dimensions..if it is shaded */
@@ -1878,7 +1823,8 @@ void action_growtoedge(union ActionData *data)
     frame_frame_gravity(c->frame, &x, &y, width, height);
     client_action_start(data);
     client_move_resize(c, x, y, width, height);
-    client_action_end(data);
+    client_action_end(data, FALSE);
+    g_free(a);
 }
 
 void action_send_to_layer(union ActionData *data)
@@ -1895,7 +1841,7 @@ void action_toggle_layer(union ActionData *data)
         client_set_layer(c, c->below ? 0 : -1);
     else if (data->layer.layer > 0)
         client_set_layer(c, c->above ? 0 : 1);
-    client_action_end(data);
+    client_action_end(data, config_focus_under_mouse);
 }
 
 void action_toggle_dockautohide(union ActionData *data)
@@ -1904,23 +1850,81 @@ void action_toggle_dockautohide(union ActionData *data)
     dock_configure();
 }
 
-void action_toggle_show_desktop(union ActionData *data)
+void action_break_chroot(union ActionData *data)
 {
-    screen_show_desktop(!screen_showing_desktop, NULL);
+    /* break out of one chroot */
+    keyboard_reset_chains(1);
 }
 
-void action_show_desktop(union ActionData *data)
+void action_add_desktop(union ActionData *data)
 {
-    screen_show_desktop(TRUE, NULL);
-}
+    client_action_start(data);
+    screen_set_num_desktops(screen_num_desktops+1);
 
-void action_unshow_desktop(union ActionData *data)
-{
-    screen_show_desktop(FALSE, NULL);
+    /* move all the clients over */
+    if (data->addremovedesktop.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)
+                client_set_desktop(c, c->desktop+1, FALSE, TRUE);
+        }
+    }
+
+    client_action_end(data, config_focus_under_mouse);
 }
 
-void action_break_chroot(union ActionData *data)
+void action_remove_desktop(union ActionData *data)
 {
-    /* break out of one chroot */
-    keyboard_reset_chains(1);
+    guint rmdesktop, movedesktop;
+    GList *it, *stacking_copy;
+
+    if (screen_num_desktops < 2) return;
+
+    client_action_start(data);
+
+    /* what desktop are we removing and moving to? */
+    if (data->addremovedesktop.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) {
+                client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
+                ob_debug("moving window %s\n", c->title);
+            }
+            /* 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);
+            }
+        }
+    }
+
+    /* act like we're changing desktops */
+    if (screen_desktop < screen_num_desktops - 1) {
+        gint d = screen_desktop;
+        screen_desktop = screen_last_desktop;
+        screen_set_desktop(d, TRUE);
+        ob_debug("fake desktop change\n");
+    }
+
+    screen_set_num_desktops(screen_num_desktops-1);
+
+    client_action_end(data, config_focus_under_mouse);
 }