Clean up the FillToEdge action implementation
[dana/openbox.git] / openbox / moveresize.c
index dd0605c..d12a64d 100644 (file)
 #include "grab.h"
 #include "framerender.h"
 #include "screen.h"
-#include "prop.h"
 #include "client.h"
+#include "focus.h"
 #include "frame.h"
 #include "openbox.h"
 #include "resist.h"
-#include "mainloop.h"
-#include "modkeys.h"
 #include "popup.h"
 #include "moveresize.h"
 #include "config.h"
 #include "event.h"
 #include "debug.h"
-#include "extensions.h"
-#include "render/render.h"
-#include "render/theme.h"
+#include "obrender/render.h"
+#include "obrender/theme.h"
+#include "obt/display.h"
+#include "obt/xqueue.h"
+#include "obt/prop.h"
+#include "obt/keyboard.h"
 
 #include <X11/Xlib.h>
 #include <glib.h>
 
 /* how far windows move and resize with the keyboard arrows */
 #define KEY_DIST 8
+#define SYNC_TIMEOUTS 4
 
 gboolean moveresize_in_progress = FALSE;
 ObClient *moveresize_client = NULL;
@@ -50,18 +52,27 @@ XSyncAlarm moveresize_alarm = None;
 
 static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */
 
+/* starting geometry for the window being moved/resized, so it can be
+   restored */
 static gint start_x, start_y, start_cx, start_cy, start_cw, start_ch;
+static gboolean was_max_horz, was_max_vert;
+static Rect pre_max_area;
 static gint cur_x, cur_y, cur_w, cur_h;
 static guint button;
 static guint32 corner;
 static ObDirection edge_warp_dir = -1;
+static gboolean edge_warp_odd = FALSE;
+static guint edge_warp_timer = 0;
 static ObDirection key_resize_edge = -1;
+static guint waiting_for_sync;
 #ifdef SYNC
-static gboolean waiting_for_sync;
+static guint sync_timer = 0;
 #endif
 
 static ObPopup *popup = NULL;
 
+static void do_move(gboolean keyboard, gint keydist);
+static void do_resize(void);
 static void do_edge_warp(gint x, gint y);
 static void cancel_edge_warp();
 #ifdef SYNC
@@ -72,11 +83,13 @@ static void client_dest(ObClient *client, gpointer data)
 {
     if (moveresize_client == client)
         moveresize_end(TRUE);
+    if (popup && client == popup->client)
+        popup->client = NULL;
 }
 
 void moveresize_startup(gboolean reconfig)
 {
-    popup = popup_new(FALSE);
+    popup = popup_new();
     popup_set_text_align(popup, RR_JUSTIFY_CENTER);
 
     if (!reconfig)
@@ -100,17 +113,63 @@ static void popup_coords(ObClient *c, const gchar *format, gint a, gint b)
     gchar *text;
 
     text = g_strdup_printf(format, a, b);
-    if (config_resize_popup_pos == 1) /* == "Top" */
+    if (config_resize_popup_pos == OB_RESIZE_POS_TOP)
         popup_position(popup, SouthGravity,
                        c->frame->area.x
                      + c->frame->area.width/2,
                        c->frame->area.y - ob_rr_theme->fbwidth);
-    else /* == "Center" */
+    else if (config_resize_popup_pos == OB_RESIZE_POS_CENTER)
         popup_position(popup, CenterGravity,
-                       c->frame->area.x + c->frame->size.left +
-                       c->area.width / 2,
-                       c->frame->area.y + c->frame->size.top +
-                       c->area.height / 2);
+                       c->frame->area.x + c->frame->area.width / 2,
+                       c->frame->area.y + c->frame->area.height / 2);
+    else /* Fixed */ {
+        const Rect *area = screen_physical_area_active();
+        gint gravity, x, y;
+
+        x = config_resize_popup_fixed.x.pos;
+        if (config_resize_popup_fixed.x.center)
+            x = area->x + area->width/2;
+        else if (config_resize_popup_fixed.x.opposite)
+            x = RECT_RIGHT(*area) - x;
+        else
+            x = area->x + x;
+
+        y = config_resize_popup_fixed.y.pos;
+        if (config_resize_popup_fixed.y.center)
+            y = area->y + area->height/2;
+        else if (config_resize_popup_fixed.y.opposite)
+            y = RECT_RIGHT(*area) - y;
+        else
+            y = area->y + y;
+
+        if (config_resize_popup_fixed.x.center) {
+            if (config_resize_popup_fixed.y.center)
+                gravity = CenterGravity;
+            else if (config_resize_popup_fixed.y.opposite)
+                gravity = SouthGravity;
+            else
+                gravity = NorthGravity;
+        }
+        else if (config_resize_popup_fixed.x.opposite) {
+            if (config_resize_popup_fixed.y.center)
+                gravity = EastGravity;
+            else if (config_resize_popup_fixed.y.opposite)
+                gravity = SouthEastGravity;
+            else
+                gravity = NorthEastGravity;
+        }
+        else {
+            if (config_resize_popup_fixed.y.center)
+                gravity = WestGravity;
+            else if (config_resize_popup_fixed.y.opposite)
+                gravity = SouthWestGravity;
+            else
+                gravity = NorthWestGravity;
+        }
+
+        popup_position(popup, gravity, x, y);
+    }
+    popup->client = c;
     popup_show(popup, text);
     g_free(text);
 }
