From 454473ac6c91a0f72e9e940841e44a29b9bb697c Mon Sep 17 00:00:00 2001 From: Albert Safin Date: Wed, 12 Dec 2018 11:01:36 +0700 Subject: [PATCH 1/6] Move drag_pointer() to its own source file Move drag_pointer() and related definitions from floating.c to new file drag_pointer.c since it's applicable not only to floating windows but also to resizing of tiled windows. --- Makefile.am | 2 + include/all.h | 1 + include/drag.h | 55 ++++++++++++ include/floating.h | 43 --------- src/drag.c | 213 +++++++++++++++++++++++++++++++++++++++++++++ src/floating.c | 203 ------------------------------------------ 6 files changed, 271 insertions(+), 246 deletions(-) create mode 100644 include/drag.h create mode 100644 src/drag.c diff --git a/Makefile.am b/Makefile.am index ee0e038b5..d379a854a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -503,6 +503,7 @@ i3_SOURCES = \ include/con.h \ include/data.h \ include/display_version.h \ + include/drag.h \ include/ewmh.h \ include/fake_outputs.h \ include/floating.h \ @@ -548,6 +549,7 @@ i3_SOURCES = \ src/config_directives.c \ src/config_parser.c \ src/display_version.c \ + src/drag.c \ src/ewmh.c \ src/fake_outputs.c \ src/floating.c \ diff --git a/include/all.h b/include/all.h index e93b066bc..aa2b5b250 100644 --- a/include/all.h +++ b/include/all.h @@ -53,6 +53,7 @@ #include "click.h" #include "key_press.h" #include "floating.h" +#include "drag.h" #include "configuration.h" #include "handlers.h" #include "randr.h" diff --git a/include/drag.h b/include/drag.h new file mode 100644 index 000000000..5d4ca581e --- /dev/null +++ b/include/drag.h @@ -0,0 +1,55 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * © 2009 Michael Stapelberg and contributors (see also: LICENSE) + * + * drag.c: click and drag. + * + */ +#pragma once + +#include + +/** Callback for dragging */ +typedef void (*callback_t)(Con *, Rect *, uint32_t, uint32_t, const void *); + +/** Macro to create a callback function for dragging */ +#define DRAGGING_CB(name) \ + static void name(Con *con, Rect *old_rect, uint32_t new_x, \ + uint32_t new_y, const void *extra) + +/** + * This is the return value of a drag operation like drag_pointer. + * + * DRAGGING will indicate the drag action is still in progress and can be + * continued or resolved. + * + * DRAG_SUCCESS will indicate the intention of the drag action should be + * carried out. + * + * DRAG_REVERT will indicate an attempt should be made to restore the state of + * the involved windows to their condition before the drag. + * + * DRAG_ABORT will indicate that the intention of the drag action cannot be + * carried out (e.g. because the window has been unmapped). + * + */ +typedef enum { + DRAGGING = 0, + DRAG_SUCCESS, + DRAG_REVERT, + DRAG_ABORT +} drag_result_t; + +/** + * This function grabs your pointer and keyboard and lets you drag stuff around + * (borders). Every time you move your mouse, an XCB_MOTION_NOTIFY event will + * be received and the given callback will be called with the parameters + * specified (client, border on which the click originally was), the original + * rect of the client, the event and the new coordinates (x, y). + * + */ +drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, + xcb_window_t confine_to, border_t border, int cursor, + callback_t callback, const void *extra); diff --git a/include/floating.h b/include/floating.h index a7813099c..4b66eef70 100644 --- a/include/floating.h +++ b/include/floating.h @@ -13,14 +13,6 @@ #include "tree.h" -/** Callback for dragging */ -typedef void (*callback_t)(Con *, Rect *, uint32_t, uint32_t, const void *); - -/** Macro to create a callback function for dragging */ -#define DRAGGING_CB(name) \ - static void name(Con *con, Rect *old_rect, uint32_t new_x, \ - uint32_t new_y, const void *extra) - /** On which border was the dragging initiated? */ typedef enum { BORDER_LEFT = (1 << 0), BORDER_RIGHT = (1 << 1), @@ -106,41 +98,6 @@ void floating_resize_window(Con *con, const bool proportional, const xcb_button_ */ void floating_check_size(Con *floating_con, bool prefer_height); -/** - * This is the return value of a drag operation like drag_pointer. - * - * DRAGGING will indicate the drag action is still in progress and can be - * continued or resolved. - * - * DRAG_SUCCESS will indicate the intention of the drag action should be - * carried out. - * - * DRAG_REVERT will indicate an attempt should be made to restore the state of - * the involved windows to their condition before the drag. - * - * DRAG_ABORT will indicate that the intention of the drag action cannot be - * carried out (e.g. because the window has been unmapped). - * - */ -typedef enum { - DRAGGING = 0, - DRAG_SUCCESS, - DRAG_REVERT, - DRAG_ABORT -} drag_result_t; - -/** - * This function grabs your pointer and keyboard and lets you drag stuff around - * (borders). Every time you move your mouse, an XCB_MOTION_NOTIFY event will - * be received and the given callback will be called with the parameters - * specified (client, border on which the click originally was), the original - * rect of the client, the event and the new coordinates (x, y). - * - */ -drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, - xcb_window_t confine_to, border_t border, int cursor, - callback_t callback, const void *extra); - /** * Repositions the CT_FLOATING_CON to have the coordinates specified by * newrect, but only if the coordinates are not out-of-bounds. Also reassigns diff --git a/src/drag.c b/src/drag.c new file mode 100644 index 000000000..af019b255 --- /dev/null +++ b/src/drag.c @@ -0,0 +1,213 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * © 2009 Michael Stapelberg and contributors (see also: LICENSE) + * + * drag.c: click and drag. + * + */ +#include "all.h" + +/* Custom data structure used to track dragging-related events. */ +struct drag_x11_cb { + ev_prepare prepare; + + /* Whether this modal event loop should be exited and with which result. */ + drag_result_t result; + + /* The container that is being dragged or resized, or NULL if this is a + * drag of the resize handle. */ + Con *con; + + /* The dimensions of con when the loop was started. */ + Rect old_rect; + + /* The callback to invoke after every pointer movement. */ + callback_t callback; + + /* User data pointer for callback. */ + const void *extra; +}; + +static bool drain_drag_events(EV_P, struct drag_x11_cb *dragloop) { + xcb_motion_notify_event_t *last_motion_notify = NULL; + xcb_generic_event_t *event; + + while ((event = xcb_poll_for_event(conn)) != NULL) { + if (event->response_type == 0) { + xcb_generic_error_t *error = (xcb_generic_error_t *)event; + DLOG("X11 Error received (probably harmless)! sequence 0x%x, error_code = %d\n", + error->sequence, error->error_code); + free(event); + continue; + } + + /* Strip off the highest bit (set if the event is generated) */ + int type = (event->response_type & 0x7F); + + switch (type) { + case XCB_BUTTON_RELEASE: + dragloop->result = DRAG_SUCCESS; + break; + + case XCB_KEY_PRESS: + DLOG("A key was pressed during drag, reverting changes.\n"); + dragloop->result = DRAG_REVERT; + handle_event(type, event); + break; + + case XCB_UNMAP_NOTIFY: { + xcb_unmap_notify_event_t *unmap_event = (xcb_unmap_notify_event_t *)event; + Con *con = con_by_window_id(unmap_event->window); + + if (con != NULL) { + DLOG("UnmapNotify for window 0x%08x (container %p)\n", unmap_event->window, con); + + if (con_get_workspace(con) == con_get_workspace(focused)) { + DLOG("UnmapNotify for a managed window on the current workspace, aborting\n"); + dragloop->result = DRAG_ABORT; + } + } + + handle_event(type, event); + break; + } + + case XCB_MOTION_NOTIFY: + /* motion_notify events are saved for later */ + FREE(last_motion_notify); + last_motion_notify = (xcb_motion_notify_event_t *)event; + break; + + default: + DLOG("Passing to original handler\n"); + handle_event(type, event); + break; + } + + if (last_motion_notify != (xcb_motion_notify_event_t *)event) + free(event); + + if (dragloop->result != DRAGGING) { + ev_break(EV_A_ EVBREAK_ONE); + if (dragloop->result == DRAG_SUCCESS) { + /* Ensure motion notify events are handled. */ + break; + } else { + free(last_motion_notify); + return true; + } + } + } + + if (last_motion_notify == NULL) { + return true; + } + + /* Ensure that we are either dragging the resize handle (con is NULL) or that the + * container still exists. The latter might not be true, e.g., if the window closed + * for any reason while the user was dragging it. */ + if (!dragloop->con || con_exists(dragloop->con)) { + dragloop->callback( + dragloop->con, + &(dragloop->old_rect), + last_motion_notify->root_x, + last_motion_notify->root_y, + dragloop->extra); + } + FREE(last_motion_notify); + + xcb_flush(conn); + return dragloop->result != DRAGGING; +} + +static void xcb_drag_prepare_cb(EV_P_ ev_prepare *w, int revents) { + struct drag_x11_cb *dragloop = (struct drag_x11_cb *)w->data; + while (!drain_drag_events(EV_A, dragloop)) { + /* repeatedly drain events: draining might produce additional ones */ + } +} + +/* + * This function grabs your pointer and keyboard and lets you drag stuff around + * (borders). Every time you move your mouse, an XCB_MOTION_NOTIFY event will + * be received and the given callback will be called with the parameters + * specified (client, border on which the click originally was), the original + * rect of the client, the event and the new coordinates (x, y). + * + */ +drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t confine_to, + border_t border, int cursor, callback_t callback, const void *extra) { + xcb_cursor_t xcursor = (cursor && xcursor_supported) ? xcursor_get_cursor(cursor) : XCB_NONE; + + /* Grab the pointer */ + xcb_grab_pointer_cookie_t cookie; + xcb_grab_pointer_reply_t *reply; + xcb_generic_error_t *error; + + cookie = xcb_grab_pointer(conn, + false, /* get all pointer events specified by the following mask */ + root, /* grab the root window */ + XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION, /* which events to let through */ + XCB_GRAB_MODE_ASYNC, /* pointer events should continue as normal */ + XCB_GRAB_MODE_ASYNC, /* keyboard mode */ + confine_to, /* confine_to = in which window should the cursor stay */ + xcursor, /* possibly display a special cursor */ + XCB_CURRENT_TIME); + + if ((reply = xcb_grab_pointer_reply(conn, cookie, &error)) == NULL) { + ELOG("Could not grab pointer (error_code = %d)\n", error->error_code); + free(error); + return DRAG_ABORT; + } + + free(reply); + + /* Grab the keyboard */ + xcb_grab_keyboard_cookie_t keyb_cookie; + xcb_grab_keyboard_reply_t *keyb_reply; + + keyb_cookie = xcb_grab_keyboard(conn, + false, /* get all keyboard events */ + root, /* grab the root window */ + XCB_CURRENT_TIME, + XCB_GRAB_MODE_ASYNC, /* continue processing pointer events as normal */ + XCB_GRAB_MODE_ASYNC /* keyboard mode */ + ); + + if ((keyb_reply = xcb_grab_keyboard_reply(conn, keyb_cookie, &error)) == NULL) { + ELOG("Could not grab keyboard (error_code = %d)\n", error->error_code); + free(error); + xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); + return DRAG_ABORT; + } + + free(keyb_reply); + + /* Go into our own event loop */ + struct drag_x11_cb loop = { + .result = DRAGGING, + .con = con, + .callback = callback, + .extra = extra, + }; + ev_prepare *prepare = &loop.prepare; + if (con) + loop.old_rect = con->rect; + ev_prepare_init(prepare, xcb_drag_prepare_cb); + prepare->data = &loop; + main_set_x11_cb(false); + ev_prepare_start(main_loop, prepare); + + ev_loop(main_loop, 0); + + ev_prepare_stop(main_loop, prepare); + main_set_x11_cb(true); + + xcb_ungrab_keyboard(conn, XCB_CURRENT_TIME); + xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); + xcb_flush(conn); + + return loop.result; +} diff --git a/src/floating.c b/src/floating.c index 70e7bc176..b022ee193 100644 --- a/src/floating.c +++ b/src/floating.c @@ -727,209 +727,6 @@ void floating_resize_window(Con *con, const bool proportional, con->scratchpad_state = SCRATCHPAD_CHANGED; } -/* Custom data structure used to track dragging-related events. */ -struct drag_x11_cb { - ev_prepare prepare; - - /* Whether this modal event loop should be exited and with which result. */ - drag_result_t result; - - /* The container that is being dragged or resized, or NULL if this is a - * drag of the resize handle. */ - Con *con; - - /* The dimensions of con when the loop was started. */ - Rect old_rect; - - /* The callback to invoke after every pointer movement. */ - callback_t callback; - - /* User data pointer for callback. */ - const void *extra; -}; - -static bool drain_drag_events(EV_P, struct drag_x11_cb *dragloop) { - xcb_motion_notify_event_t *last_motion_notify = NULL; - xcb_generic_event_t *event; - - while ((event = xcb_poll_for_event(conn)) != NULL) { - if (event->response_type == 0) { - xcb_generic_error_t *error = (xcb_generic_error_t *)event; - DLOG("X11 Error received (probably harmless)! sequence 0x%x, error_code = %d\n", - error->sequence, error->error_code); - free(event); - continue; - } - - /* Strip off the highest bit (set if the event is generated) */ - int type = (event->response_type & 0x7F); - - switch (type) { - case XCB_BUTTON_RELEASE: - dragloop->result = DRAG_SUCCESS; - break; - - case XCB_KEY_PRESS: - DLOG("A key was pressed during drag, reverting changes.\n"); - dragloop->result = DRAG_REVERT; - handle_event(type, event); - break; - - case XCB_UNMAP_NOTIFY: { - xcb_unmap_notify_event_t *unmap_event = (xcb_unmap_notify_event_t *)event; - Con *con = con_by_window_id(unmap_event->window); - - if (con != NULL) { - DLOG("UnmapNotify for window 0x%08x (container %p)\n", unmap_event->window, con); - - if (con_get_workspace(con) == con_get_workspace(focused)) { - DLOG("UnmapNotify for a managed window on the current workspace, aborting\n"); - dragloop->result = DRAG_ABORT; - } - } - - handle_event(type, event); - break; - } - - case XCB_MOTION_NOTIFY: - /* motion_notify events are saved for later */ - FREE(last_motion_notify); - last_motion_notify = (xcb_motion_notify_event_t *)event; - break; - - default: - DLOG("Passing to original handler\n"); - handle_event(type, event); - break; - } - - if (last_motion_notify != (xcb_motion_notify_event_t *)event) - free(event); - - if (dragloop->result != DRAGGING) { - ev_break(EV_A_ EVBREAK_ONE); - if (dragloop->result == DRAG_SUCCESS) { - /* Ensure motion notify events are handled. */ - break; - } else { - free(last_motion_notify); - return true; - } - } - } - - if (last_motion_notify == NULL) { - return true; - } - - /* Ensure that we are either dragging the resize handle (con is NULL) or that the - * container still exists. The latter might not be true, e.g., if the window closed - * for any reason while the user was dragging it. */ - if (!dragloop->con || con_exists(dragloop->con)) { - dragloop->callback( - dragloop->con, - &(dragloop->old_rect), - last_motion_notify->root_x, - last_motion_notify->root_y, - dragloop->extra); - } - FREE(last_motion_notify); - - xcb_flush(conn); - return dragloop->result != DRAGGING; -} - -static void xcb_drag_prepare_cb(EV_P_ ev_prepare *w, int revents) { - struct drag_x11_cb *dragloop = (struct drag_x11_cb *)w->data; - while (!drain_drag_events(EV_A, dragloop)) { - /* repeatedly drain events: draining might produce additional ones */ - } -} - -/* - * This function grabs your pointer and keyboard and lets you drag stuff around - * (borders). Every time you move your mouse, an XCB_MOTION_NOTIFY event will - * be received and the given callback will be called with the parameters - * specified (client, border on which the click originally was), the original - * rect of the client, the event and the new coordinates (x, y). - * - */ -drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t confine_to, - border_t border, int cursor, callback_t callback, const void *extra) { - xcb_cursor_t xcursor = (cursor && xcursor_supported) ? xcursor_get_cursor(cursor) : XCB_NONE; - - /* Grab the pointer */ - xcb_grab_pointer_cookie_t cookie; - xcb_grab_pointer_reply_t *reply; - xcb_generic_error_t *error; - - cookie = xcb_grab_pointer(conn, - false, /* get all pointer events specified by the following mask */ - root, /* grab the root window */ - XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION, /* which events to let through */ - XCB_GRAB_MODE_ASYNC, /* pointer events should continue as normal */ - XCB_GRAB_MODE_ASYNC, /* keyboard mode */ - confine_to, /* confine_to = in which window should the cursor stay */ - xcursor, /* possibly display a special cursor */ - XCB_CURRENT_TIME); - - if ((reply = xcb_grab_pointer_reply(conn, cookie, &error)) == NULL) { - ELOG("Could not grab pointer (error_code = %d)\n", error->error_code); - free(error); - return DRAG_ABORT; - } - - free(reply); - - /* Grab the keyboard */ - xcb_grab_keyboard_cookie_t keyb_cookie; - xcb_grab_keyboard_reply_t *keyb_reply; - - keyb_cookie = xcb_grab_keyboard(conn, - false, /* get all keyboard events */ - root, /* grab the root window */ - XCB_CURRENT_TIME, - XCB_GRAB_MODE_ASYNC, /* continue processing pointer events as normal */ - XCB_GRAB_MODE_ASYNC /* keyboard mode */ - ); - - if ((keyb_reply = xcb_grab_keyboard_reply(conn, keyb_cookie, &error)) == NULL) { - ELOG("Could not grab keyboard (error_code = %d)\n", error->error_code); - free(error); - xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); - return DRAG_ABORT; - } - - free(keyb_reply); - - /* Go into our own event loop */ - struct drag_x11_cb loop = { - .result = DRAGGING, - .con = con, - .callback = callback, - .extra = extra, - }; - ev_prepare *prepare = &loop.prepare; - if (con) - loop.old_rect = con->rect; - ev_prepare_init(prepare, xcb_drag_prepare_cb); - prepare->data = &loop; - main_set_x11_cb(false); - ev_prepare_start(main_loop, prepare); - - ev_loop(main_loop, 0); - - ev_prepare_stop(main_loop, prepare); - main_set_x11_cb(true); - - xcb_ungrab_keyboard(conn, XCB_CURRENT_TIME); - xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); - xcb_flush(conn); - - return loop.result; -} - /* * Repositions the CT_FLOATING_CON to have the coordinates specified by * newrect, but only if the coordinates are not out-of-bounds. Also reassigns From 2795c51d4b4f1c414aaa0c493061c908ac0260f1 Mon Sep 17 00:00:00 2001 From: Albert Safin Date: Wed, 12 Dec 2018 11:12:05 +0700 Subject: [PATCH 2/6] drag_pointer(): drop unused parameter `border` --- include/drag.h | 2 +- src/drag.c | 2 +- src/floating.c | 4 ++-- src/resize.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/drag.h b/include/drag.h index 5d4ca581e..924ff98c6 100644 --- a/include/drag.h +++ b/include/drag.h @@ -51,5 +51,5 @@ typedef enum { * */ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, - xcb_window_t confine_to, border_t border, int cursor, + xcb_window_t confine_to, int cursor, callback_t callback, const void *extra); diff --git a/src/drag.c b/src/drag.c index af019b255..57d9f9a9b 100644 --- a/src/drag.c +++ b/src/drag.c @@ -138,7 +138,7 @@ static void xcb_drag_prepare_cb(EV_P_ ev_prepare *w, int revents) { * */ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t confine_to, - border_t border, int cursor, callback_t callback, const void *extra) { + int cursor, callback_t callback, const void *extra) { xcb_cursor_t xcursor = (cursor && xcursor_supported) ? xcursor_get_cursor(cursor) : XCB_NONE; /* Grab the pointer */ diff --git a/src/floating.c b/src/floating.c index b022ee193..6772128ae 100644 --- a/src/floating.c +++ b/src/floating.c @@ -595,7 +595,7 @@ void floating_drag_window(Con *con, const xcb_button_press_event_t *event) { Rect initial_rect = con->rect; /* Drag the window */ - drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, XCURSOR_CURSOR_MOVE, drag_window_callback, event); + drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, XCURSOR_CURSOR_MOVE, drag_window_callback, event); if (!con_exists(con)) { DLOG("The container has been closed in the meantime.\n"); @@ -711,7 +711,7 @@ void floating_resize_window(Con *con, const bool proportional, /* get the initial rect in case of revert/cancel */ Rect initial_rect = con->rect; - drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, cursor, resize_window_callback, ¶ms); + drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, cursor, resize_window_callback, ¶ms); if (!con_exists(con)) { DLOG("The container has been closed in the meantime.\n"); diff --git a/src/resize.c b/src/resize.c index 5ddee5c1e..a63e337b9 100644 --- a/src/resize.c +++ b/src/resize.c @@ -208,7 +208,7 @@ void resize_graphical_handler(Con *first, Con *second, orientation_t orientation const struct callback_params params = {orientation, output, helpwin, &new_position}; /* `drag_pointer' blocks until the drag is completed. */ - drag_result_t drag_result = drag_pointer(NULL, event, grabwin, BORDER_TOP, 0, resize_callback, ¶ms); + drag_result_t drag_result = drag_pointer(NULL, event, grabwin, 0, resize_callback, ¶ms); xcb_destroy_window(conn, helpwin); xcb_destroy_window(conn, grabwin); From 551ec20941060a02b1d621abfd4c43731176c5d8 Mon Sep 17 00:00:00 2001 From: Albert Safin Date: Wed, 12 Dec 2018 15:35:11 +0700 Subject: [PATCH 3/6] drag_pointer(): add use_treshold parameter --- include/drag.h | 20 +++++++++++++------- src/drag.c | 51 ++++++++++++++++++++++++++++++++++++++++++++------ src/floating.c | 10 +++------- src/resize.c | 2 +- 4 files changed, 62 insertions(+), 21 deletions(-) diff --git a/include/drag.h b/include/drag.h index 924ff98c6..2027f9349 100644 --- a/include/drag.h +++ b/include/drag.h @@ -12,12 +12,13 @@ #include /** Callback for dragging */ -typedef void (*callback_t)(Con *, Rect *, uint32_t, uint32_t, const void *); +typedef void (*callback_t)(Con *, Rect *, uint32_t, uint32_t, + const xcb_button_press_event_t *, const void *); /** Macro to create a callback function for dragging */ -#define DRAGGING_CB(name) \ - static void name(Con *con, Rect *old_rect, uint32_t new_x, \ - uint32_t new_y, const void *extra) +#define DRAGGING_CB(name) \ + static void name(Con *con, Rect *old_rect, uint32_t new_x, uint32_t new_y, \ + const xcb_button_press_event_t *event, const void *extra) /** * This is the return value of a drag operation like drag_pointer. @@ -46,10 +47,15 @@ typedef enum { * This function grabs your pointer and keyboard and lets you drag stuff around * (borders). Every time you move your mouse, an XCB_MOTION_NOTIFY event will * be received and the given callback will be called with the parameters - * specified (client, border on which the click originally was), the original - * rect of the client, the event and the new coordinates (x, y). + * specified (client, the original event), the original rect of the client, + * and the new coordinates (x, y). + * + * If use_threshold is set, dragging only starts after the user moves the + * pointer past a certain threshold. That is, the cursor will not be set and the + * callback will not be called until then. * */ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t confine_to, int cursor, - callback_t callback, const void *extra); + bool use_threshold, callback_t callback, + const void *extra); diff --git a/src/drag.c b/src/drag.c index 57d9f9a9b..6b05311af 100644 --- a/src/drag.c +++ b/src/drag.c @@ -20,16 +20,32 @@ struct drag_x11_cb { * drag of the resize handle. */ Con *con; + /* The original event that initiated the drag. */ + const xcb_button_press_event_t *event; + /* The dimensions of con when the loop was started. */ Rect old_rect; /* The callback to invoke after every pointer movement. */ callback_t callback; + /* Drag distance threshold exceeded. If use_threshold is not set, then + * threshold_exceeded is always true. */ + bool threshold_exceeded; + + /* Cursor to set after the threshold is exceeded. */ + xcb_cursor_t xcursor; + /* User data pointer for callback. */ const void *extra; }; +static bool threshold_exceeded(uint32_t x1, uint32_t y1, + uint32_t x2, uint32_t y2) { + const uint32_t threshold = 9; + return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) > threshold * threshold; +} + static bool drain_drag_events(EV_P, struct drag_x11_cb *dragloop) { xcb_motion_notify_event_t *last_motion_notify = NULL; xcb_generic_event_t *event; @@ -105,15 +121,29 @@ static bool drain_drag_events(EV_P, struct drag_x11_cb *dragloop) { return true; } + if (!dragloop->threshold_exceeded && + threshold_exceeded(last_motion_notify->root_x, last_motion_notify->root_y, + dragloop->event->root_x, dragloop->event->root_y)) { + if (dragloop->xcursor != XCB_NONE) { + xcb_change_active_pointer_grab( + conn, + dragloop->xcursor, + XCB_CURRENT_TIME, + XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION); + } + dragloop->threshold_exceeded = true; + } + /* Ensure that we are either dragging the resize handle (con is NULL) or that the * container still exists. The latter might not be true, e.g., if the window closed * for any reason while the user was dragging it. */ - if (!dragloop->con || con_exists(dragloop->con)) { + if (dragloop->threshold_exceeded && (!dragloop->con || con_exists(dragloop->con))) { dragloop->callback( dragloop->con, &(dragloop->old_rect), last_motion_notify->root_x, last_motion_notify->root_y, + dragloop->event, dragloop->extra); } FREE(last_motion_notify); @@ -133,12 +163,18 @@ static void xcb_drag_prepare_cb(EV_P_ ev_prepare *w, int revents) { * This function grabs your pointer and keyboard and lets you drag stuff around * (borders). Every time you move your mouse, an XCB_MOTION_NOTIFY event will * be received and the given callback will be called with the parameters - * specified (client, border on which the click originally was), the original - * rect of the client, the event and the new coordinates (x, y). + * specified (client, the original event), the original rect of the client, + * and the new coordinates (x, y). + * + * If use_threshold is set, dragging only starts after the user moves the + * pointer past a certain threshold. That is, the cursor will not be set and the + * callback will not be called until then. * */ -drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t confine_to, - int cursor, callback_t callback, const void *extra) { +drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, + xcb_window_t confine_to, int cursor, + bool use_threshold, callback_t callback, + const void *extra) { xcb_cursor_t xcursor = (cursor && xcursor_supported) ? xcursor_get_cursor(cursor) : XCB_NONE; /* Grab the pointer */ @@ -153,7 +189,7 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_ XCB_GRAB_MODE_ASYNC, /* pointer events should continue as normal */ XCB_GRAB_MODE_ASYNC, /* keyboard mode */ confine_to, /* confine_to = in which window should the cursor stay */ - xcursor, /* possibly display a special cursor */ + use_threshold ? XCB_NONE : xcursor, /* possibly display a special cursor */ XCB_CURRENT_TIME); if ((reply = xcb_grab_pointer_reply(conn, cookie, &error)) == NULL) { @@ -189,7 +225,10 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_ struct drag_x11_cb loop = { .result = DRAGGING, .con = con, + .event = event, .callback = callback, + .threshold_exceeded = !use_threshold, + .xcursor = xcursor, .extra = extra, }; ev_prepare *prepare = &loop.prepare; diff --git a/src/floating.c b/src/floating.c index 6772128ae..59c8c7979 100644 --- a/src/floating.c +++ b/src/floating.c @@ -561,8 +561,6 @@ void floating_move_to_pointer(Con *con) { } DRAGGING_CB(drag_window_callback) { - const struct xcb_button_press_event_t *event = extra; - /* Reposition the client correctly while moving */ con->rect.x = old_rect->x + (new_x - event->root_x); con->rect.y = old_rect->y + (new_y - event->root_y); @@ -595,7 +593,7 @@ void floating_drag_window(Con *con, const xcb_button_press_event_t *event) { Rect initial_rect = con->rect; /* Drag the window */ - drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, XCURSOR_CURSOR_MOVE, drag_window_callback, event); + drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, XCURSOR_CURSOR_MOVE, false, drag_window_callback, NULL); if (!con_exists(con)) { DLOG("The container has been closed in the meantime.\n"); @@ -625,12 +623,10 @@ void floating_drag_window(Con *con, const xcb_button_press_event_t *event) { struct resize_window_callback_params { const border_t corner; const bool proportional; - const xcb_button_press_event_t *event; }; DRAGGING_CB(resize_window_callback) { const struct resize_window_callback_params *params = extra; - const xcb_button_press_event_t *event = params->event; border_t corner = params->corner; int32_t dest_x = con->rect.x; @@ -706,12 +702,12 @@ void floating_resize_window(Con *con, const bool proportional, cursor = (corner & BORDER_LEFT) ? XCURSOR_CURSOR_BOTTOM_LEFT_CORNER : XCURSOR_CURSOR_BOTTOM_RIGHT_CORNER; } - struct resize_window_callback_params params = {corner, proportional, event}; + struct resize_window_callback_params params = {corner, proportional}; /* get the initial rect in case of revert/cancel */ Rect initial_rect = con->rect; - drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, cursor, resize_window_callback, ¶ms); + drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, cursor, false, resize_window_callback, ¶ms); if (!con_exists(con)) { DLOG("The container has been closed in the meantime.\n"); diff --git a/src/resize.c b/src/resize.c index a63e337b9..f3c1f6781 100644 --- a/src/resize.c +++ b/src/resize.c @@ -208,7 +208,7 @@ void resize_graphical_handler(Con *first, Con *second, orientation_t orientation const struct callback_params params = {orientation, output, helpwin, &new_position}; /* `drag_pointer' blocks until the drag is completed. */ - drag_result_t drag_result = drag_pointer(NULL, event, grabwin, 0, resize_callback, ¶ms); + drag_result_t drag_result = drag_pointer(NULL, event, grabwin, 0, false, resize_callback, ¶ms); xcb_destroy_window(conn, helpwin); xcb_destroy_window(conn, grabwin); From c6b56b09ab83477dc551f3411462359a2461af2e Mon Sep 17 00:00:00 2001 From: Albert Safin Date: Wed, 12 Dec 2018 18:24:03 +0700 Subject: [PATCH 4/6] Floating move drag: use threshold --- include/floating.h | 2 +- src/click.c | 8 ++++---- src/floating.c | 4 ++-- src/handlers.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/floating.h b/include/floating.h index 4b66eef70..4af434cf4 100644 --- a/include/floating.h +++ b/include/floating.h @@ -75,7 +75,7 @@ void floating_move_to_pointer(Con *con); * Calls the drag_pointer function with the drag_window callback * */ -void floating_drag_window(Con *con, const xcb_button_press_event_t *event); +void floating_drag_window(Con *con, const xcb_button_press_event_t *event, bool use_threshold); /** * Called when the user clicked on a floating window while holding the diff --git a/src/click.c b/src/click.c index 58ebbf3d8..4fcc22ec1 100644 --- a/src/click.c +++ b/src/click.c @@ -221,6 +221,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod Con *floatingcon = con_inside_floating(con); const bool proportional = (event->state & XCB_KEY_BUT_MASK_SHIFT) == XCB_KEY_BUT_MASK_SHIFT; const bool in_stacked = (con->parent->layout == L_STACKED || con->parent->layout == L_TABBED); + const bool was_focused = focused == con; /* 1: see if the user scrolled on the decoration of a stacked/tabbed con */ if (in_stacked && @@ -258,7 +259,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod if (floatingcon != NULL && fs != con) { /* 4: floating_modifier plus left mouse button drags */ if (mod_pressed && event->detail == XCB_BUTTON_CLICK_LEFT) { - floating_drag_window(floatingcon, event); + floating_drag_window(floatingcon, event, false); return 1; } @@ -293,9 +294,8 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod /* 6: dragging, if this was a click on a decoration (which did not lead * to a resize) */ - if (!in_stacked && dest == CLICK_DECORATION && - (event->detail == XCB_BUTTON_CLICK_LEFT)) { - floating_drag_window(floatingcon, event); + if (dest == CLICK_DECORATION && event->detail == XCB_BUTTON_CLICK_LEFT) { + floating_drag_window(floatingcon, event, !was_focused); return 1; } diff --git a/src/floating.c b/src/floating.c index 59c8c7979..719cc1946 100644 --- a/src/floating.c +++ b/src/floating.c @@ -582,7 +582,7 @@ DRAGGING_CB(drag_window_callback) { * Calls the drag_pointer function with the drag_window callback * */ -void floating_drag_window(Con *con, const xcb_button_press_event_t *event) { +void floating_drag_window(Con *con, const xcb_button_press_event_t *event, bool use_threshold) { DLOG("floating_drag_window\n"); /* Push changes before dragging, so that the window gets raised now and not @@ -593,7 +593,7 @@ void floating_drag_window(Con *con, const xcb_button_press_event_t *event) { Rect initial_rect = con->rect; /* Drag the window */ - drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, XCURSOR_CURSOR_MOVE, false, drag_window_callback, NULL); + drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, XCURSOR_CURSOR_MOVE, use_threshold, drag_window_callback, NULL); if (!con_exists(con)) { DLOG("The container has been closed in the meantime.\n"); diff --git a/src/handlers.c b/src/handlers.c index 7a3349780..e177c8fab 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -913,7 +913,7 @@ static void handle_client_message(xcb_client_message_event_t *event) { .event_y = y_root - (con->rect.y)}; switch (direction) { case _NET_WM_MOVERESIZE_MOVE: - floating_drag_window(con->parent, &fake); + floating_drag_window(con->parent, &fake, false); break; case _NET_WM_MOVERESIZE_SIZE_TOPLEFT ... _NET_WM_MOVERESIZE_SIZE_LEFT: floating_resize_window(con->parent, false, &fake); From 371dc23101ac0a46646259da608a754ec930748d Mon Sep 17 00:00:00 2001 From: Albert Safin Date: Wed, 12 Dec 2018 20:53:53 +0700 Subject: [PATCH 5/6] Tiling resize drag: use threshold --- include/resize.h | 4 +++- src/click.c | 49 +++++++++++++++++------------------------------- src/resize.c | 47 ++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 59 insertions(+), 41 deletions(-) diff --git a/include/resize.h b/include/resize.h index 162d8f6ba..5439fab54 100644 --- a/include/resize.h +++ b/include/resize.h @@ -13,7 +13,9 @@ bool resize_find_tiling_participants(Con **current, Con **other, direction_t direction, bool both_sides); -void resize_graphical_handler(Con *first, Con *second, orientation_t orientation, const xcb_button_press_event_t *event); +void resize_graphical_handler(Con *first, Con *second, orientation_t orientation, + const xcb_button_press_event_t *event, + bool use_threshold); /** * Resize the two given containers using the given amount of pixels or diff --git a/src/click.c b/src/click.c index 4fcc22ec1..8ab5c5f06 100644 --- a/src/click.c +++ b/src/click.c @@ -26,7 +26,7 @@ typedef enum { CLICK_BORDER = 0, * then calls resize_graphical_handler(). * */ -static bool tiling_resize_for_border(Con *con, border_t border, xcb_button_press_event_t *event) { +static bool tiling_resize_for_border(Con *con, border_t border, xcb_button_press_event_t *event, bool use_threshold) { DLOG("border = %d, con = %p\n", border, con); Con *second = NULL; Con *first = con; @@ -64,7 +64,7 @@ static bool tiling_resize_for_border(Con *con, border_t border, xcb_button_press const orientation_t orientation = ((border == BORDER_LEFT || border == BORDER_RIGHT) ? HORIZ : VERT); - resize_graphical_handler(first, second, orientation, event); + resize_graphical_handler(first, second, orientation, event, use_threshold); DLOG("After resize handler, rendering\n"); tree_render(); @@ -94,22 +94,22 @@ static bool floating_mod_on_tiled_client(Con *con, xcb_button_press_event_t *eve if (to_right < to_left && to_right < to_top && to_right < to_bottom) - return tiling_resize_for_border(con, BORDER_RIGHT, event); + return tiling_resize_for_border(con, BORDER_RIGHT, event, false); if (to_left < to_right && to_left < to_top && to_left < to_bottom) - return tiling_resize_for_border(con, BORDER_LEFT, event); + return tiling_resize_for_border(con, BORDER_LEFT, event, false); if (to_top < to_right && to_top < to_left && to_top < to_bottom) - return tiling_resize_for_border(con, BORDER_TOP, event); + return tiling_resize_for_border(con, BORDER_TOP, event, false); if (to_bottom < to_right && to_bottom < to_left && to_bottom < to_top) - return tiling_resize_for_border(con, BORDER_BOTTOM, event); + return tiling_resize_for_border(con, BORDER_BOTTOM, event, false); return false; } @@ -118,45 +118,26 @@ static bool floating_mod_on_tiled_client(Con *con, xcb_button_press_event_t *eve * Finds out which border was clicked on and calls tiling_resize_for_border(). * */ -static bool tiling_resize(Con *con, xcb_button_press_event_t *event, const click_destination_t dest) { +static bool tiling_resize(Con *con, xcb_button_press_event_t *event, const click_destination_t dest, bool use_threshold) { /* check if this was a click on the window border (and on which one) */ Rect bsr = con_border_style_rect(con); DLOG("BORDER x = %d, y = %d for con %p, window 0x%08x\n", event->event_x, event->event_y, con, event->event); DLOG("checks for right >= %d\n", con->window_rect.x + con->window_rect.width); if (dest == CLICK_DECORATION) { - /* The user clicked on a window decoration. We ignore the following case: - * The container is a h-split, tabbed or stacked container with > 1 - * window. Decorations will end up next to each other and the user - * expects to switch to a window by clicking on its decoration. */ - - /* Since the container might either be the child *or* already a split - * container (in the case of a nested split container), we need to make - * sure that we are dealing with the split container here. */ - Con *check_con = con; - if (con_is_leaf(check_con) && check_con->parent->type == CT_CON) - check_con = check_con->parent; - - if ((check_con->layout == L_STACKED || - check_con->layout == L_TABBED || - con_orientation(check_con) == HORIZ) && - con_num_children(check_con) > 1) { - DLOG("Not handling this resize, this container has > 1 child.\n"); - return false; - } - return tiling_resize_for_border(con, BORDER_TOP, event); + return tiling_resize_for_border(con, BORDER_TOP, event, use_threshold); } if (event->event_x >= 0 && event->event_x <= (int32_t)bsr.x && event->event_y >= (int32_t)bsr.y && event->event_y <= (int32_t)(con->rect.height + bsr.height)) - return tiling_resize_for_border(con, BORDER_LEFT, event); + return tiling_resize_for_border(con, BORDER_LEFT, event, false); if (event->event_x >= (int32_t)(con->window_rect.x + con->window_rect.width) && event->event_y >= (int32_t)bsr.y && event->event_y <= (int32_t)(con->rect.height + bsr.height)) - return tiling_resize_for_border(con, BORDER_RIGHT, event); + return tiling_resize_for_border(con, BORDER_RIGHT, event, false); if (event->event_y >= (int32_t)(con->window_rect.y + con->window_rect.height)) - return tiling_resize_for_border(con, BORDER_BOTTOM, event); + return tiling_resize_for_border(con, BORDER_BOTTOM, event, false); return false; } @@ -276,7 +257,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod is_left_or_right_click) { /* try tiling resize, but continue if it doesn’t work */ DLOG("tiling resize with fallback\n"); - if (tiling_resize(con, event, dest)) + if (tiling_resize(con, event, dest, !was_focused)) goto done; } @@ -311,7 +292,11 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod else if ((dest == CLICK_BORDER || dest == CLICK_DECORATION) && is_left_or_right_click) { DLOG("Trying to resize (tiling)\n"); - tiling_resize(con, event, dest); + /* Since we updated the tree (con_activate() above), we need to + * re-render the tree before returning to the event loop (drag_pointer() + * inside tiling_resize() runs its own event-loop). */ + tree_render(); + tiling_resize(con, event, dest, dest == CLICK_DECORATION && !was_focused); } done: diff --git a/src/resize.c b/src/resize.c index f3c1f6781..97a0f946b 100644 --- a/src/resize.c +++ b/src/resize.c @@ -21,12 +21,32 @@ struct callback_params { Con *output; xcb_window_t helpwin; uint32_t *new_position; + bool *threshold_exceeded; }; DRAGGING_CB(resize_callback) { const struct callback_params *params = extra; Con *output = params->output; DLOG("new x = %d, y = %d\n", new_x, new_y); + + if (!*params->threshold_exceeded) { + xcb_map_window(conn, params->helpwin); + /* Warp pointer in the same way as resize_graphical_handler() would do + * if threshold wasn't enabled, but also take into account travelled + * distance. */ + if (params->orientation == HORIZ) { + xcb_warp_pointer(conn, XCB_NONE, event->root, 0, 0, 0, 0, + *params->new_position + new_x - event->root_x, + new_y); + } else { + xcb_warp_pointer(conn, XCB_NONE, event->root, 0, 0, 0, 0, + new_x, + *params->new_position + new_y - event->root_y); + } + *params->threshold_exceeded = true; + return; + } + if (params->orientation == HORIZ) { /* Check if the new coordinates are within screen boundaries */ if (new_x > (output->rect.x + output->rect.width - 25) || @@ -148,7 +168,9 @@ bool resize_neighboring_cons(Con *first, Con *second, int px, int ppt) { return true; } -void resize_graphical_handler(Con *first, Con *second, orientation_t orientation, const xcb_button_press_event_t *event) { +void resize_graphical_handler(Con *first, Con *second, orientation_t orientation, + const xcb_button_press_event_t *event, + bool use_threshold) { Con *output = con_get_output(first); DLOG("x = %d, width = %d\n", output->rect.x, output->rect.width); @@ -179,14 +201,10 @@ void resize_graphical_handler(Con *first, Con *second, orientation_t orientation helprect.width = logical_px(2); helprect.height = second->rect.height; initial_position = second->rect.x; - xcb_warp_pointer(conn, XCB_NONE, event->root, 0, 0, 0, 0, - second->rect.x, event->root_y); } else { helprect.width = second->rect.width; helprect.height = logical_px(2); initial_position = second->rect.y; - xcb_warp_pointer(conn, XCB_NONE, event->root, 0, 0, 0, 0, - event->root_x, second->rect.y); } mask = XCB_CW_BACK_PIXEL; @@ -196,7 +214,18 @@ void resize_graphical_handler(Con *first, Con *second, orientation_t orientation values[1] = 1; xcb_window_t helpwin = create_window(conn, helprect, XCB_COPY_FROM_PARENT, XCB_COPY_FROM_PARENT, - XCB_WINDOW_CLASS_INPUT_OUTPUT, (orientation == HORIZ ? XCURSOR_CURSOR_RESIZE_HORIZONTAL : XCURSOR_CURSOR_RESIZE_VERTICAL), true, mask, values); + XCB_WINDOW_CLASS_INPUT_OUTPUT, (orientation == HORIZ ? XCURSOR_CURSOR_RESIZE_HORIZONTAL : XCURSOR_CURSOR_RESIZE_VERTICAL), false, mask, values); + + if (!use_threshold) { + xcb_map_window(conn, helpwin); + if (orientation == HORIZ) { + xcb_warp_pointer(conn, XCB_NONE, event->root, 0, 0, 0, 0, + second->rect.x, event->root_y); + } else { + xcb_warp_pointer(conn, XCB_NONE, event->root, 0, 0, 0, 0, + event->root_x, second->rect.y); + } + } xcb_circulate_window(conn, XCB_CIRCULATE_RAISE_LOWEST, helpwin); @@ -205,10 +234,12 @@ void resize_graphical_handler(Con *first, Con *second, orientation_t orientation /* `new_position' will be updated by the `resize_callback'. */ new_position = initial_position; - const struct callback_params params = {orientation, output, helpwin, &new_position}; + bool threshold_exceeded = !use_threshold; + + const struct callback_params params = {orientation, output, helpwin, &new_position, &threshold_exceeded}; /* `drag_pointer' blocks until the drag is completed. */ - drag_result_t drag_result = drag_pointer(NULL, event, grabwin, 0, false, resize_callback, ¶ms); + drag_result_t drag_result = drag_pointer(NULL, event, grabwin, 0, use_threshold, resize_callback, ¶ms); xcb_destroy_window(conn, helpwin); xcb_destroy_window(conn, grabwin); From 7e4eb51d23cb3aaa871d64e002243b97cb82bffc Mon Sep 17 00:00:00 2001 From: Albert Safin Date: Wed, 9 Oct 2019 07:47:51 +0000 Subject: [PATCH 6/6] When renaming a workspace, update the previous_workspace_name too Fixes #3694 --- src/commands.c | 7 +++++ testcases/t/176-workspace-baf.t | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/src/commands.c b/src/commands.c index 5b11fce1c..615238dae 100644 --- a/src/commands.c +++ b/src/commands.c @@ -2013,6 +2013,13 @@ void cmd_rename_workspace(I3_CMD, const char *old_name, const char *new_name) { con_focus(previously_focused); } + /* Let back-and-forth work after renaming the previous workspace. + * See #3694. */ + if (previous_workspace_name && !strcmp(previous_workspace_name, old_name_copy)) { + FREE(previous_workspace_name); + previous_workspace_name = sstrdup(new_name); + } + cmd_output->needs_tree_render = true; ysuccess(true); diff --git a/testcases/t/176-workspace-baf.t b/testcases/t/176-workspace-baf.t index 133e00fc0..167384291 100644 --- a/testcases/t/176-workspace-baf.t +++ b/testcases/t/176-workspace-baf.t @@ -171,6 +171,53 @@ cmd 'restart'; cmd 'workspace back_and_forth'; is(focused_ws, '5: foo', 'workspace 5 focused after restart'); +################################################################################ +# Check BAF switching to renamed workspace. +# Issue: #3694 +################################################################################ + +kill_all_windows; +cmd 'workspace --no-auto-back-and-forth 1'; +open_window; +cmd 'workspace --no-auto-back-and-forth 2'; + +cmd 'rename workspace 1 to 3'; +cmd 'workspace back_and_forth'; +is(focused_ws, '3', 'workspace 3 focused after rename'); + +################################################################################ +# Check BAF switching to renamed and then closed workspace. +# Issue: #3694 +################################################################################ + +kill_all_windows; +cmd 'workspace --no-auto-back-and-forth 1'; +$first_win = open_window; +cmd 'workspace --no-auto-back-and-forth 2'; + +cmd 'rename workspace 1 to 3'; +cmd '[id="' . $first_win->id . '"] kill'; + +cmd 'workspace back_and_forth'; +is(focused_ws, '3', 'workspace 3 focused after renaming and destroying'); + +################################################################################ +# See if renaming current workspace doesn't affect BAF switching to another +# renamed workspace. +# Issue: #3694 +################################################################################ + +kill_all_windows; +cmd 'workspace --no-auto-back-and-forth 1'; +$first_win = open_window; +cmd 'workspace --no-auto-back-and-forth 2'; + +cmd 'rename workspace 1 to 3'; +cmd 'rename workspace 2 to 4'; + +cmd 'workspace back_and_forth'; +is(focused_ws, '3', 'workspace 3 focused after renaming'); + exit_gracefully($pid); done_testing;