aboutsummaryrefslogtreecommitdiffstats
path: root/sway/input/cursor.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/input/cursor.c')
-rw-r--r--sway/input/cursor.c295
1 files changed, 275 insertions, 20 deletions
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 944e35aa..65d04cac 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -5,14 +5,19 @@
5#elif __FreeBSD__ 5#elif __FreeBSD__
6#include <dev/evdev/input-event-codes.h> 6#include <dev/evdev/input-event-codes.h>
7#endif 7#endif
8#include <limits.h>
8#include <wlr/types/wlr_cursor.h> 9#include <wlr/types/wlr_cursor.h>
9#include <wlr/types/wlr_xcursor_manager.h> 10#include <wlr/types/wlr_xcursor_manager.h>
10#include <wlr/types/wlr_idle.h> 11#include <wlr/types/wlr_idle.h>
11#include "list.h" 12#include "list.h"
12#include "log.h" 13#include "log.h"
14#include "sway/desktop.h"
15#include "sway/desktop/transaction.h"
13#include "sway/input/cursor.h" 16#include "sway/input/cursor.h"
17#include "sway/input/keyboard.h"
14#include "sway/layers.h" 18#include "sway/layers.h"
15#include "sway/output.h" 19#include "sway/output.h"
20#include "sway/tree/arrange.h"
16#include "sway/tree/view.h" 21#include "sway/tree/view.h"
17#include "sway/tree/workspace.h" 22#include "sway/tree/workspace.h"
18#include "wlr-layer-shell-unstable-v1-protocol.h" 23#include "wlr-layer-shell-unstable-v1-protocol.h"
@@ -126,7 +131,7 @@ static struct sway_container *container_at_coords(
126 return ws; 131 return ws;
127 } 132 }
128 133
129 c = seat_get_focus_inactive(seat, output->swayc); 134 c = seat_get_active_child(seat, output->swayc);
130 if (c) { 135 if (c) {
131 return c; 136 return c;
132 } 137 }
@@ -138,6 +143,171 @@ static struct sway_container *container_at_coords(
138 return output->swayc; 143 return output->swayc;
139} 144}
140 145
146static enum wlr_edges find_resize_edge(struct sway_container *cont,
147 struct sway_cursor *cursor) {
148 if (cont->type != C_VIEW) {
149 return WLR_EDGE_NONE;
150 }
151 struct sway_view *view = cont->sway_view;
152 if (view->border == B_NONE || !view->border_thickness || view->using_csd) {
153 return WLR_EDGE_NONE;
154 }
155
156 enum wlr_edges edge = 0;
157 if (cursor->cursor->x < cont->x + view->border_thickness) {
158 edge |= WLR_EDGE_LEFT;
159 }
160 if (cursor->cursor->y < cont->y + view->border_thickness) {
161 edge |= WLR_EDGE_TOP;
162 }
163 if (cursor->cursor->x >= cont->x + cont->width - view->border_thickness) {
164 edge |= WLR_EDGE_RIGHT;
165 }
166 if (cursor->cursor->y >= cont->y + cont->height - view->border_thickness) {
167 edge |= WLR_EDGE_BOTTOM;
168 }
169 return edge;
170}
171
172static void handle_move_motion(struct sway_seat *seat,
173 struct sway_cursor *cursor) {
174 struct sway_container *con = seat->op_container;
175 desktop_damage_whole_container(con);
176 container_floating_translate(con,
177 cursor->cursor->x - cursor->previous.x,
178 cursor->cursor->y - cursor->previous.y);
179 desktop_damage_whole_container(con);
180}
181
182static void calculate_floating_constraints(struct sway_container *con,
183 int *min_width, int *max_width, int *min_height, int *max_height) {
184 if (config->floating_minimum_width == -1) { // no minimum
185 *min_width = 0;
186 } else if (config->floating_minimum_width == 0) { // automatic
187 *min_width = 75;
188 } else {
189 *min_width = config->floating_minimum_width;
190 }
191
192 if (config->floating_minimum_height == -1) { // no minimum
193 *min_height = 0;
194 } else if (config->floating_minimum_height == 0) { // automatic
195 *min_height = 50;
196 } else {
197 *min_height = config->floating_minimum_height;
198 }
199
200 if (config->floating_maximum_width == -1) { // no maximum
201 *max_width = INT_MAX;
202 } else if (config->floating_maximum_width == 0) { // automatic
203 struct sway_container *ws = container_parent(con, C_WORKSPACE);
204 *max_width = ws->width;
205 } else {
206 *max_width = config->floating_maximum_width;
207 }
208
209 if (config->floating_maximum_height == -1) { // no maximum
210 *max_height = INT_MAX;
211 } else if (config->floating_maximum_height == 0) { // automatic
212 struct sway_container *ws = container_parent(con, C_WORKSPACE);
213 *max_height = ws->height;
214 } else {
215 *max_height = config->floating_maximum_height;
216 }
217}
218
219static void handle_resize_motion(struct sway_seat *seat,
220 struct sway_cursor *cursor) {
221 struct sway_container *con = seat->op_container;
222 enum wlr_edges edge = seat->op_resize_edge;
223
224 // The amount the mouse has moved since the start of the resize operation
225 // Positive is down/right
226 double mouse_move_x = cursor->cursor->x - seat->op_ref_lx;
227 double mouse_move_y = cursor->cursor->y - seat->op_ref_ly;
228
229 if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) {
230 mouse_move_x = 0;
231 }
232 if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) {
233 mouse_move_y = 0;
234 }
235
236 double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x;
237 double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y;
238
239 if (seat->op_resize_preserve_ratio) {
240 double x_multiplier = grow_width / seat->op_ref_width;
241 double y_multiplier = grow_height / seat->op_ref_height;
242 double max_multiplier = fmax(x_multiplier, y_multiplier);
243 grow_width = seat->op_ref_width * max_multiplier;
244 grow_height = seat->op_ref_height * max_multiplier;
245 }
246
247 // Determine new width/height, and accommodate for floating min/max values
248 double width = seat->op_ref_width + grow_width;
249 double height = seat->op_ref_height + grow_height;
250 int min_width, max_width, min_height, max_height;
251 calculate_floating_constraints(con, &min_width, &max_width,
252 &min_height, &max_height);
253 width = fmax(min_width, fmin(width, max_width));
254 height = fmax(min_height, fmin(height, max_height));
255
256 // Apply the view's min/max size
257 if (con->type == C_VIEW) {
258 double view_min_width, view_max_width, view_min_height, view_max_height;
259 view_get_constraints(con->sway_view, &view_min_width, &view_max_width,
260 &view_min_height, &view_max_height);
261 width = fmax(view_min_width, fmin(width, view_max_width));
262 height = fmax(view_min_height, fmin(height, view_max_height));
263 }
264
265 // Recalculate these, in case we hit a min/max limit
266 grow_width = width - seat->op_ref_width;
267 grow_height = height - seat->op_ref_height;
268
269 // Determine grow x/y values - these are relative to the container's x/y at
270 // the start of the resize operation.
271 double grow_x = 0, grow_y = 0;
272 if (edge & WLR_EDGE_LEFT) {
273 grow_x = -grow_width;
274 } else if (edge & WLR_EDGE_RIGHT) {
275 grow_x = 0;
276 } else {
277 grow_x = -grow_width / 2;
278 }
279 if (edge & WLR_EDGE_TOP) {
280 grow_y = -grow_height;
281 } else if (edge & WLR_EDGE_BOTTOM) {
282 grow_y = 0;
283 } else {
284 grow_y = -grow_height / 2;
285 }
286
287 // Determine the amounts we need to bump everything relative to the current
288 // size.
289 int relative_grow_width = width - con->width;
290 int relative_grow_height = height - con->height;
291 int relative_grow_x = (seat->op_ref_con_lx + grow_x) - con->x;
292 int relative_grow_y = (seat->op_ref_con_ly + grow_y) - con->y;
293
294 // Actually resize stuff
295 con->x += relative_grow_x;
296 con->y += relative_grow_y;
297 con->width += relative_grow_width;
298 con->height += relative_grow_height;
299
300 if (con->type == C_VIEW) {
301 struct sway_view *view = con->sway_view;
302 view->x += relative_grow_x;
303 view->y += relative_grow_y;
304 view->width += relative_grow_width;
305 view->height += relative_grow_height;
306 }
307
308 arrange_windows(con);
309}
310
141void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, 311void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
142 bool allow_refocusing) { 312 bool allow_refocusing) {
143 if (time_msec == 0) { 313 if (time_msec == 0) {
@@ -145,6 +315,18 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
145 } 315 }
146 316
147 struct sway_seat *seat = cursor->seat; 317 struct sway_seat *seat = cursor->seat;
318
319 if (seat->operation != OP_NONE) {
320 if (seat->operation == OP_MOVE) {
321 handle_move_motion(seat, cursor);
322 } else {
323 handle_resize_motion(seat, cursor);
324 }
325 cursor->previous.x = cursor->cursor->x;
326 cursor->previous.y = cursor->cursor->y;
327 return;
328 }
329
148 struct wlr_seat *wlr_seat = seat->wlr_seat; 330 struct wlr_seat *wlr_seat = seat->wlr_seat;
149 struct wlr_surface *surface = NULL; 331 struct wlr_surface *surface = NULL;
150 double sx, sy; 332 double sx, sy;
@@ -193,15 +375,21 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
193 } 375 }
194 } 376 }
195 377
196 // reset cursor if switching between clients 378 // Handle cursor image
197 struct wl_client *client = NULL; 379 if (surface) {
198 if (surface != NULL) { 380 // Reset cursor if switching between clients
199 client = wl_resource_get_client(surface->resource); 381 struct wl_client *client = wl_resource_get_client(surface->resource);
200 } 382 if (client != cursor->image_client) {
201 if (client != cursor->image_client) { 383 cursor_set_image(cursor, "left_ptr", client);
202 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, 384 }
203 "left_ptr", cursor->cursor); 385 } else if (c && container_is_floating(c)) {
204 cursor->image_client = client; 386 // Try a floating container's resize edge
387 enum wlr_edges edge = find_resize_edge(c, cursor);
388 const char *image = edge == WLR_EDGE_NONE ?
389 "left_ptr" : wlr_xcursor_get_resize_name(edge);
390 cursor_set_image(cursor, image, NULL);
391 } else {
392 cursor_set_image(cursor, "left_ptr", NULL);
205 } 393 }
206 394
207 // send pointer enter/leave 395 // send pointer enter/leave
@@ -228,6 +416,7 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) {
228 wlr_cursor_move(cursor->cursor, event->device, 416 wlr_cursor_move(cursor->cursor, event->device,
229 event->delta_x, event->delta_y); 417 event->delta_x, event->delta_y);
230 cursor_send_pointer_motion(cursor, event->time_msec, true); 418 cursor_send_pointer_motion(cursor, event->time_msec, true);
419 transaction_commit_dirty();
231} 420}
232 421
233static void handle_cursor_motion_absolute( 422static void handle_cursor_motion_absolute(
@@ -238,10 +427,56 @@ static void handle_cursor_motion_absolute(
238 struct wlr_event_pointer_motion_absolute *event = data; 427 struct wlr_event_pointer_motion_absolute *event = data;
239 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); 428 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y);
240 cursor_send_pointer_motion(cursor, event->time_msec, true); 429 cursor_send_pointer_motion(cursor, event->time_msec, true);
430 transaction_commit_dirty();
431}
432
433static void dispatch_cursor_button_floating(struct sway_cursor *cursor,
434 uint32_t time_msec, uint32_t button, enum wlr_button_state state,
435 struct wlr_surface *surface, double sx, double sy,
436 struct sway_container *cont) {
437 struct sway_seat *seat = cursor->seat;
438
439 // Deny moving or resizing a fullscreen view
440 if (cont->type == C_VIEW && cont->sway_view->is_fullscreen) {
441 seat_pointer_notify_button(seat, time_msec, button, state);
442 return;
443 }
444
445 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
446 bool mod_pressed = keyboard &&
447 (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod);
448 enum wlr_edges edge = find_resize_edge(cont, cursor);
449 bool over_title = edge == WLR_EDGE_NONE && !surface;
450
451 // Check for beginning move
452 if (button == BTN_LEFT && state == WLR_BUTTON_PRESSED &&
453 (mod_pressed || over_title)) {
454 seat_begin_move(seat, cont, BTN_LEFT);
455 return;
456 }
457
458 // Check for beginning resize
459 bool resizing_via_border = button == BTN_LEFT && edge != WLR_EDGE_NONE;
460 bool resizing_via_mod = button == BTN_RIGHT && mod_pressed;
461 if ((resizing_via_border || resizing_via_mod) &&
462 state == WLR_BUTTON_PRESSED) {
463 seat_begin_resize(seat, cont, button, edge);
464 return;
465 }
466
467 // Send event to surface
468 seat_set_focus(seat, cont);
469 seat_pointer_notify_button(seat, time_msec, button, state);
241} 470}
242 471
243void dispatch_cursor_button(struct sway_cursor *cursor, 472void dispatch_cursor_button(struct sway_cursor *cursor,
244 uint32_t time_msec, uint32_t button, enum wlr_button_state state) { 473 uint32_t time_msec, uint32_t button, enum wlr_button_state state) {
474 if (cursor->seat->operation != OP_NONE &&
475 button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) {
476 seat_end_mouse_operation(cursor->seat);
477 seat_pointer_notify_button(cursor->seat, time_msec, button, state);
478 return;
479 }
245 if (time_msec == 0) { 480 if (time_msec == 0) {
246 time_msec = get_current_time_msec(); 481 time_msec = get_current_time_msec();
247 } 482 }
@@ -255,14 +490,16 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
255 wlr_layer_surface_from_wlr_surface(surface); 490 wlr_layer_surface_from_wlr_surface(surface);
256 if (layer->current.keyboard_interactive) { 491 if (layer->current.keyboard_interactive) {
257 seat_set_focus_layer(cursor->seat, layer); 492 seat_set_focus_layer(cursor->seat, layer);
258 return;
259 } 493 }
260 } 494 seat_pointer_notify_button(cursor->seat, time_msec, button, state);
261 // Avoid moving keyboard focus from a surface that accepts it to one 495 } else if (cont && container_is_floating(cont)) {
262 // that does not unless the change would move us to a new workspace. 496 dispatch_cursor_button_floating(cursor, time_msec, button, state,
263 // 497 surface, sx, sy, cont);
264 // This prevents, for example, losing focus when clicking on swaybar. 498 } else if (surface && cont && cont->type != C_VIEW) {
265 if (surface && cont && cont->type != C_VIEW) { 499 // Avoid moving keyboard focus from a surface that accepts it to one
500 // that does not unless the change would move us to a new workspace.
501 //
502 // This prevents, for example, losing focus when clicking on swaybar.
266 struct sway_container *new_ws = cont; 503 struct sway_container *new_ws = cont;
267 if (new_ws && new_ws->type != C_WORKSPACE) { 504 if (new_ws && new_ws->type != C_WORKSPACE) {
268 new_ws = container_parent(new_ws, C_WORKSPACE); 505 new_ws = container_parent(new_ws, C_WORKSPACE);
@@ -274,12 +511,15 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
274 if (new_ws != old_ws) { 511 if (new_ws != old_ws) {
275 seat_set_focus(cursor->seat, cont); 512 seat_set_focus(cursor->seat, cont);
276 } 513 }
514 seat_pointer_notify_button(cursor->seat, time_msec, button, state);
277 } else if (cont) { 515 } else if (cont) {
278 seat_set_focus(cursor->seat, cont); 516 seat_set_focus(cursor->seat, cont);
517 seat_pointer_notify_button(cursor->seat, time_msec, button, state);
518 } else {
519 seat_pointer_notify_button(cursor->seat, time_msec, button, state);
279 } 520 }
280 521
281 wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, 522 transaction_commit_dirty();
282 time_msec, button, state);
283} 523}
284 524
285static void handle_cursor_button(struct wl_listener *listener, void *data) { 525static void handle_cursor_button(struct wl_listener *listener, void *data) {
@@ -425,6 +665,7 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) {
425 665
426 wlr_cursor_warp_absolute(cursor->cursor, event->device, x, y); 666 wlr_cursor_warp_absolute(cursor->cursor, event->device, x, y);
427 cursor_send_pointer_motion(cursor, event->time_msec, true); 667 cursor_send_pointer_motion(cursor, event->time_msec, true);
668 transaction_commit_dirty();
428} 669}
429 670
430static void handle_tool_tip(struct wl_listener *listener, void *data) { 671static void handle_tool_tip(struct wl_listener *listener, void *data) {
@@ -464,6 +705,9 @@ static void handle_request_set_cursor(struct wl_listener *listener,
464 void *data) { 705 void *data) {
465 struct sway_cursor *cursor = 706 struct sway_cursor *cursor =
466 wl_container_of(listener, cursor, request_set_cursor); 707 wl_container_of(listener, cursor, request_set_cursor);
708 if (cursor->seat->operation != OP_NONE) {
709 return;
710 }
467 struct wlr_seat_pointer_request_set_cursor_event *event = data; 711 struct wlr_seat_pointer_request_set_cursor_event *event = data;
468 712
469 struct wl_client *focused_client = NULL; 713 struct wl_client *focused_client = NULL;
@@ -476,15 +720,26 @@ static void handle_request_set_cursor(struct wl_listener *listener,
476 // TODO: check cursor mode 720 // TODO: check cursor mode
477 if (focused_client == NULL || 721 if (focused_client == NULL ||
478 event->seat_client->client != focused_client) { 722 event->seat_client->client != focused_client) {
479 wlr_log(L_DEBUG, "denying request to set cursor from unfocused client"); 723 wlr_log(WLR_DEBUG, "denying request to set cursor from unfocused client");
480 return; 724 return;
481 } 725 }
482 726
483 wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x, 727 wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x,
484 event->hotspot_y); 728 event->hotspot_y);
729 cursor->image = NULL;
485 cursor->image_client = focused_client; 730 cursor->image_client = focused_client;
486} 731}
487 732
733void cursor_set_image(struct sway_cursor *cursor, const char *image,
734 struct wl_client *client) {
735 if (!cursor->image || strcmp(cursor->image, image) != 0) {
736 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image,
737 cursor->cursor);
738 cursor->image = image;
739 }
740 cursor->image_client = client;
741}
742
488void sway_cursor_destroy(struct sway_cursor *cursor) { 743void sway_cursor_destroy(struct sway_cursor *cursor) {
489 if (!cursor) { 744 if (!cursor) {
490 return; 745 return;