@@ -118,8 +177,10 @@ static void popup_coords(ObClient *c, const gchar *format, gint a, gint b)
 void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
 {
     ObCursor cur;
-    gboolean mv = (cnr == prop_atoms.net_wm_moveresize_move ||
-                   cnr == prop_atoms.net_wm_moveresize_move_keyboard);
+    gboolean mv = (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE) ||
+                   cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD));
+    gint up = 1;
+    gint left = 1;
 
     if (moveresize_in_progress || !c->frame->visible ||
         !(mv ?
@@ -127,27 +188,37 @@ void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
           (c->functions & OB_CLIENT_FUNC_RESIZE)))
         return;
 
-    if (cnr == prop_atoms.net_wm_moveresize_size_topleft)
+    if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
         cur = OB_CURSOR_NORTHWEST;
-    else if (cnr == prop_atoms.net_wm_moveresize_size_top)
+        up = left = -1;
+    }
+    else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
         cur = OB_CURSOR_NORTH;
-    else if (cnr == prop_atoms.net_wm_moveresize_size_topright)
+        up = -1;
+    }
+    else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
         cur = OB_CURSOR_NORTHEAST;
-    else if (cnr == prop_atoms.net_wm_moveresize_size_right)
+        up = -1;
+    }
+    else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT))
         cur = OB_CURSOR_EAST;
-    else if (cnr == prop_atoms.net_wm_moveresize_size_bottomright)
+    else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT))
         cur = OB_CURSOR_SOUTHEAST;
-    else if (cnr == prop_atoms.net_wm_moveresize_size_bottom)
+    else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
         cur = OB_CURSOR_SOUTH;
-    else if (cnr == prop_atoms.net_wm_moveresize_size_bottomleft)
+    else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
         cur = OB_CURSOR_SOUTHWEST;
-    else if (cnr == prop_atoms.net_wm_moveresize_size_left)
+        left = -1;
+    }
+    else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
         cur = OB_CURSOR_WEST;
-    else if (cnr == prop_atoms.net_wm_moveresize_size_keyboard)
+        left = -1;
+    }
+    else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD))
         cur = OB_CURSOR_SOUTHEAST;
-    else if (cnr == prop_atoms.net_wm_moveresize_move)
+    else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE))
         cur = OB_CURSOR_MOVE;
-    else if (cnr == prop_atoms.net_wm_moveresize_move_keyboard)
+    else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
         cur = OB_CURSOR_MOVE;
     else
         g_assert_not_reached();
@@ -172,12 +243,15 @@ void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
        friendly. you essentially start the resize in the middle of the
        increment instead of at 0, so you have to move half an increment
        either way instead of a full increment one and 1 px the other. */
-    start_x = x - (mv ? 0 : c->size_inc.width / 2);
-    start_y = y - (mv ? 0 : c->size_inc.height / 2);
+    start_x = x - (mv ? 0 : left * c->size_inc.width / 2);
+    start_y = y - (mv ? 0 : up * c->size_inc.height / 2);
     corner = cnr;
     button = b;
     key_resize_edge = -1;
 
+    /* default to not putting max back on cancel */
+    was_max_horz = was_max_vert = FALSE;
+
     /*
       have to change start_cx and start_cy if going to do this..
     if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
@@ -192,10 +266,12 @@ void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
     cur_h = start_ch;
 
     moveresize_in_progress = TRUE;
+    waiting_for_sync = 0;
 
 #ifdef SYNC
-    if (config_resize_redraw && !moving && extensions_sync &&
-        moveresize_client->sync_request && moveresize_client->sync_counter)
+    if (config_resize_redraw && !moving && obt_display_extension_sync &&
+        moveresize_client->sync_request && moveresize_client->sync_counter &&
+        !moveresize_client->not_responding)
     {
         /* Initialize values for the resize syncing, and create an alarm for
            the client's xsync counter */
@@ -205,7 +281,7 @@ void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
 
         /* set the counter to an initial value */
         XSyncIntToValue(&val, 0);
-        XSyncSetCounter(ob_display, moveresize_client->sync_counter, val);
+        XSyncSetCounter(obt_display, moveresize_client->sync_counter, val);
 
         /* this will be incremented when we tell the client what we're
            looking for */
@@ -221,7 +297,7 @@ void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
         aa.trigger.test_type = XSyncPositiveTransition;
         aa.events = True;
         XSyncIntToValue(&aa.delta, 1);
