diff --git a/include/commands.h b/include/commands.h index 8e53c74d..f6777930 100644 --- a/include/commands.h +++ b/include/commands.h @@ -2,6 +2,7 @@ #define _SWAY_COMMANDS_H #include #include +#include #include "config.h" diff --git a/include/container.h b/include/container.h index ae9a9fc5..d5eb27c1 100644 --- a/include/container.h +++ b/include/container.h @@ -98,6 +98,8 @@ swayc_t *swayc_by_name(const char *name); swayc_t *swayc_active_output(void); swayc_t *swayc_active_workspace(void); swayc_t *swayc_active_workspace_for(swayc_t *view); +// set focus to current pointer location and return focused container +swayc_t *container_under_pointer(void); // Container information diff --git a/include/handlers.h b/include/handlers.h index 4c71f953..d7f6ffdd 100644 --- a/include/handlers.h +++ b/include/handlers.h @@ -5,10 +5,6 @@ #include extern struct wlc_interface interface; -extern struct wlc_origin mouse_origin; extern uint32_t keys_pressed[32]; -// set focus to current pointer location and return focused container -swayc_t *container_under_pointer(void); - #endif diff --git a/include/input_state.h b/include/input_state.h index d87ae18c..a1f238e1 100644 --- a/include/input_state.h +++ b/include/input_state.h @@ -48,11 +48,6 @@ extern struct pointer_state { struct pointer_button_state right; struct pointer_button_state scroll; - // pointer position - struct mouse_origin{ - int x, y; - } origin; - // change in pointer position struct { int x, y; @@ -65,6 +60,9 @@ extern struct pointer_state { int mode; } pointer_state; +void pointer_position_set(struct wlc_origin *new_origin, bool force_focus); +void center_pointer_on(swayc_t *view); + // on button release unset mode depending on the button. // on button press set mode conditionally depending on the button void pointer_mode_set(uint32_t button, bool condition); diff --git a/sway.5.txt b/sway.5.txt index dad55c5f..27735db6 100644 --- a/sway.5.txt +++ b/sway.5.txt @@ -102,6 +102,10 @@ Commands Moves the focused container to the workspace identified by _name_. _name_ may be a special workspace name. See **workspace**. +**mouse_warping** :: + When _output_: place mouse at center of newly focused window when changing + output. When _none_: don't move mouse. + **output** :: Configures the specified output. It will use the given resolution and be arranged at the given position in the layout tree. You may omit either of diff --git a/sway/commands.c b/sway/commands.c index 441b6fd7..00a4d1b0 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -21,6 +21,7 @@ #include "handlers.h" #include "sway.h" #include "resize.h" +#include "input_state.h" typedef struct cmd_results *sway_cmd(int argc, char **argv); @@ -45,6 +46,7 @@ static sway_cmd cmd_kill; static sway_cmd cmd_layout; static sway_cmd cmd_log_colors; static sway_cmd cmd_mode; +static sway_cmd cmd_mouse_warping; static sway_cmd cmd_move; static sway_cmd cmd_output; static sway_cmd cmd_reload; @@ -383,9 +385,13 @@ static struct cmd_results *cmd_focus(int argc, char **argv) { } else if (!workspace_switch(swayc_active_workspace_for(output))) { return cmd_results_new(CMD_FAILURE, "focus output", "Switching to workspace on output '%s' was blocked", argv[1]); - } else { - return cmd_results_new(CMD_SUCCESS, NULL, NULL); + } else if (config->mouse_warping) { + swayc_t *focused = get_focused_view(output); + if (focused && focused->type == C_VIEW) { + center_pointer_on(focused); + } } + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } else if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1))) { return error; } @@ -528,6 +534,20 @@ static struct cmd_results *cmd_mode(int argc, char **argv) { return cmd_results_new(mode_make ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL); } +static struct cmd_results *cmd_mouse_warping(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "mouse_warping", EXPECTED_EQUAL_TO, 1))) { + return error; + } else if (strcasecmp(argv[0], "output") == 0) { + config->mouse_warping = true; + } else if (strcasecmp(argv[0], "none") == 0) { + config->mouse_warping = false; + } else { + return cmd_results_new(CMD_FAILURE, "mouse_warping", "Expected 'mouse_warping output|none'"); + } + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + static struct cmd_results *cmd_move(int argc, char **argv) { struct cmd_results *error = NULL; if (config->reading) return cmd_results_new(CMD_FAILURE, "move", "Can't be used in config file."); @@ -1165,7 +1185,16 @@ static struct cmd_results *cmd_workspace(int argc, char **argv) { ws = workspace_create(argv[0]); } } + swayc_t *old_output = swayc_active_output(); workspace_switch(ws); + swayc_t *new_output = swayc_active_output(); + + if (config->mouse_warping && old_output != new_output) { + swayc_t *focused = get_focused_view(ws); + if (focused && focused->type == C_VIEW) { + center_pointer_on(focused); + } + } } else { if (strcasecmp(argv[1], "output") == 0) { if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, 3))) { @@ -1217,6 +1246,7 @@ static struct cmd_handler handlers[] = { { "layout", cmd_layout }, { "log_colors", cmd_log_colors }, { "mode", cmd_mode }, + { "mouse_warping", cmd_mouse_warping }, { "move", cmd_move }, { "output", cmd_output }, { "reload", cmd_reload }, diff --git a/sway/container.c b/sway/container.c index 6c4206fb..f3e2b3ae 100644 --- a/sway/container.c +++ b/sway/container.c @@ -8,6 +8,7 @@ #include "workspace.h" #include "focus.h" #include "layout.h" +#include "input_state.h" #include "log.h" #define ASSERT_NONNULL(PTR) \ @@ -508,6 +509,70 @@ swayc_t *swayc_active_workspace_for(swayc_t *cont) { } } +static bool pointer_test(swayc_t *view, void *_origin) { + const struct wlc_origin *origin = _origin; + // Determine the output that the view is under + swayc_t *parent = swayc_parent_by_type(view, C_OUTPUT); + if (origin->x >= view->x && origin->y >= view->y + && origin->x < view->x + view->width && origin->y < view->y + view->height + && view->visible && parent == root_container.focused) { + return true; + } + return false; +} + +swayc_t *container_under_pointer(void) { + // root.output->workspace + if (!root_container.focused || !root_container.focused->focused) { + return NULL; + } + swayc_t *lookup = root_container.focused->focused; + // Case of empty workspace + if (lookup->children == 0) { + return NULL; + } + struct wlc_origin origin; + wlc_pointer_get_origin(&origin); + while (lookup->type != C_VIEW) { + int i; + int len; + // if tabbed/stacked go directly to focused container, otherwise search + // children + if (lookup->layout == L_TABBED || lookup->layout == L_STACKED) { + lookup = lookup->focused; + continue; + } + // if workspace, search floating + if (lookup->type == C_WORKSPACE) { + i = len = lookup->floating->length; + bool got_floating = false; + while (--i > -1) { + if (pointer_test(lookup->floating->items[i], &origin)) { + lookup = lookup->floating->items[i]; + got_floating = true; + break; + } + } + if (got_floating) { + continue; + } + } + // search children + len = lookup->children->length; + for (i = 0; i < len; ++i) { + if (pointer_test(lookup->children->items[i], &origin)) { + lookup = lookup->children->items[i]; + break; + } + } + // when border and titles are done, this could happen + if (i == len) { + break; + } + } + return lookup; +} + // Container information bool swayc_is_fullscreen(swayc_t *view) { diff --git a/sway/focus.c b/sway/focus.c index 1aa7579b..7f0b1599 100644 --- a/sway/focus.c +++ b/sway/focus.c @@ -4,6 +4,8 @@ #include "log.h" #include "workspace.h" #include "layout.h" +#include "config.h" +#include "input_state.h" bool locked_container_focus = false; bool locked_view_focus = false; @@ -49,14 +51,24 @@ static void update_focus(swayc_t *c) { } bool move_focus(enum movement_direction direction) { - swayc_t *view = get_focused_container(&root_container); - view = get_swayc_in_direction(view, direction); - if (view) { - if (direction == MOVE_PARENT) { - return set_focused_container(view); - } else { - return set_focused_container(get_focused_view(view)); + swayc_t *old_view = get_focused_container(&root_container); + swayc_t *new_view = get_swayc_in_direction(old_view, direction); + if (!new_view) { + return false; + } else if (direction == MOVE_PARENT) { + return set_focused_container(new_view); + } else if (config->mouse_warping) { + swayc_t *old_op = old_view->type == C_OUTPUT ? + old_view : swayc_parent_by_type(old_view, C_OUTPUT); + swayc_t *focused = get_focused_view(new_view); + if (set_focused_container(focused)) { + if (old_op != swayc_active_output() && focused && focused->type == C_VIEW) { + center_pointer_on(focused); + } + return true; } + } else { + return set_focused_container(get_focused_view(new_view)); } return false; } diff --git a/sway/handlers.c b/sway/handlers.c index 1c5abca6..510b4261 100644 --- a/sway/handlers.c +++ b/sway/handlers.c @@ -24,68 +24,6 @@ // Event handled by sway and should not be sent to client #define EVENT_HANDLED true -static bool pointer_test(swayc_t *view, void *_origin) { - const struct mouse_origin *origin = _origin; - // Determine the output that the view is under - swayc_t *parent = swayc_parent_by_type(view, C_OUTPUT); - if (origin->x >= view->x && origin->y >= view->y - && origin->x < view->x + view->width && origin->y < view->y + view->height - && view->visible && parent == root_container.focused) { - return true; - } - return false; -} - -swayc_t *container_under_pointer(void) { - // root.output->workspace - if (!root_container.focused || !root_container.focused->focused) { - return NULL; - } - swayc_t *lookup = root_container.focused->focused; - // Case of empty workspace - if (lookup->children == 0) { - return NULL; - } - while (lookup->type != C_VIEW) { - int i; - int len; - // if tabbed/stacked go directly to focused container, otherwise search - // children - if (lookup->layout == L_TABBED || lookup->layout == L_STACKED) { - lookup = lookup->focused; - continue; - } - // if workspace, search floating - if (lookup->type == C_WORKSPACE) { - i = len = lookup->floating->length; - bool got_floating = false; - while (--i > -1) { - if (pointer_test(lookup->floating->items[i], &pointer_state.origin)) { - lookup = lookup->floating->items[i]; - got_floating = true; - break; - } - } - if (got_floating) { - continue; - } - } - // search children - len = lookup->children->length; - for (i = 0; i < len; ++i) { - if (pointer_test(lookup->children->items[i], &pointer_state.origin)) { - lookup = lookup->children->items[i]; - break; - } - } - // when border and titles are done, this could happen - if (i == len) { - break; - } - } - return lookup; -} - /* Handles */ static bool handle_output_created(wlc_handle output) { @@ -394,27 +332,7 @@ static bool handle_pointer_motion(wlc_handle handle, uint32_t time, const struct } } - // Update pointer origin - pointer_state.delta.x = origin->x - pointer_state.origin.x; - pointer_state.delta.y = origin->y - pointer_state.origin.y; - pointer_state.origin.x = origin->x; - pointer_state.origin.y = origin->y; - - // Update view under pointer - swayc_t *prev_view = pointer_state.view; - pointer_state.view = container_under_pointer(); - - // If pointer is in a mode, update it - if (pointer_state.mode) { - pointer_mode_update(); - } - // Otherwise change focus if config is set an - else if (prev_view != pointer_state.view && config->focus_follows_mouse) { - if (pointer_state.view && pointer_state.view->type == C_VIEW) { - set_focused_container(pointer_state.view); - } - } - wlc_pointer_set_origin(&new_origin); + pointer_position_set(&new_origin, false); return EVENT_PASSTHROUGH; } @@ -425,10 +343,6 @@ static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct w // Update view pointer is on pointer_state.view = container_under_pointer(); - // Update pointer origin - pointer_state.origin.x = origin->x; - pointer_state.origin.y = origin->y; - // Update pointer_state switch (button) { case M_LEFT_CLICK: diff --git a/sway/input_state.c b/sway/input_state.c index 2743a9ea..9bd8ad4d 100644 --- a/sway/input_state.c +++ b/sway/input_state.c @@ -2,6 +2,7 @@ #include #include #include "log.h" +#include "config.h" #include "input_state.h" @@ -161,6 +162,36 @@ static void reset_initial_sibling(void) { pointer_state.mode = 0; } +void pointer_position_set(struct wlc_origin *new_origin, bool force_focus) { + struct wlc_origin origin; + wlc_pointer_get_origin(&origin); + pointer_state.delta.x = new_origin->x - origin.x; + pointer_state.delta.y = new_origin->y - origin.y; + + // Update view under pointer + swayc_t *prev_view = pointer_state.view; + pointer_state.view = container_under_pointer(); + + // If pointer is in a mode, update it + if (pointer_state.mode) { + pointer_mode_update(); + // Otherwise change focus if config is set + } else if (force_focus || (prev_view != pointer_state.view && config->focus_follows_mouse)) { + if (pointer_state.view && pointer_state.view->type == C_VIEW) { + set_focused_container(pointer_state.view); + } + } + + wlc_pointer_set_origin(new_origin); +} + +void center_pointer_on(swayc_t *view) { + struct wlc_origin new_origin; + new_origin.x = view->x + view->width/2; + new_origin.y = view->y + view->height/2; + pointer_position_set(&new_origin, true); +} + // Mode set left/right click static void pointer_mode_set_left(void) { @@ -183,8 +214,10 @@ static void pointer_mode_set_right(void) { int midway_x = initial.ptr->x + initial.ptr->width/2; int midway_y = initial.ptr->y + initial.ptr->height/2; - lock.left = pointer_state.origin.x > midway_x; - lock.top = pointer_state.origin.y > midway_y; + struct wlc_origin origin; + wlc_pointer_get_origin(&origin); + lock.left = origin.x > midway_x; + lock.top = origin.y > midway_y; if (initial.ptr->is_floating) { pointer_state.mode = M_RESIZING | M_FLOATING; @@ -246,8 +279,10 @@ void pointer_mode_update(void) { pointer_state.mode = 0; return; } - int dx = pointer_state.origin.x; - int dy = pointer_state.origin.y; + struct wlc_origin origin; + wlc_pointer_get_origin(&origin); + int dx = origin.x; + int dy = origin.y; switch (pointer_state.mode) { case M_FLOATING | M_DRAGGING: