aboutsummaryrefslogtreecommitdiffstats
path: root/sway/input/cursor.c
diff options
context:
space:
mode:
authorLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-07-18 16:13:28 +1000
committerLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-07-22 23:10:19 +1000
commit9fbe13b9be18c732b58033a57a22a299af91a170 (patch)
treea24901c1bb4eff87877c0d9fb96767b662a9d533 /sway/input/cursor.c
parentMerge pull request #2320 from RedSoxFan/reset-outputs-on-reload (diff)
downloadsway-9fbe13b9be18c732b58033a57a22a299af91a170.tar.gz
sway-9fbe13b9be18c732b58033a57a22a299af91a170.tar.zst
sway-9fbe13b9be18c732b58033a57a22a299af91a170.zip
Implement floating_modifier and mouse operations for floating views
This implements the following: * `floating_modifier` configuration directive * Drag a floating window by its title bar * Hold mod + drag a floating window from anywhere * Resize a floating view by dragging the border * Resize a floating view by holding mod and right clicking anywhere on the view * Resize a floating view and keep aspect ratio by holding shift while resizing using either method * Mouse cursor turns into resize when hovering floating border or corner
Diffstat (limited to 'sway/input/cursor.c')
-rw-r--r--sway/input/cursor.c322
1 files changed, 311 insertions, 11 deletions
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index c76c20b3..6ad214b5 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -5,15 +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"
13#include "sway/desktop/transaction.h" 15#include "sway/desktop/transaction.h"
14#include "sway/input/cursor.h" 16#include "sway/input/cursor.h"
17#include "sway/input/keyboard.h"
15#include "sway/layers.h" 18#include "sway/layers.h"
16#include "sway/output.h" 19#include "sway/output.h"
20#include "sway/tree/arrange.h"
17#include "sway/tree/view.h" 21#include "sway/tree/view.h"
18#include "sway/tree/workspace.h" 22#include "sway/tree/workspace.h"
19#include "wlr-layer-shell-unstable-v1-protocol.h" 23#include "wlr-layer-shell-unstable-v1-protocol.h"
@@ -127,7 +131,7 @@ static struct sway_container *container_at_coords(
127 return ws; 131 return ws;
128 } 132 }
129 133
130 c = seat_get_focus_inactive(seat, output->swayc); 134 c = seat_get_active_child(seat, output->swayc);
131 if (c) { 135 if (c) {
132 return c; 136 return c;
133 } 137 }
@@ -139,6 +143,173 @@ static struct sway_container *container_at_coords(
139 return output->swayc; 143 return output->swayc;
140} 144}
141 145
146static enum resize_edge find_resize_edge(struct sway_container *cont,
147 struct sway_cursor *cursor) {
148 if (cont->type != C_VIEW) {
149 return RESIZE_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 RESIZE_EDGE_NONE;
154 }
155
156 enum resize_edge edge = 0;
157 if (cursor->cursor->x < cont->x + view->border_thickness) {
158 edge |= RESIZE_EDGE_LEFT;
159 }
160 if (cursor->cursor->y < cont->y + view->border_thickness) {
161 edge |= RESIZE_EDGE_TOP;
162 }
163 if (cursor->cursor->x >= cont->x + cont->width - view->border_thickness) {
164 edge |= RESIZE_EDGE_RIGHT;
165 }
166 if (cursor->cursor->y >= cont->y + cont->height - view->border_thickness) {
167 edge |= RESIZE_EDGE_BOTTOM;
168 }
169 return edge;
170}
171
172static void handle_drag_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}
218static void handle_resize_motion(struct sway_seat *seat,
219 struct sway_cursor *cursor) {
220 struct sway_container *con = seat->op_container;
221 double center_lx = con->x + con->width / 2;
222 double center_ly = con->y + con->height / 2;
223 enum resize_edge edge = seat->op_resize_edge;
224
225 // The amount the mouse has moved since the start of the resize operation
226 // Positive is down/right
227 double mouse_move_x = cursor->cursor->x - seat->op_ref_lx;
228 double mouse_move_y = cursor->cursor->y - seat->op_ref_ly;
229
230 if (edge == RESIZE_EDGE_TOP || edge == RESIZE_EDGE_BOTTOM) {
231 mouse_move_x = 0;
232 }
233 if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) {
234 mouse_move_y = 0;
235 }
236
237 double grow_width = seat->op_ref_lx > center_lx ?
238 mouse_move_x : -mouse_move_x;
239 double grow_height = seat->op_ref_ly > center_ly ?
240 mouse_move_y : -mouse_move_y;
241
242 if (seat->op_resize_preserve_ratio) {
243 double x_multiplier = grow_width / seat->op_ref_width;
244 double y_multiplier = grow_height / seat->op_ref_height;
245 double avg_multiplier = (x_multiplier + y_multiplier) / 2;
246 grow_width = seat->op_ref_width * avg_multiplier;
247 grow_height = seat->op_ref_height * avg_multiplier;
248 }
249
250 // If we're resizing from the center (mod + right click), we need to double
251 // the amount we're growing because we're doing it in both directions.
252 if (edge == RESIZE_EDGE_NONE) {
253 grow_width *= 2;
254 grow_height *= 2;
255 }
256
257 // Determine new width/height, and accommodate for min/max values
258 double width = seat->op_ref_width + grow_width;
259 double height = seat->op_ref_height + grow_height;
260 int min_width, max_width, min_height, max_height;
261 calculate_floating_constraints(con, &min_width, &max_width,
262 &min_height, &max_height);
263 width = fmax(min_width, fmin(width, max_width));
264 height = fmax(min_height, fmin(height, max_height));
265
266 // Recalculate these, in case we hit a min/max limit
267 grow_width = width - seat->op_ref_width;
268 grow_height = height - seat->op_ref_height;
269
270 // Determine grow x/y values - these are relative to the container's x/y at
271 // the start of the resize operation.
272 double grow_x = 0, grow_y = 0;
273 if (edge & RESIZE_EDGE_LEFT) {
274 grow_x = -grow_width;
275 } else if (edge & RESIZE_EDGE_RIGHT) {
276 grow_x = 0;
277 } else {
278 grow_x = -grow_width / 2;
279 }
280 if (edge & RESIZE_EDGE_TOP) {
281 grow_y = -grow_height;
282 } else if (edge & RESIZE_EDGE_BOTTOM) {
283 grow_y = 0;
284 } else {
285 grow_y = -grow_height / 2;
286 }
287
288 // Determine the amounts we need to bump everything relative to the current
289 // size.
290 int relative_grow_width = width - con->width;
291 int relative_grow_height = height - con->height;
292 int relative_grow_x = (seat->op_ref_con_lx + grow_x) - con->x;
293 int relative_grow_y = (seat->op_ref_con_ly + grow_y) - con->y;
294
295 // Actually resize stuff
296 con->x += relative_grow_x;
297 con->y += relative_grow_y;
298 con->width += relative_grow_width;
299 con->height += relative_grow_height;
300
301 if (con->type == C_VIEW) {
302 struct sway_view *view = con->sway_view;
303 view->x += relative_grow_x;
304 view->y += relative_grow_y;
305 view->width += relative_grow_width;
306 view->height += relative_grow_height;
307 }
308
309 arrange_windows(con);
310 transaction_commit_dirty();
311}
312
142void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, 313void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
143 bool allow_refocusing) { 314 bool allow_refocusing) {
144 if (time_msec == 0) { 315 if (time_msec == 0) {
@@ -146,6 +317,18 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
146 } 317 }
147 318
148 struct sway_seat *seat = cursor->seat; 319 struct sway_seat *seat = cursor->seat;
320
321 if (seat->operation != OP_NONE) {
322 if (seat->operation == OP_DRAG) {
323 handle_drag_motion(seat, cursor);
324 } else {
325 handle_resize_motion(seat, cursor);
326 }
327 cursor->previous.x = cursor->cursor->x;
328 cursor->previous.y = cursor->cursor->y;
329 return;
330 }
331
149 struct wlr_seat *wlr_seat = seat->wlr_seat; 332 struct wlr_seat *wlr_seat = seat->wlr_seat;
150 struct wlr_surface *surface = NULL; 333 struct wlr_surface *surface = NULL;
151 double sx, sy; 334 double sx, sy;
@@ -194,15 +377,54 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
194 } 377 }
195 } 378 }
196 379
197 // reset cursor if switching between clients 380 // Handle cursor image
198 struct wl_client *client = NULL; 381 if (surface) {
199 if (surface != NULL) { 382 // Reset cursor if switching between clients
200 client = wl_resource_get_client(surface->resource); 383 struct wl_client *client = wl_resource_get_client(surface->resource);
201 } 384 if (client != cursor->image_client) {
202 if (client != cursor->image_client) { 385 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
386 "left_ptr", cursor->cursor);
387 cursor->image_client = client;
388 }
389 } else if (c && container_is_floating(c)) {
390 // Try a floating container's resize edge
391 enum resize_edge edge = find_resize_edge(c, cursor);
392 if (edge == RESIZE_EDGE_NONE) {
393 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
394 "left_ptr", cursor->cursor);
395 } else if (edge == RESIZE_EDGE_TOP) {
396 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
397 "top_side", cursor->cursor);
398 } else if (edge == RESIZE_EDGE_RIGHT) {
399 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
400 "right_side", cursor->cursor);
401 } else if (edge == RESIZE_EDGE_BOTTOM) {
402 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
403 "bottom_side", cursor->cursor);
404 } else if (edge == RESIZE_EDGE_LEFT) {
405 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
406 "left_side", cursor->cursor);
407 } else if (edge == (RESIZE_EDGE_TOP | RESIZE_EDGE_LEFT)) {
408 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
409 "top_left_corner", cursor->cursor);
410 } else if (edge == (RESIZE_EDGE_TOP | RESIZE_EDGE_RIGHT)) {
411 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
412 "top_right_corner", cursor->cursor);
413 } else if (edge == (RESIZE_EDGE_BOTTOM | RESIZE_EDGE_LEFT)) {
414 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
415 "bottom_left_corner", cursor->cursor);
416 } else if (edge == (RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT)) {
417 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
418 "bottom_right_corner", cursor->cursor);
419 } else {
420 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
421 "left_ptr", cursor->cursor);
422 }
423 cursor->image_client = NULL;
424 } else {
203 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, 425 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
204 "left_ptr", cursor->cursor); 426 "left_ptr", cursor->cursor);
205 cursor->image_client = client; 427 cursor->image_client = NULL;
206 } 428 }
207 429
208 // send pointer enter/leave 430 // send pointer enter/leave
@@ -243,8 +465,79 @@ static void handle_cursor_motion_absolute(
243 transaction_commit_dirty(); 465 transaction_commit_dirty();
244} 466}
245 467
468static void handle_end_operation(struct sway_seat *seat) {
469 if (seat->operation == OP_DRAG) {
470 // We "move" the container to its own location so it discovers its
471 // output again.
472 struct sway_container *con = seat->op_container;
473 container_floating_move_to(con, con->x, con->y);
474 seat->operation = OP_NONE;
475 seat->op_container = NULL;
476 } else {
477 // OP_RESIZE
478 seat->operation = OP_NONE;
479 seat->op_container = NULL;
480 }
481}
482
483static void dispatch_cursor_button_floating(struct sway_cursor *cursor,
484 uint32_t time_msec, uint32_t button, enum wlr_button_state state,
485 struct wlr_surface *surface, double sx, double sy,
486 struct sway_container *cont) {
487 struct sway_seat *seat = cursor->seat;
488
489 // Deny dragging or resizing a fullscreen view
490 if (cont->type == C_VIEW && cont->sway_view->is_fullscreen) {
491 wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state);
492 return;
493 }
494
495 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
496 bool mod_pressed = keyboard &&
497 (keyboard->modifiers.depressed & config->floating_mod);
498 enum resize_edge edge = find_resize_edge(cont, cursor);
499 bool over_title = edge == RESIZE_EDGE_NONE && !surface;
500
501 // Check for beginning drag
502 if (button == BTN_LEFT && state == WLR_BUTTON_PRESSED &&
503 (mod_pressed || over_title)) {
504 seat->operation = OP_DRAG;
505 seat->op_container = cont;
506 seat->op_button = button;
507 return;
508 }
509
510 // Check for beginning resize
511 bool resizing_via_border = button == BTN_LEFT && edge;
512 bool resizing_via_mod = button == BTN_RIGHT && mod_pressed;
513 if ((resizing_via_border || resizing_via_mod) &&
514 state == WLR_BUTTON_PRESSED) {
515 seat->operation = OP_RESIZE;
516 seat->op_container = cont;
517 seat->op_resize_preserve_ratio = keyboard &&
518 (keyboard->modifiers.depressed & 1); // Shift
519 seat->op_resize_edge = edge;
520 seat->op_button = button;
521 seat->op_ref_lx = cursor->cursor->x;
522 seat->op_ref_ly = cursor->cursor->y;
523 seat->op_ref_con_lx = cont->x;
524 seat->op_ref_con_ly = cont->y;
525 seat->op_ref_width = cont->width;
526 seat->op_ref_height = cont->height;
527 return;
528 }
529
530 // Send event to surface
531 wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state);
532}
533
246void dispatch_cursor_button(struct sway_cursor *cursor, 534void dispatch_cursor_button(struct sway_cursor *cursor,
247 uint32_t time_msec, uint32_t button, enum wlr_button_state state) { 535 uint32_t time_msec, uint32_t button, enum wlr_button_state state) {
536 if (cursor->seat->operation != OP_NONE &&
537 button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) {
538 handle_end_operation(cursor->seat);
539 return;
540 }
248 if (time_msec == 0) { 541 if (time_msec == 0) {
249 time_msec = get_current_time_msec(); 542 time_msec = get_current_time_msec();
250 } 543 }
@@ -259,6 +552,11 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
259 if (layer->current.keyboard_interactive) { 552 if (layer->current.keyboard_interactive) {
260 seat_set_focus_layer(cursor->seat, layer); 553 seat_set_focus_layer(cursor->seat, layer);
261 } 554 }
555 wlr_seat_pointer_notify_button(cursor->seat->wlr_seat,
556 time_msec, button, state);
557 } else if (cont && container_is_floating(cont)) {
558 dispatch_cursor_button_floating(cursor, time_msec, button, state,
559 surface, sx, sy, cont);
262 } else if (surface && cont && cont->type != C_VIEW) { 560 } else if (surface && cont && cont->type != C_VIEW) {
263 // Avoid moving keyboard focus from a surface that accepts it to one 561 // Avoid moving keyboard focus from a surface that accepts it to one
264 // that does not unless the change would move us to a new workspace. 562 // that does not unless the change would move us to a new workspace.
@@ -275,12 +573,14 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
275 if (new_ws != old_ws) { 573 if (new_ws != old_ws) {
276 seat_set_focus(cursor->seat, cont); 574 seat_set_focus(cursor->seat, cont);
277 } 575 }
576 wlr_seat_pointer_notify_button(cursor->seat->wlr_seat,
577 time_msec, button, state);
278 } else if (cont) { 578 } else if (cont) {
279 seat_set_focus(cursor->seat, cont); 579 seat_set_focus(cursor->seat, cont);
580 wlr_seat_pointer_notify_button(cursor->seat->wlr_seat,
581 time_msec, button, state);
280 } 582 }
281 583
282 wlr_seat_pointer_notify_button(cursor->seat->wlr_seat,
283 time_msec, button, state);
284 transaction_commit_dirty(); 584 transaction_commit_dirty();
285} 585}
286 586