-        moveresize_alarm = XSyncCreateAlarm(ob_display,
+        moveresize_alarm = XSyncCreateAlarm(obt_display,
                                             XSyncCACounter |
                                             XSyncCAValue |
                                             XSyncCAValueType |
@@ -229,8 +305,6 @@ void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
                                             XSyncCADelta |
                                             XSyncCAEvents,
                                             &aa);
-
-        waiting_for_sync = FALSE;
     }
 #endif
 }
@@ -241,28 +315,52 @@ void moveresize_end(gboolean cancel)
     ungrab_pointer();
 
     popup_hide(popup);
+    popup->client = NULL;
 
-    if (moving) {
-        client_move(moveresize_client,
-                    (cancel ? start_cx : cur_x),
-                    (cancel ? start_cy : cur_y));
-    } else {
+    if (!moving) {
 #ifdef SYNC
         /* turn off the alarm */
         if (moveresize_alarm != None) {
-            XSyncDestroyAlarm(ob_display, moveresize_alarm);
+            XSyncDestroyAlarm(obt_display, moveresize_alarm);
             moveresize_alarm = None;
         }
 
-        ob_main_loop_timeout_remove(ob_main_loop, sync_timeout_func);
+        if (sync_timer) g_source_remove(sync_timer);
+        sync_timer = 0;
 #endif
+    }
 
-        client_configure(moveresize_client,
-                         (cancel ? start_cx : cur_x),
-                         (cancel ? start_cy : cur_y),
-                         (cancel ? start_cw : cur_w),
-                         (cancel ? start_ch : cur_h),
-                         TRUE, TRUE, FALSE);
+    /* don't use client_move() here, use the same width/height as
+       we've been using during the move, otherwise we get different results
+       when moving maximized windows between monitors of different sizes !
+    */
+    client_configure(moveresize_client,
+                     (cancel ? start_cx : cur_x),
+                     (cancel ? start_cy : cur_y),
+                     (cancel ? start_cw : cur_w),
+                     (cancel ? start_ch : cur_h),
+                     TRUE, TRUE, FALSE);
+
+    /* restore the client's maximized state. do this after putting the window
+       back in its original spot to minimize visible flicker */
+    if (cancel && (was_max_horz || was_max_vert)) {
+        const gboolean h = moveresize_client->max_horz;
+        const gboolean v = moveresize_client->max_vert;
+
+        client_maximize(moveresize_client, TRUE,
+                        was_max_horz && was_max_vert ? 0 :
+                        (was_max_horz ? 1 : 2));
+
+        /* replace the premax values with the ones we had saved if
+           the client doesn't have any already set */
+        if (was_max_horz && !h) {
+            moveresize_client->pre_max_area.x = pre_max_area.x;
+            moveresize_client->pre_max_area.width = pre_max_area.width;
+        }
+        if (was_max_vert && !v) {
+            moveresize_client->pre_max_area.y = pre_max_area.y;
+            moveresize_client->pre_max_area.height = pre_max_area.height;
+        }
     }
 
     /* dont edge warp after its ended */
@@ -290,64 +388,68 @@ static void do_move(gboolean keyboard, gint keydist)
                      moveresize_client->frame->area.y);
 }
 
