make menu event handling work more like how other events are handled, less special...
[dana/openbox.git] / openbox / event.c
index 5d01a0f..80f3c72 100644 (file)
@@ -24,8 +24,6 @@
 #include "dock.h"
 #include "actions.h"
 #include "client.h"
-#include "xerror.h"
-#include "prop.h"
 #include "config.h"
 #include "screen.h"
 #include "frame.h"
 #include "menu.h"
 #include "menuframe.h"
 #include "keyboard.h"
-#include "modkeys.h"
 #include "mouse.h"
-#include "mainloop.h"
 #include "focus.h"
 #include "focus_cycle.h"
 #include "moveresize.h"
 #include "group.h"
 #include "stacking.h"
-#include "extensions.h"
-#include "translate.h"
+#include "ping.h"
+#include "obt/display.h"
+#include "obt/prop.h"
+#include "obt/keyboard.h"
 
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
@@ -74,6 +72,7 @@ typedef struct
 {
     ObClient *client;
     Time time;
+    gulong serial;
 } ObFocusDelayData;
 
 typedef struct
@@ -84,22 +83,25 @@ typedef struct
 
 static void event_process(const XEvent *e, gpointer data);
 static void event_handle_root(XEvent *e);
-static gboolean event_handle_menu_keyboard(XEvent *e);
-static gboolean event_handle_menu(XEvent *e);
+static gboolean event_handle_menu_input(XEvent *e);
+static void event_handle_menu(ObMenuFrame *frame, XEvent *e);
 static void event_handle_dock(ObDock *s, XEvent *e);
 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
 static void event_handle_client(ObClient *c, XEvent *e);
 static void event_handle_user_input(ObClient *client, XEvent *e);
 static gboolean is_enter_focus_event_ignored(XEvent *e);
+static void event_ignore_enter_range(gulong start, gulong end);
 
 static void focus_delay_dest(gpointer data);
 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2);
 static gboolean focus_delay_func(gpointer data);
 static void focus_delay_client_dest(ObClient *client, gpointer data);
 
-/* The time for the current event being processed */
 Time event_curtime = CurrentTime;
+Time event_last_user_time = CurrentTime;
+/*! The serial of the current X event */
 
+static gulong event_curserial;
 static gboolean focus_left_screen = FALSE;
 /*! A list of ObSerialRanges which are to be ignored for mouse enter events */
 static GSList *ignore_serials = NULL;
@@ -118,9 +120,9 @@ static void ice_watch(IceConn conn, IcePointer data, Bool opening,
 
     if (opening) {
         fd = IceConnectionNumber(conn);
-        ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
+        obt_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
     } else {
-        ob_main_loop_fd_remove(ob_main_loop, fd);
+        obt_main_loop_fd_remove(ob_main_loop, fd);
         fd = -1;
     }
 }
