diff options
Diffstat (limited to 'sway/input/cursor.c')
-rw-r--r-- | sway/input/cursor.c | 295 |
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 | ||
146 | static 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 | |||
172 | static 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 | |||
182 | static 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 | |||
219 | static 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 | |||
141 | void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | 311 | void 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 | ||
233 | static void handle_cursor_motion_absolute( | 422 | static 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 | |||
433 | static 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 | ||
243 | void dispatch_cursor_button(struct sway_cursor *cursor, | 472 | void 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 | ||
285 | static void handle_cursor_button(struct wl_listener *listener, void *data) { | 525 | static 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 | ||
430 | static void handle_tool_tip(struct wl_listener *listener, void *data) { | 671 | static 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 | ||
733 | void 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 | |||
488 | void sway_cursor_destroy(struct sway_cursor *cursor) { | 743 | void sway_cursor_destroy(struct sway_cursor *cursor) { |
489 | if (!cursor) { | 744 | if (!cursor) { |
490 | return; | 745 | return; |