This repository has been archived by the owner on Apr 18, 2023. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
Showing
6 changed files
with
271 additions
and
246 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <config.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) | ||
|
||
/** | ||
* 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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
Oops, something went wrong.