-
-static void do_resize()
+static void do_resize(void)
 {
     gint x, y, w, h, lw, lh;
 
-    /* see if it is actually going to resize */
-    x = 0;
-    y = 0;
+    /* see if it is actually going to resize
+       USE cur_x AND cur_y HERE !  Otherwise the try_configure won't know
+       what struts to use !!
+     */
+    x = cur_x;
+    y = cur_y;
     w = cur_w;
     h = cur_h;
     client_try_configure(moveresize_client, &x, &y, &w, &h,
                          &lw, &lh, TRUE);
-    if (w == moveresize_client->area.width &&
-        h == moveresize_client->area.height)
+    if (!(w == moveresize_client->area.width &&
+          h == moveresize_client->area.height) &&
+        /* if waiting_for_sync == 0, then we aren't waiting.
+           if it is > SYNC_TIMEOUTS, then we have timed out
+           that many times already, so forget about waiting more */
+        (waiting_for_sync == 0 || waiting_for_sync > SYNC_TIMEOUTS))
     {
-        return;
-    }
-
 #ifdef SYNC
-    if (config_resize_redraw && extensions_sync &&
-        moveresize_client->sync_request && moveresize_client->sync_counter)
-    {
-        XEvent ce;
-        XSyncValue val;
-
-        /* are we already waiting for the sync counter to catch up? */
-        if (waiting_for_sync)
-            return;
-
-        /* increment the value we're waiting for */
-        ++moveresize_client->sync_counter_value;
-        XSyncIntToValue(&val, moveresize_client->sync_counter_value);
-
-        /* tell the client what we're waiting for */
-        ce.xclient.type = ClientMessage;
-        ce.xclient.message_type = prop_atoms.wm_protocols;
-        ce.xclient.display = ob_display;
-        ce.xclient.window = moveresize_client->window;
-        ce.xclient.format = 32;
-        ce.xclient.data.l[0] = prop_atoms.net_wm_sync_request;
-        ce.xclient.data.l[1] = event_curtime;
-        ce.xclient.data.l[2] = XSyncValueLow32(val);
-        ce.xclient.data.l[3] = XSyncValueHigh32(val);
-        ce.xclient.data.l[4] = 0l;
-        XSendEvent(ob_display, moveresize_client->window, FALSE,
-                   NoEventMask, &ce);
-
-        waiting_for_sync = TRUE;
-
-        ob_main_loop_timeout_remove(ob_main_loop, sync_timeout_func);
-        ob_main_loop_timeout_add(ob_main_loop, G_USEC_PER_SEC * 2,
-                                 sync_timeout_func,
-                                 NULL, NULL, NULL);
-    }
+        if (config_resize_redraw && obt_display_extension_sync &&
+            /* don't send another sync when one is pending */
+            waiting_for_sync == 0 &&
+            moveresize_client->sync_request &&
+            moveresize_client->sync_counter &&
+            !moveresize_client->not_responding)
+        {
+            XEvent ce;
+            XSyncValue val;
+
+            /* increment the value we're waiting for */
+            ++moveresize_client->sync_counter_value;
+            XSyncIntToValue(&val, moveresize_client->sync_counter_value);
+
+            /* tell the client what we're waiting for */
+            ce.xclient.type = ClientMessage;
+            ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
+            ce.xclient.display = obt_display;
+            ce.xclient.window = moveresize_client->window;
+            ce.xclient.format = 32;
+            ce.xclient.data.l[0] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
+            ce.xclient.data.l[1] = event_time();
+            ce.xclient.data.l[2] = XSyncValueLow32(val);
+            ce.xclient.data.l[3] = XSyncValueHigh32(val);
+            ce.xclient.data.l[4] = 0l;
+            XSendEvent(obt_display, moveresize_client->window, FALSE,
+                       NoEventMask, &ce);
+
+            waiting_for_sync = 1;
+
+            if (sync_timer) g_source_remove(sync_timer);
+            sync_timer = g_timeout_add(2000, sync_timeout_func, NULL);
+        }
 #endif
 
-    client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h,
-                     TRUE, FALSE, FALSE);
+        /* force a ConfigureNotify, it is part of the spec for SYNC resizing
+           and MUST follow the sync counter notification */
+        client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h,
+                         TRUE, FALSE, TRUE);
+    }
 
     /* this would be better with a fixed width font ... XXX can do it better
        if there are 2 text boxes */
@@ -355,18 +457,21 @@ static void do_resize()
             (config_resize_popup_show == 1 && /* == "Nonpixel" */
              moveresize_client->size_inc.width > 1 &&
              moveresize_client->size_inc.height > 1))
-        popup_coords(moveresize_client, "%d x %d",
-                     moveresize_client->logical_size.width,
-                     moveresize_client->logical_size.height);
+        popup_coords(moveresize_client, "%d x %d", lw, lh);
 }
 
 #ifdef SYNC
 static gboolean sync_timeout_func(gpointer data)
 {
-    waiting_for_sync = FALSE; /* we timed out waiting for our sync... */
+    ++waiting_for_sync; /* we timed out waiting for our sync... */
     do_resize(); /* ...so let any pending resizes through */
 
-    return FALSE; /* don't repeat */
+    if (waiting_for_sync > SYNC_TIMEOUTS) {
+        sync_timer = 0;
+        return FALSE; /* don't repeat */
+    }
+    else
+        return TRUE; /* keep waiting */
 }
 #endif
 
@@ -472,16 +577,50 @@ static void calc_resize(gboolean keyboard, gint keydist, gint *dw, gint *dh,
     *dh = nh - oh;
 }
 
