diff options
Diffstat (limited to 'sway/input/cursor.c')
-rw-r--r-- | sway/input/cursor.c | 631 |
1 files changed, 252 insertions, 379 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 @@ | |||
1 | #define _XOPEN_SOURCE 700 | 1 | #define _POSIX_C_SOURCE 200809L |
2 | #include <math.h> | 2 | #include <math.h> |
3 | #ifdef __linux__ | 3 | #include <libevdev/libevdev.h> |
4 | #include <linux/input-event-codes.h> | 4 | #include <linux/input-event-codes.h> |
5 | #elif __FreeBSD__ | 5 | #include <errno.h> |
6 | #include <dev/evdev/input-event-codes.h> | ||
7 | #endif | ||
8 | #include <float.h> | 6 | #include <float.h> |
9 | #include <limits.h> | 7 | #include <limits.h> |
8 | #include <strings.h> | ||
10 | #include <wlr/types/wlr_cursor.h> | 9 | #include <wlr/types/wlr_cursor.h> |
11 | #include <wlr/types/wlr_xcursor_manager.h> | 10 | #include <wlr/types/wlr_xcursor_manager.h> |
12 | #include <wlr/types/wlr_idle.h> | 11 | #include <wlr/types/wlr_idle.h> |
@@ -28,10 +27,6 @@ | |||
28 | #include "sway/tree/workspace.h" | 27 | #include "sway/tree/workspace.h" |
29 | #include "wlr-layer-shell-unstable-v1-protocol.h" | 28 | #include "wlr-layer-shell-unstable-v1-protocol.h" |
30 | 29 | ||
31 | // When doing a tiling drag, this is the thickness of the dropzone | ||
32 | // when dragging to the edge of a layout container. | ||
33 | #define DROP_LAYOUT_BORDER 30 | ||
34 | |||
35 | static uint32_t get_current_time_msec(void) { | 30 | static uint32_t get_current_time_msec(void) { |
36 | struct timespec now; | 31 | struct timespec now; |
37 | clock_gettime(CLOCK_MONOTONIC, &now); | 32 | clock_gettime(CLOCK_MONOTONIC, &now); |
@@ -60,7 +55,7 @@ static struct wlr_surface *layer_surface_at(struct sway_output *output, | |||
60 | * Returns the node at the cursor's position. If there is a surface at that | 55 | * Returns the node at the cursor's position. If there is a surface at that |
61 | * location, it is stored in **surface (it may not be a view). | 56 | * location, it is stored in **surface (it may not be a view). |
62 | */ | 57 | */ |
63 | static struct sway_node *node_at_coords( | 58 | struct sway_node *node_at_coords( |
64 | struct sway_seat *seat, double lx, double ly, | 59 | struct sway_seat *seat, double lx, double ly, |
65 | struct wlr_surface **surface, double *sx, double *sy) { | 60 | struct wlr_surface **surface, double *sx, double *sy) { |
66 | // check for unmanaged views first | 61 | // check for unmanaged views first |
@@ -88,6 +83,10 @@ static struct sway_node *node_at_coords( | |||
88 | return NULL; | 83 | return NULL; |
89 | } | 84 | } |
90 | struct sway_output *output = wlr_output->data; | 85 | struct sway_output *output = wlr_output->data; |
86 | if (!output) { | ||
87 | // output is being destroyed | ||
88 | return NULL; | ||
89 | } | ||
91 | double ox = lx, oy = ly; | 90 | double ox = lx, oy = ly; |
92 | wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); | 91 | wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); |
93 | 92 | ||
@@ -223,323 +222,6 @@ static enum wlr_edges find_resize_edge(struct sway_container *cont, | |||
223 | return edge; | 222 | return edge; |
224 | } | 223 | } |
225 | 224 | ||
226 | static void handle_down_motion(struct sway_seat *seat, | ||
227 | struct sway_cursor *cursor, uint32_t time_msec) { | ||
228 | struct sway_container *con = seat->op_container; | ||
229 | if (seat_is_input_allowed(seat, con->view->surface)) { | ||
230 | double moved_x = cursor->cursor->x - seat->op_ref_lx; | ||
231 | double moved_y = cursor->cursor->y - seat->op_ref_ly; | ||
232 | double sx = seat->op_ref_con_lx + moved_x; | ||
233 | double sy = seat->op_ref_con_ly + moved_y; | ||
234 | wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy); | ||
235 | } | ||
236 | seat->op_moved = true; | ||
237 | } | ||
238 | |||
239 | static void handle_move_floating_motion(struct sway_seat *seat, | ||
240 | struct sway_cursor *cursor) { | ||
241 | struct sway_container *con = seat->op_container; | ||
242 | desktop_damage_whole_container(con); | ||
243 | container_floating_translate(con, | ||
244 | cursor->cursor->x - cursor->previous.x, | ||
245 | cursor->cursor->y - cursor->previous.y); | ||
246 | desktop_damage_whole_container(con); | ||
247 | } | ||
248 | |||
249 | static void resize_box(struct wlr_box *box, enum wlr_edges edge, | ||
250 | int thickness) { | ||
251 | switch (edge) { | ||
252 | case WLR_EDGE_TOP: | ||
253 | box->height = thickness; | ||
254 | break; | ||
255 | case WLR_EDGE_LEFT: | ||
256 | box->width = thickness; | ||
257 | break; | ||
258 | case WLR_EDGE_RIGHT: | ||
259 | box->x = box->x + box->width - thickness; | ||
260 | box->width = thickness; | ||
261 | break; | ||
262 | case WLR_EDGE_BOTTOM: | ||
263 | box->y = box->y + box->height - thickness; | ||
264 | box->height = thickness; | ||
265 | break; | ||
266 | case WLR_EDGE_NONE: | ||
267 | box->x += thickness; | ||
268 | box->y += thickness; | ||
269 | box->width -= thickness * 2; | ||
270 | box->height -= thickness * 2; | ||
271 | break; | ||
272 | } | ||
273 | } | ||
274 | |||
275 | static void handle_move_tiling_motion(struct sway_seat *seat, | ||
276 | struct sway_cursor *cursor) { | ||
277 | struct wlr_surface *surface = NULL; | ||
278 | double sx, sy; | ||
279 | struct sway_node *node = node_at_coords(seat, | ||
280 | cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); | ||
281 | // Damage the old location | ||
282 | desktop_damage_box(&seat->op_drop_box); | ||
283 | |||
284 | if (!node) { | ||
285 | // Eg. hovered over a layer surface such as swaybar | ||
286 | seat->op_target_node = NULL; | ||
287 | seat->op_target_edge = WLR_EDGE_NONE; | ||
288 | return; | ||
289 | } | ||
290 | |||
291 | if (node->type == N_WORKSPACE) { | ||
292 | // Emtpy workspace | ||
293 | seat->op_target_node = node; | ||
294 | seat->op_target_edge = WLR_EDGE_NONE; | ||
295 | workspace_get_box(node->sway_workspace, &seat->op_drop_box); | ||
296 | desktop_damage_box(&seat->op_drop_box); | ||
297 | return; | ||
298 | } | ||
299 | |||
300 | // Deny moving within own workspace if this is the only child | ||
301 | struct sway_container *con = node->sway_container; | ||
302 | if (workspace_num_tiling_views(seat->op_container->workspace) == 1 && | ||
303 | con->workspace == seat->op_container->workspace) { | ||
304 | seat->op_target_node = NULL; | ||
305 | seat->op_target_edge = WLR_EDGE_NONE; | ||
306 | return; | ||
307 | } | ||
308 | |||
309 | // Traverse the ancestors, trying to find a layout container perpendicular | ||
310 | // to the edge. Eg. close to the top or bottom of a horiz layout. | ||
311 | while (con) { | ||
312 | enum wlr_edges edge = WLR_EDGE_NONE; | ||
313 | enum sway_container_layout layout = container_parent_layout(con); | ||
314 | struct wlr_box parent; | ||
315 | con->parent ? container_get_box(con->parent, &parent) : | ||
316 | workspace_get_box(con->workspace, &parent); | ||
317 | if (layout == L_HORIZ || layout == L_TABBED) { | ||
318 | if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) { | ||
319 | edge = WLR_EDGE_TOP; | ||
320 | } else if (cursor->cursor->y > parent.y + parent.height | ||
321 | - DROP_LAYOUT_BORDER) { | ||
322 | edge = WLR_EDGE_BOTTOM; | ||
323 | } | ||
324 | } else if (layout == L_VERT || layout == L_STACKED) { | ||
325 | if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) { | ||
326 | edge = WLR_EDGE_LEFT; | ||
327 | } else if (cursor->cursor->x > parent.x + parent.width | ||
328 | - DROP_LAYOUT_BORDER) { | ||
329 | edge = WLR_EDGE_RIGHT; | ||
330 | } | ||
331 | } | ||
332 | if (edge) { | ||
333 | seat->op_target_node = node_get_parent(&con->node); | ||
334 | seat->op_target_edge = edge; | ||
335 | node_get_box(seat->op_target_node, &seat->op_drop_box); | ||
336 | resize_box(&seat->op_drop_box, edge, DROP_LAYOUT_BORDER); | ||
337 | desktop_damage_box(&seat->op_drop_box); | ||
338 | return; | ||
339 | } | ||
340 | con = con->parent; | ||
341 | } | ||
342 | |||
343 | // Use the hovered view - but we must be over the actual surface | ||
344 | con = node->sway_container; | ||
345 | if (!con->view->surface || node == &seat->op_container->node) { | ||
346 | seat->op_target_node = NULL; | ||
347 | seat->op_target_edge = WLR_EDGE_NONE; | ||
348 | return; | ||
349 | } | ||
350 | |||
351 | // Find the closest edge | ||
352 | size_t thickness = fmin(con->content_width, con->content_height) * 0.3; | ||
353 | size_t closest_dist = INT_MAX; | ||
354 | size_t dist; | ||
355 | seat->op_target_edge = WLR_EDGE_NONE; | ||
356 | if ((dist = cursor->cursor->y - con->y) < closest_dist) { | ||
357 | closest_dist = dist; | ||
358 | seat->op_target_edge = WLR_EDGE_TOP; | ||
359 | } | ||
360 | if ((dist = cursor->cursor->x - con->x) < closest_dist) { | ||
361 | closest_dist = dist; | ||
362 | seat->op_target_edge = WLR_EDGE_LEFT; | ||
363 | } | ||
364 | if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) { | ||
365 | closest_dist = dist; | ||
366 | seat->op_target_edge = WLR_EDGE_RIGHT; | ||
367 | } | ||
368 | if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) { | ||
369 | closest_dist = dist; | ||
370 | seat->op_target_edge = WLR_EDGE_BOTTOM; | ||
371 | } | ||
372 | |||
373 | if (closest_dist > thickness) { | ||
374 | seat->op_target_edge = WLR_EDGE_NONE; | ||
375 | } | ||
376 | |||
377 | seat->op_target_node = node; | ||
378 | seat->op_drop_box.x = con->content_x; | ||
379 | seat->op_drop_box.y = con->content_y; | ||
380 | seat->op_drop_box.width = con->content_width; | ||
381 | seat->op_drop_box.height = con->content_height; | ||
382 | resize_box(&seat->op_drop_box, seat->op_target_edge, thickness); | ||
383 | desktop_damage_box(&seat->op_drop_box); | ||
384 | } | ||
385 | |||
386 | static void calculate_floating_constraints(struct sway_container *con, | ||
387 | int *min_width, int *max_width, int *min_height, int *max_height) { | ||
388 | if (config->floating_minimum_width == -1) { // no minimum | ||
389 | *min_width = 0; | ||
390 | } else if (config->floating_minimum_width == 0) { // automatic | ||
391 | *min_width = 75; | ||
392 | } else { | ||
393 | *min_width = config->floating_minimum_width; | ||
394 | } | ||
395 | |||
396 | if (config->floating_minimum_height == -1) { // no minimum | ||
397 | *min_height = 0; | ||
398 | } else if (config->floating_minimum_height == 0) { // automatic | ||
399 | *min_height = 50; | ||
400 | } else { | ||
401 | *min_height = config->floating_minimum_height; | ||
402 | } | ||
403 | |||
404 | if (config->floating_maximum_width == -1) { // no maximum | ||
405 | *max_width = INT_MAX; | ||
406 | } else if (config->floating_maximum_width == 0) { // automatic | ||
407 | *max_width = con->workspace->width; | ||
408 | } else { | ||
409 | *max_width = config->floating_maximum_width; | ||
410 | } | ||
411 | |||
412 | if (config->floating_maximum_height == -1) { // no maximum | ||
413 | *max_height = INT_MAX; | ||
414 | } else if (config->floating_maximum_height == 0) { // automatic | ||
415 | *max_height = con->workspace->height; | ||
416 | } else { | ||
417 | *max_height = config->floating_maximum_height; | ||
418 | } | ||
419 | } | ||
420 | |||
421 | static void handle_resize_floating_motion(struct sway_seat *seat, | ||
422 | struct sway_cursor *cursor) { | ||
423 | struct sway_container *con = seat->op_container; | ||
424 | enum wlr_edges edge = seat->op_resize_edge; | ||
425 | |||
426 | // The amount the mouse has moved since the start of the resize operation | ||
427 | // Positive is down/right | ||
428 | double mouse_move_x = cursor->cursor->x - seat->op_ref_lx; | ||
429 | double mouse_move_y = cursor->cursor->y - seat->op_ref_ly; | ||
430 | |||
431 | if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) { | ||
432 | mouse_move_x = 0; | ||
433 | } | ||
434 | if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) { | ||
435 | mouse_move_y = 0; | ||
436 | } | ||
437 | |||
438 | double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x; | ||
439 | double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y; | ||
440 | |||
441 | if (seat->op_resize_preserve_ratio) { | ||
442 | double x_multiplier = grow_width / seat->op_ref_width; | ||
443 | double y_multiplier = grow_height / seat->op_ref_height; | ||
444 | double max_multiplier = fmax(x_multiplier, y_multiplier); | ||
445 | grow_width = seat->op_ref_width * max_multiplier; | ||
446 | grow_height = seat->op_ref_height * max_multiplier; | ||
447 | } | ||
448 | |||
449 | // Determine new width/height, and accommodate for floating min/max values | ||
450 | double width = seat->op_ref_width + grow_width; | ||
451 | double height = seat->op_ref_height + grow_height; | ||
452 | int min_width, max_width, min_height, max_height; | ||
453 | calculate_floating_constraints(con, &min_width, &max_width, | ||
454 | &min_height, &max_height); | ||
455 | width = fmax(min_width, fmin(width, max_width)); | ||
456 | height = fmax(min_height, fmin(height, max_height)); | ||
457 | |||
458 | // Apply the view's min/max size | ||
459 | if (con->view) { | ||
460 | double view_min_width, view_max_width, view_min_height, view_max_height; | ||
461 | view_get_constraints(con->view, &view_min_width, &view_max_width, | ||
462 | &view_min_height, &view_max_height); | ||
463 | width = fmax(view_min_width, fmin(width, view_max_width)); | ||
464 | height = fmax(view_min_height, fmin(height, view_max_height)); | ||
465 | } | ||
466 | |||
467 | // Recalculate these, in case we hit a min/max limit | ||
468 | grow_width = width - seat->op_ref_width; | ||
469 | grow_height = height - seat->op_ref_height; | ||
470 | |||
471 | // Determine grow x/y values - these are relative to the container's x/y at | ||
472 | // the start of the resize operation. | ||
473 | double grow_x = 0, grow_y = 0; | ||
474 | if (edge & WLR_EDGE_LEFT) { | ||
475 | grow_x = -grow_width; | ||
476 | } else if (edge & WLR_EDGE_RIGHT) { | ||
477 | grow_x = 0; | ||
478 | } else { | ||
479 | grow_x = -grow_width / 2; | ||
480 | } | ||
481 | if (edge & WLR_EDGE_TOP) { | ||
482 | grow_y = -grow_height; | ||
483 | } else if (edge & WLR_EDGE_BOTTOM) { | ||
484 | grow_y = 0; | ||
485 | } else { | ||
486 | grow_y = -grow_height / 2; | ||
487 | } | ||
488 | |||
489 | // Determine the amounts we need to bump everything relative to the current | ||
490 | // size. | ||
491 | int relative_grow_width = width - con->width; | ||
492 | int relative_grow_height = height - con->height; | ||
493 | int relative_grow_x = (seat->op_ref_con_lx + grow_x) - con->x; | ||
494 | int relative_grow_y = (seat->op_ref_con_ly + grow_y) - con->y; | ||
495 | |||
496 | // Actually resize stuff | ||
497 | con->x += relative_grow_x; | ||
498 | con->y += relative_grow_y; | ||
499 | con->width += relative_grow_width; | ||
500 | con->height += relative_grow_height; | ||
501 | |||
502 | con->content_x += relative_grow_x; | ||
503 | con->content_y += relative_grow_y; | ||
504 | con->content_width += relative_grow_width; | ||
505 | con->content_height += relative_grow_height; | ||
506 | |||
507 | arrange_container(con); | ||
508 | } | ||
509 | |||
510 | static void handle_resize_tiling_motion(struct sway_seat *seat, | ||
511 | struct sway_cursor *cursor) { | ||
512 | int amount_x = 0; | ||
513 | int amount_y = 0; | ||
514 | int moved_x = cursor->cursor->x - seat->op_ref_lx; | ||
515 | int moved_y = cursor->cursor->y - seat->op_ref_ly; | ||
516 | enum wlr_edges edge_x = WLR_EDGE_NONE; | ||
517 | enum wlr_edges edge_y = WLR_EDGE_NONE; | ||
518 | struct sway_container *con = seat->op_container; | ||
519 | |||
520 | if (seat->op_resize_edge & WLR_EDGE_TOP) { | ||
521 | amount_y = (seat->op_ref_height - moved_y) - con->height; | ||
522 | edge_y = WLR_EDGE_TOP; | ||
523 | } else if (seat->op_resize_edge & WLR_EDGE_BOTTOM) { | ||
524 | amount_y = (seat->op_ref_height + moved_y) - con->height; | ||
525 | edge_y = WLR_EDGE_BOTTOM; | ||
526 | } | ||
527 | if (seat->op_resize_edge & WLR_EDGE_LEFT) { | ||
528 | amount_x = (seat->op_ref_width - moved_x) - con->width; | ||
529 | edge_x = WLR_EDGE_LEFT; | ||
530 | } else if (seat->op_resize_edge & WLR_EDGE_RIGHT) { | ||
531 | amount_x = (seat->op_ref_width + moved_x) - con->width; | ||
532 | edge_x = WLR_EDGE_RIGHT; | ||
533 | } | ||
534 | |||
535 | if (amount_x != 0) { | ||
536 | container_resize_tiled(seat->op_container, edge_x, amount_x); | ||
537 | } | ||
538 | if (amount_y != 0) { | ||
539 | container_resize_tiled(seat->op_container, edge_y, amount_y); | ||
540 | } | ||
541 | } | ||
542 | |||
543 | static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec, | 225 | static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec, |
544 | struct sway_node *node, struct wlr_surface *surface, | 226 | struct sway_node *node, struct wlr_surface *surface, |
545 | double sx, double sy) { | 227 | double sx, double sy) { |
@@ -589,6 +271,50 @@ void cursor_rebase(struct sway_cursor *cursor) { | |||
589 | cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy); | 271 | cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy); |
590 | } | 272 | } |
591 | 273 | ||
274 | static int hide_notify(void *data) { | ||
275 | struct sway_cursor *cursor = data; | ||
276 | wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); | ||
277 | cursor->hidden = true; | ||
278 | return 1; | ||
279 | } | ||
280 | |||
281 | int cursor_get_timeout(struct sway_cursor *cursor){ | ||
282 | struct seat_config *sc = seat_get_config(cursor->seat); | ||
283 | if (!sc) { | ||
284 | sc = seat_get_config_by_name("*"); | ||
285 | } | ||
286 | int timeout = sc ? sc->hide_cursor_timeout : 0; | ||
287 | if (timeout < 0) { | ||
288 | timeout = 0; | ||
289 | } | ||
290 | return timeout; | ||
291 | } | ||
292 | |||
293 | void cursor_handle_activity(struct sway_cursor *cursor) { | ||
294 | wl_event_source_timer_update( | ||
295 | cursor->hide_source, cursor_get_timeout(cursor)); | ||
296 | |||
297 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | ||
298 | if (cursor->hidden) { | ||
299 | cursor_unhide(cursor); | ||
300 | } | ||
301 | } | ||
302 | |||
303 | void cursor_unhide(struct sway_cursor *cursor) { | ||
304 | cursor->hidden = false; | ||
305 | if (cursor->image_surface) { | ||
306 | cursor_set_image_surface(cursor, | ||
307 | cursor->image_surface, | ||
308 | cursor->hotspot_x, | ||
309 | cursor->hotspot_y, | ||
310 | cursor->image_client); | ||
311 | } else { | ||
312 | const char *image = cursor->image; | ||
313 | cursor->image = NULL; | ||
314 | cursor_set_image(cursor, image, cursor->image_client); | ||
315 | } | ||
316 | } | ||
317 | |||
592 | void cursor_send_pointer_motion(struct sway_cursor *cursor, | 318 | void cursor_send_pointer_motion(struct sway_cursor *cursor, |
593 | uint32_t time_msec) { | 319 | uint32_t time_msec) { |
594 | if (time_msec == 0) { | 320 | if (time_msec == 0) { |
@@ -598,26 +324,8 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, | |||
598 | struct sway_seat *seat = cursor->seat; | 324 | struct sway_seat *seat = cursor->seat; |
599 | struct wlr_seat *wlr_seat = seat->wlr_seat; | 325 | struct wlr_seat *wlr_seat = seat->wlr_seat; |
600 | 326 | ||
601 | if (seat->operation != OP_NONE) { | 327 | if (seat_doing_seatop(seat)) { |
602 | switch (seat->operation) { | 328 | seatop_motion(seat, time_msec); |
603 | case OP_DOWN: | ||
604 | handle_down_motion(seat, cursor, time_msec); | ||
605 | break; | ||
606 | case OP_MOVE_FLOATING: | ||
607 | handle_move_floating_motion(seat, cursor); | ||
608 | break; | ||
609 | case OP_MOVE_TILING: | ||
610 | handle_move_tiling_motion(seat, cursor); | ||
611 | break; | ||
612 | case OP_RESIZE_FLOATING: | ||
613 | handle_resize_floating_motion(seat, cursor); | ||
614 | break; | ||
615 | case OP_RESIZE_TILING: | ||
616 | handle_resize_tiling_motion(seat, cursor); | ||
617 | break; | ||
618 | case OP_NONE: | ||
619 | break; | ||
620 | } | ||
621 | cursor->previous.x = cursor->cursor->x; | 329 | cursor->previous.x = cursor->cursor->x; |
622 | cursor->previous.y = cursor->cursor->y; | 330 | cursor->previous.y = cursor->cursor->y; |
623 | return; | 331 | return; |
@@ -679,11 +387,11 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, | |||
679 | 387 | ||
680 | static void handle_cursor_motion(struct wl_listener *listener, void *data) { | 388 | static void handle_cursor_motion(struct wl_listener *listener, void *data) { |
681 | struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); | 389 | struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); |
682 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | ||
683 | struct wlr_event_pointer_motion *event = data; | 390 | struct wlr_event_pointer_motion *event = data; |
684 | wlr_cursor_move(cursor->cursor, event->device, | 391 | wlr_cursor_move(cursor->cursor, event->device, |
685 | event->delta_x, event->delta_y); | 392 | event->delta_x, event->delta_y); |
686 | cursor_send_pointer_motion(cursor, event->time_msec); | 393 | cursor_send_pointer_motion(cursor, event->time_msec); |
394 | cursor_handle_activity(cursor); | ||
687 | transaction_commit_dirty(); | 395 | transaction_commit_dirty(); |
688 | } | 396 | } |
689 | 397 | ||
@@ -691,10 +399,10 @@ static void handle_cursor_motion_absolute( | |||
691 | struct wl_listener *listener, void *data) { | 399 | struct wl_listener *listener, void *data) { |
692 | struct sway_cursor *cursor = | 400 | struct sway_cursor *cursor = |
693 | wl_container_of(listener, cursor, motion_absolute); | 401 | wl_container_of(listener, cursor, motion_absolute); |
694 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | ||
695 | struct wlr_event_pointer_motion_absolute *event = data; | 402 | struct wlr_event_pointer_motion_absolute *event = data; |
696 | wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); | 403 | wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); |
697 | cursor_send_pointer_motion(cursor, event->time_msec); | 404 | cursor_send_pointer_motion(cursor, event->time_msec); |
405 | cursor_handle_activity(cursor); | ||
698 | transaction_commit_dirty(); | 406 | transaction_commit_dirty(); |
699 | } | 407 | } |
700 | 408 | ||
@@ -794,11 +502,16 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
794 | struct sway_seat *seat = cursor->seat; | 502 | struct sway_seat *seat = cursor->seat; |
795 | 503 | ||
796 | // Handle existing seat operation | 504 | // Handle existing seat operation |
797 | if (cursor->seat->operation != OP_NONE) { | 505 | if (seat_doing_seatop(seat)) { |
798 | if (button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { | 506 | if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) { |
799 | seat_end_mouse_operation(seat); | 507 | seatop_finish(seat); |
800 | seat_pointer_notify_button(seat, time_msec, button, state); | 508 | seat_pointer_notify_button(seat, time_msec, button, state); |
801 | } | 509 | } |
510 | if (state == WLR_BUTTON_PRESSED) { | ||
511 | state_add_button(cursor, button); | ||
512 | } else { | ||
513 | state_erase_button(cursor, button); | ||
514 | } | ||
802 | return; | 515 | return; |
803 | } | 516 | } |
804 | 517 | ||
@@ -864,7 +577,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
864 | if (cont && resize_edge && button == BTN_LEFT && | 577 | if (cont && resize_edge && button == BTN_LEFT && |
865 | state == WLR_BUTTON_PRESSED && !is_floating) { | 578 | state == WLR_BUTTON_PRESSED && !is_floating) { |
866 | seat_set_focus_container(seat, cont); | 579 | seat_set_focus_container(seat, cont); |
867 | seat_begin_resize_tiling(seat, cont, button, edge); | 580 | seatop_begin_resize_tiling(seat, cont, button, edge); |
868 | return; | 581 | return; |
869 | } | 582 | } |
870 | 583 | ||
@@ -894,7 +607,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
894 | } | 607 | } |
895 | cursor_set_image(seat->cursor, image, NULL); | 608 | cursor_set_image(seat->cursor, image, NULL); |
896 | seat_set_focus_container(seat, cont); | 609 | seat_set_focus_container(seat, cont); |
897 | seat_begin_resize_tiling(seat, cont, button, edge); | 610 | seatop_begin_resize_tiling(seat, cont, button, edge); |
898 | return; | 611 | return; |
899 | } | 612 | } |
900 | } | 613 | } |
@@ -909,7 +622,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
909 | cont = cont->parent; | 622 | cont = cont->parent; |
910 | } | 623 | } |
911 | seat_set_focus_container(seat, cont); | 624 | seat_set_focus_container(seat, cont); |
912 | seat_begin_move_floating(seat, cont, button); | 625 | seatop_begin_move_floating(seat, cont, button); |
913 | return; | 626 | return; |
914 | } | 627 | } |
915 | } | 628 | } |
@@ -919,7 +632,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
919 | state == WLR_BUTTON_PRESSED) { | 632 | state == WLR_BUTTON_PRESSED) { |
920 | // Via border | 633 | // Via border |
921 | if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { | 634 | if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { |
922 | seat_begin_resize_floating(seat, cont, button, resize_edge); | 635 | seatop_begin_resize_floating(seat, cont, button, resize_edge); |
923 | return; | 636 | return; |
924 | } | 637 | } |
925 | 638 | ||
@@ -936,16 +649,30 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
936 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; | 649 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; |
937 | edge |= cursor->cursor->y > floater->y + floater->height / 2 ? | 650 | edge |= cursor->cursor->y > floater->y + floater->height / 2 ? |
938 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; | 651 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; |
939 | seat_begin_resize_floating(seat, floater, button, edge); | 652 | seatop_begin_resize_floating(seat, floater, button, edge); |
940 | return; | 653 | return; |
941 | } | 654 | } |
942 | } | 655 | } |
943 | 656 | ||
944 | // Handle moving a tiling container | 657 | // Handle moving a tiling container |
945 | if (config->tiling_drag && mod_pressed && state == WLR_BUTTON_PRESSED && | 658 | if (config->tiling_drag && (mod_pressed || on_titlebar) && |
946 | !is_floating_or_child && cont && !cont->is_fullscreen) { | 659 | state == WLR_BUTTON_PRESSED && !is_floating_or_child && |
660 | cont && !cont->is_fullscreen) { | ||
661 | struct sway_container *focus = seat_get_focused_container(seat); | ||
662 | bool focused = focus == cont || container_has_ancestor(focus, cont); | ||
663 | if (on_titlebar && !focused) { | ||
664 | node = seat_get_focus_inactive(seat, &cont->node); | ||
665 | seat_set_focus(seat, node); | ||
666 | } | ||
667 | |||
947 | seat_pointer_notify_button(seat, time_msec, button, state); | 668 | seat_pointer_notify_button(seat, time_msec, button, state); |
948 | seat_begin_move_tiling(seat, cont, button); | 669 | |
670 | // If moving a container by it's title bar, use a threshold for the drag | ||
671 | if (!mod_pressed && config->tiling_drag_threshold > 0) { | ||
672 | seatop_begin_move_tiling_threshold(seat, cont, button); | ||
673 | } else { | ||
674 | seatop_begin_move_tiling(seat, cont, button); | ||
675 | } | ||
949 | return; | 676 | return; |
950 | } | 677 | } |
951 | 678 | ||
@@ -953,7 +680,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
953 | if (surface && cont && state == WLR_BUTTON_PRESSED) { | 680 | if (surface && cont && state == WLR_BUTTON_PRESSED) { |
954 | seat_set_focus_container(seat, cont); | 681 | seat_set_focus_container(seat, cont); |
955 | seat_pointer_notify_button(seat, time_msec, button, state); | 682 | seat_pointer_notify_button(seat, time_msec, button, state); |
956 | seat_begin_down(seat, cont, button, sx, sy); | 683 | seatop_begin_down(seat, cont, button, sx, sy); |
957 | return; | 684 | return; |
958 | } | 685 | } |
959 | 686 | ||
@@ -970,18 +697,32 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
970 | 697 | ||
971 | static void handle_cursor_button(struct wl_listener *listener, void *data) { | 698 | static void handle_cursor_button(struct wl_listener *listener, void *data) { |
972 | struct sway_cursor *cursor = wl_container_of(listener, cursor, button); | 699 | struct sway_cursor *cursor = wl_container_of(listener, cursor, button); |
973 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | ||
974 | struct wlr_event_pointer_button *event = data; | 700 | struct wlr_event_pointer_button *event = data; |
975 | dispatch_cursor_button(cursor, event->device, | 701 | dispatch_cursor_button(cursor, event->device, |
976 | event->time_msec, event->button, event->state); | 702 | event->time_msec, event->button, event->state); |
703 | cursor_handle_activity(cursor); | ||
977 | transaction_commit_dirty(); | 704 | transaction_commit_dirty(); |
978 | } | 705 | } |
979 | 706 | ||
980 | static void dispatch_cursor_axis(struct sway_cursor *cursor, | 707 | static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { |
708 | switch (event->orientation) { | ||
709 | case WLR_AXIS_ORIENTATION_VERTICAL: | ||
710 | return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; | ||
711 | case WLR_AXIS_ORIENTATION_HORIZONTAL: | ||
712 | return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; | ||
713 | default: | ||
714 | wlr_log(WLR_DEBUG, "Unknown axis orientation"); | ||
715 | return 0; | ||
716 | } | ||
717 | } | ||
718 | |||
719 | void dispatch_cursor_axis(struct sway_cursor *cursor, | ||
981 | struct wlr_event_pointer_axis *event) { | 720 | struct wlr_event_pointer_axis *event) { |
982 | struct sway_seat *seat = cursor->seat; | 721 | struct sway_seat *seat = cursor->seat; |
983 | struct sway_input_device *input_device = event->device->data; | 722 | struct sway_input_device *input_device = |
984 | struct input_config *ic = input_device_get_config(input_device); | 723 | event->device ? event->device->data : NULL; |
724 | struct input_config *ic = | ||
725 | input_device ? input_device_get_config(input_device) : NULL; | ||
985 | 726 | ||
986 | // Determine what's under the cursor | 727 | // Determine what's under the cursor |
987 | struct wlr_surface *surface = NULL; | 728 | struct wlr_surface *surface = NULL; |
@@ -993,11 +734,35 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor, | |||
993 | enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; | 734 | enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; |
994 | bool on_border = edge != WLR_EDGE_NONE; | 735 | bool on_border = edge != WLR_EDGE_NONE; |
995 | bool on_titlebar = cont && !on_border && !surface; | 736 | bool on_titlebar = cont && !on_border && !surface; |
737 | bool on_titlebar_border = cont && on_border && | ||
738 | cursor->cursor->y < cont->content_y; | ||
739 | bool on_contents = cont && !on_border && surface; | ||
996 | float scroll_factor = | 740 | float scroll_factor = |
997 | (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; | 741 | (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; |
998 | 742 | ||
999 | // Scrolling on a tabbed or stacked title bar | 743 | bool handled = false; |
1000 | if (on_titlebar) { | 744 | |
745 | // Gather information needed for mouse bindings | ||
746 | struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); | ||
747 | uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; | ||
748 | struct wlr_input_device *device = | ||
749 | input_device ? input_device->wlr_device : NULL; | ||
750 | char *dev_id = device ? input_device_get_identifier(device) : strdup("*"); | ||
751 | uint32_t button = wl_axis_to_button(event); | ||
752 | |||
753 | // Handle mouse bindings - x11 mouse buttons 4-7 - press event | ||
754 | struct sway_binding *binding = NULL; | ||
755 | state_add_button(cursor, button); | ||
756 | binding = get_active_mouse_binding(cursor, | ||
757 | config->current_mode->mouse_bindings, modifiers, false, | ||
758 | on_titlebar, on_border, on_contents, dev_id); | ||
759 | if (binding) { | ||
760 | seat_execute_command(seat, binding); | ||
761 | handled = true; | ||
762 | } | ||
763 | |||
764 | // Scrolling on a tabbed or stacked title bar (handled as press event) | ||
765 | if (!handled && (on_titlebar || on_titlebar_border)) { | ||
1001 | enum sway_container_layout layout = container_parent_layout(cont); | 766 | enum sway_container_layout layout = container_parent_layout(cont); |
1002 | if (layout == L_TABBED || layout == L_STACKED) { | 767 | if (layout == L_TABBED || layout == L_STACKED) { |
1003 | struct sway_node *tabcontainer = node_get_parent(node); | 768 | struct sway_node *tabcontainer = node_get_parent(node); |
@@ -1024,20 +789,33 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor, | |||
1024 | seat_set_raw_focus(seat, new_focus); | 789 | seat_set_raw_focus(seat, new_focus); |
1025 | seat_set_raw_focus(seat, old_focus); | 790 | seat_set_raw_focus(seat, old_focus); |
1026 | } | 791 | } |
1027 | return; | 792 | handled = true; |
1028 | } | 793 | } |
1029 | } | 794 | } |
1030 | 795 | ||
1031 | wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, | 796 | // Handle mouse bindings - x11 mouse buttons 4-7 - release event |
1032 | event->orientation, scroll_factor * event->delta, | 797 | binding = get_active_mouse_binding(cursor, |
1033 | round(scroll_factor * event->delta_discrete), event->source); | 798 | config->current_mode->mouse_bindings, modifiers, true, |
799 | on_titlebar, on_border, on_contents, dev_id); | ||
800 | state_erase_button(cursor, button); | ||
801 | if (binding) { | ||
802 | seat_execute_command(seat, binding); | ||
803 | handled = true; | ||
804 | } | ||
805 | free(dev_id); | ||
806 | |||
807 | if (!handled) { | ||
808 | wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, | ||
809 | event->orientation, scroll_factor * event->delta, | ||
810 | round(scroll_factor * event->delta_discrete), event->source); | ||
811 | } | ||
1034 | } | 812 | } |
1035 | 813 | ||
1036 | static void handle_cursor_axis(struct wl_listener *listener, void *data) { | 814 | static void handle_cursor_axis(struct wl_listener *listener, void *data) { |
1037 | struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); | 815 | struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); |
1038 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | ||
1039 | struct wlr_event_pointer_axis *event = data; | 816 | struct wlr_event_pointer_axis *event = data; |
1040 | dispatch_cursor_axis(cursor, event); | 817 | dispatch_cursor_axis(cursor, event); |
818 | cursor_handle_activity(cursor); | ||
1041 | transaction_commit_dirty(); | 819 | transaction_commit_dirty(); |
1042 | } | 820 | } |
1043 | 821 | ||
@@ -1209,7 +987,7 @@ static void handle_request_set_cursor(struct wl_listener *listener, | |||
1209 | void *data) { | 987 | void *data) { |
1210 | struct sway_cursor *cursor = | 988 | struct sway_cursor *cursor = |
1211 | wl_container_of(listener, cursor, request_set_cursor); | 989 | wl_container_of(listener, cursor, request_set_cursor); |
1212 | if (cursor->seat->operation != OP_NONE) { | 990 | if (seat_doing_seatop(cursor->seat)) { |
1213 | return; | 991 | return; |
1214 | } | 992 | } |
1215 | struct wlr_seat_pointer_request_set_cursor_event *event = data; | 993 | struct wlr_seat_pointer_request_set_cursor_event *event = data; |
@@ -1228,10 +1006,8 @@ static void handle_request_set_cursor(struct wl_listener *listener, | |||
1228 | return; | 1006 | return; |
1229 | } | 1007 | } |
1230 | 1008 | ||
1231 | wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x, | 1009 | cursor_set_image_surface(cursor, event->surface, event->hotspot_x, |
1232 | event->hotspot_y); | 1010 | event->hotspot_y, focused_client); |
1233 | cursor->image = NULL; | ||
1234 | cursor->image_client = focused_client; | ||
1235 | } | 1011 | } |
1236 | 1012 | ||
1237 | void cursor_set_image(struct sway_cursor *cursor, const char *image, | 1013 | 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, | |||
1239 | if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) { | 1015 | if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) { |
1240 | return; | 1016 | return; |
1241 | } | 1017 | } |
1018 | |||
1019 | const char *current_image = cursor->image; | ||
1020 | cursor->image = image; | ||
1021 | cursor->image_surface = NULL; | ||
1022 | cursor->hotspot_x = cursor->hotspot_y = 0; | ||
1023 | cursor->image_client = client; | ||
1024 | |||
1025 | if (cursor->hidden) { | ||
1026 | return; | ||
1027 | } | ||
1028 | |||
1242 | if (!image) { | 1029 | if (!image) { |
1243 | wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); | 1030 | wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); |
1244 | } else if (!cursor->image || strcmp(cursor->image, image) != 0) { | 1031 | } else if (!current_image || strcmp(current_image, image) != 0) { |
1245 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, | 1032 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, |
1246 | cursor->cursor); | 1033 | cursor->cursor); |
1247 | } | 1034 | } |
1248 | cursor->image = image; | 1035 | } |
1036 | |||
1037 | void cursor_set_image_surface(struct sway_cursor *cursor, | ||
1038 | struct wlr_surface *surface, int32_t hotspot_x, int32_t hotspot_y, | ||
1039 | struct wl_client *client) { | ||
1040 | if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) { | ||
1041 | return; | ||
1042 | } | ||
1043 | |||
1044 | cursor->image = NULL; | ||
1045 | cursor->image_surface = surface; | ||
1046 | cursor->hotspot_x = hotspot_x; | ||
1047 | cursor->hotspot_y = hotspot_y; | ||
1249 | cursor->image_client = client; | 1048 | cursor->image_client = client; |
1049 | |||
1050 | if (cursor->hidden) { | ||
1051 | return; | ||
1052 | } | ||
1053 | |||
1054 | wlr_cursor_set_surface(cursor->cursor, surface, hotspot_x, hotspot_y); | ||
1250 | } | 1055 | } |
1251 | 1056 | ||
1252 | void sway_cursor_destroy(struct sway_cursor *cursor) { | 1057 | void sway_cursor_destroy(struct sway_cursor *cursor) { |
@@ -1254,6 +1059,8 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { | |||
1254 | return; | 1059 | return; |
1255 | } | 1060 | } |
1256 | 1061 | ||
1062 | wl_event_source_remove(cursor->hide_source); | ||
1063 | |||
1257 | wlr_xcursor_manager_destroy(cursor->xcursor_manager); | 1064 | wlr_xcursor_manager_destroy(cursor->xcursor_manager); |
1258 | wlr_cursor_destroy(cursor->cursor); | 1065 | wlr_cursor_destroy(cursor->cursor); |
1259 | free(cursor); | 1066 | free(cursor); |
@@ -1277,6 +1084,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { | |||
1277 | cursor->seat = seat; | 1084 | cursor->seat = seat; |
1278 | wlr_cursor_attach_output_layout(wlr_cursor, root->output_layout); | 1085 | wlr_cursor_attach_output_layout(wlr_cursor, root->output_layout); |
1279 | 1086 | ||
1087 | cursor->hide_source = wl_event_loop_add_timer(server.wl_event_loop, | ||
1088 | hide_notify, cursor); | ||
1089 | |||
1280 | // input events | 1090 | // input events |
1281 | wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); | 1091 | wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); |
1282 | cursor->motion.notify = handle_cursor_motion; | 1092 | cursor->motion.notify = handle_cursor_motion; |
@@ -1362,3 +1172,66 @@ void cursor_warp_to_workspace(struct sway_cursor *cursor, | |||
1362 | 1172 | ||
1363 | wlr_cursor_warp(cursor->cursor, NULL, x, y); | 1173 | wlr_cursor_warp(cursor->cursor, NULL, x, y); |
1364 | } | 1174 | } |
1175 | |||
1176 | uint32_t get_mouse_bindsym(const char *name, char **error) { | ||
1177 | if (strncasecmp(name, "button", strlen("button")) == 0) { | ||
1178 | // Map to x11 mouse buttons | ||
1179 | int number = name[strlen("button")] - '0'; | ||
1180 | if (number < 1 || number > 9 || strlen(name) > strlen("button0")) { | ||
1181 | *error = strdup("Only buttons 1-9 are supported. For other mouse " | ||
1182 | "buttons, use the name of the event code."); | ||
1183 | return 0; | ||
1184 | } | ||
1185 | static const uint32_t buttons[] = {BTN_LEFT, BTN_MIDDLE, BTN_RIGHT, | ||
1186 | SWAY_SCROLL_UP, SWAY_SCROLL_DOWN, SWAY_SCROLL_LEFT, | ||
1187 | SWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA}; | ||
1188 | return buttons[number - 1]; | ||
1189 | } else if (strncmp(name, "BTN_", strlen("BTN_")) == 0) { | ||
1190 | // Get event code from name | ||
1191 | int code = libevdev_event_code_from_name(EV_KEY, name); | ||
1192 | if (code == -1) { | ||
1193 | size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1; | ||
1194 | *error = malloc(len); | ||
1195 | if (*error) { | ||
1196 | snprintf(*error, len, "Unknown event %s", name); | ||
1197 | } | ||
1198 | return 0; | ||
1199 | } | ||
1200 | return code; | ||
1201 | } | ||
1202 | return 0; | ||
1203 | } | ||
1204 | |||
1205 | uint32_t get_mouse_bindcode(const char *name, char **error) { | ||
1206 | // Validate event code | ||
1207 | errno = 0; | ||
1208 | char *endptr; | ||
1209 | int code = strtol(name, &endptr, 10); | ||
1210 | if (endptr == name && code <= 0) { | ||
1211 | *error = strdup("Button event code must be a positive integer."); | ||
1212 | return 0; | ||
1213 | } else if (errno == ERANGE) { | ||
1214 | *error = strdup("Button event code out of range."); | ||
1215 | return 0; | ||
1216 | } | ||
1217 | const char *event = libevdev_event_code_get_name(EV_KEY, code); | ||
1218 | if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { | ||
1219 | size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button", | ||
1220 | code, event) + 1; | ||
1221 | *error = malloc(len); | ||
1222 | if (*error) { | ||
1223 | snprintf(*error, len, "Event code %d (%s) is not a button", | ||
1224 | code, event); | ||
1225 | } | ||
1226 | return 0; | ||
1227 | } | ||
1228 | return code; | ||
1229 | } | ||
1230 | |||
1231 | uint32_t get_mouse_button(const char *name, char **error) { | ||
1232 | uint32_t button = get_mouse_bindsym(name, error); | ||
1233 | if (!button && !*error) { | ||
1234 | button = get_mouse_bindcode(name, error); | ||
1235 | } | ||
1236 | return button; | ||
1237 | } | ||