commit
ea216f88d8
|
@ -503,6 +503,7 @@ i3_SOURCES = \
|
||||||
include/con.h \
|
include/con.h \
|
||||||
include/data.h \
|
include/data.h \
|
||||||
include/display_version.h \
|
include/display_version.h \
|
||||||
|
include/drag.h \
|
||||||
include/ewmh.h \
|
include/ewmh.h \
|
||||||
include/fake_outputs.h \
|
include/fake_outputs.h \
|
||||||
include/floating.h \
|
include/floating.h \
|
||||||
|
@ -548,6 +549,7 @@ i3_SOURCES = \
|
||||||
src/config_directives.c \
|
src/config_directives.c \
|
||||||
src/config_parser.c \
|
src/config_parser.c \
|
||||||
src/display_version.c \
|
src/display_version.c \
|
||||||
|
src/drag.c \
|
||||||
src/ewmh.c \
|
src/ewmh.c \
|
||||||
src/fake_outputs.c \
|
src/fake_outputs.c \
|
||||||
src/floating.c \
|
src/floating.c \
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
#include "click.h"
|
#include "click.h"
|
||||||
#include "key_press.h"
|
#include "key_press.h"
|
||||||
#include "floating.h"
|
#include "floating.h"
|
||||||
|
#include "drag.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "handlers.h"
|
#include "handlers.h"
|
||||||
#include "randr.h"
|
#include "randr.h"
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* 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 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 xcb_button_press_event_t *event, 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, 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,
|
||||||
|
bool use_threshold, callback_t callback,
|
||||||
|
const void *extra);
|
|
@ -13,14 +13,6 @@
|
||||||
|
|
||||||
#include "tree.h"
|
#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? */
|
/** On which border was the dragging initiated? */
|
||||||
typedef enum { BORDER_LEFT = (1 << 0),
|
typedef enum { BORDER_LEFT = (1 << 0),
|
||||||
BORDER_RIGHT = (1 << 1),
|
BORDER_RIGHT = (1 << 1),
|
||||||
|
@ -83,7 +75,7 @@ void floating_move_to_pointer(Con *con);
|
||||||
* Calls the drag_pointer function with the 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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the user clicked on a floating window while holding the
|
* Called when the user clicked on a floating window while holding the
|
||||||
|
@ -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);
|
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
|
* Repositions the CT_FLOATING_CON to have the coordinates specified by
|
||||||
* newrect, but only if the coordinates are not out-of-bounds. Also reassigns
|
* newrect, but only if the coordinates are not out-of-bounds. Also reassigns
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
|
|
||||||
bool resize_find_tiling_participants(Con **current, Con **other, direction_t direction, bool both_sides);
|
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
|
* Resize the two given containers using the given amount of pixels or
|
||||||
|
|
57
src/click.c
57
src/click.c
|
@ -26,7 +26,7 @@ typedef enum { CLICK_BORDER = 0,
|
||||||
* then calls resize_graphical_handler().
|
* 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);
|
DLOG("border = %d, con = %p\n", border, con);
|
||||||
Con *second = NULL;
|
Con *second = NULL;
|
||||||
Con *first = con;
|
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);
|
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");
|
DLOG("After resize handler, rendering\n");
|
||||||
tree_render();
|
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 &&
|
if (to_right < to_left &&
|
||||||
to_right < to_top &&
|
to_right < to_top &&
|
||||||
to_right < to_bottom)
|
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 &&
|
if (to_left < to_right &&
|
||||||
to_left < to_top &&
|
to_left < to_top &&
|
||||||
to_left < to_bottom)
|
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 &&
|
if (to_top < to_right &&
|
||||||
to_top < to_left &&
|
to_top < to_left &&
|
||||||
to_top < to_bottom)
|
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 &&
|
if (to_bottom < to_right &&
|
||||||
to_bottom < to_left &&
|
to_bottom < to_left &&
|
||||||
to_bottom < to_top)
|
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;
|
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().
|
* 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) */
|
/* check if this was a click on the window border (and on which one) */
|
||||||
Rect bsr = con_border_style_rect(con);
|
Rect bsr = con_border_style_rect(con);
|
||||||
DLOG("BORDER x = %d, y = %d for con %p, window 0x%08x\n",
|
DLOG("BORDER x = %d, y = %d for con %p, window 0x%08x\n",
|
||||||
event->event_x, event->event_y, con, event->event);
|
event->event_x, event->event_y, con, event->event);
|
||||||
DLOG("checks for right >= %d\n", con->window_rect.x + con->window_rect.width);
|
DLOG("checks for right >= %d\n", con->window_rect.x + con->window_rect.width);
|
||||||
if (dest == CLICK_DECORATION) {
|
if (dest == CLICK_DECORATION) {
|
||||||
/* The user clicked on a window decoration. We ignore the following case:
|
return tiling_resize_for_border(con, BORDER_TOP, event, use_threshold);
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event->event_x >= 0 && event->event_x <= (int32_t)bsr.x &&
|
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))
|
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) &&
|
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))
|
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))
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -221,6 +202,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
|
||||||
Con *floatingcon = con_inside_floating(con);
|
Con *floatingcon = con_inside_floating(con);
|
||||||
const bool proportional = (event->state & XCB_KEY_BUT_MASK_SHIFT) == XCB_KEY_BUT_MASK_SHIFT;
|
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 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 */
|
/* 1: see if the user scrolled on the decoration of a stacked/tabbed con */
|
||||||
if (in_stacked &&
|
if (in_stacked &&
|
||||||
|
@ -258,7 +240,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
|
||||||
if (floatingcon != NULL && fs != con) {
|
if (floatingcon != NULL && fs != con) {
|
||||||
/* 4: floating_modifier plus left mouse button drags */
|
/* 4: floating_modifier plus left mouse button drags */
|
||||||
if (mod_pressed && event->detail == XCB_BUTTON_CLICK_LEFT) {
|
if (mod_pressed && event->detail == XCB_BUTTON_CLICK_LEFT) {
|
||||||
floating_drag_window(floatingcon, event);
|
floating_drag_window(floatingcon, event, false);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +257,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
|
||||||
is_left_or_right_click) {
|
is_left_or_right_click) {
|
||||||
/* try tiling resize, but continue if it doesn’t work */
|
/* try tiling resize, but continue if it doesn’t work */
|
||||||
DLOG("tiling resize with fallback\n");
|
DLOG("tiling resize with fallback\n");
|
||||||
if (tiling_resize(con, event, dest))
|
if (tiling_resize(con, event, dest, !was_focused))
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,9 +275,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
|
/* 6: dragging, if this was a click on a decoration (which did not lead
|
||||||
* to a resize) */
|
* to a resize) */
|
||||||
if (!in_stacked && dest == CLICK_DECORATION &&
|
if (dest == CLICK_DECORATION && event->detail == XCB_BUTTON_CLICK_LEFT) {
|
||||||
(event->detail == XCB_BUTTON_CLICK_LEFT)) {
|
floating_drag_window(floatingcon, event, !was_focused);
|
||||||
floating_drag_window(floatingcon, event);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) &&
|
else if ((dest == CLICK_BORDER || dest == CLICK_DECORATION) &&
|
||||||
is_left_or_right_click) {
|
is_left_or_right_click) {
|
||||||
DLOG("Trying to resize (tiling)\n");
|
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:
|
done:
|
||||||
|
|
|
@ -0,0 +1,252 @@
|
||||||
|
/*
|
||||||
|
* 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 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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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->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);
|
||||||
|
|
||||||
|
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, 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,
|
||||||
|
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 */
|
||||||
|
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 */
|
||||||
|
use_threshold ? XCB_NONE : 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,
|
||||||
|
.event = event,
|
||||||
|
.callback = callback,
|
||||||
|
.threshold_exceeded = !use_threshold,
|
||||||
|
.xcursor = xcursor,
|
||||||
|
.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;
|
||||||
|
}
|
215
src/floating.c
215
src/floating.c
|
@ -561,8 +561,6 @@ void floating_move_to_pointer(Con *con) {
|
||||||
}
|
}
|
||||||
|
|
||||||
DRAGGING_CB(drag_window_callback) {
|
DRAGGING_CB(drag_window_callback) {
|
||||||
const struct xcb_button_press_event_t *event = extra;
|
|
||||||
|
|
||||||
/* Reposition the client correctly while moving */
|
/* Reposition the client correctly while moving */
|
||||||
con->rect.x = old_rect->x + (new_x - event->root_x);
|
con->rect.x = old_rect->x + (new_x - event->root_x);
|
||||||
con->rect.y = old_rect->y + (new_y - event->root_y);
|
con->rect.y = old_rect->y + (new_y - event->root_y);
|
||||||
|
@ -584,7 +582,7 @@ DRAGGING_CB(drag_window_callback) {
|
||||||
* Calls the drag_pointer function with the 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");
|
DLOG("floating_drag_window\n");
|
||||||
|
|
||||||
/* Push changes before dragging, so that the window gets raised now and not
|
/* Push changes before dragging, so that the window gets raised now and not
|
||||||
|
@ -595,7 +593,7 @@ void floating_drag_window(Con *con, const xcb_button_press_event_t *event) {
|
||||||
Rect initial_rect = con->rect;
|
Rect initial_rect = con->rect;
|
||||||
|
|
||||||
/* Drag the window */
|
/* 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, use_threshold, drag_window_callback, NULL);
|
||||||
|
|
||||||
if (!con_exists(con)) {
|
if (!con_exists(con)) {
|
||||||
DLOG("The container has been closed in the meantime.\n");
|
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 {
|
struct resize_window_callback_params {
|
||||||
const border_t corner;
|
const border_t corner;
|
||||||
const bool proportional;
|
const bool proportional;
|
||||||
const xcb_button_press_event_t *event;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DRAGGING_CB(resize_window_callback) {
|
DRAGGING_CB(resize_window_callback) {
|
||||||
const struct resize_window_callback_params *params = extra;
|
const struct resize_window_callback_params *params = extra;
|
||||||
const xcb_button_press_event_t *event = params->event;
|
|
||||||
border_t corner = params->corner;
|
border_t corner = params->corner;
|
||||||
|
|
||||||
int32_t dest_x = con->rect.x;
|
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;
|
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 */
|
/* get the initial rect in case of revert/cancel */
|
||||||
Rect initial_rect = con->rect;
|
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, false, resize_window_callback, ¶ms);
|
||||||
|
|
||||||
if (!con_exists(con)) {
|
if (!con_exists(con)) {
|
||||||
DLOG("The container has been closed in the meantime.\n");
|
DLOG("The container has been closed in the meantime.\n");
|
||||||
|
@ -727,209 +723,6 @@ void floating_resize_window(Con *con, const bool proportional,
|
||||||
con->scratchpad_state = SCRATCHPAD_CHANGED;
|
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
|
* Repositions the CT_FLOATING_CON to have the coordinates specified by
|
||||||
* newrect, but only if the coordinates are not out-of-bounds. Also reassigns
|
* newrect, but only if the coordinates are not out-of-bounds. Also reassigns
|
||||||
|
|
|
@ -913,7 +913,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
|
||||||
.event_y = y_root - (con->rect.y)};
|
.event_y = y_root - (con->rect.y)};
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case _NET_WM_MOVERESIZE_MOVE:
|
case _NET_WM_MOVERESIZE_MOVE:
|
||||||
floating_drag_window(con->parent, &fake);
|
floating_drag_window(con->parent, &fake, false);
|
||||||
break;
|
break;
|
||||||
case _NET_WM_MOVERESIZE_SIZE_TOPLEFT ... _NET_WM_MOVERESIZE_SIZE_LEFT:
|
case _NET_WM_MOVERESIZE_SIZE_TOPLEFT ... _NET_WM_MOVERESIZE_SIZE_LEFT:
|
||||||
floating_resize_window(con->parent, false, &fake);
|
floating_resize_window(con->parent, false, &fake);
|
||||||
|
|
47
src/resize.c
47
src/resize.c
|
@ -21,12 +21,32 @@ struct callback_params {
|
||||||
Con *output;
|
Con *output;
|
||||||
xcb_window_t helpwin;
|
xcb_window_t helpwin;
|
||||||
uint32_t *new_position;
|
uint32_t *new_position;
|
||||||
|
bool *threshold_exceeded;
|
||||||
};
|
};
|
||||||
|
|
||||||
DRAGGING_CB(resize_callback) {
|
DRAGGING_CB(resize_callback) {
|
||||||
const struct callback_params *params = extra;
|
const struct callback_params *params = extra;
|
||||||
Con *output = params->output;
|
Con *output = params->output;
|
||||||
DLOG("new x = %d, y = %d\n", new_x, new_y);
|
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) {
|
if (params->orientation == HORIZ) {
|
||||||
/* Check if the new coordinates are within screen boundaries */
|
/* Check if the new coordinates are within screen boundaries */
|
||||||
if (new_x > (output->rect.x + output->rect.width - 25) ||
|
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;
|
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);
|
Con *output = con_get_output(first);
|
||||||
DLOG("x = %d, width = %d\n", output->rect.x, output->rect.width);
|
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.width = logical_px(2);
|
||||||
helprect.height = second->rect.height;
|
helprect.height = second->rect.height;
|
||||||
initial_position = second->rect.x;
|
initial_position = second->rect.x;
|
||||||
xcb_warp_pointer(conn, XCB_NONE, event->root, 0, 0, 0, 0,
|
|
||||||
second->rect.x, event->root_y);
|
|
||||||
} else {
|
} else {
|
||||||
helprect.width = second->rect.width;
|
helprect.width = second->rect.width;
|
||||||
helprect.height = logical_px(2);
|
helprect.height = logical_px(2);
|
||||||
initial_position = second->rect.y;
|
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;
|
mask = XCB_CW_BACK_PIXEL;
|
||||||
|
@ -196,7 +214,18 @@ void resize_graphical_handler(Con *first, Con *second, orientation_t orientation
|
||||||
values[1] = 1;
|
values[1] = 1;
|
||||||
|
|
||||||
xcb_window_t helpwin = create_window(conn, helprect, XCB_COPY_FROM_PARENT, XCB_COPY_FROM_PARENT,
|
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);
|
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' will be updated by the `resize_callback'. */
|
||||||
new_position = initial_position;
|
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_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, use_threshold, resize_callback, ¶ms);
|
||||||
|
|
||||||
xcb_destroy_window(conn, helpwin);
|
xcb_destroy_window(conn, helpwin);
|
||||||
xcb_destroy_window(conn, grabwin);
|
xcb_destroy_window(conn, grabwin);
|
||||||
|
|
Loading…
Reference in New Issue