@@ -130,7 +132,7 @@ void event_startup(gboolean reconfig)
 {
     if (reconfig) return;
 
-    ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
+    obt_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
 
 #ifdef USE_SM
     IceAddConnectionWatch(ice_watch, NULL);
@@ -157,7 +159,7 @@ static Window event_get_window(XEvent *e)
     /* pick a window */
     switch (e->type) {
     case SelectionClear:
-        window = RootWindow(ob_display, ob_screen);
+        window = RootWindow(obt_display, ob_screen);
         break;
     case MapRequest:
         window = e->xmap.window;
@@ -176,7 +178,9 @@ static Window event_get_window(XEvent *e)
         break;
     default:
 #ifdef XKB
-        if (extensions_xkb && e->type == extensions_xkb_event_basep) {
+        if (obt_display_extension_xkb &&
+            e->type == obt_display_extension_xkb_basep)
+        {
             switch (((XkbAnyEvent*)e)->xkb_type) {
             case XkbBellNotify:
                 window = ((XkbBellNotifyEvent*)e)->window;
@@ -186,8 +190,8 @@ static Window event_get_window(XEvent *e)
         } else
 #endif
 #ifdef SYNC
-        if (extensions_sync &&
-            e->type == extensions_sync_event_basep + XSyncAlarmNotify)
+        if (obt_display_extension_sync &&
+            e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
         {
             window = None;
         } else
@@ -225,8 +229,8 @@ static void event_set_curtime(XEvent *e)
         break;
     default:
 #ifdef SYNC
-        if (extensions_sync &&
-            e->type == extensions_sync_event_basep + XSyncAlarmNotify)
+        if (obt_display_extension_sync &&
+            e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
         {
             t = ((XSyncAlarmNotifyEvent*)e)->time;
         }
@@ -236,31 +240,51 @@ static void event_set_curtime(XEvent *e)
         break;
     }
 
+    /* watch that if we get an event earlier than the last specified user_time,
+       which can happen if the clock goes backwards, we erase the last
+       specified user_time */
+    if (t && event_last_user_time && event_time_after(event_last_user_time, t))
+        event_last_user_time = CurrentTime;
+
     event_curtime = t;
 }
 
 static void event_hack_mods(XEvent *e)
 {
+#ifdef XKB
+    XkbStateRec xkb_state;
+#endif
+
     switch (e->type) {
     case ButtonPress:
     case ButtonRelease:
-        e->xbutton.state = modkeys_only_modifier_masks(e->xbutton.state);
+        e->xbutton.state = obt_keyboard_only_modmasks(e->xbutton.state);
         break;
     case KeyPress:
-        e->xkey.state = modkeys_only_modifier_masks(e->xkey.state);
+        e->xkey.state = obt_keyboard_only_modmasks(e->xkey.state);
         break;
     case KeyRelease:
-        e->xkey.state = modkeys_only_modifier_masks(e->xkey.state);
-        /* remove from the state the mask of the modifier key being released,
-           if it is a modifier key being released that is */
-        e->xkey.state &= ~modkeys_keycode_to_mask(e->xkey.keycode);
+#ifdef XKB
+        /* If XKB is present, then the modifiers are all strange from its
+           magic.  Our X core protocol stuff won't work, so we use this to
+           find what the modifier state is instead. */
+        if (XkbGetState(obt_display, XkbUseCoreKbd, &xkb_state) == Success)
+            e->xkey.state = xkb_state.compat_state;
+        else
+#endif
+        {
+            e->xkey.state = obt_keyboard_only_modmasks(e->xkey.state);
+            /* remove from the state the mask of the modifier key being
+               released, if it is a modifier key being released that is */
+            e->xkey.state &= ~obt_keyboard_keycode_to_modmask(e->xkey.keycode);
+        }
         break;
     case MotionNotify:
-        e->xmotion.state = modkeys_only_modifier_masks(e->xmotion.state);
+        e->xmotion.state = obt_keyboard_only_modmasks(e->xmotion.state);
         /* compress events */
         {
             XEvent ce;
-            while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
+            while (XCheckTypedWindowEvent(obt_display, e->xmotion.window,
                                           e->type, &ce)) {
                 e->xmotion.x = ce.xmotion.x;
                 e->xmotion.y = ce.xmotion.y;
@@ -290,7 +314,7 @@ static gboolean wanted_focusevent(XEvent *e, gboolean in_client_only)
 
         /* These are the ones we want.. */
 
-        if (win == RootWindow(ob_display, ob_screen)) {
+        if (win == RootWindow(obt_display, ob_screen)) {
             /* If looking for a focus in on a client, then always return
                FALSE for focus in's to the root window */
             if (in_client_only)
@@ -311,7 +335,7 @@ static gboolean wanted_focusevent(XEvent *e, gboolean in_client_only)
            but has disappeared.
         */
         if (in_client_only) {
-            ObWindow *w = g_hash_table_lookup(window_map, &e->xfocus.window);
+            ObWindow *w = window_find(e->xfocus.window);
             if (!w || !WINDOW_IS_CLIENT(w))
                 return FALSE;
         }
@@ -344,7 +368,7 @@ static gboolean wanted_focusevent(XEvent *e, gboolean in_client_only)
             return FALSE;
 
         /* Focus left the root window revertedto state */
-        if (win == RootWindow(ob_display, ob_screen))
+        if (win == RootWindow(obt_display, ob_screen))
             return FALSE;
 
         /* These are the ones we want.. */
@@ -426,39 +450,42 @@ static gboolean event_ignore(XEvent *e, ObClient *client)
 
 static void event_process(const XEvent *ec, gpointer data)
 {
+    XEvent ee, *e;
+    ObEventData *ed = data;
+
     Window window;
     ObClient *client = NULL;
     ObDock *dock = NULL;
     ObDockApp *dockapp = NULL;
     ObWindow *obwin = NULL;
-    XEvent ee, *e;
-    ObEventData *ed = data;
+    ObMenuFrame *menu = NULL;
 
     /* make a copy we can mangle */
     ee = *ec;
     e = &ee;
 
     window = event_get_window(e);
-    if ((obwin = g_hash_table_lookup(window_map, &window))) {
+    if ((obwin = window_find(window))) {
         switch (obwin->type) {
-        case Window_Dock:
+        case OB_WINDOW_CLASS_DOCK:
             dock = WINDOW_AS_DOCK(obwin);
             break;
-        case Window_DockApp:
-            dockapp = WINDOW_AS_DOCKAPP(obwin);
-            break;
-        case Window_Client:
+        case OB_WINDOW_CLASS_CLIENT:
             client = WINDOW_AS_CLIENT(obwin);
             break;
-        case Window_Menu:
-        case Window_Internal:
-            /* not to be used for events */
-            g_assert_not_reached();
+        case OB_WINDOW_CLASS_MENUFRAME:
+            menu = WINDOW_AS_MENUFRAME(obwin);
+            break;
+        case OB_WINDOW_CLASS_INTERNALWINDOW:
+            /* we don't do anything with events directly on these windows */
             break;
         }
     }
+    else
+        dockapp = dock_find_dockapp(window);
 
     event_set_curtime(e);
+    event_curserial = e->xany.serial;
     event_hack_mods(e);
     if (event_ignore(e, client)) {
         if (ed)
@@ -469,12 +496,7 @@ static void event_process(const XEvent *ec, gpointer data)
 
     /* deal with it in the kernel */
 
-    if (menu_frame_visible &&
-        (e->type == EnterNotify || e->type == LeaveNotify))
-    {
-        /* crossing events for menu */
-        event_handle_menu(e);
-    } else if (e->type == FocusIn) {
+    if (e->type == FocusIn) {
         if (client &&
             e->xfocus.detail == NotifyInferior)
         {
@@ -483,7 +505,7 @@ static void event_process(const XEvent *ec, gpointer data)
 
             focus_left_screen = FALSE;
 
-            focus_fallback(FALSE, config_focus_under_mouse, TRUE);
+            focus_fallback(FALSE, config_focus_under_mouse, TRUE, TRUE);
 
             /* We don't get a FocusOut for this case, because it's just moving
                from our Inferior up to us. This happens when iconifying a
@@ -492,10 +514,10 @@ static void event_process(const XEvent *ec, gpointer data)
             /* focus_set_client(NULL) has already been called */
             client_calc_layer(client);
         }
-        if (e->xfocus.detail == NotifyPointerRoot ||
-            e->xfocus.detail == NotifyDetailNone ||
-            e->xfocus.detail == NotifyInferior ||
-            e->xfocus.detail == NotifyNonlinear)
+        else if (e->xfocus.detail == NotifyPointerRoot ||
+                 e->xfocus.detail == NotifyDetailNone ||
+                 e->xfocus.detail == NotifyInferior ||
+                 e->xfocus.detail == NotifyNonlinear)
         {
             XEvent ce;
 
@@ -521,10 +543,10 @@ static void event_process(const XEvent *ec, gpointer data)
                But if the other focus in is something like PointerRoot then we
                still want to fall back.
             */
-            if (XCheckIfEvent(ob_display, &ce, event_look_for_focusin_client,
+            if (XCheckIfEvent(obt_display, &ce, event_look_for_focusin_client,
                               NULL))
             {
-                XPutBackEvent(ob_display, &ce);
+                XPutBackEvent(obt_display, &ce);
                 ob_debug_type(OB_DEBUG_FOCUS,
                               "  but another FocusIn is coming\n");
             } else {
@@ -535,7 +557,8 @@ static void event_process(const XEvent *ec, gpointer data)
                 */
 
                 if (!focus_left_screen)
-                    focus_fallback(FALSE, config_focus_under_mouse, TRUE);
+                    focus_fallback(FALSE, config_focus_under_mouse,
+                                   TRUE, TRUE);
             }
         }
         else if (!client)
@@ -560,16 +583,16 @@ static void event_process(const XEvent *ec, gpointer data)
         XEvent ce;
 
         /* Look for the followup FocusIn */
-        if (!XCheckIfEvent(ob_display, &ce, event_look_for_focusin, NULL)) {
+        if (!XCheckIfEvent(obt_display, &ce, event_look_for_focusin, NULL)) {
             /* There is no FocusIn, this means focus went to a window that
                is not being managed, or a window on another screen. */
             Window win, root;
             gint i;
             guint u;
-            xerror_set_ignore(TRUE);
-            if (XGetInputFocus(ob_display, &win, &i) != 0 &&
-                XGetGeometry(ob_display, win, &root, &i,&i,&u,&u,&u,&u) != 0 &&
-                root != RootWindow(ob_display, ob_screen))
+            obt_display_ignore_errors(TRUE);
+            if (XGetInputFocus(obt_display, &win, &i) &&
+                XGetGeometry(obt_display, win, &root, &i,&i,&u,&u,&u,&u) &&
+                root != RootWindow(obt_display, ob_screen))
             {
                 ob_debug_type(OB_DEBUG_FOCUS,
                               "Focus went to another screen !\n");
@@ -578,7 +601,7 @@ static void event_process(const XEvent *ec, gpointer data)
             else
                 ob_debug_type(OB_DEBUG_FOCUS,
                               "Focus went to a black hole !\n");
-            xerror_set_ignore(FALSE);
+            obt_display_ignore_errors(FALSE);
             /* nothing is focused */
             focus_set_client(NULL);
         } else {
@@ -591,7 +614,7 @@ static void event_process(const XEvent *ec, gpointer data)
                 ob_debug_type(OB_DEBUG_FOCUS,
                               "Focus went to an unmanaged window 0x%x !\n",
                               ce.xfocus.window);
-                focus_fallback(TRUE, config_focus_under_mouse, TRUE);
+                focus_fallback(TRUE, config_focus_under_mouse, TRUE, TRUE);
             }
         }
 
@@ -608,14 +631,25 @@ static void event_process(const XEvent *ec, gpointer data)
         event_handle_dockapp(dockapp, e);
     else if (dock)
         event_handle_dock(dock, e);
-    else if (window == RootWindow(ob_display, ob_screen))
+    else if (menu)
+        event_handle_menu(menu, e);
+    else if (window == RootWindow(obt_display, ob_screen))
         event_handle_root(e);
     else if (e->type == MapRequest)
         client_manage(window);
+    else if (e->type == MappingNotify) {
+        /* keyboard layout changes for modifier mapping changes. reload the
+           modifier map, and rebind all the key bindings as appropriate */
+        ob_debug("Kepboard map changed. Reloading keyboard bindings.\n");
+        obt_keyboard_reload();
+        keyboard_rebind();
+    }
     else if (e->type == ClientMessage) {
         /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
            windows that are not managed yet. */
-        if (e->xclient.message_type == prop_atoms.net_request_frame_extents) {
+        if (e->xclient.message_type ==
+            OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS))
+        {
             /* Pretend to manage the client, getting information used to
                determine its decorations */
             ObClient *c = client_fake_manage(e->xclient.window);
@@ -626,8 +660,8 @@ static void event_process(const XEvent *ec, gpointer data)
             vals[1] = c->frame->size.right;
             vals[2] = c->frame->size.top;
             vals[3] = c->frame->size.bottom;
-            PROP_SETA32(e->xclient.window, net_frame_extents,
-                        cardinal, vals, 4);
+            OBT_PROP_SETA32(e->xclient.window, NET_FRAME_EXTENTS,
+                            CARDINAL, vals, 4);
 
             /* Free the pretend client */
             client_fake_unmanage(c);
@@ -648,14 +682,14 @@ static void event_process(const XEvent *ec, gpointer data)
 
         /* we are not to be held responsible if someone sends us an
            invalid request! */
-        xerror_set_ignore(TRUE);
-        XConfigureWindow(ob_display, window,
+        obt_display_ignore_errors(TRUE);
+        XConfigureWindow(obt_display, window,
                          e->xconfigurerequest.value_mask, &xwc);
-        xerror_set_ignore(FALSE);
+        obt_display_ignore_errors(FALSE);
     }
 #ifdef SYNC
-    else if (extensions_sync &&
-        e->type == extensions_sync_event_basep + XSyncAlarmNotify)
+    else if (obt_display_extension_sync &&
+             e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
     {
         XSyncAlarmNotifyEvent *se = (XSyncAlarmNotifyEvent*)e;
         if (se->alarm == moveresize_alarm && moveresize_in_progress)
@@ -663,16 +697,34 @@ static void event_process(const XEvent *ec, gpointer data)
     }
 #endif
 
-    if (e->type == ButtonPress || e->type == ButtonRelease ||
-        e->type == MotionNotify || e->type == KeyPress ||
-        e->type == KeyRelease)
-    {
-        event_handle_user_input(client, e);
+    if (e->type == ButtonPress || e->type == ButtonRelease) {
+        /* If the button press was on some non-root window, or was physically
+           on the root window, the process it */
+        if (window != RootWindow(obt_display, ob_screen) ||
+            e->xbutton.subwindow == None)
+        {
+            event_handle_user_input(client, e);
+        }
+        /* Otherwise only process it if it was physically on an openbox
+           internal window */
+        else {
+            ObWindow *w;
+
+            if ((w = window_find(e->xbutton.subwindow)) &&
+                WINDOW_IS_INTERNALWINDOW(w))
+            {
+                event_handle_user_input(client, e);
+            }
+        }
     }
+    else if (e->type == KeyPress || e->type == KeyRelease ||
+             e->type == MotionNotify)
+        event_handle_user_input(client, e);
 
     /* if something happens and it's not from an XEvent, then we don't know
        the time */
     event_curtime = CurrentTime;
+    event_curserial = 0;
 }
 
 static void event_handle_root(XEvent *e)
@@ -689,7 +741,7 @@ static void event_handle_root(XEvent *e)
         if (e->xclient.format != 32) break;
 
         msgtype = e->xclient.message_type;
-        if (msgtype == prop_atoms.net_current_desktop) {
+        if (msgtype == OBT_PROP_ATOM(NET_CURRENT_DESKTOP)) {
             guint d = e->xclient.data.l[0];
             if (d < screen_num_desktops) {
                 event_curtime = e->xclient.data.l[1];
@@ -699,26 +751,31 @@ static void event_handle_root(XEvent *e)
                                   "a timestamp\n");
                 screen_set_desktop(d, TRUE);
             }
-        } else if (msgtype == prop_atoms.net_number_of_desktops) {
+        } else if (msgtype == OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS)) {
             guint d = e->xclient.data.l[0];
             if (d > 0 && d <= 1000)
                 screen_set_num_desktops(d);
-        } else if (msgtype == prop_atoms.net_showing_desktop) {
+        } else if (msgtype == OBT_PROP_ATOM(NET_SHOWING_DESKTOP)) {
             screen_show_desktop(e->xclient.data.l[0] != 0, NULL);
-        } else if (msgtype == prop_atoms.ob_control) {
+        } else if (msgtype == OBT_PROP_ATOM(OB_CONTROL)) {
             ob_debug("OB_CONTROL: %d\n", e->xclient.data.l[0]);
             if (e->xclient.data.l[0] == 1)
                 ob_reconfigure();
             else if (e->xclient.data.l[0] == 2)
                 ob_restart();
+            else if (e->xclient.data.l[0] == 3)
+                ob_exit(0);
+        } else if (msgtype == OBT_PROP_ATOM(WM_PROTOCOLS)) {
+            if ((Atom)e->xclient.data.l[0] == OBT_PROP_ATOM(NET_WM_PING))
+                ping_got_pong(e->xclient.data.l[1]);
         }
         break;
     case PropertyNotify:
-        if (e->xproperty.atom == prop_atoms.net_desktop_names) {
+        if (e->xproperty.atom == OBT_PROP_ATOM(NET_DESKTOP_NAMES)) {
             ob_debug("UPDATE DESKTOP NAMES\n");
             screen_update_desktop_names();
         }
-        else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
+        else if (e->xproperty.atom == OBT_PROP_ATOM(NET_DESKTOP_LAYOUT))
             screen_update_layout();
         break;
     case ConfigureNotify:
@@ -740,20 +797,22 @@ void event_enter_client(ObClient *client)
         if (config_focus_delay) {
             ObFocusDelayData *data;
 
-            ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
+            obt_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
 
             data = g_new(ObFocusDelayData, 1);
             data->client = client;
             data->time = event_curtime;
+            data->serial = event_curserial;
 
-            ob_main_loop_timeout_add(ob_main_loop,
-                                     config_focus_delay,
-                                     focus_delay_func,
-                                     data, focus_delay_cmp, focus_delay_dest);
+            obt_main_loop_timeout_add(ob_main_loop,
+                                      config_focus_delay * 1000,
+                                      focus_delay_func,
+                                      data, focus_delay_cmp, focus_delay_dest);
         } else {
             ObFocusDelayData data;
             data.client = client;
             data.time = event_curtime;
+            data.serial = event_curserial;
             focus_delay_func(&data);
         }
     }
@@ -938,9 +997,9 @@ static void event_handle_client(ObClient *client, XEvent *e)
                    delay is up */
                 e->xcrossing.detail != NotifyInferior)
             {
-                ob_main_loop_timeout_remove_data(ob_main_loop,
-                                                 focus_delay_func,
-                                                 client, FALSE);
+                obt_main_loop_timeout_remove_data(ob_main_loop,
+                                                  focus_delay_func,
+                                                  client, FALSE);
             }
             break;
         default:
@@ -982,18 +1041,23 @@ static void event_handle_client(ObClient *client, XEvent *e)
                 is_enter_focus_event_ignored(e))
             {
                 ob_debug_type(OB_DEBUG_FOCUS,
-                              "%sNotify mode %d detail %d on %lx IGNORED\n",
+                              "%sNotify mode %d detail %d serial %lu on %lx "
+                              "IGNORED\n",
                               (e->type == EnterNotify ? "Enter" : "Leave"),
                               e->xcrossing.mode,
-                              e->xcrossing.detail, client?client->window:0);
+                              e->xcrossing.detail,
+                              e->xcrossing.serial,
+                              client?client->window:0);
             }
             else {
                 ob_debug_type(OB_DEBUG_FOCUS,
-                              "%sNotify mode %d detail %d on %lx, "
+                              "%sNotify mode %d detail %d serial %lu on %lx, "
                               "focusing window\n",
                               (e->type == EnterNotify ? "Enter" : "Leave"),
                               e->xcrossing.mode,
-                              e->xcrossing.detail, (client?client->window:0));
+                              e->xcrossing.detail,
+                              e->xcrossing.serial,
+                              (client?client->window:0));
                 if (config_focus_follow)
                     event_enter_client(client);
             }
@@ -1046,8 +1110,7 @@ static void event_handle_client(ObClient *client, XEvent *e)
             /* get the sibling */
             if (e->xconfigurerequest.value_mask & CWSibling) {
                 ObWindow *win;
-                win = g_hash_table_lookup(window_map,
-                                          &e->xconfigurerequest.above);
+                win = window_find(e->xconfigurerequest.above);
                 if (win && WINDOW_IS_CLIENT(win) &&
                     WINDOW_AS_CLIENT(win) != client)
                 {
@@ -1191,7 +1254,7 @@ static void event_handle_client(ObClient *client, XEvent *e)
 
         /* we don't want the reparent event, put it back on the stack for the
            X server to deal with after we unmanage the window */
-        XPutBackEvent(ob_display, e);
+        XPutBackEvent(obt_display, e);
 
         ob_debug("ReparentNotify for window 0x%x\n", client->window);
         client_unmanage(client);
@@ -1212,29 +1275,29 @@ static void event_handle_client(ObClient *client, XEvent *e)
         if (e->xclient.format != 32) return;
 
         msgtype = e->xclient.message_type;
-        if (msgtype == prop_atoms.wm_change_state) {
+        if (msgtype == OBT_PROP_ATOM(WM_CHANGE_STATE)) {
             /* compress changes into a single change */
-            while (XCheckTypedWindowEvent(ob_display, client->window,
+            while (XCheckTypedWindowEvent(obt_display, client->window,
                                           e->type, &ce)) {
                 /* XXX: it would be nice to compress ALL messages of a
                    type, not just messages in a row without other
                    message types between. */
                 if (ce.xclient.message_type != msgtype) {
-                    XPutBackEvent(ob_display, &ce);
+                    XPutBackEvent(obt_display, &ce);
                     break;
                 }
                 e->xclient = ce.xclient;
             }
             client_set_wm_state(client, e->xclient.data.l[0]);
-        } else if (msgtype == prop_atoms.net_wm_desktop) {
+        } else if (msgtype == OBT_PROP_ATOM(NET_WM_DESKTOP)) {
             /* compress changes into a single change */
-            while (XCheckTypedWindowEvent(ob_display, client->window,
+            while (XCheckTypedWindowEvent(obt_display, client->window,
                                           e->type, &ce)) {
                 /* XXX: it would be nice to compress ALL messages of a
                    type, not just messages in a row without other
                    message types between. */
                 if (ce.xclient.message_type != msgtype) {
-                    XPutBackEvent(ob_display, &ce);
+                    XPutBackEvent(obt_display, &ce);
                     break;
                 }
                 e->xclient = ce.xclient;
@@ -1243,7 +1306,7 @@ static void event_handle_client(ObClient *client, XEvent *e)
                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
                                    FALSE, FALSE);
-        } else if (msgtype == prop_atoms.net_wm_state) {
+        } else if (msgtype == OBT_PROP_ATOM(NET_WM_STATE)) {
             gulong ignore_start;
 
             /* can't compress these */
@@ -1261,10 +1324,10 @@ static void event_handle_client(ObClient *client, XEvent *e)
                              e->xclient.data.l[1], e->xclient.data.l[2]);
             if (!config_focus_under_mouse)
                 event_end_ignore_all_enters(ignore_start);
-        } else if (msgtype == prop_atoms.net_close_window) {
+        } else if (msgtype == OBT_PROP_ATOM(NET_CLOSE_WINDOW)) {
             ob_debug("net_close_window for 0x%lx\n", client->window);
             client_close(client);
-        } else if (msgtype == prop_atoms.net_active_window) {
+        } else if (msgtype == OBT_PROP_ATOM(NET_ACTIVE_WINDOW)) {
             ob_debug("net_active_window for 0x%lx source=%s\n",
                      client->window,
                      (e->xclient.data.l[0] == 0 ? "unknown" :
@@ -1287,42 +1350,42 @@ static void event_handle_client(ObClient *client, XEvent *e)
             client_activate(client, FALSE, TRUE, TRUE,
                             (e->xclient.data.l[0] == 0 ||
                              e->xclient.data.l[0] == 2));
-        } else if (msgtype == prop_atoms.net_wm_moveresize) {
+        } else if (msgtype == OBT_PROP_ATOM(NET_WM_MOVERESIZE)) {
             ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
                      client->window, e->xclient.data.l[2]);
             if ((Atom)e->xclient.data.l[2] ==
-                prop_atoms.net_wm_moveresize_size_topleft ||
+                OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
                 (Atom)e->xclient.data.l[2] ==
-                prop_atoms.net_wm_moveresize_size_top ||
+                OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
                 (Atom)e->xclient.data.l[2] ==
-                prop_atoms.net_wm_moveresize_size_topright ||
+                OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT) ||
                 (Atom)e->xclient.data.l[2] ==
-                prop_atoms.net_wm_moveresize_size_right ||
+                OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT) ||
                 (Atom)e->xclient.data.l[2] ==
-                prop_atoms.net_wm_moveresize_size_right ||
+                OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT) ||
                 (Atom)e->xclient.data.l[2] ==
-                prop_atoms.net_wm_moveresize_size_bottomright ||
+                OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) ||
                 (Atom)e->xclient.data.l[2] ==
-                prop_atoms.net_wm_moveresize_size_bottom ||
+                OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM) ||
                 (Atom)e->xclient.data.l[2] ==
-                prop_atoms.net_wm_moveresize_size_bottomleft ||
+                OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) ||
                 (Atom)e->xclient.data.l[2] ==
-                prop_atoms.net_wm_moveresize_size_left ||
+                OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
                 (Atom)e->xclient.data.l[2] ==
-                prop_atoms.net_wm_moveresize_move ||
+                OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE) ||
                 (Atom)e->xclient.data.l[2] ==
-                prop_atoms.net_wm_moveresize_size_keyboard ||
+                OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD) ||
                 (Atom)e->xclient.data.l[2] ==
-                prop_atoms.net_wm_moveresize_move_keyboard) {
-
+                OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
+            {
                 moveresize_start(client, e->xclient.data.l[0],
                                  e->xclient.data.l[1], e->xclient.data.l[3],
                                  e->xclient.data.l[2]);
             }
             else if ((Atom)e->xclient.data.l[2] ==
-                     prop_atoms.net_wm_moveresize_cancel)
+                     OBT_PROP_ATOM(NET_WM_MOVERESIZE_CANCEL))
                 moveresize_end(TRUE);
-        } else if (msgtype == prop_atoms.net_moveresize_window) {
+        } else if (msgtype == OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW)) {
             gint ograv, x, y, w, h;
 
             ograv = client->gravity;
@@ -1371,7 +1434,7 @@ static void event_handle_client(ObClient *client, XEvent *e)
             client_configure(client, x, y, w, h, FALSE, TRUE, FALSE);
 
             client->gravity = ograv;
-        } else if (msgtype == prop_atoms.net_restack_window) {
+        } else if (msgtype == OBT_PROP_ATOM(NET_RESTACK_WINDOW)) {
             if (e->xclient.data.l[0] != 2) {
                 ob_debug_type(OB_DEBUG_APP_BUGS,
                               "_NET_RESTACK_WINDOW sent for window %s with "
@@ -1380,8 +1443,7 @@ static void event_handle_client(ObClient *client, XEvent *e)
             } else {
                 ObClient *sibling = NULL;
                 if (e->xclient.data.l[1]) {
-                    ObWindow *win = g_hash_table_lookup
-                        (window_map, &e->xclient.data.l[1]);
+                    ObWindow *win = window_find(e->xclient.data.l[1]);
                     if (WINDOW_IS_CLIENT(win) &&
                         WINDOW_AS_CLIENT(win) != client)
                     {
@@ -1425,7 +1487,7 @@ static void event_handle_client(ObClient *client, XEvent *e)
         if (!client_validate(client)) break;
 
         /* compress changes to a single property into a single change */
-        while (XCheckTypedWindowEvent(ob_display, client->window,
+        while (XCheckTypedWindowEvent(obt_display, client->window,
                                       e->type, &ce)) {
             Atom a, b;
 
@@ -1437,22 +1499,22 @@ static void event_handle_client(ObClient *client, XEvent *e)
 
             if (a == b)
                 continue;
-            if ((a == prop_atoms.net_wm_name ||
-                 a == prop_atoms.wm_name ||
-                 a == prop_atoms.net_wm_icon_name ||
-                 a == prop_atoms.wm_icon_name)
+            if ((a == OBT_PROP_ATOM(NET_WM_NAME) ||
+                 a == OBT_PROP_ATOM(WM_NAME) ||
+                 a == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
+                 a == OBT_PROP_ATOM(WM_ICON_NAME))
                 &&
-                (b == prop_atoms.net_wm_name ||
-                 b == prop_atoms.wm_name ||
-                 b == prop_atoms.net_wm_icon_name ||
-                 b == prop_atoms.wm_icon_name)) {
+                (b == OBT_PROP_ATOM(NET_WM_NAME) ||
+                 b == OBT_PROP_ATOM(WM_NAME) ||
+                 b == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
+                 b == OBT_PROP_ATOM(WM_ICON_NAME))) {
                 continue;
             }
-            if (a == prop_atoms.net_wm_icon &&
-                b == prop_atoms.net_wm_icon)
+            if (a == OBT_PROP_ATOM(NET_WM_ICON) &&
+                b == OBT_PROP_ATOM(NET_WM_ICON))
                 continue;
 
-            XPutBackEvent(ob_display, &ce);
+            XPutBackEvent(obt_display, &ce);
             break;
         }
 
@@ -1475,29 +1537,40 @@ static void event_handle_client(ObClient *client, XEvent *e)
             /* type may have changed, so update the layer */
             client_calc_layer(client);
             client_setup_decor_and_functions(client, TRUE);
-        } else if (msgtype == prop_atoms.net_wm_name ||
-                   msgtype == prop_atoms.wm_name ||
-                   msgtype == prop_atoms.net_wm_icon_name ||
-                   msgtype == prop_atoms.wm_icon_name) {
+        } else if (msgtype == OBT_PROP_ATOM(NET_WM_NAME) ||
+                   msgtype == OBT_PROP_ATOM(WM_NAME) ||
+                   msgtype == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
+                   msgtype == OBT_PROP_ATOM(WM_ICON_NAME)) {
             client_update_title(client);
-        } else if (msgtype == prop_atoms.wm_protocols) {
+        } else if (msgtype == OBT_PROP_ATOM(WM_PROTOCOLS)) {
             client_update_protocols(client);
             client_setup_decor_and_functions(client, TRUE);
         }
-        else if (msgtype == prop_atoms.net_wm_strut) {
+        else if (msgtype == OBT_PROP_ATOM(NET_WM_STRUT)) {
             client_update_strut(client);
         }
-        else if (msgtype == prop_atoms.net_wm_strut_partial) {
+        else if (msgtype == OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL)) {
             client_update_strut(client);
         }
-        else if (msgtype == prop_atoms.net_wm_icon) {
+        else if (msgtype == OBT_PROP_ATOM(NET_WM_ICON)) {
             client_update_icons(client);
         }
-        else if (msgtype == prop_atoms.net_wm_icon_geometry) {
+        else if (msgtype == OBT_PROP_ATOM(NET_WM_ICON_GEOMETRY)) {
             client_update_icon_geometry(client);
         }
+        else if (msgtype == OBT_PROP_ATOM(NET_WM_USER_TIME)) {
+            guint32 t;
+            if (client == focus_client &&
+                OBT_PROP_GET32(client->window, NET_WM_USER_TIME, CARDINAL, &t)
+                && t && !event_time_after(t, e->xproperty.time) &&
+                (!event_last_user_time ||
+                 event_time_after(t, event_last_user_time)))
+            {
+                event_last_user_time = t;
+            }
+        }
 #ifdef SYNC
-        else if (msgtype == prop_atoms.net_wm_sync_request_counter) {
+        else if (msgtype == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER)) {
             client_update_sync_request_counter(client);
         }
 #endif
@@ -1508,7 +1581,9 @@ static void event_handle_client(ObClient *client, XEvent *e)
     default:
         ;
 #ifdef SHAPE
-        if (extensions_shape && e->type == extensions_shape_event_basep) {
+        if (obt_display_extension_shape &&
+            e->type == obt_display_extension_shape_basep)
+        {
             client->shaped = ((XShapeEvent*)e)->shaped;
             frame_adjust_shape(client->frame);
         }
@@ -1561,7 +1636,7 @@ static void event_handle_dockapp(ObDockApp *app, XEvent *e)
     }
 }
 
-static ObMenuFrame* find_active_menu()
+static ObMenuFrame* find_active_menu(void)
 {
     GList *it;
     ObMenuFrame *ret = NULL;
@@ -1575,7 +1650,7 @@ static ObMenuFrame* find_active_menu()
     return ret;
 }
 
-static ObMenuFrame* find_active_or_last_menu()
+static ObMenuFrame* find_active_or_last_menu(void)
 {
     ObMenuFrame *ret = NULL;
 
@@ -1585,122 +1660,156 @@ static ObMenuFrame* find_active_or_last_menu()
     return ret;
 }
 
-static gboolean event_handle_menu_keyboard(XEvent *ev)
+static gboolean event_handle_menu_input(XEvent *ev)
 {
-    guint keycode, state;
-    gunichar unikey;
-    ObMenuFrame *frame;
     gboolean ret = FALSE;
 
-    keycode = ev->xkey.keycode;
-    state = ev->xkey.state;
-    unikey = translate_unichar(keycode);
-
-    frame = find_active_or_last_menu();
-    if (frame == NULL)
-        g_assert_not_reached(); /* there is no active menu */
+    if (ev->type == ButtonRelease) {
+        ObMenuEntryFrame *e;
 
-    /* Allow control while going thru the menu */
-    else if (ev->type == KeyPress && (state & ~ControlMask) == 0) {
-        if (keycode == ob_keycode(OB_KEY_ESCAPE)) {
-            menu_frame_hide_all();
-            ret = TRUE;
+        if (menu_hide_delay_reached() &&
+            (ev->xbutton.button < 4 || ev->xbutton.button > 5))
+        {
+            if ((e = menu_entry_frame_under(ev->xbutton.x_root,
+                                            ev->xbutton.y_root)))
+            {
+                menu_frame_select(e->frame, e, TRUE);
+                menu_entry_frame_execute(e, ev->xbutton.state);
+            }
+            else
+                menu_frame_hide_all();
         }
+        ret = TRUE;
+    }
+    else if (ev->type == MotionNotify) {
+        ObMenuFrame *f;
+        ObMenuEntryFrame *e;
 
-        else if (keycode == ob_keycode(OB_KEY_LEFT)) {
-            /* Left goes to the parent menu */
-            menu_frame_select(frame, NULL, TRUE);
-            ret = TRUE;
-        }
+        if ((e = menu_entry_frame_under(ev->xmotion.x_root,
+                                        ev->xmotion.y_root)))
+            if (!(f = find_active_menu()) ||
+                f == e->frame ||
+                f->parent == e->frame ||
+                f->child == e->frame)
+                menu_frame_select(e->frame, e, FALSE);
+    }
+    else if (ev->type == KeyPress || ev->type == KeyRelease) {
+        guint keycode, state;
+        gunichar unikey;
+        ObMenuFrame *frame;
 
-        else if (keycode == ob_keycode(OB_KEY_RIGHT)) {
-            /* Right goes to the selected submenu */
-            if (frame->child) menu_frame_select_next(frame->child);
-            ret = TRUE;
-        }
+        keycode = ev->xkey.keycode;
+        state = ev->xkey.state;
+        unikey = obt_keyboard_keycode_to_unichar(keycode);
 
-        else if (keycode == ob_keycode(OB_KEY_UP)) {
-            menu_frame_select_previous(frame);
-            ret = TRUE;
-        }
+        frame = find_active_or_last_menu();
+        if (frame == NULL)
+            g_assert_not_reached(); /* there is no active menu */
 
-        else if (keycode == ob_keycode(OB_KEY_DOWN)) {
-            menu_frame_select_next(frame);
-            ret = TRUE;
-        }
-    }
+        /* Allow control while going thru the menu */
+        else if (ev->type == KeyPress && (state & ~ControlMask) == 0) {
+            frame->got_press = TRUE;
 
-    /* Use KeyRelease events for running things so that the key release doesn't
-       get sent to the focused application.
+            if (keycode == ob_keycode(OB_KEY_ESCAPE)) {
+                menu_frame_hide_all();
+                ret = TRUE;
+            }
 
-       Allow ControlMask only, and don't bother if the menu is empty */
-    else if (ev->type == KeyRelease && (state & ~ControlMask) == 0 &&
-             frame->entries)
-    {
-        if (keycode == ob_keycode(OB_KEY_RETURN)) {
-            /* Enter runs the active item or goes into the submenu.
-               Control-Enter runs it without closing the menu. */
-            if (frame->child)
-                menu_frame_select_next(frame->child);
-            else if (frame->selected)
-                menu_entry_frame_execute(frame->selected, state);
-
-            ret = TRUE;
-        }
+            else if (keycode == ob_keycode(OB_KEY_LEFT)) {
+                /* Left goes to the parent menu */
+                menu_frame_select(frame, NULL, TRUE);
+                ret = TRUE;
+            }
 
-        /* keyboard accelerator shortcuts. (if it was a valid key) */
-        else if (unikey != 0) {
-            GList *start;
-            GList *it;
-            ObMenuEntryFrame *found = NULL;
-            guint num_found = 0;
-
-            /* start after the selected one */
-            start = frame->entries;
-            if (frame->selected) {
-                for (it = start; frame->selected != it->data;
-                     it = g_list_next(it))
-                    g_assert(it != NULL); /* nothing was selected? */
-                /* next with wraparound */
-                start = g_list_next(it);
-                if (start == NULL) start = frame->entries;
+            else if (keycode == ob_keycode(OB_KEY_RIGHT)) {
+                /* Right goes to the selected submenu */
+                if (frame->child) menu_frame_select_next(frame->child);
+                ret = TRUE;
             }
 
-            it = start;
-            do {
-                ObMenuEntryFrame *e = it->data;
-                gunichar entrykey = 0;
+            else if (keycode == ob_keycode(OB_KEY_UP)) {
+                menu_frame_select_previous(frame);
+                ret = TRUE;
+            }
 
-                if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
-                    entrykey = e->entry->data.normal.shortcut;
-                else if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
-                    entrykey = e->entry->data.submenu.submenu->shortcut;
+            else if (keycode == ob_keycode(OB_KEY_DOWN)) {
+                menu_frame_select_next(frame);
+                ret = TRUE;
+            }
+        }
 
-                if (unikey == entrykey) {
-                    if (found == NULL) found = e;
-                    ++num_found;
-                }
+        /* Use KeyRelease events for running things so that the key release
+           doesn't get sent to the focused application.
 
-                /* next with wraparound */
-                it = g_list_next(it);
-                if (it == NULL) it = frame->entries;
-            } while (it != start);
+           Allow ControlMask only, and don't bother if the menu is empty */
+        else if (ev->type == KeyRelease && (state & ~ControlMask) == 0 &&
+                 frame->entries && frame->got_press)
+        {
+            if (keycode == ob_keycode(OB_KEY_RETURN)) {
+                /* Enter runs the active item or goes into the submenu.
+                   Control-Enter runs it without closing the menu. */
+                if (frame->child)
+                    menu_frame_select_next(frame->child);
+                else if (frame->selected)
+                    menu_entry_frame_execute(frame->selected, state);
 
-            if (found) {
-                if (found->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
-                    num_found == 1)
-                {
-                    menu_frame_select(frame, found, TRUE);
-                    usleep(50000); /* highlight the item for a short bit so the
-                                      user can see what happened */
-                    menu_entry_frame_execute(found, state);
-                } else {
-                    menu_frame_select(frame, found, TRUE);
-                    if (num_found == 1)
-                        menu_frame_select_next(frame->child);
+                ret = TRUE;
+            }
+
+            /* keyboard accelerator shortcuts. (if it was a valid key) */
+            else if (unikey != 0) {
+                GList *start;
+                GList *it;
+                ObMenuEntryFrame *found = NULL;
+                guint num_found = 0;
+
+                /* start after the selected one */
+                start = frame->entries;
+                if (frame->selected) {
+                    for (it = start; frame->selected != it->data;
+                         it = g_list_next(it))
+                        g_assert(it != NULL); /* nothing was selected? */
+                    /* next with wraparound */
+                    start = g_list_next(it);
+                    if (start == NULL) start = frame->entries;
                 }
 
-                ret = TRUE;
+                it = start;
+                do {
+                    ObMenuEntryFrame *e = it->data;
+                    gunichar entrykey = 0;
+
+                    if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
+                        entrykey = e->entry->data.normal.shortcut;
+                    else if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
+                        entrykey = e->entry->data.submenu.submenu->shortcut;
+
+                    if (unikey == entrykey) {
+                        if (found == NULL) found = e;
+                        ++num_found;
+                    }
+
+                    /* next with wraparound */
+                    it = g_list_next(it);
+                    if (it == NULL) it = frame->entries;
+                } while (it != start);
+
+                if (found) {
+                    if (found->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+                        num_found == 1)
+                    {
+                        menu_frame_select(frame, found, TRUE);
+                        usleep(50000); /* highlight the item for a short bit so
+                                          the user can see what happened */
+                        menu_entry_frame_execute(found, state);
+                    } else {
+                        menu_frame_select(frame, found, TRUE);
+                        if (num_found == 1)
+                            menu_frame_select_next(frame->child);
+                    }
+
+                    ret = TRUE;
+                }
             }
         }
     }
@@ -1708,27 +1817,12 @@ static gboolean event_handle_menu_keyboard(XEvent *ev)
     return ret;
 }
 
-static gboolean event_handle_menu(XEvent *ev)
+static void event_handle_menu(ObMenuFrame *frame, XEvent *ev)
 {
     ObMenuFrame *f;
     ObMenuEntryFrame *e;
-    gboolean ret = TRUE;
 
     switch (ev->type) {
-    case ButtonRelease:
-        if (menu_hide_delay_reached() &&
-            (ev->xbutton.button < 4 || ev->xbutton.button > 5))
-        {
-            if ((e = menu_entry_frame_under(ev->xbutton.x_root,
-                                            ev->xbutton.y_root)))
-            {
-                menu_frame_select(e->frame, e, TRUE);
-                menu_entry_frame_execute(e, ev->xbutton.state);
-            }
-            else
-                menu_frame_hide_all();
-        }
-        break;
     case EnterNotify:
         if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
             if (e->ignore_enters)
@@ -1752,21 +1846,7 @@ static gboolean event_handle_menu(XEvent *ev)
             menu_frame_select(e->frame, NULL, FALSE);
         }
         break;
-    case MotionNotify:
-        if ((e = menu_entry_frame_under(ev->xmotion.x_root,
-                                        ev->xmotion.y_root)))
-            if (!(f = find_active_menu()) ||
-                f == e->frame ||
-                f->parent == e->frame ||
-                f->child == e->frame)
-                menu_frame_select(e->frame, e, FALSE);
-        break;
-    case KeyPress:
-    case KeyRelease:
-        ret = event_handle_menu_keyboard(ev);
-        break;
     }
-    return ret;
 }
 
 static void event_handle_user_input(ObClient *client, XEvent *e)
@@ -1776,7 +1856,7 @@ static void event_handle_user_input(ObClient *client, XEvent *e)
              e->type == KeyRelease);
 
     if (menu_frame_visible) {
-        if (event_handle_menu(e))
+        if (event_handle_menu_input(e))
             /* don't use the event if the menu used it, but if the menu
                didn't use it and it's a keypress that is bound, it will
                close the menu and be used */
@@ -1821,46 +1901,59 @@ static gboolean focus_delay_func(gpointer data)
     ObFocusDelayData *d = data;
     Time old = event_curtime;
 
+    /* don't move focus and kill the menu or the move/resize */
+    if (menu_frame_visible || moveresize_in_progress) return FALSE;
+
     event_curtime = d->time;
-    if (focus_client != d->client) {
-        if (client_focus(d->client) && config_focus_raise)
-            stacking_raise(CLIENT_AS_WINDOW(d->client));
-    }
+    event_curserial = d->serial;
+    if (client_focus(d->client) && config_focus_raise)
+        stacking_raise(CLIENT_AS_WINDOW(d->client));
     event_curtime = old;
     return FALSE; /* no repeat */
 }
 
 static void focus_delay_client_dest(ObClient *client, gpointer data)
 {
-    ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
-                                     client, FALSE);
+    obt_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
+                                      client, FALSE);
 }
 
-void event_halt_focus_delay()
+void event_halt_focus_delay(void)
 {
-    ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
+    /* ignore all enter events up till the event which caused this to occur */
+    if (event_curserial) event_ignore_enter_range(1, event_curserial);
+    obt_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
 }
 
-gulong event_start_ignore_all_enters()
+gulong event_start_ignore_all_enters(void)
 {
-    XSync(ob_display, FALSE);
-    return LastKnownRequestProcessed(ob_display);
+    XSync(obt_display, FALSE);
+    return LastKnownRequestProcessed(obt_display);
 }
 
-void event_end_ignore_all_enters(gulong start)
+static void event_ignore_enter_range(gulong start, gulong end)
 {
     ObSerialRange *r;
 
     g_assert(start != 0);
-    XSync(ob_display, FALSE);
+    g_assert(end != 0);
 
     r = g_new(ObSerialRange, 1);
     r->start = start;
-    r->end = LastKnownRequestProcessed(ob_display);
+    r->end = end;
     ignore_serials = g_slist_prepend(ignore_serials, r);
 
+    ob_debug_type(OB_DEBUG_FOCUS, "ignoring enters from %lu until %lu\n",
+                  r->start, r->end);
+
     /* increment the serial so we don't ignore events we weren't meant to */
-    XSync(ob_display, FALSE);
+    XSync(obt_display, FALSE);
+}
+
+void event_end_ignore_all_enters(gulong start)
+{
+    XSync(obt_display, FALSE);
+    event_ignore_enter_range(start, LastKnownRequestProcessed(obt_display));
 }
 
 static gboolean is_enter_focus_event_ignored(XEvent *e)
@@ -1888,7 +1981,7 @@ static gboolean is_enter_focus_event_ignored(XEvent *e)
     return FALSE;
 }
 
-void event_cancel_all_key_grabs()
+void event_cancel_all_key_grabs(void)
 {
     if (actions_interactive_act_running()) {
         actions_interactive_cancel_act();
@@ -1908,6 +2001,8 @@ void event_cancel_all_key_grabs()
     }
     else
         ungrab_passive_key();
+
+    XSync(obt_display, FALSE);
 }
 
 gboolean event_time_after(Time t1, Time t2)
@@ -1934,3 +2029,15 @@ gboolean event_time_after(Time t1, Time t2)
         /* t2 is in the first half so t1 has to come after it */
         return t1 >= t2 && t1 < (t2 + TIME_HALF);
 }
+
+Time event_get_server_time(void)
+{
+    /* Generate a timestamp */
+    XEvent event;
+
+    XChangeProperty(obt_display, screen_support_win,
+                    OBT_PROP_ATOM(WM_CLASS), OBT_PROP_ATOM(STRING),
+                    8, PropModeAppend, NULL, 0);
+    XWindowEvent(obt_display, screen_support_win, PropertyChangeMask, &event);
+    return event.xproperty.time;
+}