From 454473ac6c91a0f72e9e940841e44a29b9bb697c Mon Sep 17 00:00:00 2001 From: Albert Safin Date: Wed, 12 Dec 2018 11:01:36 +0700 Subject: [PATCH] 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 ee0e038b..d379a854 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 e93b066b..aa2b5b25 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 00000000..5d4ca581 --- /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 a7813099..4b66eef7 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 00000000..af019b25 --- /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 70e7bc17..b022ee19 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