+static void edge_warp_move_ptr(void)
+{
+    gint x, y;
+    const Rect* a;
+
+    screen_pointer_pos(&x, &y);
+    a = screen_physical_area_all_monitors();
+
+    switch (edge_warp_dir) {
+    case OB_DIRECTION_NORTH:
+        y = a->height - 1;
+        break;
+    case OB_DIRECTION_EAST:
+        x = a->x;
+        break;
+    case OB_DIRECTION_SOUTH:
+        y = a->y;
+        break;
+    case OB_DIRECTION_WEST:
+        x = a->width - 1;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+
+    XWarpPointer(obt_display, 0, obt_root(ob_screen), 0, 0, 0, 0, x, y);
+}
+
 static gboolean edge_warp_delay_func(gpointer data)
 {
     guint d;
 
-    d = screen_find_desktop(screen_desktop, edge_warp_dir, TRUE, FALSE);
-    if (d != screen_desktop) screen_set_desktop(d, TRUE);
-
-    edge_warp_dir = -1;
+    /* only fire every second time. so it's fast the first time, but slower
+       after that */
+    if (edge_warp_odd) {
+        d = screen_find_desktop(screen_desktop, edge_warp_dir, TRUE, FALSE);
+        if (d != screen_desktop) {
+            if (config_mouse_screenedgewarp) edge_warp_move_ptr();
+            screen_set_desktop(d, TRUE);
+        }
+    }
+    edge_warp_odd = !edge_warp_odd;
 
-    return FALSE; /* don't repeat */
+    return TRUE; /* do repeat ! */
 }
 
 static void do_edge_warp(gint x, gint y)
@@ -494,7 +633,11 @@ static void do_edge_warp(gint x, gint y)
     dir = -1;
 
     for (i = 0; i < screen_num_monitors; ++i) {
-        Rect *a = screen_physical_area_monitor(i);
+        const Rect *a = screen_physical_area_monitor(i);
+
+        if (!RECT_CONTAINS(*a, x, y))
+            continue;
+
         if (x == RECT_LEFT(*a)) dir = OB_DIRECTION_WEST;
         if (x == RECT_RIGHT(*a)) dir = OB_DIRECTION_EAST;
         if (y == RECT_TOP(*a)) dir = OB_DIRECTION_NORTH;
@@ -511,44 +654,44 @@ static void do_edge_warp(gint x, gint y)
         {
             dir = -1;
         }
-        g_free(a);
     }
 
     if (dir != edge_warp_dir) {
-        if (dir == (ObDirection)-1)
-            cancel_edge_warp();
-        else
-            ob_main_loop_timeout_add(ob_main_loop,
-                                     config_mouse_screenedgetime * 1000,
-                                     edge_warp_delay_func,
-                                     NULL, NULL, NULL);
+        cancel_edge_warp();
+        if (dir != (ObDirection)-1) {
+            edge_warp_odd = TRUE; /* switch on the first timeout */
+            edge_warp_timer = g_timeout_add(config_mouse_screenedgetime,
+                                            edge_warp_delay_func, NULL);
+        }
         edge_warp_dir = dir;
     }
 }
 
-static void cancel_edge_warp()
+static void cancel_edge_warp(void)
 {
-    ob_main_loop_timeout_remove(ob_main_loop, edge_warp_delay_func);
+    if (edge_warp_timer) g_source_remove(edge_warp_timer);
+    edge_warp_timer = 0;
 }
 
-static void move_with_keys(gint keycode, gint state)
+static void move_with_keys(KeySym sym, guint state)
 {
     gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
     gint opx, px, opy, py;
     gint dist = 0;
 
     /* shift means jump to edge */
-    if (state & modkeys_key_to_mask(OB_MODKEY_KEY_SHIFT)) {
+    if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT))
+    {
         gint x, y;
         ObDirection dir;
 
-        if (keycode == ob_keycode(OB_KEY_RIGHT))
+        if (sym == XK_Right)
             dir = OB_DIRECTION_EAST;
-        else if (keycode == ob_keycode(OB_KEY_LEFT))
+        else if (sym == XK_Left)
             dir = OB_DIRECTION_WEST;
-        else if (keycode == ob_keycode(OB_KEY_DOWN))
+        else if (sym == XK_Down)
             dir = OB_DIRECTION_SOUTH;
-        else /* if (keycode == ob_keycode(OB_KEY_UP)) */
+        else /* sym == XK_Up */
             dir = OB_DIRECTION_NORTH;
 
         client_find_move_directional(moveresize_client, dir, &x, &y);
@@ -556,28 +699,32 @@ static void move_with_keys(gint keycode, gint state)
         dy = y - moveresize_client->area.y;
     } else {
         /* control means fine grained */
-        if (state & modkeys_key_to_mask(OB_MODKEY_KEY_CONTROL))
+        if (state &
+            obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
+        {
             dist = 1;
+        }
         else
             dist = KEY_DIST;
 
-        if (keycode == ob_keycode(OB_KEY_RIGHT))
+        if (sym == XK_Right)
             dx = dist;
-        else if (keycode == ob_keycode(OB_KEY_LEFT))
+        else if (sym == XK_Left)
             dx = -dist;
-        else if (keycode == ob_keycode(OB_KEY_DOWN))
+        else if (sym == XK_Down)
             dy = dist;
-        else /* if (keycode == ob_keycode(OB_KEY_UP)) */
+        else /* if (sym == XK_Up) */
             dy = -dist;
     }
 
     screen_pointer_pos(&opx, &opy);
-    XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
+    XWarpPointer(obt_display, None, None, 0, 0, 0, 0, dx, dy);
     /* steal the motion events this causes */
-    XSync(ob_display, FALSE);
+    XSync(obt_display, FALSE);
     {
         XEvent ce;
-        while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
+        while (xqueue_remove_local(&ce, xqueue_match_type,
+                                   GINT_TO_POINTER(MotionNotify)));
     }
     screen_pointer_pos(&px, &py);
 
@@ -593,14 +740,14 @@ static void move_with_keys(gint keycode, gint state)
     start_y += (py - opy) - (cur_y - oy);
 }
 
