2021-09-24 21:45:48 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2020-12-23 18:52:46 +00:00
|
|
|
#include <assert.h>
|
2021-07-09 21:58:54 +01:00
|
|
|
#include <linux/input-event-codes.h>
|
2021-08-02 17:30:34 +01:00
|
|
|
#include <sys/time.h>
|
|
|
|
|
#include <time.h>
|
2020-05-29 21:36:12 +01:00
|
|
|
#include "labwc.h"
|
2020-10-19 22:14:17 +01:00
|
|
|
#include "menu/menu.h"
|
2021-10-24 21:31:05 -04:00
|
|
|
#include "resistance.h"
|
2021-03-21 20:54:55 +00:00
|
|
|
#include "ssd.h"
|
2021-08-29 14:22:49 -04:00
|
|
|
#include "config/mousebind.h"
|
2021-08-18 23:41:07 +01:00
|
|
|
#include <wlr/types/wlr_primary_selection.h>
|
2020-05-29 21:36:12 +01:00
|
|
|
|
2020-10-02 21:19:56 +01:00
|
|
|
static void
|
|
|
|
|
request_cursor_notify(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct seat *seat = wl_container_of(listener, seat, request_cursor);
|
|
|
|
|
/*
|
|
|
|
|
* This event is rasied by the seat when a client provides a cursor
|
|
|
|
|
* image
|
|
|
|
|
*/
|
|
|
|
|
struct wlr_seat_pointer_request_set_cursor_event *event = data;
|
|
|
|
|
struct wlr_seat_client *focused_client =
|
|
|
|
|
seat->seat->pointer_state.focused_client;
|
|
|
|
|
|
2021-09-21 22:05:56 +01:00
|
|
|
/*
|
|
|
|
|
* This can be sent by any client, so we check to make sure this one is
|
|
|
|
|
* actually has pointer focus first.
|
|
|
|
|
*/
|
2020-10-02 21:19:56 +01:00
|
|
|
if (focused_client == event->seat_client) {
|
2021-09-21 22:05:56 +01:00
|
|
|
/*
|
|
|
|
|
* Once we've vetted the client, we can tell the cursor to use
|
2020-10-02 21:19:56 +01:00
|
|
|
* the provided surface as the cursor image. It will set the
|
|
|
|
|
* hardware cursor on the output that it's currently on and
|
2021-09-21 22:05:56 +01:00
|
|
|
* continue to do so as the cursor moves between outputs.
|
|
|
|
|
*/
|
2020-10-02 21:19:56 +01:00
|
|
|
wlr_cursor_set_surface(seat->cursor, event->surface,
|
|
|
|
|
event->hotspot_x, event->hotspot_y);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
request_set_selection_notify(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct seat *seat = wl_container_of(
|
|
|
|
|
listener, seat, request_set_selection);
|
|
|
|
|
struct wlr_seat_request_set_selection_event *event = data;
|
|
|
|
|
wlr_seat_set_selection(seat->seat, event->source,
|
|
|
|
|
event->serial);
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-18 23:41:07 +01:00
|
|
|
static void
|
|
|
|
|
request_set_primary_selection_notify(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct seat *seat = wl_container_of(
|
|
|
|
|
listener, seat, request_set_primary_selection);
|
|
|
|
|
struct wlr_seat_request_set_primary_selection_event *event = data;
|
|
|
|
|
wlr_seat_set_primary_selection(seat->seat, event->source,
|
|
|
|
|
event->serial);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-03 13:33:38 -04:00
|
|
|
static void
|
|
|
|
|
request_start_drag_notify(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct seat *seat = wl_container_of(
|
|
|
|
|
listener, seat, request_start_drag);
|
|
|
|
|
struct wlr_seat_request_start_drag_event *event = data;
|
|
|
|
|
if (wlr_seat_validate_pointer_grab_serial(seat->seat, event->origin, event->serial)) {
|
|
|
|
|
wlr_seat_start_pointer_drag(seat->seat, event->drag, event->serial);
|
|
|
|
|
} else {
|
|
|
|
|
wlr_data_source_destroy(event->drag->source);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-28 20:41:41 +01:00
|
|
|
static void
|
|
|
|
|
process_cursor_move(struct server *server, uint32_t time)
|
2020-05-29 21:36:12 +01:00
|
|
|
{
|
2021-01-09 22:51:20 +00:00
|
|
|
damage_all_outputs(server);
|
2020-10-02 21:19:56 +01:00
|
|
|
double dx = server->seat.cursor->x - server->grab_x;
|
|
|
|
|
double dy = server->seat.cursor->y - server->grab_y;
|
2020-05-29 21:36:12 +01:00
|
|
|
struct view *view = server->grabbed_view;
|
2021-07-23 21:15:55 +01:00
|
|
|
|
|
|
|
|
/* Move the grabbed view to the new position. */
|
2021-10-23 22:31:39 -04:00
|
|
|
dx += server->grab_box.x;
|
|
|
|
|
dy += server->grab_box.y;
|
2021-10-24 21:31:05 -04:00
|
|
|
resistance_move_apply(view, &dx, &dy);
|
2021-10-23 22:31:39 -04:00
|
|
|
view_move(view, dx, dy);
|
2020-05-29 21:36:12 +01:00
|
|
|
}
|
|
|
|
|
|
2020-09-28 20:41:41 +01:00
|
|
|
static void
|
|
|
|
|
process_cursor_resize(struct server *server, uint32_t time)
|
2020-05-29 21:36:12 +01:00
|
|
|
{
|
2021-01-09 22:51:20 +00:00
|
|
|
damage_all_outputs(server);
|
2020-10-02 21:19:56 +01:00
|
|
|
double dx = server->seat.cursor->x - server->grab_x;
|
|
|
|
|
double dy = server->seat.cursor->y - server->grab_y;
|
2020-05-29 21:36:12 +01:00
|
|
|
|
|
|
|
|
struct view *view = server->grabbed_view;
|
2020-09-15 20:41:01 +01:00
|
|
|
struct wlr_box new_view_geo = {
|
|
|
|
|
.x = view->x, .y = view->y, .width = view->w, .height = view->h
|
|
|
|
|
};
|
2020-05-29 21:36:12 +01:00
|
|
|
|
2021-10-17 18:32:27 +00:00
|
|
|
int min_width, min_height;
|
|
|
|
|
view_min_size(view, &min_width, &min_height);
|
|
|
|
|
|
2020-05-29 21:36:12 +01:00
|
|
|
if (server->resize_edges & WLR_EDGE_TOP) {
|
2021-10-17 18:32:27 +00:00
|
|
|
if (server->grab_box.height - dy < min_height) {
|
|
|
|
|
dy = server->grab_box.height - min_height;
|
|
|
|
|
}
|
2020-05-29 21:36:12 +01:00
|
|
|
new_view_geo.y = server->grab_box.y + dy;
|
|
|
|
|
new_view_geo.height = server->grab_box.height - dy;
|
|
|
|
|
} else if (server->resize_edges & WLR_EDGE_BOTTOM) {
|
2021-10-17 18:32:27 +00:00
|
|
|
if (server->grab_box.height + dy < min_height) {
|
|
|
|
|
dy = min_height - server->grab_box.height;
|
|
|
|
|
}
|
2020-05-29 21:36:12 +01:00
|
|
|
new_view_geo.height = server->grab_box.height + dy;
|
|
|
|
|
}
|
|
|
|
|
if (server->resize_edges & WLR_EDGE_LEFT) {
|
2021-10-17 18:32:27 +00:00
|
|
|
if (server->grab_box.width - dx < min_width) {
|
|
|
|
|
dx = server->grab_box.width - min_width;
|
|
|
|
|
}
|
2020-05-29 21:36:12 +01:00
|
|
|
new_view_geo.x = server->grab_box.x + dx;
|
|
|
|
|
new_view_geo.width = server->grab_box.width - dx;
|
|
|
|
|
} else if (server->resize_edges & WLR_EDGE_RIGHT) {
|
2021-10-17 18:32:27 +00:00
|
|
|
if (server->grab_box.width + dx < min_width) {
|
|
|
|
|
dx = min_width - server->grab_box.width;
|
|
|
|
|
}
|
2020-05-29 21:36:12 +01:00
|
|
|
new_view_geo.width = server->grab_box.width + dx;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-24 21:31:05 -04:00
|
|
|
resistance_resize_apply(view, &new_view_geo);
|
2020-12-22 20:35:06 +00:00
|
|
|
view_move_resize(view, new_view_geo);
|
2020-05-29 21:36:12 +01:00
|
|
|
}
|
|
|
|
|
|
2021-11-28 21:47:24 +00:00
|
|
|
void
|
|
|
|
|
cursor_set(struct seat *seat, const char *cursor_name)
|
2021-07-30 14:26:54 +01:00
|
|
|
{
|
|
|
|
|
wlr_xcursor_manager_set_cursor_image(
|
2021-11-28 21:47:24 +00:00
|
|
|
seat->xcursor_manager, cursor_name, seat->cursor);
|
2021-07-30 14:26:54 +01:00
|
|
|
}
|
|
|
|
|
|
2021-08-25 19:59:49 +01:00
|
|
|
bool
|
|
|
|
|
input_inhibit_blocks_surface(struct seat *seat, struct wl_resource *resource)
|
2021-08-21 17:12:02 +01:00
|
|
|
{
|
2021-08-25 19:59:49 +01:00
|
|
|
struct wl_client *inhibiting_client =
|
2021-08-21 17:12:02 +01:00
|
|
|
seat->active_client_while_inhibited;
|
|
|
|
|
return (inhibiting_client != NULL) &&
|
|
|
|
|
inhibiting_client != wl_resource_get_client(resource);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-28 20:41:41 +01:00
|
|
|
static void
|
|
|
|
|
process_cursor_motion(struct server *server, uint32_t time)
|
2020-05-29 21:36:12 +01:00
|
|
|
{
|
2021-02-27 19:26:13 +00:00
|
|
|
static bool cursor_name_set_by_server;
|
|
|
|
|
|
2020-05-29 21:36:12 +01:00
|
|
|
/* If the mode is non-passthrough, delegate to those functions. */
|
2020-10-21 20:30:06 +01:00
|
|
|
if (server->input_mode == LAB_INPUT_STATE_MOVE) {
|
2020-05-29 21:36:12 +01:00
|
|
|
process_cursor_move(server, time);
|
|
|
|
|
return;
|
2020-10-21 20:30:06 +01:00
|
|
|
} else if (server->input_mode == LAB_INPUT_STATE_RESIZE) {
|
2020-05-29 21:36:12 +01:00
|
|
|
process_cursor_resize(server, time);
|
|
|
|
|
return;
|
2020-10-21 20:30:06 +01:00
|
|
|
} else if (server->input_mode == LAB_INPUT_STATE_MENU) {
|
2020-10-19 22:14:17 +01:00
|
|
|
menu_set_selected(server->rootmenu,
|
|
|
|
|
server->seat.cursor->x, server->seat.cursor->y);
|
2021-01-09 22:51:20 +00:00
|
|
|
damage_all_outputs(server);
|
2020-10-19 22:14:17 +01:00
|
|
|
return;
|
2020-05-29 21:36:12 +01:00
|
|
|
}
|
|
|
|
|
|
2021-09-25 10:04:37 +01:00
|
|
|
/* Otherwise, find view under the pointer and send the event along */
|
2020-05-29 21:36:12 +01:00
|
|
|
double sx, sy;
|
2020-10-02 21:19:56 +01:00
|
|
|
struct wlr_seat *wlr_seat = server->seat.seat;
|
2020-05-29 21:36:12 +01:00
|
|
|
struct wlr_surface *surface = NULL;
|
2021-03-20 14:41:39 +00:00
|
|
|
int view_area = LAB_SSD_NONE;
|
2021-09-25 10:04:37 +01:00
|
|
|
struct view *view = desktop_surface_and_view_at(server,
|
|
|
|
|
server->seat.cursor->x, server->seat.cursor->y, &surface,
|
|
|
|
|
&sx, &sy, &view_area);
|
|
|
|
|
|
2020-05-29 21:36:12 +01:00
|
|
|
if (!view) {
|
2021-11-28 21:47:24 +00:00
|
|
|
cursor_set(&server->seat, XCURSOR_DEFAULT);
|
2020-11-29 01:20:01 +00:00
|
|
|
} else {
|
2021-07-26 20:06:52 +01:00
|
|
|
uint32_t resize_edges = ssd_resize_edges(view_area);
|
2021-02-27 19:26:13 +00:00
|
|
|
if (resize_edges) {
|
|
|
|
|
cursor_name_set_by_server = true;
|
2021-11-28 21:47:24 +00:00
|
|
|
cursor_set(&server->seat,
|
2021-09-21 22:05:56 +01:00
|
|
|
wlr_xcursor_get_resize_name(resize_edges));
|
2021-03-20 14:41:39 +00:00
|
|
|
} else if (view_area != LAB_SSD_NONE) {
|
2021-07-30 14:26:54 +01:00
|
|
|
/* title and buttons */
|
2021-11-28 21:47:24 +00:00
|
|
|
cursor_set(&server->seat, XCURSOR_DEFAULT);
|
2021-03-03 21:19:52 +00:00
|
|
|
cursor_name_set_by_server = true;
|
|
|
|
|
} else if (cursor_name_set_by_server) {
|
2021-11-28 21:47:24 +00:00
|
|
|
cursor_set(&server->seat, XCURSOR_DEFAULT);
|
2021-02-27 19:26:13 +00:00
|
|
|
cursor_name_set_by_server = false;
|
2020-11-29 01:20:01 +00:00
|
|
|
}
|
2020-05-29 21:36:12 +01:00
|
|
|
}
|
2021-07-30 14:26:54 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
if (view && rc.focus_follow_mouse) {
|
2021-10-16 19:53:00 +01:00
|
|
|
desktop_focus_and_activate_view(&server->seat, view);
|
2021-07-30 14:26:54 +01:00
|
|
|
if (rc.raise_on_focus) {
|
2021-10-16 19:44:54 +01:00
|
|
|
desktop_raise_view(view);
|
2021-07-30 14:26:54 +01:00
|
|
|
}
|
2020-05-29 21:36:12 +01:00
|
|
|
}
|
2020-11-29 01:20:01 +00:00
|
|
|
|
2021-02-15 18:04:04 +00:00
|
|
|
/* Required for iconify/maximize/close button mouse-over deco */
|
|
|
|
|
damage_all_outputs(server);
|
|
|
|
|
|
2021-08-21 17:12:02 +01:00
|
|
|
if (surface &&
|
2021-08-25 19:59:49 +01:00
|
|
|
!input_inhibit_blocks_surface(&server->seat, surface->resource)) {
|
2020-09-28 20:41:41 +01:00
|
|
|
bool focus_changed =
|
2020-10-02 21:19:56 +01:00
|
|
|
wlr_seat->pointer_state.focused_surface != surface;
|
2020-05-29 21:36:12 +01:00
|
|
|
/*
|
|
|
|
|
* "Enter" the surface if necessary. This lets the client know
|
|
|
|
|
* that the cursor has entered one of its surfaces.
|
|
|
|
|
*
|
|
|
|
|
* Note that this gives the surface "pointer focus", which is
|
|
|
|
|
* distinct from keyboard focus. You get pointer focus by moving
|
|
|
|
|
* the pointer over a window.
|
|
|
|
|
*/
|
2020-10-02 21:19:56 +01:00
|
|
|
wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy);
|
2021-10-03 13:33:38 -04:00
|
|
|
if (!focus_changed || server->seat.drag_icon) {
|
2020-09-28 20:41:41 +01:00
|
|
|
/*
|
|
|
|
|
* The enter event contains coordinates, so we only need
|
|
|
|
|
* to notify on motion if the focus did not change.
|
|
|
|
|
*/
|
2020-10-02 21:19:56 +01:00
|
|
|
wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy);
|
2020-05-29 21:36:12 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
2021-09-21 22:05:56 +01:00
|
|
|
/*
|
|
|
|
|
* Clear pointer focus so future button events and such are not
|
|
|
|
|
* sent to the last client to have the cursor over it.
|
|
|
|
|
*/
|
2020-10-02 21:19:56 +01:00
|
|
|
wlr_seat_pointer_clear_focus(wlr_seat);
|
2020-05-29 21:36:12 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-03 13:33:38 -04:00
|
|
|
void
|
|
|
|
|
start_drag(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct seat *seat = wl_container_of(listener, seat, start_drag);
|
|
|
|
|
struct wlr_drag *wlr_drag = data;
|
|
|
|
|
seat->drag_icon = wlr_drag->icon;
|
|
|
|
|
wl_signal_add(&seat->drag_icon->events.destroy, &seat->destroy_drag);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-17 16:54:35 -04:00
|
|
|
void
|
|
|
|
|
handle_constraint_commit(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct seat *seat = wl_container_of(listener, seat, constraint_commit);
|
|
|
|
|
struct wlr_pointer_constraint_v1 *constraint = seat->current_constraint;
|
|
|
|
|
assert(constraint->surface = data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
destroy_constraint(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct constraint *constraint = wl_container_of(listener, constraint,
|
|
|
|
|
destroy);
|
|
|
|
|
struct wlr_pointer_constraint_v1 *wlr_constraint = data;
|
|
|
|
|
struct seat *seat = constraint->seat;
|
|
|
|
|
|
|
|
|
|
wl_list_remove(&constraint->destroy.link);
|
|
|
|
|
if (seat->current_constraint == wlr_constraint) {
|
|
|
|
|
if (seat->constraint_commit.link.next != NULL) {
|
|
|
|
|
wl_list_remove(&seat->constraint_commit.link);
|
|
|
|
|
}
|
|
|
|
|
wl_list_init(&seat->constraint_commit.link);
|
|
|
|
|
seat->current_constraint = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(constraint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
create_constraint(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct wlr_pointer_constraint_v1 *wlr_constraint = data;
|
|
|
|
|
struct server *server = wl_container_of(listener, server,
|
|
|
|
|
new_constraint);
|
|
|
|
|
struct view *view;
|
|
|
|
|
struct constraint *constraint = calloc(1, sizeof(struct constraint));
|
|
|
|
|
|
|
|
|
|
constraint->constraint = wlr_constraint;
|
|
|
|
|
constraint->seat = &server->seat;
|
|
|
|
|
constraint->destroy.notify = destroy_constraint;
|
|
|
|
|
wl_signal_add(&wlr_constraint->events.destroy, &constraint->destroy);
|
|
|
|
|
|
|
|
|
|
view = desktop_focused_view(server);
|
|
|
|
|
if (view->surface == wlr_constraint->surface) {
|
|
|
|
|
constrain_cursor(server, wlr_constraint);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
constrain_cursor(struct server *server, struct wlr_pointer_constraint_v1
|
|
|
|
|
*constraint)
|
|
|
|
|
{
|
|
|
|
|
struct seat *seat = &server->seat;
|
|
|
|
|
if (seat->current_constraint == constraint) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
wl_list_remove(&seat->constraint_commit.link);
|
|
|
|
|
if (seat->current_constraint) {
|
|
|
|
|
wlr_pointer_constraint_v1_send_deactivated(
|
|
|
|
|
seat->current_constraint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
seat->current_constraint = constraint;
|
|
|
|
|
|
|
|
|
|
if (constraint == NULL) {
|
|
|
|
|
wl_list_init(&seat->constraint_commit.link);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wlr_pointer_constraint_v1_send_activated(constraint);
|
|
|
|
|
seat->constraint_commit.notify = handle_constraint_commit;
|
|
|
|
|
wl_signal_add(&constraint->surface->events.commit,
|
|
|
|
|
&seat->constraint_commit);
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-24 20:53:22 +01:00
|
|
|
static void
|
2020-09-28 20:41:41 +01:00
|
|
|
cursor_motion(struct wl_listener *listener, void *data)
|
2020-05-29 21:36:12 +01:00
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* This event is forwarded by the cursor when a pointer emits a
|
|
|
|
|
* _relative_ pointer motion event (i.e. a delta)
|
|
|
|
|
*/
|
2020-10-02 21:19:56 +01:00
|
|
|
struct seat *seat = wl_container_of(listener, seat, cursor_motion);
|
2021-10-17 16:54:35 -04:00
|
|
|
struct server *server = seat->server;
|
2020-05-29 21:36:12 +01:00
|
|
|
struct wlr_event_pointer_motion *event = data;
|
2021-11-22 16:35:57 -05:00
|
|
|
wlr_idle_notify_activity(seat->wlr_idle, seat->seat);
|
2020-05-29 21:36:12 +01:00
|
|
|
|
2021-10-17 16:54:35 -04:00
|
|
|
wlr_relative_pointer_manager_v1_send_relative_motion(
|
|
|
|
|
server->relative_pointer_manager,
|
|
|
|
|
seat->seat, (uint64_t)event->time_msec * 1000,
|
|
|
|
|
event->delta_x, event->delta_y, event->unaccel_dx,
|
|
|
|
|
event->unaccel_dy);
|
|
|
|
|
if (!seat->current_constraint) {
|
|
|
|
|
/*
|
|
|
|
|
* The cursor doesn't move unless we tell it to. The cursor
|
|
|
|
|
* automatically handles constraining the motion to the output layout,
|
|
|
|
|
* as well as any special configuration applied for the specific input
|
|
|
|
|
* device which generated the event. You can pass NULL for the device
|
|
|
|
|
* if you want to move the cursor around without any input.
|
|
|
|
|
*/
|
|
|
|
|
wlr_cursor_move(seat->cursor, event->device, event->delta_x,
|
|
|
|
|
event->delta_y);
|
|
|
|
|
}
|
2020-10-02 21:19:56 +01:00
|
|
|
process_cursor_motion(seat->server, event->time_msec);
|
2020-05-29 21:36:12 +01:00
|
|
|
}
|
|
|
|
|
|
2021-10-03 13:33:38 -04:00
|
|
|
void
|
|
|
|
|
destroy_drag(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct seat *seat = wl_container_of(listener, seat, destroy_drag);
|
|
|
|
|
|
|
|
|
|
if (!seat->drag_icon) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
seat->drag_icon = NULL;
|
|
|
|
|
desktop_focus_topmost_mapped_view(seat->server);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-28 20:41:41 +01:00
|
|
|
void
|
|
|
|
|
cursor_motion_absolute(struct wl_listener *listener, void *data)
|
2020-05-29 21:36:12 +01:00
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* This event is forwarded by the cursor when a pointer emits an
|
|
|
|
|
* _absolute_ motion event, from 0..1 on each axis. This happens, for
|
|
|
|
|
* example, when wlroots is running under a Wayland window rather than
|
|
|
|
|
* KMS+DRM, and you move the mouse over the window. You could enter the
|
|
|
|
|
* window from any edge, so we have to warp the mouse there. There is
|
|
|
|
|
* also some hardware which emits these events.
|
|
|
|
|
*/
|
2020-10-02 21:19:56 +01:00
|
|
|
struct seat *seat = wl_container_of(
|
|
|
|
|
listener, seat, cursor_motion_absolute);
|
2020-05-29 21:36:12 +01:00
|
|
|
struct wlr_event_pointer_motion_absolute *event = data;
|
2021-11-22 16:35:57 -05:00
|
|
|
wlr_idle_notify_activity(seat->wlr_idle, seat->seat);
|
2021-10-17 21:57:10 +00:00
|
|
|
|
|
|
|
|
double lx, ly;
|
|
|
|
|
wlr_cursor_absolute_to_layout_coords(seat->cursor, event->device,
|
|
|
|
|
event->x, event->y, &lx, &ly);
|
|
|
|
|
|
|
|
|
|
double dx = lx - seat->cursor->x;
|
|
|
|
|
double dy = ly - seat->cursor->y;
|
|
|
|
|
|
|
|
|
|
wlr_relative_pointer_manager_v1_send_relative_motion(
|
|
|
|
|
seat->server->relative_pointer_manager,
|
|
|
|
|
seat->seat, (uint64_t)event->time_msec * 1000,
|
|
|
|
|
dx, dy, dx, dy);
|
|
|
|
|
|
|
|
|
|
if (!seat->current_constraint) {
|
|
|
|
|
/*
|
|
|
|
|
* The cursor doesn't move unless we tell it to. The cursor
|
|
|
|
|
* automatically handles constraining the motion to the output layout,
|
|
|
|
|
* as well as any special configuration applied for the specific input
|
|
|
|
|
* device which generated the event. You can pass NULL for the device
|
|
|
|
|
* if you want to move the cursor around without any input.
|
|
|
|
|
*/
|
|
|
|
|
wlr_cursor_move(seat->cursor, event->device, dx, dy);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-02 21:19:56 +01:00
|
|
|
process_cursor_motion(seat->server, event->time_msec);
|
2020-05-29 21:36:12 +01:00
|
|
|
}
|
|
|
|
|
|
2021-07-09 21:58:54 +01:00
|
|
|
static void
|
|
|
|
|
handle_cursor_button_with_meta_key(struct view *view, uint32_t button,
|
|
|
|
|
double lx, double ly)
|
|
|
|
|
{
|
2021-07-21 19:27:11 +01:00
|
|
|
if (!view) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-09 21:58:54 +01:00
|
|
|
/* move */
|
|
|
|
|
if (button == BTN_LEFT) {
|
|
|
|
|
interactive_begin(view, LAB_INPUT_STATE_MOVE, 0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* resize */
|
|
|
|
|
uint32_t edges;
|
|
|
|
|
edges = lx < view->x + view->w / 2 ? WLR_EDGE_LEFT : WLR_EDGE_RIGHT;
|
|
|
|
|
edges |= ly < view->y + view->h / 2 ? WLR_EDGE_TOP : WLR_EDGE_BOTTOM;
|
|
|
|
|
interactive_begin(view, LAB_INPUT_STATE_RESIZE, edges);
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-01 20:32:14 -04:00
|
|
|
static void
|
|
|
|
|
handle_release_mousebinding(struct server *server, uint32_t button, enum ssd_part_type view_area)
|
|
|
|
|
{
|
|
|
|
|
struct mousebind *mousebind;
|
|
|
|
|
wl_list_for_each_reverse(mousebind, &rc.mousebinds, link) {
|
|
|
|
|
if (mousebind->context == view_area
|
|
|
|
|
&& mousebind->button == button) {
|
|
|
|
|
if (mousebind->mouse_event
|
|
|
|
|
== MOUSE_ACTION_RELEASE) {
|
|
|
|
|
action(server, mousebind->action,
|
|
|
|
|
mousebind->command);
|
|
|
|
|
}
|
|
|
|
|
if (mousebind->pressed_in_context) {
|
|
|
|
|
mousebind->pressed_in_context = false;
|
|
|
|
|
action(server, mousebind->action,
|
|
|
|
|
mousebind->command);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-02 17:30:34 +01:00
|
|
|
static bool
|
|
|
|
|
is_double_click(long double_click_speed)
|
|
|
|
|
{
|
|
|
|
|
static struct timespec last_double_click;
|
|
|
|
|
struct timespec now;
|
|
|
|
|
|
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
|
long ms = (now.tv_sec - last_double_click.tv_sec) * 1000 +
|
|
|
|
|
(now.tv_nsec - last_double_click.tv_nsec) / 1000000;
|
|
|
|
|
last_double_click = now;
|
|
|
|
|
return ms < double_click_speed && ms >= 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-01 20:32:14 -04:00
|
|
|
static bool
|
|
|
|
|
handle_press_mousebinding(struct server *server, uint32_t button, enum ssd_part_type view_area)
|
|
|
|
|
{
|
|
|
|
|
struct mousebind *mousebind;
|
|
|
|
|
bool double_click = is_double_click(rc.doubleclick_time);
|
|
|
|
|
bool bound;
|
|
|
|
|
|
|
|
|
|
wl_list_for_each_reverse(mousebind, &rc.mousebinds, link) {
|
|
|
|
|
if (mousebind->context == view_area
|
|
|
|
|
&& mousebind->button == button) {
|
|
|
|
|
if (mousebind->mouse_event
|
|
|
|
|
== MOUSE_ACTION_PRESS) {
|
|
|
|
|
bound = true;
|
|
|
|
|
action(server, mousebind->action,
|
|
|
|
|
mousebind->command);
|
|
|
|
|
} else if (mousebind->mouse_event
|
|
|
|
|
== MOUSE_ACTION_CLICK) {
|
|
|
|
|
bound = true;
|
|
|
|
|
mousebind->pressed_in_context = true;
|
|
|
|
|
} else if (double_click
|
|
|
|
|
&& mousebind->mouse_event
|
|
|
|
|
== MOUSE_ACTION_DOUBLECLICK) {
|
|
|
|
|
bound = true;
|
|
|
|
|
action(server, mousebind->action,
|
|
|
|
|
mousebind->command);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return bound;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-28 20:41:41 +01:00
|
|
|
void
|
|
|
|
|
cursor_button(struct wl_listener *listener, void *data)
|
2020-05-29 21:36:12 +01:00
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* This event is forwarded by the cursor when a pointer emits a button
|
|
|
|
|
* event.
|
|
|
|
|
*/
|
2020-10-02 21:19:56 +01:00
|
|
|
struct seat *seat = wl_container_of(listener, seat, cursor_button);
|
|
|
|
|
struct server *server = seat->server;
|
2020-05-29 21:36:12 +01:00
|
|
|
struct wlr_event_pointer_button *event = data;
|
2021-11-22 16:35:57 -05:00
|
|
|
wlr_idle_notify_activity(seat->wlr_idle, seat->seat);
|
2020-05-29 21:36:12 +01:00
|
|
|
|
|
|
|
|
double sx, sy;
|
|
|
|
|
struct wlr_surface *surface;
|
2021-07-12 19:59:19 +01:00
|
|
|
int view_area = LAB_SSD_NONE;
|
2020-11-29 01:20:01 +00:00
|
|
|
uint32_t resize_edges;
|
2021-07-12 16:44:30 +01:00
|
|
|
|
2021-09-25 10:04:37 +01:00
|
|
|
struct view *view = desktop_surface_and_view_at(server,
|
|
|
|
|
server->seat.cursor->x, server->seat.cursor->y, &surface,
|
|
|
|
|
&sx, &sy, &view_area);
|
2020-10-19 22:14:17 +01:00
|
|
|
|
2021-07-09 22:29:48 +01:00
|
|
|
/* handle alt + _press_ on view */
|
|
|
|
|
struct wlr_input_device *device = seat->keyboard_group->input_device;
|
|
|
|
|
uint32_t modifiers = wlr_keyboard_get_modifiers(device->keyboard);
|
2021-10-17 22:03:11 +00:00
|
|
|
if (modifiers & WLR_MODIFIER_ALT && event->state == WLR_BUTTON_PRESSED && !seat->current_constraint) {
|
2021-07-09 22:29:48 +01:00
|
|
|
handle_cursor_button_with_meta_key(view, event->button,
|
|
|
|
|
server->seat.cursor->x, server->seat.cursor->y);
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Notify client with pointer focus of button press */
|
|
|
|
|
wlr_seat_pointer_notify_button(seat->seat, event->time_msec,
|
|
|
|
|
event->button, event->state);
|
|
|
|
|
|
2020-10-19 22:14:17 +01:00
|
|
|
/* handle _release_ */
|
2020-05-29 21:36:12 +01:00
|
|
|
if (event->state == WLR_BUTTON_RELEASED) {
|
2020-10-21 20:30:06 +01:00
|
|
|
if (server->input_mode == LAB_INPUT_STATE_MENU) {
|
2020-10-19 22:14:17 +01:00
|
|
|
return;
|
2020-05-29 21:36:12 +01:00
|
|
|
}
|
2021-01-09 22:51:20 +00:00
|
|
|
damage_all_outputs(server);
|
2021-11-01 20:32:14 -04:00
|
|
|
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
|
|
|
|
|
/* Exit interactive move/resize/menu mode. */
|
|
|
|
|
server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
goto mousebindings;
|
2020-10-19 22:14:17 +01:00
|
|
|
}
|
|
|
|
|
|
2020-10-21 20:30:06 +01:00
|
|
|
if (server->input_mode == LAB_INPUT_STATE_MENU) {
|
2020-10-19 22:14:17 +01:00
|
|
|
menu_action_selected(server, server->rootmenu);
|
2020-10-21 20:30:06 +01:00
|
|
|
server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
|
2020-10-19 22:14:17 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-12 16:44:30 +01:00
|
|
|
/* Handle _press_ on a layer surface */
|
|
|
|
|
if (!view && surface) {
|
2021-10-21 20:37:30 +01:00
|
|
|
if (!wlr_surface_is_layer_surface(surface)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-08-02 17:30:34 +01:00
|
|
|
struct wlr_layer_surface_v1 *layer =
|
|
|
|
|
wlr_layer_surface_v1_from_wlr_surface(surface);
|
2021-07-12 16:44:30 +01:00
|
|
|
if (layer->current.keyboard_interactive) {
|
|
|
|
|
seat_set_focus_layer(&server->seat, layer);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Handle _press_ on root window */
|
2020-10-19 22:14:17 +01:00
|
|
|
if (!view) {
|
2020-10-31 15:27:22 +00:00
|
|
|
action(server, "ShowMenu", "root-menu");
|
2020-10-19 22:14:17 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Handle _press_ on view */
|
2021-10-16 19:44:54 +01:00
|
|
|
desktop_focus_and_activate_view(&server->seat, view);
|
|
|
|
|
desktop_raise_view(view);
|
2021-03-26 20:12:01 +00:00
|
|
|
damage_all_outputs(server);
|
2020-11-29 01:20:01 +00:00
|
|
|
|
2021-07-26 20:06:52 +01:00
|
|
|
resize_edges = ssd_resize_edges(view_area);
|
2021-08-02 17:30:34 +01:00
|
|
|
if (resize_edges) {
|
2020-11-29 01:20:01 +00:00
|
|
|
interactive_begin(view, LAB_INPUT_STATE_RESIZE, resize_edges);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-01 20:32:14 -04:00
|
|
|
mousebindings:
|
|
|
|
|
if (event->state == WLR_BUTTON_RELEASED) {
|
|
|
|
|
handle_release_mousebinding(server, event->button, view_area);
|
|
|
|
|
} else if (event->state == WLR_BUTTON_PRESSED) {
|
2021-11-26 13:03:15 -05:00
|
|
|
handle_press_mousebinding(server, event->button, view_area);
|
2020-05-29 21:36:12 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-28 20:41:41 +01:00
|
|
|
void
|
|
|
|
|
cursor_axis(struct wl_listener *listener, void *data)
|
2020-05-29 21:36:12 +01:00
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* This event is forwarded by the cursor when a pointer emits an axis
|
|
|
|
|
* event, for example when you move the scroll wheel.
|
|
|
|
|
*/
|
2020-10-02 21:19:56 +01:00
|
|
|
struct seat *seat = wl_container_of(listener, seat, cursor_axis);
|
2020-05-29 21:36:12 +01:00
|
|
|
struct wlr_event_pointer_axis *event = data;
|
2021-11-22 16:35:57 -05:00
|
|
|
wlr_idle_notify_activity(seat->wlr_idle, seat->seat);
|
2020-05-29 21:36:12 +01:00
|
|
|
|
|
|
|
|
/* Notify the client with pointer focus of the axis event. */
|
2020-10-02 21:19:56 +01:00
|
|
|
wlr_seat_pointer_notify_axis(seat->seat, event->time_msec,
|
|
|
|
|
event->orientation, event->delta, event->delta_discrete,
|
|
|
|
|
event->source);
|
2020-05-29 21:36:12 +01:00
|
|
|
}
|
|
|
|
|
|
2020-09-28 20:41:41 +01:00
|
|
|
void
|
|
|
|
|
cursor_frame(struct wl_listener *listener, void *data)
|
2020-05-29 21:36:12 +01:00
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* This event is forwarded by the cursor when a pointer emits an frame
|
|
|
|
|
* event. Frame events are sent after regular pointer events to group
|
|
|
|
|
* multiple events together. For instance, two axis events may happen
|
|
|
|
|
* at the same time, in which case a frame event won't be sent in
|
|
|
|
|
* between.
|
|
|
|
|
*/
|
2020-10-02 21:19:56 +01:00
|
|
|
struct seat *seat = wl_container_of(listener, seat, cursor_frame);
|
2020-05-29 21:36:12 +01:00
|
|
|
/* Notify the client with pointer focus of the frame event. */
|
2020-10-02 21:19:56 +01:00
|
|
|
wlr_seat_pointer_notify_frame(seat->seat);
|
2020-05-29 21:36:12 +01:00
|
|
|
}
|
2020-05-29 22:18:03 +01:00
|
|
|
|
2020-09-28 20:41:41 +01:00
|
|
|
void
|
2020-10-02 21:19:56 +01:00
|
|
|
cursor_init(struct seat *seat)
|
2020-05-29 22:18:03 +01:00
|
|
|
{
|
2020-10-02 21:19:56 +01:00
|
|
|
seat->xcursor_manager = wlr_xcursor_manager_create(NULL, 24);
|
|
|
|
|
wlr_xcursor_manager_load(seat->xcursor_manager, 1);
|
|
|
|
|
|
|
|
|
|
seat->cursor_motion.notify = cursor_motion;
|
|
|
|
|
wl_signal_add(&seat->cursor->events.motion, &seat->cursor_motion);
|
|
|
|
|
seat->cursor_motion_absolute.notify = cursor_motion_absolute;
|
|
|
|
|
wl_signal_add(&seat->cursor->events.motion_absolute,
|
|
|
|
|
&seat->cursor_motion_absolute);
|
|
|
|
|
seat->cursor_button.notify = cursor_button;
|
|
|
|
|
wl_signal_add(&seat->cursor->events.button, &seat->cursor_button);
|
|
|
|
|
seat->cursor_axis.notify = cursor_axis;
|
|
|
|
|
wl_signal_add(&seat->cursor->events.axis, &seat->cursor_axis);
|
|
|
|
|
seat->cursor_frame.notify = cursor_frame;
|
|
|
|
|
wl_signal_add(&seat->cursor->events.frame, &seat->cursor_frame);
|
|
|
|
|
|
|
|
|
|
seat->request_cursor.notify = request_cursor_notify;
|
2021-08-25 19:59:49 +01:00
|
|
|
wl_signal_add(&seat->seat->events.request_set_cursor,
|
|
|
|
|
&seat->request_cursor);
|
2020-10-02 21:19:56 +01:00
|
|
|
seat->request_set_selection.notify = request_set_selection_notify;
|
2021-08-25 19:59:49 +01:00
|
|
|
wl_signal_add(&seat->seat->events.request_set_selection,
|
|
|
|
|
&seat->request_set_selection);
|
2021-10-03 13:33:38 -04:00
|
|
|
seat->request_start_drag.notify = request_start_drag_notify;
|
|
|
|
|
wl_signal_add(&seat->seat->events.request_start_drag,
|
|
|
|
|
&seat->request_start_drag);
|
|
|
|
|
seat->start_drag.notify = start_drag;
|
|
|
|
|
wl_signal_add(&seat->seat->events.start_drag,
|
|
|
|
|
&seat->start_drag);
|
|
|
|
|
seat->destroy_drag.notify = destroy_drag;
|
2020-10-02 21:19:56 +01:00
|
|
|
|
2021-08-18 23:41:07 +01:00
|
|
|
seat->request_set_primary_selection.notify =
|
|
|
|
|
request_set_primary_selection_notify;
|
|
|
|
|
wl_signal_add(&seat->seat->events.request_set_primary_selection,
|
|
|
|
|
&seat->request_set_primary_selection);
|
2020-05-29 22:18:03 +01:00
|
|
|
}
|