From 5bf4daf2634929e7c94a6c7e56eda66d4667ca2f Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 1 Jan 2019 08:45:21 -0500 Subject: [PATCH] Implement tiling_drag_threshold Implements `tiling_drag_threshold ` to prevent accidental dragging of tiling containers. If a container (and all of its descendants) are unfocused and the tile bar is pressed, a threshold will be used before actually starting the drag. Once the threshold has been exceeded, the cursor will change to the grab icon and the operation will switch from `OP_MOVE_TILING_THRESHOLD` to `OP_MOVE_TILING`. --- include/sway/commands.h | 1 + include/sway/config.h | 2 ++ include/sway/input/seat.h | 4 +++ sway/commands.c | 1 + sway/commands/tiling_drag_threshold.c | 22 ++++++++++++++ sway/config.c | 1 + sway/input/cursor.c | 41 +++++++++++++++++++++++++-- sway/input/seat.c | 11 +++++++ sway/meson.build | 1 + sway/sway.5.scd | 9 ++++++ 10 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 sway/commands/tiling_drag_threshold.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 0e2d7931..7d0ff838 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -172,6 +172,7 @@ sway_cmd cmd_swaybg_command; sway_cmd cmd_swaynag_command; sway_cmd cmd_swap; sway_cmd cmd_tiling_drag; +sway_cmd cmd_tiling_drag_threshold; sway_cmd cmd_title_align; sway_cmd cmd_title_format; sway_cmd cmd_titlebar_border_thickness; diff --git a/include/sway/config.h b/include/sway/config.h index f604b054..ebf16e6a 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -426,7 +426,9 @@ struct sway_config { bool auto_back_and_forth; bool show_marks; enum alignment title_align; + bool tiling_drag; + int tiling_drag_threshold; bool smart_gaps; int gaps_inner; diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index d665c86e..a3c20346 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -39,6 +39,7 @@ enum sway_seat_operation { OP_NONE, OP_DOWN, OP_MOVE_FLOATING, + OP_MOVE_TILING_THRESHOLD, OP_MOVE_TILING, OP_RESIZE_FLOATING, OP_RESIZE_TILING, @@ -186,6 +187,9 @@ void seat_begin_down(struct sway_seat *seat, struct sway_container *con, void seat_begin_move_floating(struct sway_seat *seat, struct sway_container *con, uint32_t button); +void seat_begin_move_tiling_threshold(struct sway_seat *seat, + struct sway_container *con, uint32_t button); + void seat_begin_move_tiling(struct sway_seat *seat, struct sway_container *con, uint32_t button); diff --git a/sway/commands.c b/sway/commands.c index 927434bc..0883b57b 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -87,6 +87,7 @@ static struct cmd_handler handlers[] = { { "smart_borders", cmd_smart_borders }, { "smart_gaps", cmd_smart_gaps }, { "tiling_drag", cmd_tiling_drag }, + { "tiling_drag_threshold", cmd_tiling_drag_threshold }, { "title_align", cmd_title_align }, { "titlebar_border_thickness", cmd_titlebar_border_thickness }, { "titlebar_padding", cmd_titlebar_padding }, diff --git a/sway/commands/tiling_drag_threshold.c b/sway/commands/tiling_drag_threshold.c new file mode 100644 index 00000000..6b0531c3 --- /dev/null +++ b/sway/commands/tiling_drag_threshold.c @@ -0,0 +1,22 @@ +#include +#include "sway/commands.h" +#include "sway/config.h" +#include "log.h" + +struct cmd_results *cmd_tiling_drag_threshold(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "tiling_drag_threshold", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + char *inv; + int value = strtol(argv[0], &inv, 10); + if (*inv != '\0' || value < 0) { + return cmd_results_new(CMD_INVALID, "tiling_drag_threshold", + "Invalid threshold specified"); + } + + config->tiling_drag_threshold = value; + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/config.c b/sway/config.c index bb7f796d..5d631b7e 100644 --- a/sway/config.c +++ b/sway/config.c @@ -233,6 +233,7 @@ static void config_defaults(struct sway_config *config) { config->show_marks = true; config->title_align = ALIGN_LEFT; config->tiling_drag = true; + config->tiling_drag_threshold = 9; config->smart_gaps = false; config->gaps_inner = 0; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index f8302ddf..516251f7 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -384,6 +384,30 @@ static void handle_move_tiling_motion(struct sway_seat *seat, desktop_damage_box(&seat->op_drop_box); } +static void handle_move_tiling_threshold_motion(struct sway_seat *seat, + struct sway_cursor *cursor) { + double cx = seat->cursor->cursor->x; + double cy = seat->cursor->cursor->y; + double sx = seat->op_ref_lx; + double sy = seat->op_ref_ly; + + // Get the scaled threshold for the output. Even if the operation goes + // across multiple outputs of varying scales, just use the scale for the + // output that the cursor is currently on for simplicity. + struct wlr_output *wlr_output = wlr_output_layout_output_at( + root->output_layout, cx, cy); + double output_scale = wlr_output ? wlr_output->scale : 1; + double threshold = config->tiling_drag_threshold * output_scale; + threshold *= threshold; + + // If the threshold has been exceeded, start the actual drag + if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) { + seat->operation = OP_MOVE_TILING; + cursor_set_image(cursor, "grab", NULL); + handle_move_tiling_motion(seat, cursor); + } +} + static void calculate_floating_constraints(struct sway_container *con, int *min_width, int *max_width, int *min_height, int *max_height) { if (config->floating_minimum_width == -1) { // no minimum @@ -640,6 +664,9 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, case OP_MOVE_FLOATING: handle_move_floating_motion(seat, cursor); break; + case OP_MOVE_TILING_THRESHOLD: + handle_move_tiling_threshold_motion(seat, cursor); + break; case OP_MOVE_TILING: handle_move_tiling_motion(seat, cursor); break; @@ -984,12 +1011,22 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (config->tiling_drag && (mod_pressed || on_titlebar) && state == WLR_BUTTON_PRESSED && !is_floating_or_child && cont && !cont->is_fullscreen) { - if (on_titlebar) { + struct sway_container *focus = seat_get_focused_container(seat); + bool focused = focus == cont || container_has_ancestor(focus, cont); + if (on_titlebar && !focused) { node = seat_get_focus_inactive(seat, &cont->node); seat_set_focus(seat, node); } + seat_pointer_notify_button(seat, time_msec, button, state); - seat_begin_move_tiling(seat, cont, button); + + // If moving a previously unfocused container by it's title bar, use a + // threshold for the drag. + if (!mod_pressed && !focused && config->tiling_drag_threshold > 0) { + seat_begin_move_tiling_threshold(seat, cont, button); + } else { + seat_begin_move_tiling(seat, cont, button); + } return; } diff --git a/sway/input/seat.c b/sway/input/seat.c index fa82c9ce..9422713d 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1052,6 +1052,17 @@ void seat_begin_move_floating(struct sway_seat *seat, cursor_set_image(seat->cursor, "grab", NULL); } +void seat_begin_move_tiling_threshold(struct sway_seat *seat, + struct sway_container *con, uint32_t button) { + seat->operation = OP_MOVE_TILING_THRESHOLD; + seat->op_container = con; + seat->op_button = button; + seat->op_target_node = NULL; + seat->op_target_edge = 0; + seat->op_ref_lx = seat->cursor->cursor->x; + seat->op_ref_ly = seat->cursor->cursor->y; +} + void seat_begin_move_tiling(struct sway_seat *seat, struct sway_container *con, uint32_t button) { seat->operation = OP_MOVE_TILING; diff --git a/sway/meson.build b/sway/meson.build index 7f739287..98676ce0 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -89,6 +89,7 @@ sway_sources = files( 'commands/swaynag_command.c', 'commands/swap.c', 'commands/tiling_drag.c', + 'commands/tiling_drag_threshold.c', 'commands/title_align.c', 'commands/title_format.c', 'commands/titlebar_border_thickness.c', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index e6abef56..37a9b24c 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -572,6 +572,15 @@ The default colors are: the _floating\_mod_ will also allow the container to be dragged. _toggle_ should not be used in the config file. +*tiling\_drag\_threshold* + Sets the threshold that must be exceeded for an unfocused tiling container + to be dragged by its titlebar. This has no effect if _floating\_mod_ is + used, if the container is focused, or if _tiling\_drag_ is set to + _disable_. Once the threshold has been exceeded once, the drag starts and + the cursor can come back inside the threshold without stopping the drag. + _threshold_ is multiplied by the scale of the output that the cursor on. + The default is 9. + *title\_align* left|center|right Sets the title alignment. If _right_ is selected and _show\_marks_ is set to _yes_, the marks will be shown on the _left_ side instead of the