-static void resize_with_keys(gint keycode, gint state)
+static void resize_with_keys(KeySym sym, guint state)
 {
     gint dw = 0, dh = 0, pdx = 0, pdy = 0, opx, opy, px, py;
-    gint dist = 0, resist = 0;
+    gint resist = 0;
     ObDirection dir;
 
     /* pick the edge if it needs to move */
-    if (keycode == ob_keycode(OB_KEY_RIGHT)) {
+    if (sym == XK_Right) {
         dir = OB_DIRECTION_EAST;
         if (key_resize_edge != OB_DIRECTION_WEST &&
             key_resize_edge != OB_DIRECTION_EAST)
@@ -608,8 +755,7 @@ static void resize_with_keys(gint keycode, gint state)
             key_resize_edge = OB_DIRECTION_EAST;
             return;
         }
-    }
-    if (keycode == ob_keycode(OB_KEY_LEFT)) {
+    } else if (sym == XK_Left) {
         dir = OB_DIRECTION_WEST;
         if (key_resize_edge != OB_DIRECTION_WEST &&
             key_resize_edge != OB_DIRECTION_EAST)
@@ -617,8 +763,7 @@ static void resize_with_keys(gint keycode, gint state)
             key_resize_edge = OB_DIRECTION_WEST;
             return;
         }
-    }
-    if (keycode == ob_keycode(OB_KEY_UP)) {
+    } else if (sym == XK_Up) {
         dir = OB_DIRECTION_NORTH;
         if (key_resize_edge != OB_DIRECTION_NORTH &&
             key_resize_edge != OB_DIRECTION_SOUTH)
@@ -626,8 +771,7 @@ static void resize_with_keys(gint keycode, gint state)
             key_resize_edge = OB_DIRECTION_NORTH;
             return;
         }
-    }
-    if (keycode == ob_keycode(OB_KEY_DOWN)) {
+    } else /* if (sym == XK_Down) */ {
         dir = OB_DIRECTION_SOUTH;
         if (key_resize_edge != OB_DIRECTION_NORTH &&
             key_resize_edge != OB_DIRECTION_SOUTH)
@@ -638,20 +782,26 @@ static void resize_with_keys(gint keycode, gint state)
     }
 
     /* shift means jump to edge */
-    if (state & modkeys_key_to_mask(OB_MODKEY_KEY_SHIFT)) {
+    if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT))
+    {
         gint x, y, w, h;
 
-        if (keycode == ob_keycode(OB_KEY_RIGHT))
+        if (sym == XK_Right)
             dir = OB_DIRECTION_EAST;
-        else if (keycode == ob_keycode(OB_KEY_LEFT))
+        else if (sym == XK_Left)
             dir = OB_DIRECTION_WEST;
-        else if (keycode == ob_keycode(OB_KEY_DOWN))
+        else if (sym == XK_Down)
             dir = OB_DIRECTION_SOUTH;
-        else /* if (keycode == ob_keycode(OB_KEY_UP)) */
+        else /* if (sym == XK_Up)) */
             dir = OB_DIRECTION_NORTH;
 
-        client_find_resize_directional(moveresize_client, key_resize_edge,
-                                       key_resize_edge == dir,
+        ObClientDirectionalResizeType resize_type =
+            key_resize_edge == dir ? CLIENT_RESIZE_GROW
+                                   : CLIENT_RESIZE_SHRINK;
+
+        client_find_resize_directional(moveresize_client,
+                                       key_resize_edge,
+                                       resize_type,
                                        &x, &y, &w, &h);
         dw = w - moveresize_client->area.width;
         dh = h - moveresize_client->area.height;
@@ -663,7 +813,9 @@ static void resize_with_keys(gint keycode, gint state)
             distw = moveresize_client->size_inc.width;
             resist = 1;
         }
-        else if (state & modkeys_key_to_mask(OB_MODKEY_KEY_CONTROL)) {
+        else if (state &
+                 obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
+        {
             distw = 1;
             resist = 1;
         }
@@ -675,7 +827,9 @@ static void resize_with_keys(gint keycode, gint state)
             disth = moveresize_client->size_inc.height;
             resist = 1;
         }
-        else if (state & modkeys_key_to_mask(OB_MODKEY_KEY_CONTROL)) {
+        else if (state &
+                 obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
+        {
             disth = 1;
             resist = 1;
         }
@@ -686,30 +840,57 @@ static void resize_with_keys(gint keycode, gint state)
 
         if (key_resize_edge == OB_DIRECTION_WEST) {
             if (dir == OB_DIRECTION_WEST)
-                dw = (dist = distw);
+                dw = distw;
             else
-                dw = -(dist = distw);
+                dw = -distw;
         }
         else if (key_resize_edge == OB_DIRECTION_EAST) {
             if (dir == OB_DIRECTION_EAST)
-                dw = (dist = distw);
+                dw = distw;
             else
-                dw = -(dist = distw);
+                dw = -distw;
         }
         else if (key_resize_edge == OB_DIRECTION_NORTH) {
             if (dir == OB_DIRECTION_NORTH)
-                dh = (dist = disth);
+                dh = disth;
             else
-                dh = -(dist = disth);
+                dh = -disth;
         }
         else /*if (key_resize_edge == OB_DIRECTION_SOUTH)*/ {
             if (dir == OB_DIRECTION_SOUTH)
-                dh = (dist = disth);
+                dh = disth;
             else
-                dh = -(dist = disth);
+                dh = -disth;
         }
     }
 
+    if (moveresize_client->max_horz &&
+        (key_resize_edge == OB_DIRECTION_WEST ||
+         key_resize_edge == OB_DIRECTION_EAST))
+    {
+        /* unmax horz */
+        was_max_horz = TRUE;
+        pre_max_area.x = moveresize_client->pre_max_area.x;
+        pre_max_area.width = moveresize_client->pre_max_area.width;
+
+        moveresize_client->pre_max_area.x = cur_x;
+        moveresize_client->pre_max_area.width = cur_w;
+        client_maximize(moveresize_client, FALSE, 1);
+    }
+    else if (moveresize_client->max_vert &&
+             (key_resize_edge == OB_DIRECTION_NORTH ||
+              key_resize_edge == OB_DIRECTION_SOUTH))
+    {
+        /* unmax vert */
+        was_max_vert = TRUE;
+        pre_max_area.y = moveresize_client->pre_max_area.y;
+        pre_max_area.height = moveresize_client->pre_max_area.height;
+
+        moveresize_client->pre_max_area.y = cur_y;
+        moveresize_client->pre_max_area.height = cur_h;
+        client_maximize(moveresize_client, FALSE, 2);
+    }
+
     calc_resize(TRUE, resist, &dw, &dh, dir);
     if (key_resize_edge == OB_DIRECTION_WEST)
         cur_x -= dw;
@@ -729,12 +910,13 @@ static void resize_with_keys(gint keycode, gint state)
         pdy = dh;
 
     screen_pointer_pos(&opx, &opy);
-    XWarpPointer(ob_display, None, None, 0, 0, 0, 0, pdx, pdy);
+    XWarpPointer(obt_display, None, None, 0, 0, 0, 0, pdx, pdy);
     /* steal the motion events this causes */
-    XSync(ob_display, FALSE);
+    XSync(obt_display, FALSE);
     {
         XEvent ce;
-        while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
+        while (xqueue_remove_local(&ce, xqueue_match_type,
+                                   GINT_TO_POINTER(MotionNotify)));
     }
     screen_pointer_pos(&px, &py);
 
@@ -777,47 +959,90 @@ gboolean moveresize_event(XEvent *e)
             gint dw, dh;
             ObDirection dir;
 
-            if (corner == prop_atoms.net_wm_moveresize_size_topleft) {
+            if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
                 dw = -(e->xmotion.x_root - start_x);
                 dh = -(e->xmotion.y_root - start_y);
                 dir = OB_DIRECTION_NORTHWEST;
-            } else if (corner == prop_atoms.net_wm_moveresize_size_top) {
+            } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
                 dw = 0;
                 dh = -(e->xmotion.y_root - start_y);
                 dir = OB_DIRECTION_NORTH;
-            } else if (corner == prop_atoms.net_wm_moveresize_size_topright) {
+            } else if (corner ==
+                       OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
                 dw = (e->xmotion.x_root - start_x);
                 dh = -(e->xmotion.y_root - start_y);
                 dir = OB_DIRECTION_NORTHEAST;
-            } else if (corner == prop_atoms.net_wm_moveresize_size_right) {
+            } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT)) {
                 dw = (e->xmotion.x_root - start_x);
                 dh = 0;
                 dir = OB_DIRECTION_EAST;
             } else if (corner ==
-                       prop_atoms.net_wm_moveresize_size_bottomright) {
+                       OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)) {
                 dw = (e->xmotion.x_root - start_x);
                 dh = (e->xmotion.y_root - start_y);
                 dir = OB_DIRECTION_SOUTHEAST;
-            } else if (corner == prop_atoms.net_wm_moveresize_size_bottom) {
+            } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
+            {
                 dw = 0;
                 dh = (e->xmotion.y_root - start_y);
                 dir = OB_DIRECTION_SOUTH;
             } else if (corner ==
-                       prop_atoms.net_wm_moveresize_size_bottomleft) {
+                       OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
                 dw = -(e->xmotion.x_root - start_x);
                 dh = (e->xmotion.y_root - start_y);
                 dir = OB_DIRECTION_SOUTHWEST;
-            } else if (corner == prop_atoms.net_wm_moveresize_size_left) {
+            } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
                 dw = -(e->xmotion.x_root - start_x);
                 dh = 0;
                 dir = OB_DIRECTION_WEST;
