diff options
Diffstat (limited to 'sway/input')
-rw-r--r-- | sway/input/cursor.c | 631 | ||||
-rw-r--r-- | sway/input/input-manager.c | 227 | ||||
-rw-r--r-- | sway/input/keyboard.c | 13 | ||||
-rw-r--r-- | sway/input/seat.c | 297 | ||||
-rw-r--r-- | sway/input/seatop_down.c | 77 | ||||
-rw-r--r-- | sway/input/seatop_move_floating.c | 65 | ||||
-rw-r--r-- | sway/input/seatop_move_tiling.c | 335 | ||||
-rw-r--r-- | sway/input/seatop_resize_floating.c | 199 | ||||
-rw-r--r-- | sway/input/seatop_resize_tiling.c | 92 |
9 files changed, 1344 insertions, 592 deletions
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index d89f64d8..08222494 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -1,12 +1,11 @@ -#define _XOPEN_SOURCE 700 +#define _POSIX_C_SOURCE 200809L #include <math.h> -#ifdef __linux__ +#include <libevdev/libevdev.h> #include <linux/input-event-codes.h> -#elif __FreeBSD__ -#include <dev/evdev/input-event-codes.h> -#endif +#include <errno.h> #include <float.h> #include <limits.h> +#include <strings.h> #include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_xcursor_manager.h> #include <wlr/types/wlr_idle.h> @@ -28,10 +27,6 @@ #include "sway/tree/workspace.h" #include "wlr-layer-shell-unstable-v1-protocol.h" -// When doing a tiling drag, this is the thickness of the dropzone -// when dragging to the edge of a layout container. -#define DROP_LAYOUT_BORDER 30 - static uint32_t get_current_time_msec(void) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); @@ -60,7 +55,7 @@ static struct wlr_surface *layer_surface_at(struct sway_output *output, * Returns the node at the cursor's position. If there is a surface at that * location, it is stored in **surface (it may not be a view). */ -static struct sway_node *node_at_coords( +struct sway_node *node_at_coords( struct sway_seat *seat, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { // check for unmanaged views first @@ -88,6 +83,10 @@ static struct sway_node *node_at_coords( return NULL; } struct sway_output *output = wlr_output->data; + if (!output) { + // output is being destroyed + return NULL; + } double ox = lx, oy = ly; wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); @@ -223,323 +222,6 @@ static enum wlr_edges find_resize_edge(struct sway_container *cont, return edge; } -static void handle_down_motion(struct sway_seat *seat, - struct sway_cursor *cursor, uint32_t time_msec) { - struct sway_container *con = seat->op_container; - if (seat_is_input_allowed(seat, con->view->surface)) { - double moved_x = cursor->cursor->x - seat->op_ref_lx; - double moved_y = cursor->cursor->y - seat->op_ref_ly; - double sx = seat->op_ref_con_lx + moved_x; - double sy = seat->op_ref_con_ly + moved_y; - wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy); - } - seat->op_moved = true; -} - -static void handle_move_floating_motion(struct sway_seat *seat, - struct sway_cursor *cursor) { - struct sway_container *con = seat->op_container; - desktop_damage_whole_container(con); - container_floating_translate(con, - cursor->cursor->x - cursor->previous.x, - cursor->cursor->y - cursor->previous.y); - desktop_damage_whole_container(con); -} - -static void resize_box(struct wlr_box *box, enum wlr_edges edge, - int thickness) { - switch (edge) { - case WLR_EDGE_TOP: - box->height = thickness; - break; - case WLR_EDGE_LEFT: - box->width = thickness; - break; - case WLR_EDGE_RIGHT: - box->x = box->x + box->width - thickness; - box->width = thickness; - break; - case WLR_EDGE_BOTTOM: - box->y = box->y + box->height - thickness; - box->height = thickness; - break; - case WLR_EDGE_NONE: - box->x += thickness; - box->y += thickness; - box->width -= thickness * 2; - box->height -= thickness * 2; - break; - } -} - -static void handle_move_tiling_motion(struct sway_seat *seat, - struct sway_cursor *cursor) { - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - // Damage the old location - desktop_damage_box(&seat->op_drop_box); - - if (!node) { - // Eg. hovered over a layer surface such as swaybar - seat->op_target_node = NULL; - seat->op_target_edge = WLR_EDGE_NONE; - return; - } - - if (node->type == N_WORKSPACE) { - // Emtpy workspace - seat->op_target_node = node; - seat->op_target_edge = WLR_EDGE_NONE; - workspace_get_box(node->sway_workspace, &seat->op_drop_box); - desktop_damage_box(&seat->op_drop_box); - return; - } - - // Deny moving within own workspace if this is the only child - struct sway_container *con = node->sway_container; - if (workspace_num_tiling_views(seat->op_container->workspace) == 1 && - con->workspace == seat->op_container->workspace) { - seat->op_target_node = NULL; - seat->op_target_edge = WLR_EDGE_NONE; - return; - } - - // Traverse the ancestors, trying to find a layout container perpendicular - // to the edge. Eg. close to the top or bottom of a horiz layout. - while (con) { - enum wlr_edges edge = WLR_EDGE_NONE; - enum sway_container_layout layout = container_parent_layout(con); - struct wlr_box parent; - con->parent ? container_get_box(con->parent, &parent) : - workspace_get_box(con->workspace, &parent); - if (layout == L_HORIZ || layout == L_TABBED) { - if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) { - edge = WLR_EDGE_TOP; - } else if (cursor->cursor->y > parent.y + parent.height - - DROP_LAYOUT_BORDER) { - edge = WLR_EDGE_BOTTOM; - } - } else if (layout == L_VERT || layout == L_STACKED) { - if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) { - edge = WLR_EDGE_LEFT; - } else if (cursor->cursor->x > parent.x + parent.width - - DROP_LAYOUT_BORDER) { - edge = WLR_EDGE_RIGHT; - } - } - if (edge) { - seat->op_target_node = node_get_parent(&con->node); - seat->op_target_edge = edge; - node_get_box(seat->op_target_node, &seat->op_drop_box); - resize_box(&seat->op_drop_box, edge, DROP_LAYOUT_BORDER); - desktop_damage_box(&seat->op_drop_box); - return; - } - con = con->parent; - } - - // Use the hovered view - but we must be over the actual surface - con = node->sway_container; - if (!con->view->surface || node == &seat->op_container->node) { - seat->op_target_node = NULL; - seat->op_target_edge = WLR_EDGE_NONE; - return; - } - - // Find the closest edge - size_t thickness = fmin(con->content_width, con->content_height) * 0.3; - size_t closest_dist = INT_MAX; - size_t dist; - seat->op_target_edge = WLR_EDGE_NONE; - if ((dist = cursor->cursor->y - con->y) < closest_dist) { - closest_dist = dist; - seat->op_target_edge = WLR_EDGE_TOP; - } - if ((dist = cursor->cursor->x - con->x) < closest_dist) { - closest_dist = dist; - seat->op_target_edge = WLR_EDGE_LEFT; - } - if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) { - closest_dist = dist; - seat->op_target_edge = WLR_EDGE_RIGHT; - } - if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) { - closest_dist = dist; - seat->op_target_edge = WLR_EDGE_BOTTOM; - } - - if (closest_dist > thickness) { - seat->op_target_edge = WLR_EDGE_NONE; - } - - seat->op_target_node = node; - seat->op_drop_box.x = con->content_x; - seat->op_drop_box.y = con->content_y; - seat->op_drop_box.width = con->content_width; - seat->op_drop_box.height = con->content_height; - resize_box(&seat->op_drop_box, seat->op_target_edge, thickness); - desktop_damage_box(&seat->op_drop_box); -} - -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 - *min_width = 0; - } else if (config->floating_minimum_width == 0) { // automatic - *min_width = 75; - } else { - *min_width = config->floating_minimum_width; - } - - if (config->floating_minimum_height == -1) { // no minimum - *min_height = 0; - } else if (config->floating_minimum_height == 0) { // automatic - *min_height = 50; - } else { - *min_height = config->floating_minimum_height; - } - - if (config->floating_maximum_width == -1) { // no maximum - *max_width = INT_MAX; - } else if (config->floating_maximum_width == 0) { // automatic - *max_width = con->workspace->width; - } else { - *max_width = config->floating_maximum_width; - } - - if (config->floating_maximum_height == -1) { // no maximum - *max_height = INT_MAX; - } else if (config->floating_maximum_height == 0) { // automatic - *max_height = con->workspace->height; - } else { - *max_height = config->floating_maximum_height; - } -} - -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; - - // The amount the mouse has moved since the start of the resize operation - // Positive is down/right - double mouse_move_x = cursor->cursor->x - seat->op_ref_lx; - double mouse_move_y = cursor->cursor->y - seat->op_ref_ly; - - if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) { - mouse_move_x = 0; - } - if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) { - mouse_move_y = 0; - } - - double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x; - double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y; - - if (seat->op_resize_preserve_ratio) { - double x_multiplier = grow_width / seat->op_ref_width; - double y_multiplier = grow_height / seat->op_ref_height; - double max_multiplier = fmax(x_multiplier, y_multiplier); - grow_width = seat->op_ref_width * max_multiplier; - grow_height = seat->op_ref_height * max_multiplier; - } - - // Determine new width/height, and accommodate for floating min/max values - double width = seat->op_ref_width + grow_width; - double height = seat->op_ref_height + grow_height; - int min_width, max_width, min_height, max_height; - calculate_floating_constraints(con, &min_width, &max_width, - &min_height, &max_height); - width = fmax(min_width, fmin(width, max_width)); - height = fmax(min_height, fmin(height, max_height)); - - // Apply the view's min/max size - if (con->view) { - double view_min_width, view_max_width, view_min_height, view_max_height; - view_get_constraints(con->view, &view_min_width, &view_max_width, - &view_min_height, &view_max_height); - width = fmax(view_min_width, fmin(width, view_max_width)); - height = fmax(view_min_height, fmin(height, view_max_height)); - } - - // Recalculate these, in case we hit a min/max limit - grow_width = width - seat->op_ref_width; - grow_height = height - seat->op_ref_height; - - // Determine grow x/y values - these are relative to the container's x/y at - // the start of the resize operation. - double grow_x = 0, grow_y = 0; - if (edge & WLR_EDGE_LEFT) { - grow_x = -grow_width; - } else if (edge & WLR_EDGE_RIGHT) { - grow_x = 0; - } else { - grow_x = -grow_width / 2; - } - if (edge & WLR_EDGE_TOP) { - grow_y = -grow_height; - } else if (edge & WLR_EDGE_BOTTOM) { - grow_y = 0; - } else { - grow_y = -grow_height / 2; - } - - // Determine the amounts we need to bump everything relative to the current - // size. - int relative_grow_width = width - con->width; - int relative_grow_height = height - con->height; - int relative_grow_x = (seat->op_ref_con_lx + grow_x) - con->x; - int relative_grow_y = (seat->op_ref_con_ly + grow_y) - con->y; - - // Actually resize stuff - con->x += relative_grow_x; - con->y += relative_grow_y; - con->width += relative_grow_width; - con->height += relative_grow_height; - - con->content_x += relative_grow_x; - con->content_y += relative_grow_y; - con->content_width += relative_grow_width; - con->content_height += relative_grow_height; - - arrange_container(con); -} - -static void handle_resize_tiling_motion(struct sway_seat *seat, - struct sway_cursor *cursor) { - 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; - - 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); - } -} - static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec, struct sway_node *node, struct wlr_surface *surface, double sx, double sy) { @@ -589,6 +271,50 @@ void cursor_rebase(struct sway_cursor *cursor) { cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy); } +static int hide_notify(void *data) { + struct sway_cursor *cursor = data; + wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); + cursor->hidden = true; + return 1; +} + +int cursor_get_timeout(struct sway_cursor *cursor){ + struct seat_config *sc = seat_get_config(cursor->seat); + if (!sc) { + sc = seat_get_config_by_name("*"); + } + int timeout = sc ? sc->hide_cursor_timeout : 0; + if (timeout < 0) { + timeout = 0; + } + return timeout; +} + +void cursor_handle_activity(struct sway_cursor *cursor) { + wl_event_source_timer_update( + cursor->hide_source, cursor_get_timeout(cursor)); + + wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); + if (cursor->hidden) { + cursor_unhide(cursor); + } +} + +void cursor_unhide(struct sway_cursor *cursor) { + cursor->hidden = false; + if (cursor->image_surface) { + cursor_set_image_surface(cursor, + cursor->image_surface, + cursor->hotspot_x, + cursor->hotspot_y, + cursor->image_client); + } else { + const char *image = cursor->image; + cursor->image = NULL; + cursor_set_image(cursor, image, cursor->image_client); + } +} + void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec) { if (time_msec == 0) { @@ -598,26 +324,8 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, struct sway_seat *seat = cursor->seat; struct wlr_seat *wlr_seat = seat->wlr_seat; - if (seat->operation != OP_NONE) { - switch (seat->operation) { - case OP_DOWN: - handle_down_motion(seat, cursor, time_msec); - break; - case OP_MOVE_FLOATING: - handle_move_floating_motion(seat, cursor); - break; - case OP_MOVE_TILING: - handle_move_tiling_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; - } + if (seat_doing_seatop(seat)) { + seatop_motion(seat, time_msec); cursor->previous.x = cursor->cursor->x; cursor->previous.y = cursor->cursor->y; return; @@ -679,11 +387,11 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, static void handle_cursor_motion(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); - wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); struct wlr_event_pointer_motion *event = data; wlr_cursor_move(cursor->cursor, event->device, event->delta_x, event->delta_y); cursor_send_pointer_motion(cursor, event->time_msec); + cursor_handle_activity(cursor); transaction_commit_dirty(); } @@ -691,10 +399,10 @@ static void handle_cursor_motion_absolute( struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, motion_absolute); - wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); struct wlr_event_pointer_motion_absolute *event = data; wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); cursor_send_pointer_motion(cursor, event->time_msec); + cursor_handle_activity(cursor); transaction_commit_dirty(); } @@ -794,11 +502,16 @@ void dispatch_cursor_button(struct sway_cursor *cursor, struct sway_seat *seat = cursor->seat; // Handle existing seat operation - if (cursor->seat->operation != OP_NONE) { - if (button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { - seat_end_mouse_operation(seat); + if (seat_doing_seatop(seat)) { + if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) { + seatop_finish(seat); seat_pointer_notify_button(seat, time_msec, button, state); } + if (state == WLR_BUTTON_PRESSED) { + state_add_button(cursor, button); + } else { + state_erase_button(cursor, button); + } return; } @@ -864,7 +577,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (cont && resize_edge && button == BTN_LEFT && state == WLR_BUTTON_PRESSED && !is_floating) { seat_set_focus_container(seat, cont); - seat_begin_resize_tiling(seat, cont, button, edge); + seatop_begin_resize_tiling(seat, cont, button, edge); return; } @@ -894,7 +607,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } cursor_set_image(seat->cursor, image, NULL); seat_set_focus_container(seat, cont); - seat_begin_resize_tiling(seat, cont, button, edge); + seatop_begin_resize_tiling(seat, cont, button, edge); return; } } @@ -909,7 +622,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, cont = cont->parent; } seat_set_focus_container(seat, cont); - seat_begin_move_floating(seat, cont, button); + seatop_begin_move_floating(seat, cont, button); return; } } @@ -919,7 +632,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, state == WLR_BUTTON_PRESSED) { // Via border if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { - seat_begin_resize_floating(seat, cont, button, resize_edge); + seatop_begin_resize_floating(seat, cont, button, resize_edge); return; } @@ -936,16 +649,30 @@ void dispatch_cursor_button(struct sway_cursor *cursor, 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); + seatop_begin_resize_floating(seat, floater, button, edge); return; } } // Handle moving a tiling container - if (config->tiling_drag && mod_pressed && state == WLR_BUTTON_PRESSED && - !is_floating_or_child && cont && !cont->is_fullscreen) { + if (config->tiling_drag && (mod_pressed || on_titlebar) && + state == WLR_BUTTON_PRESSED && !is_floating_or_child && + cont && !cont->is_fullscreen) { + 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 container by it's title bar, use a threshold for the drag + if (!mod_pressed && config->tiling_drag_threshold > 0) { + seatop_begin_move_tiling_threshold(seat, cont, button); + } else { + seatop_begin_move_tiling(seat, cont, button); + } return; } @@ -953,7 +680,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (surface && cont && state == WLR_BUTTON_PRESSED) { seat_set_focus_container(seat, cont); seat_pointer_notify_button(seat, time_msec, button, state); - seat_begin_down(seat, cont, button, sx, sy); + seatop_begin_down(seat, cont, button, sx, sy); return; } @@ -970,18 +697,32 @@ void dispatch_cursor_button(struct sway_cursor *cursor, static void handle_cursor_button(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, button); - wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); struct wlr_event_pointer_button *event = data; dispatch_cursor_button(cursor, event->device, event->time_msec, event->button, event->state); + cursor_handle_activity(cursor); transaction_commit_dirty(); } -static void dispatch_cursor_axis(struct sway_cursor *cursor, +static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { + switch (event->orientation) { + case WLR_AXIS_ORIENTATION_VERTICAL: + return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; + case WLR_AXIS_ORIENTATION_HORIZONTAL: + return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; + default: + wlr_log(WLR_DEBUG, "Unknown axis orientation"); + return 0; + } +} + +void dispatch_cursor_axis(struct sway_cursor *cursor, struct wlr_event_pointer_axis *event) { struct sway_seat *seat = cursor->seat; - struct sway_input_device *input_device = event->device->data; - struct input_config *ic = input_device_get_config(input_device); + struct sway_input_device *input_device = + event->device ? event->device->data : NULL; + struct input_config *ic = + input_device ? input_device_get_config(input_device) : NULL; // Determine what's under the cursor struct wlr_surface *surface = NULL; @@ -993,11 +734,35 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor, enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; bool on_border = edge != WLR_EDGE_NONE; bool on_titlebar = cont && !on_border && !surface; + bool on_titlebar_border = cont && on_border && + cursor->cursor->y < cont->content_y; + bool on_contents = cont && !on_border && surface; float scroll_factor = (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; - // Scrolling on a tabbed or stacked title bar - if (on_titlebar) { + bool handled = false; + + // Gather information needed for mouse bindings + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + struct wlr_input_device *device = + input_device ? input_device->wlr_device : NULL; + char *dev_id = device ? input_device_get_identifier(device) : strdup("*"); + uint32_t button = wl_axis_to_button(event); + + // Handle mouse bindings - x11 mouse buttons 4-7 - press event + struct sway_binding *binding = NULL; + state_add_button(cursor, button); + binding = get_active_mouse_binding(cursor, + config->current_mode->mouse_bindings, modifiers, false, + on_titlebar, on_border, on_contents, dev_id); + if (binding) { + seat_execute_command(seat, binding); + handled = true; + } + + // Scrolling on a tabbed or stacked title bar (handled as press event) + if (!handled && (on_titlebar || on_titlebar_border)) { enum sway_container_layout layout = container_parent_layout(cont); if (layout == L_TABBED || layout == L_STACKED) { struct sway_node *tabcontainer = node_get_parent(node); @@ -1024,20 +789,33 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor, seat_set_raw_focus(seat, new_focus); seat_set_raw_focus(seat, old_focus); } - return; + handled = true; } } - wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, - event->orientation, scroll_factor * event->delta, - round(scroll_factor * event->delta_discrete), event->source); + // Handle mouse bindings - x11 mouse buttons 4-7 - release event + binding = get_active_mouse_binding(cursor, + config->current_mode->mouse_bindings, modifiers, true, + on_titlebar, on_border, on_contents, dev_id); + state_erase_button(cursor, button); + if (binding) { + seat_execute_command(seat, binding); + handled = true; + } + free(dev_id); + + if (!handled) { + wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, + event->orientation, scroll_factor * event->delta, + round(scroll_factor * event->delta_discrete), event->source); + } } static void handle_cursor_axis(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); - wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); struct wlr_event_pointer_axis *event = data; dispatch_cursor_axis(cursor, event); + cursor_handle_activity(cursor); transaction_commit_dirty(); } @@ -1209,7 +987,7 @@ static void handle_request_set_cursor(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, request_set_cursor); - if (cursor->seat->operation != OP_NONE) { + if (seat_doing_seatop(cursor->seat)) { return; } struct wlr_seat_pointer_request_set_cursor_event *event = data; @@ -1228,10 +1006,8 @@ static void handle_request_set_cursor(struct wl_listener *listener, return; } - wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x, - event->hotspot_y); - cursor->image = NULL; - cursor->image_client = focused_client; + cursor_set_image_surface(cursor, event->surface, event->hotspot_x, + event->hotspot_y, focused_client); } void cursor_set_image(struct sway_cursor *cursor, const char *image, @@ -1239,14 +1015,43 @@ void cursor_set_image(struct sway_cursor *cursor, const char *image, if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) { return; } + + const char *current_image = cursor->image; + cursor->image = image; + cursor->image_surface = NULL; + cursor->hotspot_x = cursor->hotspot_y = 0; + cursor->image_client = client; + + if (cursor->hidden) { + return; + } + if (!image) { wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); - } else if (!cursor->image || strcmp(cursor->image, image) != 0) { + } else if (!current_image || strcmp(current_image, image) != 0) { wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, cursor->cursor); } - cursor->image = image; +} + +void cursor_set_image_surface(struct sway_cursor *cursor, + struct wlr_surface *surface, int32_t hotspot_x, int32_t hotspot_y, + struct wl_client *client) { + if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) { + return; + } + + cursor->image = NULL; + cursor->image_surface = surface; + cursor->hotspot_x = hotspot_x; + cursor->hotspot_y = hotspot_y; cursor->image_client = client; + + if (cursor->hidden) { + return; + } + + wlr_cursor_set_surface(cursor->cursor, surface, hotspot_x, hotspot_y); } void sway_cursor_destroy(struct sway_cursor *cursor) { @@ -1254,6 +1059,8 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { return; } + wl_event_source_remove(cursor->hide_source); + wlr_xcursor_manager_destroy(cursor->xcursor_manager); wlr_cursor_destroy(cursor->cursor); free(cursor); @@ -1277,6 +1084,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { cursor->seat = seat; wlr_cursor_attach_output_layout(wlr_cursor, root->output_layout); + cursor->hide_source = wl_event_loop_add_timer(server.wl_event_loop, + hide_notify, cursor); + // input events wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); cursor->motion.notify = handle_cursor_motion; @@ -1362,3 +1172,66 @@ void cursor_warp_to_workspace(struct sway_cursor *cursor, wlr_cursor_warp(cursor->cursor, NULL, x, y); } + +uint32_t get_mouse_bindsym(const char *name, char **error) { + if (strncasecmp(name, "button", strlen("button")) == 0) { + // Map to x11 mouse buttons + int number = name[strlen("button")] - '0'; + if (number < 1 || number > 9 || strlen(name) > strlen("button0")) { + *error = strdup("Only buttons 1-9 are supported. For other mouse " + "buttons, use the name of the event code."); + return 0; + } + static const uint32_t buttons[] = {BTN_LEFT, BTN_MIDDLE, BTN_RIGHT, + SWAY_SCROLL_UP, SWAY_SCROLL_DOWN, SWAY_SCROLL_LEFT, + SWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA}; + return buttons[number - 1]; + } else if (strncmp(name, "BTN_", strlen("BTN_")) == 0) { + // Get event code from name + int code = libevdev_event_code_from_name(EV_KEY, name); + if (code == -1) { + size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1; + *error = malloc(len); + if (*error) { + snprintf(*error, len, "Unknown event %s", name); + } + return 0; + } + return code; + } + return 0; +} + +uint32_t get_mouse_bindcode(const char *name, char **error) { + // Validate event code + errno = 0; + char *endptr; + int code = strtol(name, &endptr, 10); + if (endptr == name && code <= 0) { + *error = strdup("Button event code must be a positive integer."); + return 0; + } else if (errno == ERANGE) { + *error = strdup("Button event code out of range."); + return 0; + } + const char *event = libevdev_event_code_get_name(EV_KEY, code); + if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { + size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button", + code, event) + 1; + *error = malloc(len); + if (*error) { + snprintf(*error, len, "Event code %d (%s) is not a button", + code, event); + } + return 0; + } + return code; +} + +uint32_t get_mouse_button(const char *name, char **error) { + uint32_t button = get_mouse_bindsym(name, error); + if (!button && !*error) { + button = get_mouse_bindcode(name, error); + } + return button; +} diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 68445d68..d90803f6 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -1,4 +1,4 @@ -#define _XOPEN_SOURCE 700 +#define _POSIX_C_SOURCE 200809L #include <ctype.h> #include <float.h> #include <limits.h> @@ -49,7 +49,7 @@ char *input_device_get_identifier(struct wlr_input_device *device) { int vendor = device->vendor; int product = device->product; char *name = strdup(device->name); - name = strip_whitespace(name); + strip_whitespace(name); char *p = name; for (; *p; ++p) { @@ -82,11 +82,12 @@ static struct sway_input_device *input_sway_device_from_wlr( return NULL; } -static bool input_has_seat_configuration(void) { +static bool input_has_seat_fallback_configuration(void) { struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { struct seat_config *seat_config = seat_get_config(seat); - if (seat_config) { + if (seat_config && strcmp(seat_config->name, "*") != 0 + && seat_config->fallback != -1) { return true; } } @@ -94,6 +95,18 @@ static bool input_has_seat_configuration(void) { return false; } +void input_manager_verify_fallback_seat(void) { + struct sway_seat *seat = NULL; + if (!input_has_seat_fallback_configuration()) { + wlr_log(WLR_DEBUG, "no fallback seat config - creating default"); + seat = input_manager_get_default_seat(); + struct seat_config *sc = new_seat_config(seat->wlr_seat->name); + sc->fallback = true; + sc = store_seat_config(sc); + input_manager_apply_seat_config(sc); + } +} + static void input_manager_libinput_config_keyboard( struct sway_input_device *input_device) { struct wlr_input_device *wlr_device = input_device->wlr_device; @@ -116,6 +129,24 @@ static void input_manager_libinput_config_keyboard( } } +static void input_manager_libinput_reset_keyboard( + struct sway_input_device *input_device) { + struct wlr_input_device *wlr_device = input_device->wlr_device; + struct libinput_device *libinput_device; + + if (!wlr_input_device_is_libinput(wlr_device)) { + return; + } + + libinput_device = wlr_libinput_get_device_handle(wlr_device); + + uint32_t send_events = + libinput_device_config_send_events_get_default_mode(libinput_device); + wlr_log(WLR_DEBUG, "libinput_reset_keyboard(%s) send_events_set_mode(%d)", + input_device->identifier, send_events); + libinput_device_config_send_events_set_mode(libinput_device, send_events); +} + static void input_manager_libinput_config_touch( struct sway_input_device *input_device) { struct wlr_input_device *wlr_device = input_device->wlr_device; @@ -138,6 +169,24 @@ static void input_manager_libinput_config_touch( } } +static void input_manager_libinput_reset_touch( + struct sway_input_device *input_device) { + struct wlr_input_device *wlr_device = input_device->wlr_device; + struct libinput_device *libinput_device; + + if (!wlr_input_device_is_libinput(wlr_device)) { + return; + } + + libinput_device = wlr_libinput_get_device_handle(wlr_device); + + uint32_t send_events = + libinput_device_config_send_events_get_default_mode(libinput_device); + wlr_log(WLR_DEBUG, "libinput_reset_touch(%s) send_events_set_mode(%d)", + input_device->identifier, send_events); + libinput_device_config_send_events_set_mode(libinput_device, send_events); +} + static void input_manager_libinput_config_pointer( struct sway_input_device *input_device) { struct wlr_input_device *wlr_device = input_device->wlr_device; @@ -167,14 +216,14 @@ static void input_manager_libinput_config_pointer( if (ic->drag != INT_MIN) { wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_drag_enabled(%d)", - ic->identifier, ic->click_method); + ic->identifier, ic->drag); libinput_device_config_tap_set_drag_enabled(libinput_device, ic->drag); } if (ic->drag_lock != INT_MIN) { wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)", - ic->identifier, ic->click_method); + ic->identifier, ic->drag_lock); libinput_device_config_tap_set_drag_lock_enabled(libinput_device, ic->drag_lock); } @@ -235,12 +284,118 @@ static void input_manager_libinput_config_pointer( } if (ic->tap_button_map != INT_MIN) { wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_button_map(%d)", - ic->identifier, ic->tap); + ic->identifier, ic->tap_button_map); libinput_device_config_tap_set_button_map(libinput_device, ic->tap_button_map); } } +static void input_manager_libinput_reset_pointer( + struct sway_input_device *input_device) { + struct wlr_input_device *wlr_device = input_device->wlr_device; + + if (!wlr_input_device_is_libinput(wlr_device)) { + return; + } + + struct libinput_device *libinput_device = + wlr_libinput_get_device_handle(wlr_device); + + enum libinput_config_accel_profile accel_profile = + libinput_device_config_accel_get_default_profile(libinput_device); + wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) accel_set_profile(%d)", + input_device->identifier, accel_profile); + libinput_device_config_accel_set_profile(libinput_device, accel_profile); + + enum libinput_config_click_method click_method = + libinput_device_config_click_get_default_method(libinput_device); + wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) click_set_method(%d)", + input_device->identifier, click_method); + libinput_device_config_click_set_method(libinput_device, click_method); + + enum libinput_config_drag_state drag = + libinput_device_config_tap_get_default_drag_enabled(libinput_device); + wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) tap_set_drag_enabled(%d)", + input_device->identifier, drag); + libinput_device_config_tap_set_drag_enabled(libinput_device, drag); + + enum libinput_config_drag_lock_state drag_lock = + libinput_device_config_tap_get_default_drag_lock_enabled( + libinput_device); + wlr_log(WLR_DEBUG, + "libinput_reset_pointer(%s) tap_set_drag_lock_enabled(%d)", + input_device->identifier, drag_lock); + libinput_device_config_tap_set_drag_lock_enabled(libinput_device, + drag_lock); + + enum libinput_config_dwt_state dwt = + libinput_device_config_dwt_get_default_enabled(libinput_device); + wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) dwt_set_enabled(%d)", + input_device->identifier, dwt); + libinput_device_config_dwt_set_enabled(libinput_device, dwt); + + int left_handed = + libinput_device_config_left_handed_get_default(libinput_device); + wlr_log(WLR_DEBUG, + "libinput_reset_pointer(%s) left_handed_set_enabled(%d)", + input_device->identifier, left_handed); + libinput_device_config_left_handed_set(libinput_device, left_handed); + + enum libinput_config_middle_emulation_state middle_emulation = + libinput_device_config_middle_emulation_get_default_enabled( + libinput_device); + wlr_log(WLR_DEBUG, + "libinput_reset_pointer(%s) middle_emulation_set_enabled(%d)", + input_device->identifier, middle_emulation); + libinput_device_config_middle_emulation_set_enabled(libinput_device, + middle_emulation); + + int natural_scroll = + libinput_device_config_scroll_get_default_natural_scroll_enabled( + libinput_device); + wlr_log(WLR_DEBUG, + "libinput_reset_pointer(%s) natural_scroll_set_enabled(%d)", + input_device->identifier, natural_scroll); + libinput_device_config_scroll_set_natural_scroll_enabled( + libinput_device, natural_scroll); + + double pointer_accel = + libinput_device_config_accel_get_default_speed(libinput_device); + wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) accel_set_speed(%f)", + input_device->identifier, pointer_accel); + libinput_device_config_accel_set_speed(libinput_device, pointer_accel); + + uint32_t scroll_button = + libinput_device_config_scroll_get_default_button(libinput_device); + wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) scroll_set_button(%d)", + input_device->identifier, scroll_button); + libinput_device_config_scroll_set_button(libinput_device, scroll_button); + + enum libinput_config_scroll_method scroll_method = + libinput_device_config_scroll_get_default_method(libinput_device); + wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) scroll_set_method(%d)", + input_device->identifier, scroll_method); + libinput_device_config_scroll_set_method(libinput_device, scroll_method); + + uint32_t send_events = + libinput_device_config_send_events_get_default_mode(libinput_device); + wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) send_events_set_mode(%d)", + input_device->identifier, send_events); + libinput_device_config_send_events_set_mode(libinput_device, send_events); + + enum libinput_config_tap_state tap = + libinput_device_config_tap_get_default_enabled(libinput_device); + wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) tap_set_enabled(%d)", + input_device->identifier, tap); + libinput_device_config_tap_set_enabled(libinput_device, tap); + + enum libinput_config_tap_button_map tap_button_map = + libinput_device_config_tap_get_button_map(libinput_device); + wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) tap_set_button_map(%d)", + input_device->identifier, tap_button_map); + libinput_device_config_tap_set_button_map(libinput_device, tap_button_map); +} + static void handle_device_destroy(struct wl_listener *listener, void *data) { struct wlr_input_device *device = data; @@ -295,15 +450,10 @@ static void handle_new_input(struct wl_listener *listener, void *data) { wl_signal_add(&device->events.destroy, &input_device->device_destroy); input_device->device_destroy.notify = handle_device_destroy; - struct sway_seat *seat = NULL; - if (!input_has_seat_configuration()) { - wlr_log(WLR_DEBUG, "no seat configuration, using default seat"); - seat = input_manager_get_default_seat(); - seat_add_device(seat, input_device); - return; - } + input_manager_verify_fallback_seat(); bool added = false; + struct sway_seat *seat = NULL; wl_list_for_each(seat, &input->seats, link) { struct seat_config *seat_config = seat_get_config(seat); bool has_attachment = seat_config && @@ -458,15 +608,50 @@ void input_manager_apply_input_config(struct input_config *input_config) { } } -void input_manager_apply_seat_config(struct seat_config *seat_config) { - wlr_log(WLR_DEBUG, "applying new seat config for seat %s", - seat_config->name); - struct sway_seat *seat = input_manager_get_seat(seat_config->name); - if (!seat) { - return; +void input_manager_reset_input(struct sway_input_device *input_device) { + if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER || + input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { + input_manager_libinput_reset_pointer(input_device); + } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { + input_manager_libinput_reset_keyboard(input_device); + } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_TOUCH) { + input_manager_libinput_reset_touch(input_device); + } + + struct sway_seat *seat = NULL; + wl_list_for_each(seat, &server.input->seats, link) { + seat_reset_device(seat, input_device); } +} + +void input_manager_reset_all_inputs() { + struct sway_input_device *input_device = NULL; + wl_list_for_each(input_device, &server.input->devices, link) { + input_manager_reset_input(input_device); + } +} - seat_apply_config(seat, seat_config); + +void input_manager_apply_seat_config(struct seat_config *seat_config) { + wlr_log(WLR_DEBUG, "applying seat config for seat %s", seat_config->name); + if (strcmp(seat_config->name, "*") == 0) { + struct sway_seat *seat = NULL; + wl_list_for_each(seat, &server.input->seats, link) { + // Only apply the wildcard config directly if there is no seat + // specific config + struct seat_config *sc = seat_get_config(seat); + if (!sc) { + sc = seat_config; + } + seat_apply_config(seat, sc); + } + } else { + struct sway_seat *seat = input_manager_get_seat(seat_config->name); + if (!seat) { + return; + } + seat_apply_config(seat, seat_config); + } // for every device, try to add it to a seat and if no seat has it // attached, add it to the fallback seats. diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index c1b53237..46286410 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -295,14 +295,10 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { get_active_binding(&keyboard->state_keysyms_raw, config->current_mode->keysym_bindings, &binding, raw_modifiers, false, input_inhibited, device_identifier); - - if (binding) { - seat_execute_command(seat, binding); - handled = true; - } } - // Set up (or clear) keyboard repeat for a pressed binding + // Set up (or clear) keyboard repeat for a pressed binding. Since the + // binding may remove the keyboard, the timer needs to be updated first if (binding && wlr_device->keyboard->repeat_info.delay > 0) { keyboard->repeat_binding = binding; if (wl_event_source_timer_update(keyboard->key_repeat_source, @@ -316,6 +312,11 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { } } + if (binding) { + seat_execute_command(seat, binding); + handled = true; + } + // Compositor bindings if (!handled && event->state == WLR_KEY_PRESSED) { handled = keyboard_execute_compositor_binding( diff --git a/sway/input/seat.c b/sway/input/seat.c index 663c5140..a63999b6 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1,12 +1,7 @@ -#define _XOPEN_SOURCE 700 -#define _POSIX_C_SOURCE 199309L +#define _POSIX_C_SOURCE 200809L #include <assert.h> #include <errno.h> -#ifdef __linux__ #include <linux/input-event-codes.h> -#elif __FreeBSD__ -#include <dev/evdev/input-event-codes.h> -#endif #include <strings.h> #include <time.h> #include <wlr/types/wlr_cursor.h> @@ -175,6 +170,12 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { parent = node_get_parent(parent); } + if (next_focus->type == N_WORKSPACE && + !workspace_is_visible(next_focus->sway_workspace)) { + // Do not change focus to a non-visible workspace + return; + } + if (needs_new_focus) { // The structure change might have caused it to move up to the top of // the focus stack without sending focus notifications to the view @@ -307,7 +308,7 @@ static void handle_new_drag_icon(struct wl_listener *listener, void *data) { wl_list_insert(&root->drag_icons, &icon->link); drag_icon_update_position(icon); - seat_end_mouse_operation(seat); + seatop_abort(seat); } static void collect_focus_iter(struct sway_node *node, void *data) { @@ -387,6 +388,7 @@ static void seat_update_capabilities(struct sway_seat *seat) { caps |= WL_SEAT_CAPABILITY_POINTER; break; case WLR_INPUT_DEVICE_TABLET_PAD: + case WLR_INPUT_DEVICE_SWITCH: break; } } @@ -403,6 +405,14 @@ static void seat_update_capabilities(struct sway_seat *seat) { } } +static void seat_reset_input_config(struct sway_seat *seat, + struct sway_seat_device *sway_device) { + wlr_log(WLR_DEBUG, "Resetting output mapping for input device %s", + sway_device->input_device->identifier); + wlr_cursor_map_input_to_output(seat->cursor->cursor, + sway_device->input_device->wlr_device, NULL); +} + static void seat_apply_input_config(struct sway_seat *seat, struct sway_seat_device *sway_device) { const char *mapped_to_output = NULL; @@ -422,7 +432,13 @@ static void seat_apply_input_config(struct sway_seat *seat, if (mapped_to_output != NULL) { wlr_log(WLR_DEBUG, "Mapping input device %s to output %s", sway_device->input_device->identifier, mapped_to_output); - struct sway_output *output = output_by_name(mapped_to_output); + if (strcmp("*", mapped_to_output) == 0) { + wlr_cursor_map_input_to_output(seat->cursor->cursor, + sway_device->input_device->wlr_device, NULL); + wlr_log(WLR_DEBUG, "Reset output mapping"); + return; + } + struct sway_output *output = output_by_name_or_id(mapped_to_output); if (output) { wlr_cursor_map_input_to_output(seat->cursor->cursor, sway_device->input_device->wlr_device, output->wlr_output); @@ -508,6 +524,38 @@ void seat_configure_device(struct sway_seat *seat, case WLR_INPUT_DEVICE_TABLET_PAD: wlr_log(WLR_DEBUG, "TODO: configure tablet pad"); break; + case WLR_INPUT_DEVICE_SWITCH: + wlr_log(WLR_DEBUG, "TODO: configure switch device"); + break; + } +} + +void seat_reset_device(struct sway_seat *seat, + struct sway_input_device *input_device) { + struct sway_seat_device *seat_device = seat_get_device(seat, input_device); + if (!seat_device) { + return; + } + + switch (input_device->wlr_device->type) { + case WLR_INPUT_DEVICE_POINTER: + seat_reset_input_config(seat, seat_device); + break; + case WLR_INPUT_DEVICE_KEYBOARD: + sway_keyboard_configure(seat_device->keyboard); + break; + case WLR_INPUT_DEVICE_TOUCH: + seat_reset_input_config(seat, seat_device); + break; + case WLR_INPUT_DEVICE_TABLET_TOOL: + seat_reset_input_config(seat, seat_device); + break; + case WLR_INPUT_DEVICE_TABLET_PAD: + wlr_log(WLR_DEBUG, "TODO: reset tablet pad"); + break; + case WLR_INPUT_DEVICE_SWITCH: + wlr_log(WLR_DEBUG, "TODO: reset switch device"); + break; } } @@ -614,18 +662,6 @@ static int handle_urgent_timeout(void *data) { return 0; } -static void container_raise_floating(struct sway_container *con) { - // Bring container to front by putting it at the end of the floating list. - struct sway_container *floater = con; - while (floater->parent) { - floater = floater->parent; - } - if (container_is_floating(floater)) { - list_move_to_end(floater->workspace->floating, floater); - node_set_dirty(&floater->workspace->node); - } -} - static void set_workspace(struct sway_seat *seat, struct sway_workspace *new_ws) { if (seat->workspace == new_ws) { @@ -986,6 +1022,8 @@ void seat_apply_config(struct sway_seat *seat, wl_list_for_each(seat_device, &seat->devices, link) { seat_configure_device(seat, seat_device->input_device); } + + cursor_handle_activity(seat->cursor); } struct seat_config *seat_get_config(struct sway_seat *seat) { @@ -1000,174 +1038,16 @@ struct seat_config *seat_get_config(struct sway_seat *seat) { return NULL; } -void seat_begin_down(struct sway_seat *seat, struct sway_container *con, - uint32_t button, double sx, double sy) { - seat->operation = OP_DOWN; - seat->op_container = con; - 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 = sx; - seat->op_ref_con_ly = sy; - seat->op_moved = false; - - container_raise_floating(con); -} - -void seat_begin_move_floating(struct sway_seat *seat, - struct sway_container *con, uint32_t button) { - if (!seat->cursor) { - wlr_log(WLR_DEBUG, "Ignoring move request due to no cursor device"); - return; - } - seat->operation = OP_MOVE_FLOATING; - seat->op_container = con; - seat->op_button = button; - - container_raise_floating(con); - - cursor_set_image(seat->cursor, "grab", NULL); -} - -void seat_begin_move_tiling(struct sway_seat *seat, - struct sway_container *con, uint32_t button) { - seat->operation = OP_MOVE_TILING; - seat->op_container = con; - seat->op_button = button; - seat->op_target_node = NULL; - seat->op_target_edge = 0; - cursor_set_image(seat->cursor, "grab", NULL); -} - -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_FLOATING; - seat->op_container = con; - seat->op_resize_preserve_ratio = keyboard && - (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT); - seat->op_resize_edge = edge == WLR_EDGE_NONE ? - WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : 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; - - container_raise_floating(con); - - const char *image = edge == WLR_EDGE_NONE ? - "se-resize" : wlr_xcursor_get_resize_name(edge); - 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; -} - -static bool is_parallel(enum sway_container_layout layout, - enum wlr_edges edge) { - bool layout_is_horiz = layout == L_HORIZ || layout == L_TABBED; - bool edge_is_horiz = edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT; - return layout_is_horiz == edge_is_horiz; -} - -static void seat_end_move_tiling(struct sway_seat *seat) { - struct sway_container *con = seat->op_container; - struct sway_container *old_parent = con->parent; - struct sway_workspace *old_ws = con->workspace; - struct sway_node *target_node = seat->op_target_node; - struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ? - target_node->sway_workspace : target_node->sway_container->workspace; - enum wlr_edges edge = seat->op_target_edge; - int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT; - - container_detach(con); - - // Moving container into empty workspace - if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) { - workspace_add_tiling(new_ws, con); - } else if (target_node->type == N_CONTAINER) { - // Moving container before/after another - struct sway_container *target = target_node->sway_container; - enum sway_container_layout layout = container_parent_layout(target); - if (edge && !is_parallel(layout, edge)) { - enum sway_container_layout new_layout = edge == WLR_EDGE_TOP || - edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ; - container_split(target, new_layout); - } - container_add_sibling(target, con, after); - } else { - // Target is a workspace which requires splitting - enum sway_container_layout new_layout = edge == WLR_EDGE_TOP || - edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ; - workspace_split(new_ws, new_layout); - workspace_insert_tiling(new_ws, con, after); - } - - if (old_parent) { - container_reap_empty(old_parent); - } - - // This is a bit dirty, but we'll set the dimensions to that of a sibling. - // I don't think there's any other way to make it consistent without - // changing how we auto-size containers. - list_t *siblings = container_get_siblings(con); - if (siblings->length > 1) { - int index = list_find(siblings, con); - struct sway_container *sibling = index == 0 ? - siblings->items[1] : siblings->items[index - 1]; - con->width = sibling->width; - con->height = sibling->height; - } - - arrange_workspace(old_ws); - if (new_ws != old_ws) { - arrange_workspace(new_ws); - } -} - -void seat_end_mouse_operation(struct sway_seat *seat) { - enum sway_seat_operation operation = seat->operation; - if (seat->operation == OP_MOVE_FLOATING) { - // 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); - } else if (seat->operation == OP_MOVE_TILING && seat->op_target_node) { - seat_end_move_tiling(seat); - } - seat->operation = OP_NONE; - seat->op_container = NULL; - if (operation == OP_DOWN) { - // Set the cursor's previous coords to the x/y at the start of the - // operation, so the container change will be detected if using - // focus_follows_mouse and the cursor moved off the original container - // during the operation. - seat->cursor->previous.x = seat->op_ref_lx; - seat->cursor->previous.y = seat->op_ref_ly; - if (seat->op_moved) { - cursor_send_pointer_motion(seat->cursor, 0); +struct seat_config *seat_get_config_by_name(const char *name) { + struct seat_config *seat_config = NULL; + for (int i = 0; i < config->seat_configs->length; ++i ) { + seat_config = config->seat_configs->items[i]; + if (strcmp(name, seat_config->name) == 0) { + return seat_config; } - } else { - cursor_set_image(seat->cursor, "left_ptr", NULL); } + + return NULL; } void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, @@ -1197,4 +1077,49 @@ void seat_consider_warp_to_focus(struct sway_seat *seat) { } else { cursor_warp_to_workspace(seat->cursor, focus->sway_workspace); } + if (seat->cursor->hidden){ + cursor_unhide(seat->cursor); + wl_event_source_timer_update(seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); + } +} + +bool seat_doing_seatop(struct sway_seat *seat) { + return seat->seatop_impl != NULL; +} + +void seatop_unref(struct sway_seat *seat, struct sway_container *con) { + if (seat->seatop_impl && seat->seatop_impl->unref) { + seat->seatop_impl->unref(seat, con); + } +} + +void seatop_motion(struct sway_seat *seat, uint32_t time_msec) { + if (seat->seatop_impl && seat->seatop_impl->motion) { + seat->seatop_impl->motion(seat, time_msec); + } +} + +void seatop_finish(struct sway_seat *seat) { + if (seat->seatop_impl && seat->seatop_impl->finish) { + seat->seatop_impl->finish(seat); + } + free(seat->seatop_data); + seat->seatop_data = NULL; + seat->seatop_impl = NULL; +} + +void seatop_abort(struct sway_seat *seat) { + if (seat->seatop_impl && seat->seatop_impl->abort) { + seat->seatop_impl->abort(seat); + } + free(seat->seatop_data); + seat->seatop_data = NULL; + seat->seatop_impl = NULL; +} + +void seatop_render(struct sway_seat *seat, struct sway_output *output, + pixman_region32_t *damage) { + if (seat->seatop_impl && seat->seatop_impl->render) { + seat->seatop_impl->render(seat, output, damage); + } } diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c new file mode 100644 index 00000000..ad11c5ca --- /dev/null +++ b/sway/input/seatop_down.c @@ -0,0 +1,77 @@ +#define _POSIX_C_SOURCE 200809L +#include <wlr/types/wlr_cursor.h> +#include "sway/input/cursor.h" +#include "sway/input/seat.h" +#include "sway/tree/view.h" + +struct seatop_down_event { + struct sway_container *con; + double ref_lx, ref_ly; // cursor's x/y at start of op + double ref_con_lx, ref_con_ly; // container's x/y at start of op + bool moved; +}; + +static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { + struct seatop_down_event *e = seat->seatop_data; + struct sway_container *con = e->con; + if (seat_is_input_allowed(seat, con->view->surface)) { + double moved_x = seat->cursor->cursor->x - e->ref_lx; + double moved_y = seat->cursor->cursor->y - e->ref_ly; + double sx = e->ref_con_lx + moved_x; + double sy = e->ref_con_ly + moved_y; + wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy); + } + e->moved = true; +} + +static void handle_finish(struct sway_seat *seat) { + struct seatop_down_event *e = seat->seatop_data; + // Set the cursor's previous coords to the x/y at the start of the + // operation, so the container change will be detected if using + // focus_follows_mouse and the cursor moved off the original container + // during the operation. + seat->cursor->previous.x = e->ref_lx; + seat->cursor->previous.y = e->ref_ly; + if (e->moved) { + cursor_send_pointer_motion(seat->cursor, 0); + } +} + +static void handle_abort(struct sway_seat *seat) { + cursor_set_image(seat->cursor, "left_ptr", NULL); +} + +static void handle_unref(struct sway_seat *seat, struct sway_container *con) { + struct seatop_down_event *e = seat->seatop_data; + if (e->con == con) { + seatop_abort(seat); + } +} + +static const struct sway_seatop_impl seatop_impl = { + .motion = handle_motion, + .finish = handle_finish, + .abort = handle_abort, + .unref = handle_unref, +}; + +void seatop_begin_down(struct sway_seat *seat, + struct sway_container *con, uint32_t button, int sx, int sy) { + seatop_abort(seat); + + struct seatop_down_event *e = + calloc(1, sizeof(struct seatop_down_event)); + if (!e) { + return; + } + e->con = con; + e->ref_lx = seat->cursor->cursor->x; + e->ref_ly = seat->cursor->cursor->y; + e->ref_con_lx = sx; + e->ref_con_ly = sy; + e->moved = false; + + seat->seatop_impl = &seatop_impl; + seat->seatop_data = e; + seat->seatop_button = button; +} diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c new file mode 100644 index 00000000..08e3a5a4 --- /dev/null +++ b/sway/input/seatop_move_floating.c @@ -0,0 +1,65 @@ +#define _POSIX_C_SOURCE 200809L +#include <wlr/types/wlr_cursor.h> +#include "sway/desktop.h" +#include "sway/input/cursor.h" +#include "sway/input/seat.h" + +struct seatop_move_floating_event { + struct sway_container *con; +}; + +static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { + struct seatop_move_floating_event *e = seat->seatop_data; + desktop_damage_whole_container(e->con); + container_floating_translate(e->con, + seat->cursor->cursor->x - seat->cursor->previous.x, + seat->cursor->cursor->y - seat->cursor->previous.y); + desktop_damage_whole_container(e->con); +} + +static void handle_finish(struct sway_seat *seat) { + struct seatop_move_floating_event *e = seat->seatop_data; + + // We "move" the container to its own location + // so it discovers its output again. + container_floating_move_to(e->con, e->con->x, e->con->y); + cursor_set_image(seat->cursor, "left_ptr", NULL); +} + +static void handle_abort(struct sway_seat *seat) { + cursor_set_image(seat->cursor, "left_ptr", NULL); +} + +static void handle_unref(struct sway_seat *seat, struct sway_container *con) { + struct seatop_move_floating_event *e = seat->seatop_data; + if (e->con == con) { + seatop_abort(seat); + } +} + +static const struct sway_seatop_impl seatop_impl = { + .motion = handle_motion, + .finish = handle_finish, + .abort = handle_abort, + .unref = handle_unref, +}; + +void seatop_begin_move_floating(struct sway_seat *seat, + struct sway_container *con, uint32_t button) { + seatop_abort(seat); + + struct seatop_move_floating_event *e = + calloc(1, sizeof(struct seatop_move_floating_event)); + if (!e) { + return; + } + e->con = con; + + seat->seatop_impl = &seatop_impl; + seat->seatop_data = e; + seat->seatop_button = button; + + container_raise_floating(con); + + cursor_set_image(seat->cursor, "grab", NULL); +} diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c new file mode 100644 index 00000000..8b541f80 --- /dev/null +++ b/sway/input/seatop_move_tiling.c @@ -0,0 +1,335 @@ +#define _POSIX_C_SOURCE 200809L +#include <limits.h> +#include <wlr/types/wlr_cursor.h> +#include <wlr/util/edges.h> +#include "sway/desktop.h" +#include "sway/input/cursor.h" +#include "sway/input/seat.h" +#include "sway/output.h" +#include "sway/tree/arrange.h" +#include "sway/tree/node.h" +#include "sway/tree/view.h" +#include "sway/tree/workspace.h" + +// Thickness of the dropzone when dragging to the edge of a layout container +#define DROP_LAYOUT_BORDER 30 + +struct seatop_move_tiling_event { + struct sway_container *con; + struct sway_node *target_node; + enum wlr_edges target_edge; + struct wlr_box drop_box; + double ref_lx, ref_ly; // cursor's x/y at start of op + bool threshold_reached; +}; + +static void handle_render(struct sway_seat *seat, + struct sway_output *output, pixman_region32_t *damage) { + struct seatop_move_tiling_event *e = seat->seatop_data; + if (!e->threshold_reached) { + return; + } + if (e->target_node && node_get_output(e->target_node) == output) { + float color[4]; + memcpy(&color, config->border_colors.focused.indicator, + sizeof(float) * 4); + premultiply_alpha(color, 0.5); + struct wlr_box box; + memcpy(&box, &e->drop_box, sizeof(struct wlr_box)); + scale_box(&box, output->wlr_output->scale); + render_rect(output->wlr_output, damage, &box, color); + } +} + +static void handle_motion_prethreshold(struct sway_seat *seat) { + struct seatop_move_tiling_event *e = seat->seatop_data; + double cx = seat->cursor->cursor->x; + double cy = seat->cursor->cursor->y; + double sx = e->ref_lx; + double sy = e->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) { + e->threshold_reached = true; + cursor_set_image(seat->cursor, "grab", NULL); + } +} + +static void resize_box(struct wlr_box *box, enum wlr_edges edge, + int thickness) { + switch (edge) { + case WLR_EDGE_TOP: + box->height = thickness; + break; + case WLR_EDGE_LEFT: + box->width = thickness; + break; + case WLR_EDGE_RIGHT: + box->x = box->x + box->width - thickness; + box->width = thickness; + break; + case WLR_EDGE_BOTTOM: + box->y = box->y + box->height - thickness; + box->height = thickness; + break; + case WLR_EDGE_NONE: + box->x += thickness; + box->y += thickness; + box->width -= thickness * 2; + box->height -= thickness * 2; + break; + } +} + +static void handle_motion_postthreshold(struct sway_seat *seat) { + struct seatop_move_tiling_event *e = seat->seatop_data; + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_cursor *cursor = seat->cursor; + struct sway_node *node = node_at_coords(seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + // Damage the old location + desktop_damage_box(&e->drop_box); + + if (!node) { + // Eg. hovered over a layer surface such as swaybar + e->target_node = NULL; + e->target_edge = WLR_EDGE_NONE; + return; + } + + if (node->type == N_WORKSPACE) { + // Emtpy workspace + e->target_node = node; + e->target_edge = WLR_EDGE_NONE; + workspace_get_box(node->sway_workspace, &e->drop_box); + desktop_damage_box(&e->drop_box); + return; + } + + // Deny moving within own workspace if this is the only child + struct sway_container *con = node->sway_container; + if (workspace_num_tiling_views(e->con->workspace) == 1 && + con->workspace == e->con->workspace) { + e->target_node = NULL; + e->target_edge = WLR_EDGE_NONE; + return; + } + + // Traverse the ancestors, trying to find a layout container perpendicular + // to the edge. Eg. close to the top or bottom of a horiz layout. + while (con) { + enum wlr_edges edge = WLR_EDGE_NONE; + enum sway_container_layout layout = container_parent_layout(con); + struct wlr_box parent; + con->parent ? container_get_box(con->parent, &parent) : + workspace_get_box(con->workspace, &parent); + if (layout == L_HORIZ || layout == L_TABBED) { + if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) { + edge = WLR_EDGE_TOP; + } else if (cursor->cursor->y > parent.y + parent.height + - DROP_LAYOUT_BORDER) { + edge = WLR_EDGE_BOTTOM; + } + } else if (layout == L_VERT || layout == L_STACKED) { + if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) { + edge = WLR_EDGE_LEFT; + } else if (cursor->cursor->x > parent.x + parent.width + - DROP_LAYOUT_BORDER) { + edge = WLR_EDGE_RIGHT; + } + } + if (edge) { + e->target_node = node_get_parent(&con->node); + e->target_edge = edge; + node_get_box(e->target_node, &e->drop_box); + resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER); + desktop_damage_box(&e->drop_box); + return; + } + con = con->parent; + } + + // Use the hovered view - but we must be over the actual surface + con = node->sway_container; + if (!con->view->surface || node == &e->con->node) { + e->target_node = NULL; + e->target_edge = WLR_EDGE_NONE; + return; + } + + // Find the closest edge + size_t thickness = fmin(con->content_width, con->content_height) * 0.3; + size_t closest_dist = INT_MAX; + size_t dist; + e->target_edge = WLR_EDGE_NONE; + if ((dist = cursor->cursor->y - con->y) < closest_dist) { + closest_dist = dist; + e->target_edge = WLR_EDGE_TOP; + } + if ((dist = cursor->cursor->x - con->x) < closest_dist) { + closest_dist = dist; + e->target_edge = WLR_EDGE_LEFT; + } + if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) { + closest_dist = dist; + e->target_edge = WLR_EDGE_RIGHT; + } + if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) { + closest_dist = dist; + e->target_edge = WLR_EDGE_BOTTOM; + } + + if (closest_dist > thickness) { + e->target_edge = WLR_EDGE_NONE; + } + + e->target_node = node; + e->drop_box.x = con->content_x; + e->drop_box.y = con->content_y; + e->drop_box.width = con->content_width; + e->drop_box.height = con->content_height; + resize_box(&e->drop_box, e->target_edge, thickness); + desktop_damage_box(&e->drop_box); +} + +static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { + struct seatop_move_tiling_event *e = seat->seatop_data; + if (e->threshold_reached) { + handle_motion_postthreshold(seat); + } else { + handle_motion_prethreshold(seat); + } +} + +static void handle_abort(struct sway_seat *seat) { + cursor_set_image(seat->cursor, "left_ptr", NULL); +} + +static bool is_parallel(enum sway_container_layout layout, + enum wlr_edges edge) { + bool layout_is_horiz = layout == L_HORIZ || layout == L_TABBED; + bool edge_is_horiz = edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT; + return layout_is_horiz == edge_is_horiz; +} + +static void handle_finish(struct sway_seat *seat) { + struct seatop_move_tiling_event *e = seat->seatop_data; + + if (!e->target_node) { + handle_abort(seat); + return; + } + + struct sway_container *con = e->con; + struct sway_container *old_parent = con->parent; + struct sway_workspace *old_ws = con->workspace; + struct sway_node *target_node = e->target_node; + struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ? + target_node->sway_workspace : target_node->sway_container->workspace; + enum wlr_edges edge = e->target_edge; + int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT; + + container_detach(con); + + // Moving container into empty workspace + if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) { + workspace_add_tiling(new_ws, con); + } else if (target_node->type == N_CONTAINER) { + // Moving container before/after another + struct sway_container *target = target_node->sway_container; + enum sway_container_layout layout = container_parent_layout(target); + if (edge && !is_parallel(layout, edge)) { + enum sway_container_layout new_layout = edge == WLR_EDGE_TOP || + edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ; + container_split(target, new_layout); + } + container_add_sibling(target, con, after); + } else { + // Target is a workspace which requires splitting + enum sway_container_layout new_layout = edge == WLR_EDGE_TOP || + edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ; + workspace_split(new_ws, new_layout); + workspace_insert_tiling(new_ws, con, after); + } + + if (old_parent) { + container_reap_empty(old_parent); + } + + // This is a bit dirty, but we'll set the dimensions to that of a sibling. + // I don't think there's any other way to make it consistent without + // changing how we auto-size containers. + list_t *siblings = container_get_siblings(con); + if (siblings->length > 1) { + int index = list_find(siblings, con); + struct sway_container *sibling = index == 0 ? + siblings->items[1] : siblings->items[index - 1]; + con->width = sibling->width; + con->height = sibling->height; + } + + arrange_workspace(old_ws); + if (new_ws != old_ws) { + arrange_workspace(new_ws); + } + + cursor_set_image(seat->cursor, "left_ptr", NULL); +} + +static void handle_unref(struct sway_seat *seat, struct sway_container *con) { + struct seatop_move_tiling_event *e = seat->seatop_data; + if (e->target_node == &con->node) { // Drop target + e->target_node = NULL; + } + if (e->con == con) { // The container being moved + seatop_abort(seat); + } +} + +static const struct sway_seatop_impl seatop_impl = { + .motion = handle_motion, + .finish = handle_finish, + .abort = handle_abort, + .unref = handle_unref, + .render = handle_render, +}; + +void seatop_begin_move_tiling_threshold(struct sway_seat *seat, + struct sway_container *con, uint32_t button) { + seatop_abort(seat); + + struct seatop_move_tiling_event *e = + calloc(1, sizeof(struct seatop_move_tiling_event)); + if (!e) { + return; + } + e->con = con; + e->ref_lx = seat->cursor->cursor->x; + e->ref_ly = seat->cursor->cursor->y; + + seat->seatop_impl = &seatop_impl; + seat->seatop_data = e; + seat->seatop_button = button; + + container_raise_floating(con); +} + +void seatop_begin_move_tiling(struct sway_seat *seat, + struct sway_container *con, uint32_t button) { + seatop_begin_move_tiling_threshold(seat, con, button); + struct seatop_move_tiling_event *e = seat->seatop_data; + if (e) { + e->threshold_reached = true; + cursor_set_image(seat->cursor, "grab", NULL); + } +} diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c new file mode 100644 index 00000000..12851b40 --- /dev/null +++ b/sway/input/seatop_resize_floating.c @@ -0,0 +1,199 @@ +#define _POSIX_C_SOURCE 200809L +#include <limits.h> +#include <wlr/types/wlr_cursor.h> +#include <wlr/types/wlr_xcursor_manager.h> +#include "sway/input/cursor.h" +#include "sway/input/seat.h" +#include "sway/tree/arrange.h" +#include "sway/tree/view.h" +#include "sway/tree/workspace.h" + +struct seatop_resize_floating_event { + struct sway_container *con; + enum wlr_edges edge; + bool preserve_ratio; + double ref_lx, ref_ly; // cursor's x/y at start of op + double ref_width, ref_height; // container's size at start of op + double ref_con_lx, ref_con_ly; // container's x/y at start of op +}; + +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 + *min_width = 0; + } else if (config->floating_minimum_width == 0) { // automatic + *min_width = 75; + } else { + *min_width = config->floating_minimum_width; + } + + if (config->floating_minimum_height == -1) { // no minimum + *min_height = 0; + } else if (config->floating_minimum_height == 0) { // automatic + *min_height = 50; + } else { + *min_height = config->floating_minimum_height; + } + + if (config->floating_maximum_width == -1) { // no maximum + *max_width = INT_MAX; + } else if (config->floating_maximum_width == 0) { // automatic + *max_width = con->workspace->width; + } else { + *max_width = config->floating_maximum_width; + } + + if (config->floating_maximum_height == -1) { // no maximum + *max_height = INT_MAX; + } else if (config->floating_maximum_height == 0) { // automatic + *max_height = con->workspace->height; + } else { + *max_height = config->floating_maximum_height; + } +} + +static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { + struct seatop_resize_floating_event *e = seat->seatop_data; + struct sway_container *con = e->con; + enum wlr_edges edge = e->edge; + struct sway_cursor *cursor = seat->cursor; + + // The amount the mouse has moved since the start of the resize operation + // Positive is down/right + double mouse_move_x = cursor->cursor->x - e->ref_lx; + double mouse_move_y = cursor->cursor->y - e->ref_ly; + + if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) { + mouse_move_x = 0; + } + if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) { + mouse_move_y = 0; + } + + double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x; + double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y; + + if (e->preserve_ratio) { + double x_multiplier = grow_width / e->ref_width; + double y_multiplier = grow_height / e->ref_height; + double max_multiplier = fmax(x_multiplier, y_multiplier); + grow_width = e->ref_width * max_multiplier; + grow_height = e->ref_height * max_multiplier; + } + + // Determine new width/height, and accommodate for floating min/max values + double width = e->ref_width + grow_width; + double height = e->ref_height + grow_height; + int min_width, max_width, min_height, max_height; + calculate_floating_constraints(con, &min_width, &max_width, + &min_height, &max_height); + width = fmax(min_width, fmin(width, max_width)); + height = fmax(min_height, fmin(height, max_height)); + + // Apply the view's min/max size + if (con->view) { + double view_min_width, view_max_width, view_min_height, view_max_height; + view_get_constraints(con->view, &view_min_width, &view_max_width, + &view_min_height, &view_max_height); + width = fmax(view_min_width, fmin(width, view_max_width)); + height = fmax(view_min_height, fmin(height, view_max_height)); + } + + // Recalculate these, in case we hit a min/max limit + grow_width = width - e->ref_width; + grow_height = height - e->ref_height; + + // Determine grow x/y values - these are relative to the container's x/y at + // the start of the resize operation. + double grow_x = 0, grow_y = 0; + if (edge & WLR_EDGE_LEFT) { + grow_x = -grow_width; + } else if (edge & WLR_EDGE_RIGHT) { + grow_x = 0; + } else { + grow_x = -grow_width / 2; + } + if (edge & WLR_EDGE_TOP) { + grow_y = -grow_height; + } else if (edge & WLR_EDGE_BOTTOM) { + grow_y = 0; + } else { + grow_y = -grow_height / 2; + } + + // Determine the amounts we need to bump everything relative to the current + // size. + int relative_grow_width = width - con->width; + int relative_grow_height = height - con->height; + int relative_grow_x = (e->ref_con_lx + grow_x) - con->x; + int relative_grow_y = (e->ref_con_ly + grow_y) - con->y; + + // Actually resize stuff + con->x += relative_grow_x; + con->y += relative_grow_y; + con->width += relative_grow_width; + con->height += relative_grow_height; + + con->content_x += relative_grow_x; + con->content_y += relative_grow_y; + con->content_width += relative_grow_width; + con->content_height += relative_grow_height; + + arrange_container(con); +} + +static void handle_finish(struct sway_seat *seat) { + cursor_set_image(seat->cursor, "left_ptr", NULL); +} + +static void handle_abort(struct sway_seat *seat) { + cursor_set_image(seat->cursor, "left_ptr", NULL); +} + +static void handle_unref(struct sway_seat *seat, struct sway_container *con) { + struct seatop_resize_floating_event *e = seat->seatop_data; + if (e->con == con) { + seatop_abort(seat); + } +} + +static const struct sway_seatop_impl seatop_impl = { + .motion = handle_motion, + .finish = handle_finish, + .abort = handle_abort, + .unref = handle_unref, +}; + +void seatop_begin_resize_floating(struct sway_seat *seat, + struct sway_container *con, uint32_t button, enum wlr_edges edge) { + seatop_abort(seat); + + struct seatop_resize_floating_event *e = + calloc(1, sizeof(struct seatop_resize_floating_event)); + if (!e) { + return; + } + e->con = con; + + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + e->preserve_ratio = keyboard && + (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT); + + e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge; + e->ref_lx = seat->cursor->cursor->x; + e->ref_ly = seat->cursor->cursor->y; + e->ref_con_lx = con->x; + e->ref_con_ly = con->y; + e->ref_width = con->width; + e->ref_height = con->height; + + seat->seatop_impl = &seatop_impl; + seat->seatop_data = e; + seat->seatop_button = button; + + container_raise_floating(con); + + const char *image = edge == WLR_EDGE_NONE ? + "se-resize" : wlr_xcursor_get_resize_name(edge); + cursor_set_image(seat->cursor, image, NULL); +} diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c new file mode 100644 index 00000000..30431f04 --- /dev/null +++ b/sway/input/seatop_resize_tiling.c @@ -0,0 +1,92 @@ +#define _POSIX_C_SOURCE 200809L +#include <wlr/types/wlr_cursor.h> +#include "sway/commands.h" +#include "sway/input/cursor.h" +#include "sway/input/seat.h" + +struct seatop_resize_tiling_event { + struct sway_container *con; + enum wlr_edges edge; + double ref_lx, ref_ly; // cursor's x/y at start of op + double ref_width, ref_height; // container's size at start of op + double ref_con_lx, ref_con_ly; // container's x/y at start of op +}; + +static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { + struct seatop_resize_tiling_event *e = seat->seatop_data; + int amount_x = 0; + int amount_y = 0; + int moved_x = seat->cursor->cursor->x - e->ref_lx; + int moved_y = seat->cursor->cursor->y - e->ref_ly; + enum wlr_edges edge_x = WLR_EDGE_NONE; + enum wlr_edges edge_y = WLR_EDGE_NONE; + struct sway_container *con = e->con; + + if (e->edge & WLR_EDGE_TOP) { + amount_y = (e->ref_height - moved_y) - con->height; + edge_y = WLR_EDGE_TOP; + } else if (e->edge & WLR_EDGE_BOTTOM) { + amount_y = (e->ref_height + moved_y) - con->height; + edge_y = WLR_EDGE_BOTTOM; + } + if (e->edge & WLR_EDGE_LEFT) { + amount_x = (e->ref_width - moved_x) - con->width; + edge_x = WLR_EDGE_LEFT; + } else if (e->edge & WLR_EDGE_RIGHT) { + amount_x = (e->ref_width + moved_x) - con->width; + edge_x = WLR_EDGE_RIGHT; + } + + if (amount_x != 0) { + container_resize_tiled(e->con, edge_x, amount_x); + } + if (amount_y != 0) { + container_resize_tiled(e->con, edge_y, amount_y); + } +} + +static void handle_finish(struct sway_seat *seat) { + cursor_set_image(seat->cursor, "left_ptr", NULL); +} + +static void handle_abort(struct sway_seat *seat) { + cursor_set_image(seat->cursor, "left_ptr", NULL); +} + +static void handle_unref(struct sway_seat *seat, struct sway_container *con) { + struct seatop_resize_tiling_event *e = seat->seatop_data; + if (e->con == con) { + seatop_abort(seat); + } +} + +static const struct sway_seatop_impl seatop_impl = { + .motion = handle_motion, + .finish = handle_finish, + .abort = handle_abort, + .unref = handle_unref, +}; + +void seatop_begin_resize_tiling(struct sway_seat *seat, + struct sway_container *con, uint32_t button, enum wlr_edges edge) { + seatop_abort(seat); + + struct seatop_resize_tiling_event *e = + calloc(1, sizeof(struct seatop_resize_tiling_event)); + if (!e) { + return; + } + e->con = con; + e->edge = edge; + + e->ref_lx = seat->cursor->cursor->x; + e->ref_ly = seat->cursor->cursor->y; + e->ref_con_lx = con->x; + e->ref_con_ly = con->y; + e->ref_width = con->width; + e->ref_height = con->height; + + seat->seatop_impl = &seatop_impl; + seat->seatop_data = e; + seat->seatop_button = button; +} |