From b4a0363d1721b2ad2d5afb65764ecb575bd55fa4 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 10 Aug 2018 14:10:09 +1000 Subject: [PATCH 1/8] Implement resizing tiled containers via cursor * The OP_RESIZE seat operation has been renamed to OP_RESIZE_FLOATING, and OP_RESIZE_TILING has been introduced. * Similar to the above, seat_begin_resize and handle_resize_motion have been renamed and tiling variants introduced. * resize.c's resize_tiled has to be used, so container_resize_tiled has been introduced in resize.c to allow external code to call it. --- common/list.c | 9 ++++ include/list.h | 1 + include/sway/commands.h | 7 +++ include/sway/input/seat.h | 10 ++-- sway/commands/resize.c | 34 +++++++++++-- sway/desktop/xdg_shell.c | 3 +- sway/desktop/xdg_shell_v6.c | 3 +- sway/desktop/xwayland.c | 2 +- sway/input/cursor.c | 97 ++++++++++++++++++++++++++++++++++--- sway/input/seat.c | 41 ++++++++++------ 10 files changed, 173 insertions(+), 34 deletions(-) diff --git a/common/list.c b/common/list.c index 66d52f70..a3a22d8f 100644 --- a/common/list.c +++ b/common/list.c @@ -77,6 +77,15 @@ int list_seq_find(list_t *list, int compare(const void *item, const void *data), return -1; } +int list_find(list_t *list, void *item) { + for (int i = 0; i < list->length; i++) { + if (list->items[i] == item) { + return i; + } + } + return -1; +} + void list_swap(list_t *list, int src, int dest) { void *tmp = list->items[src]; list->items[src] = list->items[dest]; diff --git a/include/list.h b/include/list.h index 5a0d7d80..7c0e4bd2 100644 --- a/include/list.h +++ b/include/list.h @@ -20,6 +20,7 @@ void list_qsort(list_t *list, int compare(const void *left, const void *right)); // Return index for first item in list that returns 0 for given compare // function or -1 if none matches. int list_seq_find(list_t *list, int compare(const void *item, const void *cmp_to), const void *cmp_to); +int list_find(list_t *list, void *item); // stable sort since qsort is not guaranteed to be stable void list_stable_sort(list_t *list, int compare(const void *a, const void *b)); // swap two elements in a list diff --git a/include/sway/commands.h b/include/sway/commands.h index f83907b2..545b21e6 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -1,6 +1,7 @@ #ifndef _SWAY_COMMANDS_H #define _SWAY_COMMANDS_H +#include #include "config.h" typedef struct cmd_results *sway_cmd(int argc, char **argv); @@ -84,6 +85,12 @@ char *cmd_results_to_json(struct cmd_results *results); struct cmd_results *add_color(const char *name, char *buffer, const char *color); +/** + * TODO: Move this function and its dependent functions to container.c. + */ +void container_resize_tiled(struct sway_container *parent, enum wlr_edges edge, + int amount); + sway_cmd cmd_assign; sway_cmd cmd_bar; sway_cmd cmd_bindcode; diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 92387601..eb4202f3 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -57,7 +57,8 @@ struct sway_seat { enum { OP_NONE, OP_MOVE, - OP_RESIZE, + OP_RESIZE_FLOATING, + OP_RESIZE_TILING, } operation; struct sway_container *op_container; @@ -159,8 +160,11 @@ void drag_icon_update_position(struct sway_drag_icon *icon); void seat_begin_move(struct sway_seat *seat, struct sway_container *con, uint32_t button); -void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, - uint32_t button, enum wlr_edges edge); +void seat_begin_resize_floating(struct sway_seat *seat, + struct sway_container *con, uint32_t button, enum wlr_edges edge); + +void seat_begin_resize_tiling(struct sway_seat *seat, + struct sway_container *con, uint32_t button, enum wlr_edges edge); void seat_end_mouse_operation(struct sway_seat *seat); diff --git a/sway/commands/resize.c b/sway/commands/resize.c index c3560985..0f3005f4 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -158,8 +158,8 @@ static int parallel_size(struct sway_container *c, enum resize_axis a) { return normalize_axis(a) == RESIZE_AXIS_HORIZONTAL ? c->width : c->height; } -static void resize_tiled(int amount, enum resize_axis axis) { - struct sway_container *parent = config->handler_context.current_container; +static void resize_tiled(struct sway_container *parent, int amount, + enum resize_axis axis) { struct sway_container *focused = parent; if (!parent) { return; @@ -297,6 +297,28 @@ static void resize_tiled(int amount, enum resize_axis axis) { arrange_windows(parent->parent); } +void container_resize_tiled(struct sway_container *parent, + enum wlr_edges edge, int amount) { + enum resize_axis axis = RESIZE_AXIS_INVALID; + switch (edge) { + case WLR_EDGE_TOP: + axis = RESIZE_AXIS_UP; + break; + case WLR_EDGE_RIGHT: + axis = RESIZE_AXIS_RIGHT; + break; + case WLR_EDGE_BOTTOM: + axis = RESIZE_AXIS_DOWN; + break; + case WLR_EDGE_LEFT: + axis = RESIZE_AXIS_LEFT; + break; + case WLR_EDGE_NONE: + break; + } + resize_tiled(parent, amount, axis); +} + /** * Implement `resize ` for a floating container. */ @@ -398,7 +420,7 @@ static struct cmd_results *resize_adjust_tiled(enum resize_axis axis, } } - resize_tiled(amount->amount, axis); + resize_tiled(current, amount->amount, axis); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } @@ -421,7 +443,8 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con, } } if (width->unit == RESIZE_UNIT_PX) { - resize_tiled(width->amount - con->width, RESIZE_AXIS_HORIZONTAL); + resize_tiled(con, width->amount - con->width, + RESIZE_AXIS_HORIZONTAL); } } @@ -439,7 +462,8 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con, } } if (height->unit == RESIZE_UNIT_PX) { - resize_tiled(height->amount - con->height, RESIZE_AXIS_VERTICAL); + resize_tiled(con, height->amount - con->height, + RESIZE_AXIS_HORIZONTAL); } } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 3b73f99c..af9d49b8 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -331,7 +331,8 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_resize_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); + seat_begin_resize_floating(seat, view->swayc, + seat->last_button, e->edges); } } diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index a947fb35..c6ac0f4e 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -327,7 +327,8 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_v6_resize_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); + seat_begin_resize_floating(seat, view->swayc, + seat->last_button, e->edges); } } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 393185bd..5e8afa65 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -432,7 +432,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { } struct wlr_xwayland_resize_event *e = data; struct sway_seat *seat = input_manager_current_seat(input_manager); - seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); + seat_begin_resize_floating(seat, view->swayc, seat->last_button, e->edges); } static void handle_set_title(struct wl_listener *listener, void *data) { diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 3f417e96..4b689535 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -12,6 +12,7 @@ #include "list.h" #include "log.h" #include "config.h" +#include "sway/commands.h" #include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" @@ -136,6 +137,44 @@ static struct sway_container *container_at_coords( return output->swayc; } +/** + * Determine if the edge of the given container is on the edge of the + * workspace/output. + */ +static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { + enum sway_container_layout layout = L_NONE; + switch (edge) { + case WLR_EDGE_TOP: + case WLR_EDGE_BOTTOM: + layout = L_VERT; + break; + case WLR_EDGE_LEFT: + case WLR_EDGE_RIGHT: + layout = L_HORIZ; + break; + case WLR_EDGE_NONE: + sway_assert(false, "Never reached"); + return false; + } + + // Iterate the parents until we find one with the layout we want, + // then check if the child has siblings between it and the edge. + while (cont->type != C_OUTPUT) { + if (cont->parent->layout == layout) { + int index = list_find(cont->parent->children, cont); + if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { + return false; + } + if (index < cont->parent->children->length - 1 && + (edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) { + return false; + } + } + cont = cont->parent; + } + return true; +} + static enum wlr_edges find_resize_edge(struct sway_container *cont, struct sway_cursor *cursor) { if (cont->type != C_VIEW) { @@ -159,6 +198,11 @@ static enum wlr_edges find_resize_edge(struct sway_container *cont, if (cursor->cursor->y >= cont->y + cont->height - view->border_thickness) { edge |= WLR_EDGE_BOTTOM; } + + if (edge && !container_is_floating(cont) && edge_is_external(cont, edge)) { + return WLR_EDGE_NONE; + } + return edge; } @@ -209,7 +253,7 @@ static void calculate_floating_constraints(struct sway_container *con, } } -static void handle_resize_motion(struct sway_seat *seat, +static void handle_resize_floating_motion(struct sway_seat *seat, struct sway_cursor *cursor) { struct sway_container *con = seat->op_container; enum wlr_edges edge = seat->op_resize_edge; @@ -301,6 +345,31 @@ static void handle_resize_motion(struct sway_seat *seat, arrange_windows(con); } +static void handle_resize_tiling_motion(struct sway_seat *seat, + struct sway_cursor *cursor) { + int amount = 0; + int moved_x = cursor->cursor->x - seat->op_ref_lx; + int moved_y = cursor->cursor->y - seat->op_ref_ly; + struct sway_container *con = seat->op_container; + switch (seat->op_resize_edge) { + case WLR_EDGE_TOP: + amount = (seat->op_ref_height - moved_y) - con->height; + break; + case WLR_EDGE_BOTTOM: + amount = (seat->op_ref_height + moved_y) - con->height; + break; + case WLR_EDGE_LEFT: + amount = (seat->op_ref_width - moved_x) - con->width; + break; + case WLR_EDGE_RIGHT: + amount = (seat->op_ref_width + moved_x) - con->width; + break; + case WLR_EDGE_NONE: + break; + } + container_resize_tiled(seat->op_container, seat->op_resize_edge, amount); +} + void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, bool allow_refocusing) { if (time_msec == 0) { @@ -310,10 +379,18 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, struct sway_seat *seat = cursor->seat; if (seat->operation != OP_NONE) { - if (seat->operation == OP_MOVE) { + switch (seat->operation) { + case OP_MOVE: handle_move_motion(seat, cursor); - } else { - handle_resize_motion(seat, cursor); + break; + case OP_RESIZE_FLOATING: + handle_resize_floating_motion(seat, cursor); + break; + case OP_RESIZE_TILING: + handle_resize_tiling_motion(seat, cursor); + break; + case OP_NONE: + break; } cursor->previous.x = cursor->cursor->x; cursor->previous.y = cursor->cursor->y; @@ -375,8 +452,8 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, if (client != cursor->image_client) { cursor_set_image(cursor, "left_ptr", client); } - } else if (c && container_is_floating(c)) { - // Try a floating container's resize edge + } else if (c) { + // Try a container's resize edge enum wlr_edges edge = find_resize_edge(c, cursor); const char *image = edge == WLR_EDGE_NONE ? "left_ptr" : wlr_xcursor_get_resize_name(edge); @@ -467,7 +544,7 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, edge |= cursor->cursor->y > floater->y + floater->height / 2 ? WLR_EDGE_BOTTOM : WLR_EDGE_TOP; } - seat_begin_resize(seat, floater, button, edge); + seat_begin_resize_floating(seat, floater, button, edge); return; } @@ -592,6 +669,8 @@ void dispatch_cursor_button(struct sway_cursor *cursor, // TODO: do we want to pass on the event? } + enum wlr_edges edge = cont ? find_resize_edge(cont, cursor) : WLR_EDGE_NONE; + if (surface && wlr_surface_is_layer_surface(surface)) { struct wlr_layer_surface *layer = wlr_layer_surface_from_wlr_surface(surface); @@ -599,6 +678,10 @@ void dispatch_cursor_button(struct sway_cursor *cursor, seat_set_focus_layer(cursor->seat, layer); } seat_pointer_notify_button(cursor->seat, time_msec, button, state); + } else if (edge && button == BTN_LEFT && + !container_is_floating(cont)) { + seat_set_focus(cursor->seat, cont); + seat_begin_resize_tiling(cursor->seat, cont, BTN_LEFT, edge); } else if (cont && container_is_floating_or_child(cont)) { dispatch_cursor_button_floating(cursor, time_msec, button, state, surface, sx, sy, cont); diff --git a/sway/input/seat.c b/sway/input/seat.c index eb6d2dac..6d9e85dc 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -942,14 +942,14 @@ void seat_begin_move(struct sway_seat *seat, struct sway_container *con, cursor_set_image(seat->cursor, "grab", NULL); } -void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, - uint32_t button, enum wlr_edges edge) { +void seat_begin_resize_floating(struct sway_seat *seat, + struct sway_container *con, uint32_t button, enum wlr_edges edge) { if (!seat->cursor) { wlr_log(WLR_DEBUG, "Ignoring resize request due to no cursor device"); return; } struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); - seat->operation = OP_RESIZE; + seat->operation = OP_RESIZE_FLOATING; seat->op_container = con; seat->op_resize_preserve_ratio = keyboard && (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT); @@ -968,20 +968,29 @@ void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, cursor_set_image(seat->cursor, image, NULL); } +void seat_begin_resize_tiling(struct sway_seat *seat, + struct sway_container *con, uint32_t button, enum wlr_edges edge) { + seat->operation = OP_RESIZE_TILING; + seat->op_container = con; + seat->op_resize_edge = edge; + seat->op_button = button; + seat->op_ref_lx = seat->cursor->cursor->x; + seat->op_ref_ly = seat->cursor->cursor->y; + seat->op_ref_con_lx = con->x; + seat->op_ref_con_ly = con->y; + seat->op_ref_width = con->width; + seat->op_ref_height = con->height; + + const char *image = wlr_xcursor_get_resize_name(edge); + cursor_set_image(seat->cursor, image, NULL); +} + void seat_end_mouse_operation(struct sway_seat *seat) { - switch (seat->operation) { - case OP_MOVE: - { - // We "move" the container to its own location so it discovers its - // output again. - struct sway_container *con = seat->op_container; - container_floating_move_to(con, con->x, con->y); - } - case OP_RESIZE: - // Don't need to do anything here. - break; - case OP_NONE: - break; + if (seat->operation == OP_MOVE) { + // We "move" the container to its own location so it discovers its + // output again. + struct sway_container *con = seat->op_container; + container_floating_move_to(con, con->x, con->y); } seat->operation = OP_NONE; seat->op_container = NULL; From f4280e506b9369d21769ee14bd2ab5e484fe4e76 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 10 Aug 2018 16:51:24 +1000 Subject: [PATCH 2/8] Refactor dispatch_cursor_button There was a separate function dispatch_cursor_button_floating which dealt with the resize and move operations, but as resize is not really limited to floating views, it doesn't make as much sense to have this separate. So both functions are now combined into one. Additionally, dispatch_cursor_button now uses a pattern of returning early instead of using else-ifs. --- sway/input/cursor.c | 198 ++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 100 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 4b689535..d4dc617a 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -175,7 +175,7 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { return true; } -static enum wlr_edges find_resize_edge(struct sway_container *cont, +static enum wlr_edges find_edge(struct sway_container *cont, struct sway_cursor *cursor) { if (cont->type != C_VIEW) { return WLR_EDGE_NONE; @@ -199,10 +199,19 @@ static enum wlr_edges find_resize_edge(struct sway_container *cont, edge |= WLR_EDGE_BOTTOM; } + return edge; +} + +/** + * If the cursor is over a _resizable_ edge, return the edge. + * Edges that can't be resized are edges of the workspace. + */ +static enum wlr_edges find_resize_edge(struct sway_container *cont, + struct sway_cursor *cursor) { + enum wlr_edges edge = find_edge(cont, cursor); if (edge && !container_is_floating(cont) && edge_is_external(cont, edge)) { return WLR_EDGE_NONE; } - return edge; } @@ -500,57 +509,6 @@ static void handle_cursor_motion_absolute( transaction_commit_dirty(); } -static void dispatch_cursor_button_floating(struct sway_cursor *cursor, - uint32_t time_msec, uint32_t button, enum wlr_button_state state, - struct wlr_surface *surface, double sx, double sy, - struct sway_container *cont) { - struct sway_seat *seat = cursor->seat; - - seat_set_focus(seat, cont); - - // Deny moving or resizing a fullscreen container - if (container_is_fullscreen_or_child(cont)) { - seat_pointer_notify_button(seat, time_msec, button, state); - return; - } - struct sway_container *floater = cont; - while (floater->parent->layout != L_FLOATING) { - floater = floater->parent; - } - - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); - bool mod_pressed = keyboard && - (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); - enum wlr_edges edge = find_resize_edge(floater, cursor); - bool over_title = edge == WLR_EDGE_NONE && !surface; - - // Check for beginning move - uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; - if (button == btn_move && state == WLR_BUTTON_PRESSED && - (mod_pressed || over_title)) { - seat_begin_move(seat, floater, button); - return; - } - - // Check for beginning resize - bool resizing_via_border = button == BTN_LEFT && edge != WLR_EDGE_NONE; - uint32_t btn_resize = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT; - bool resizing_via_mod = button == btn_resize && mod_pressed; - if ((resizing_via_border || resizing_via_mod) && - state == WLR_BUTTON_PRESSED) { - if (edge == WLR_EDGE_NONE) { - edge |= cursor->cursor->x > floater->x + floater->width / 2 ? - WLR_EDGE_RIGHT : WLR_EDGE_LEFT; - edge |= cursor->cursor->y > floater->y + floater->height / 2 ? - WLR_EDGE_BOTTOM : WLR_EDGE_TOP; - } - seat_begin_resize_floating(seat, floater, button, edge); - return; - } - - seat_pointer_notify_button(seat, time_msec, button, state); -} - /** * Remove a button (and duplicates) to the sorted list of currently pressed buttons */ @@ -630,26 +588,36 @@ static struct sway_binding* get_active_mouse_binding(const struct sway_cursor *c void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, uint32_t button, enum wlr_button_state state) { - if (cursor->seat->operation != OP_NONE && - button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { - seat_end_mouse_operation(cursor->seat); - seat_pointer_notify_button(cursor->seat, time_msec, button, state); - return; - } if (time_msec == 0) { time_msec = get_current_time_msec(); } + struct sway_seat *seat = cursor->seat; + // Handle ending seat operation + if (cursor->seat->operation != OP_NONE && + button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { + seat_end_mouse_operation(seat); + seat_pointer_notify_button(seat, time_msec, button, state); + return; + } + + // Determine what's under the cursor struct wlr_surface *surface = NULL; double sx, sy; - struct sway_container *cont = container_at_coords(cursor->seat, + struct sway_container *cont = container_at_coords(seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + bool is_floating = cont && container_is_floating(cont); + bool is_floating_or_child = cont && container_is_floating_or_child(cont); + bool is_fullscreen_or_child = cont && container_is_fullscreen_or_child(cont); + enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; + enum wlr_edges resize_edge = edge ? + find_resize_edge(cont, cursor) : WLR_EDGE_NONE; + bool on_border = edge != WLR_EDGE_NONE; + bool on_contents = cont && !on_border && surface; + bool on_titlebar = cont && !on_border && !surface; // Handle mouse bindings - bool on_border = cont && (find_resize_edge(cont, cursor) != WLR_EDGE_NONE); - bool on_contents = !on_border && surface; - bool on_titlebar = !on_border && !surface; - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat); + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; struct sway_binding *binding = NULL; @@ -665,51 +633,78 @@ void dispatch_cursor_button(struct sway_cursor *cursor, state_erase_button(cursor, button); } if (binding) { - seat_execute_command(cursor->seat, binding); - // TODO: do we want to pass on the event? + seat_execute_command(seat, binding); + return; } - enum wlr_edges edge = cont ? find_resize_edge(cont, cursor) : WLR_EDGE_NONE; - + // Handle clicking a layer surface if (surface && wlr_surface_is_layer_surface(surface)) { struct wlr_layer_surface *layer = wlr_layer_surface_from_wlr_surface(surface); if (layer->current.keyboard_interactive) { - seat_set_focus_layer(cursor->seat, layer); + seat_set_focus_layer(seat, layer); } - seat_pointer_notify_button(cursor->seat, time_msec, button, state); - } else if (edge && button == BTN_LEFT && - !container_is_floating(cont)) { - seat_set_focus(cursor->seat, cont); - seat_begin_resize_tiling(cursor->seat, cont, BTN_LEFT, edge); - } else if (cont && container_is_floating_or_child(cont)) { - dispatch_cursor_button_floating(cursor, time_msec, button, state, - surface, sx, sy, cont); - } else if (surface && cont && cont->type != C_VIEW) { - // Avoid moving keyboard focus from a surface that accepts it to one - // that does not unless the change would move us to a new workspace. - // - // This prevents, for example, losing focus when clicking on swaybar. - struct sway_container *new_ws = cont; - if (new_ws && new_ws->type != C_WORKSPACE) { - new_ws = container_parent(new_ws, C_WORKSPACE); - } - struct sway_container *old_ws = seat_get_focus(cursor->seat); - if (old_ws && old_ws->type != C_WORKSPACE) { - old_ws = container_parent(old_ws, C_WORKSPACE); - } - if (new_ws != old_ws) { - seat_set_focus(cursor->seat, cont); - } - seat_pointer_notify_button(cursor->seat, time_msec, button, state); - } else if (cont) { - seat_set_focus(cursor->seat, cont); - seat_pointer_notify_button(cursor->seat, time_msec, button, state); - } else { - seat_pointer_notify_button(cursor->seat, time_msec, button, state); + seat_pointer_notify_button(seat, time_msec, button, state); + return; } - transaction_commit_dirty(); + // Handle tiling resize via border + if (resize_edge && button == BTN_LEFT && !is_floating) { + seat_set_focus(seat, cont); + seat_begin_resize_tiling(seat, cont, button, edge); + return; + } + + // Handle beginning floating move + bool mod_pressed = keyboard && + (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); + + if (is_floating_or_child && !is_fullscreen_or_child) { + uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; + if (button == btn_move && state == WLR_BUTTON_PRESSED && + (mod_pressed || on_titlebar)) { + while (cont->parent->layout != L_FLOATING) { + cont = cont->parent; + } + seat_begin_move(seat, cont, button); + return; + } + } + + // Handle beginning floating resize + if (is_floating_or_child && !is_fullscreen_or_child && + state == WLR_BUTTON_PRESSED) { + // Via border + if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { + seat_begin_resize_floating(seat, cont, button, resize_edge); + return; + } + + // Via mod+click + struct sway_container *floater = cont; + while (floater->parent->layout != L_FLOATING) { + floater = floater->parent; + } + uint32_t btn_resize = config->floating_mod_inverse ? + BTN_LEFT : BTN_RIGHT; + if (button == btn_resize) { + edge |= cursor->cursor->x > floater->x + floater->width / 2 ? + WLR_EDGE_RIGHT : WLR_EDGE_LEFT; + edge |= cursor->cursor->y > floater->y + floater->height / 2 ? + WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + seat_begin_resize_floating(seat, floater, button, edge); + return; + } + } + + // Handle clicking a container surface + if (cont) { + seat_set_focus(seat, cont); + seat_pointer_notify_button(seat, time_msec, button, state); + return; + } + + seat_pointer_notify_button(seat, time_msec, button, state); } static void handle_cursor_button(struct wl_listener *listener, void *data) { @@ -718,6 +713,7 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) { struct wlr_event_pointer_button *event = data; dispatch_cursor_button(cursor, event->time_msec, event->button, event->state); + transaction_commit_dirty(); } static void handle_cursor_axis(struct wl_listener *listener, void *data) { @@ -865,6 +861,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { dispatch_cursor_button(cursor, event->time_msec, BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ? WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED); + transaction_commit_dirty(); } static void handle_tool_button(struct wl_listener *listener, void *data) { @@ -889,6 +886,7 @@ static void handle_tool_button(struct wl_listener *listener, void *data) { cursor->tool_buttons--; break; } + transaction_commit_dirty(); } static void handle_request_set_cursor(struct wl_listener *listener, From 2dceae6224f38a706182394dc77ebd11afb22531 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 10 Aug 2018 17:22:50 +1000 Subject: [PATCH 3/8] Allow resizing tiled views via mod key --- sway/input/cursor.c | 60 +++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index d4dc617a..5f295828 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -356,27 +356,35 @@ static void handle_resize_floating_motion(struct sway_seat *seat, static void handle_resize_tiling_motion(struct sway_seat *seat, struct sway_cursor *cursor) { - int amount = 0; + int amount_x = 0; + int amount_y = 0; int moved_x = cursor->cursor->x - seat->op_ref_lx; int moved_y = cursor->cursor->y - seat->op_ref_ly; + enum wlr_edges edge_x = WLR_EDGE_NONE; + enum wlr_edges edge_y = WLR_EDGE_NONE; struct sway_container *con = seat->op_container; - switch (seat->op_resize_edge) { - case WLR_EDGE_TOP: - amount = (seat->op_ref_height - moved_y) - con->height; - break; - case WLR_EDGE_BOTTOM: - amount = (seat->op_ref_height + moved_y) - con->height; - break; - case WLR_EDGE_LEFT: - amount = (seat->op_ref_width - moved_x) - con->width; - break; - case WLR_EDGE_RIGHT: - amount = (seat->op_ref_width + moved_x) - con->width; - break; - case WLR_EDGE_NONE: - break; + + if (seat->op_resize_edge & WLR_EDGE_TOP) { + amount_y = (seat->op_ref_height - moved_y) - con->height; + edge_y = WLR_EDGE_TOP; + } else if (seat->op_resize_edge & WLR_EDGE_BOTTOM) { + amount_y = (seat->op_ref_height + moved_y) - con->height; + edge_y = WLR_EDGE_BOTTOM; + } + if (seat->op_resize_edge & WLR_EDGE_LEFT) { + amount_x = (seat->op_ref_width - moved_x) - con->width; + edge_x = WLR_EDGE_LEFT; + } else if (seat->op_resize_edge & WLR_EDGE_RIGHT) { + amount_x = (seat->op_ref_width + moved_x) - con->width; + edge_x = WLR_EDGE_RIGHT; + } + + if (amount_x != 0) { + container_resize_tiled(seat->op_container, edge_x, amount_x); + } + if (amount_y != 0) { + container_resize_tiled(seat->op_container, edge_y, amount_y); } - container_resize_tiled(seat->op_container, seat->op_resize_edge, amount); } void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, @@ -655,10 +663,25 @@ void dispatch_cursor_button(struct sway_cursor *cursor, return; } - // Handle beginning floating move + // Handle tiling resize via mod bool mod_pressed = keyboard && (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); + if (!is_floating) { + uint32_t btn_resize = config->floating_mod_inverse ? + BTN_LEFT : BTN_RIGHT; + if (button == btn_resize) { + edge = 0; + edge |= cursor->cursor->x > cont->x + cont->width / 2 ? + WLR_EDGE_RIGHT : WLR_EDGE_LEFT; + edge |= cursor->cursor->y > cont->y + cont->height / 2 ? + WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + seat_set_focus(seat, cont); + seat_begin_resize_tiling(seat, cont, button, edge); + return; + } + } + // Handle beginning floating move if (is_floating_or_child && !is_fullscreen_or_child) { uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; if (button == btn_move && state == WLR_BUTTON_PRESSED && @@ -688,6 +711,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t btn_resize = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT; if (button == btn_resize) { + edge = 0; edge |= cursor->cursor->x > floater->x + floater->width / 2 ? WLR_EDGE_RIGHT : WLR_EDGE_LEFT; edge |= cursor->cursor->y > floater->y + floater->height / 2 ? From 1893515d3a0389bf9c2289b1ae34b3add10225c6 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 10 Aug 2018 23:43:31 +1000 Subject: [PATCH 4/8] Fix right-click/popups and add state checks --- sway/input/cursor.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 5f295828..12cca7fd 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -657,7 +657,8 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } // Handle tiling resize via border - if (resize_edge && button == BTN_LEFT && !is_floating) { + if (resize_edge && button == BTN_LEFT && state == WLR_BUTTON_PRESSED && + !is_floating) { seat_set_focus(seat, cont); seat_begin_resize_tiling(seat, cont, button, edge); return; @@ -666,7 +667,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, // Handle tiling resize via mod bool mod_pressed = keyboard && (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); - if (!is_floating) { + if (!is_floating && mod_pressed && state == WLR_BUTTON_PRESSED) { uint32_t btn_resize = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT; if (button == btn_resize) { @@ -682,7 +683,8 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } // Handle beginning floating move - if (is_floating_or_child && !is_fullscreen_or_child) { + if (is_floating_or_child && !is_fullscreen_or_child && + state == WLR_BUTTON_PRESSED) { uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; if (button == btn_move && state == WLR_BUTTON_PRESSED && (mod_pressed || on_titlebar)) { From e9d46ccbddf69e380d72b23d50d21988015ab60b Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 11 Aug 2018 00:08:18 +1000 Subject: [PATCH 5/8] Use col-resize and row-resize cursor images --- sway/input/cursor.c | 26 +++++++++++++++++++++++--- sway/input/seat.c | 3 --- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 12cca7fd..8e7157a2 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -472,9 +472,17 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, } else if (c) { // Try a container's resize edge enum wlr_edges edge = find_resize_edge(c, cursor); - const char *image = edge == WLR_EDGE_NONE ? - "left_ptr" : wlr_xcursor_get_resize_name(edge); - cursor_set_image(cursor, image, NULL); + if (edge == WLR_EDGE_NONE) { + cursor_set_image(cursor, "left_ptr", NULL); + } else if (container_is_floating(c)) { + cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); + } else { + if (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) { + cursor_set_image(cursor, "col-resize", NULL); + } else { + cursor_set_image(cursor, "row-resize", NULL); + } + } } else { cursor_set_image(cursor, "left_ptr", NULL); } @@ -676,6 +684,18 @@ void dispatch_cursor_button(struct sway_cursor *cursor, WLR_EDGE_RIGHT : WLR_EDGE_LEFT; edge |= cursor->cursor->y > cont->y + cont->height / 2 ? WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + + const char *image = NULL; + if (edge == (WLR_EDGE_LEFT | WLR_EDGE_TOP)) { + image = "nw-resize"; + } else if (edge == (WLR_EDGE_TOP | WLR_EDGE_RIGHT)) { + image = "ne-resize"; + } else if (edge == (WLR_EDGE_RIGHT | WLR_EDGE_BOTTOM)) { + image = "se-resize"; + } else if (edge == (WLR_EDGE_BOTTOM | WLR_EDGE_LEFT)) { + image = "sw-resize"; + } + cursor_set_image(seat->cursor, image, NULL); seat_set_focus(seat, cont); seat_begin_resize_tiling(seat, cont, button, edge); return; diff --git a/sway/input/seat.c b/sway/input/seat.c index 6d9e85dc..57cc65f6 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -980,9 +980,6 @@ void seat_begin_resize_tiling(struct sway_seat *seat, seat->op_ref_con_ly = con->y; seat->op_ref_width = con->width; seat->op_ref_height = con->height; - - const char *image = wlr_xcursor_get_resize_name(edge); - cursor_set_image(seat->cursor, image, NULL); } void seat_end_mouse_operation(struct sway_seat *seat) { From 4d023c03900a7904fe01177816e80da965dc43f6 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 11 Aug 2018 10:47:40 +1000 Subject: [PATCH 6/8] Make mod+resize a floating container resize the container itself Rather than resizing the split within the container. --- sway/input/cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 8e7157a2..3b70b471 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -675,7 +675,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, // Handle tiling resize via mod bool mod_pressed = keyboard && (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); - if (!is_floating && mod_pressed && state == WLR_BUTTON_PRESSED) { + if (!is_floating_or_child && mod_pressed && state == WLR_BUTTON_PRESSED) { uint32_t btn_resize = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT; if (button == btn_resize) { From e7a7306063565f14052ed8b1d34ca19512a4fd66 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 11 Aug 2018 20:04:44 +1000 Subject: [PATCH 7/8] Don't progress transaction queue if any are partially complete This fixes an issue where views might commit to a transaction ahead of the first one, and applying the first transaction causes us to save a buffer of the wrong size. --- sway/desktop/transaction.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 4e6af86a..edbe34e5 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -213,6 +213,18 @@ static void transaction_apply(struct sway_transaction *transaction) { } static void transaction_progress_queue() { + // Check if any transactions in the queue are in a partially ready state. + // If so, we shouldn't progress any transactions, even ones which are fully + // ready at the front of the queue, because the views in the ready + // transactions might have committed past it to a transaction which isn't + // yet ready. + for (int i = 0; i < server.transactions->length; ++i) { + struct sway_transaction *transaction = server.transactions->items[i]; + if (transaction->num_waiting != 0 && + transaction->num_waiting != transaction->num_configures) { + return; + } + } while (server.transactions->length) { struct sway_transaction *transaction = server.transactions->items[0]; if (transaction->num_waiting) { From 4b8e3a885be74c588291c51f798de80bd81a92db Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 12 Aug 2018 10:44:45 +1000 Subject: [PATCH 8/8] Don't commit multiple transactions at the same time --- sway/desktop/transaction.c | 82 +++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 32 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index edbe34e5..a449ffaa 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -33,6 +33,7 @@ struct sway_transaction { list_t *instructions; // struct sway_transaction_instruction * size_t num_waiting; size_t num_configures; + uint32_t con_ids; // Bitwise XOR of view container IDs struct timespec create_time; struct timespec commit_time; }; @@ -212,29 +213,43 @@ static void transaction_apply(struct sway_transaction *transaction) { } } +static void transaction_commit(struct sway_transaction *transaction); + static void transaction_progress_queue() { - // Check if any transactions in the queue are in a partially ready state. - // If so, we shouldn't progress any transactions, even ones which are fully - // ready at the front of the queue, because the views in the ready - // transactions might have committed past it to a transaction which isn't - // yet ready. - for (int i = 0; i < server.transactions->length; ++i) { - struct sway_transaction *transaction = server.transactions->items[i]; - if (transaction->num_waiting != 0 && - transaction->num_waiting != transaction->num_configures) { - return; + if (!server.transactions->length) { + return; + } + // There's only ever one committed transaction, + // and it's the first one in the queue. + struct sway_transaction *transaction = server.transactions->items[0]; + if (transaction->num_waiting) { + return; + } + transaction_apply(transaction); + transaction_destroy(transaction); + list_del(server.transactions, 0); + + if (!server.transactions->length) { + idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); + return; + } + + // If there's a bunch of consecutive transactions which all apply to the + // same views, skip all except the last one. + while (server.transactions->length >= 2) { + struct sway_transaction *a = server.transactions->items[0]; + struct sway_transaction *b = server.transactions->items[1]; + if (a->con_ids == b->con_ids) { + list_del(server.transactions, 0); + transaction_destroy(a); + } else { + break; } } - while (server.transactions->length) { - struct sway_transaction *transaction = server.transactions->items[0]; - if (transaction->num_waiting) { - return; - } - transaction_apply(transaction); - transaction_destroy(transaction); - list_del(server.transactions, 0); - } - idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); + + transaction = server.transactions->items[0]; + transaction_commit(transaction); + transaction_progress_queue(); } static int handle_timeout(void *data) { @@ -288,6 +303,7 @@ static void transaction_commit(struct sway_transaction *transaction) { instruction->state.view_width, instruction->state.view_height); ++transaction->num_waiting; + transaction->con_ids ^= con->id; // From here on we are rendering a saved buffer of the view, which // means we can send a frame done event to make the client redraw it @@ -305,17 +321,6 @@ static void transaction_commit(struct sway_transaction *transaction) { if (server.debug_txn_timings) { clock_gettime(CLOCK_MONOTONIC, &transaction->commit_time); } - if (server.transactions->length || transaction->num_waiting) { - list_add(server.transactions, transaction); - } else { - // There are no other transactions in progress, and this one has nothing - // to wait for, so we can skip the queue. - wlr_log(WLR_DEBUG, "Transaction %p has nothing to wait for", transaction); - transaction_apply(transaction); - transaction_destroy(transaction); - idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); - return; - } if (transaction->num_waiting) { // Set up a timer which the views must respond within @@ -329,6 +334,9 @@ static void transaction_commit(struct sway_transaction *transaction) { strerror(errno)); handle_timeout(transaction); } + } else { + wlr_log(WLR_DEBUG, + "Transaction %p has nothing to wait for", transaction); } // The debug tree shows the pending/live tree. Here is a good place to @@ -415,5 +423,15 @@ void transaction_commit_dirty(void) { container->dirty = false; } server.dirty_containers->length = 0; - transaction_commit(transaction); + + list_add(server.transactions, transaction); + + // There's only ever one committed transaction, + // and it's the first one in the queue. + if (server.transactions->length == 1) { + transaction_commit(transaction); + // Attempting to progress the queue here is useful + // if the transaction has nothing to wait for. + transaction_progress_queue(); + } }