-            } else if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
+            } else if (corner ==
+                       OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
                 dw = (e->xmotion.x_root - start_x);
                 dh = (e->xmotion.y_root - start_y);
                 dir = OB_DIRECTION_SOUTHEAST;
             } else
                 g_assert_not_reached();
 
+            /* override the client's max state if desired */
+            if (ABS(dw) >= config_resist_edge) {
+                if (moveresize_client->max_horz) {
+                    /* unmax horz */
+                    was_max_horz = TRUE;
+                    pre_max_area.x = moveresize_client->pre_max_area.x;
+                    pre_max_area.width = moveresize_client->pre_max_area.width;
+
+                    moveresize_client->pre_max_area.x = cur_x;
+                    moveresize_client->pre_max_area.width = cur_w;
+                    client_maximize(moveresize_client, FALSE, 1);
+                }
+            }
+            else if (was_max_horz && !moveresize_client->max_horz) {
+                /* remax horz and put the premax back */
+                client_maximize(moveresize_client, TRUE, 1);
+                moveresize_client->pre_max_area.x = pre_max_area.x;
+                moveresize_client->pre_max_area.width = pre_max_area.width;
+            }
+
+            if (ABS(dh) >= config_resist_edge) {
+                if (moveresize_client->max_vert) {
+                    /* unmax vert */
+                    was_max_vert = TRUE;
+                    pre_max_area.y = moveresize_client->pre_max_area.y;
+                    pre_max_area.height =
+                        moveresize_client->pre_max_area.height;
+
+                    moveresize_client->pre_max_area.y = cur_y;
+                    moveresize_client->pre_max_area.height = cur_h;
+                    client_maximize(moveresize_client, FALSE, 2);
+                }
+            }
+            else if (was_max_vert && !moveresize_client->max_vert) {
+                /* remax vert and put the premax back */
+                client_maximize(moveresize_client, TRUE, 2);
+                moveresize_client->pre_max_area.y = pre_max_area.y;
+                moveresize_client->pre_max_area.height = pre_max_area.height;
+            }
+
             dw -= cur_w - start_cw;
             dh -= cur_h - start_ch;
 
@@ -825,15 +1050,15 @@ gboolean moveresize_event(XEvent *e)
             cur_w += dw;
             cur_h += dh;
 
-            if (corner == prop_atoms.net_wm_moveresize_size_topleft ||
-                corner == prop_atoms.net_wm_moveresize_size_left ||
-                corner == prop_atoms.net_wm_moveresize_size_bottomleft)
+            if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
+                corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
+                corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT))
             {
                 cur_x -= dw;
             }
-            if (corner == prop_atoms.net_wm_moveresize_size_topleft ||
-                corner == prop_atoms.net_wm_moveresize_size_top ||
-                corner == prop_atoms.net_wm_moveresize_size_topright)
+            if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
+                corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
+                corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT))
             {
                 cur_y -= dh;
             }
@@ -842,33 +1067,39 @@ gboolean moveresize_event(XEvent *e)
         }
         used = TRUE;
     } else if (e->type == KeyPress) {
-        if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
+        KeySym sym = obt_keyboard_keypress_to_keysym(e);
+
+        if (sym == XK_Escape) {
             moveresize_end(TRUE);
             used = TRUE;
-        } else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
+        } else if (sym == XK_Return || sym == XK_KP_Enter) {
             moveresize_end(FALSE);
             used = TRUE;
-        } else if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT) ||
-                   e->xkey.keycode == ob_keycode(OB_KEY_LEFT) ||
-                   e->xkey.keycode == ob_keycode(OB_KEY_DOWN) ||
-                   e->xkey.keycode == ob_keycode(OB_KEY_UP))
+        } else if (sym == XK_Right || sym == XK_Left ||
+                   sym == XK_Up || sym == XK_Down)
         {
-            if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
-                resize_with_keys(e->xkey.keycode, e->xkey.state);
+            if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
+                resize_with_keys(sym, e->xkey.state);
                 used = TRUE;
-            } else if (corner == prop_atoms.net_wm_moveresize_move_keyboard) {
-                move_with_keys(e->xkey.keycode, e->xkey.state);
+            } else if (corner ==
+                       OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
+            {
+                move_with_keys(sym, e->xkey.state);
                 used = TRUE;
             }
         }
     }
 #ifdef SYNC
-    else if (e->type == extensions_sync_event_basep + XSyncAlarmNotify)
+    else if (e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
     {
-        waiting_for_sync = FALSE; /* we got our sync... */
+        waiting_for_sync = 0; /* we got our sync... */
         do_resize(); /* ...so try resize if there is more change pending */
         used = TRUE;
     }
 #endif
+
+    if (used && moveresize_client == focus_client)
+        event_update_user_time();
+
     return used;
 }