diyac/cursor.c

321 lines
12 KiB
C
Raw Normal View History

2024-03-30 00:18:51 +01:00
#define _POSIX_C_SOURCE 200112L
2024-03-31 16:09:01 +02:00
#include <wlr/util/log.h>
#include <assert.h>
2024-03-30 00:18:51 +01:00
#include "cursor.h"
2024-04-01 15:38:57 +02:00
#include "view.h"
#include "seat.h"
#include "node.h"
void diyac_cursor_focus(struct diyac_server *server)
{
double sx, sy;
struct wlr_surface *surface = NULL;
struct diyac_node_descriptor *desc = diyac_node_at(server,
server->seat.cursor->x, server->seat.cursor->y, &surface, &sx, &sy);
struct diyac_layer_surface *layer;
struct diyac_view *root = NULL;
if (!desc)
2024-04-01 15:38:57 +02:00
{
return;
}
/* Focus that client if the button was _pressed_ */
switch (desc->type)
{
case DIYAC_NODE_VIEW:
2024-04-01 16:10:22 +02:00
root = diyac_get_root_view(desc->data);
if (root)
2024-04-01 16:10:22 +02:00
{
diyac_focus_view(root, true);
}
else
{
diyac_focus_view(desc->data, true);
}
2024-04-01 15:38:57 +02:00
break;
case DIYAC_NODE_LAYER_SURFACE:
layer = desc->data;
assert(layer);
assert(layer->scene_layer_surface);
diyac_seat_focus_layer(&server->seat, layer->scene_layer_surface->layer_surface);
2024-04-01 15:38:57 +02:00
break;
default:
break;
}
}
2024-03-30 00:18:51 +01:00
void diyac_reset_cursor_mode(struct diyac_server *server)
{
/* Reset the cursor mode to passthrough. */
server->seat.cursor_mode = DIYAC_CURSOR_PASSTHROUGH;
2024-03-31 16:09:01 +02:00
server->grabbed_view = NULL;
2024-03-30 00:18:51 +01:00
}
static void process_cursor_move(struct diyac_server *server, uint32_t time)
{
2024-03-31 16:09:01 +02:00
struct diyac_view *toplevel = server->grabbed_view;
2024-04-02 23:00:11 +02:00
/* Move the grabbed toplevel to the new position. */
if (toplevel->state.fullscreen)
2024-04-02 23:00:11 +02:00
{
return;
}
if (toplevel->state.maximized)
2024-04-01 15:38:57 +02:00
{
toplevel->requested.maximized = false;
2024-04-01 15:38:57 +02:00
// move the windows to cursor
server->grab_x = toplevel->original.width * server->grab_x / toplevel->output->usable_area.width;
}
toplevel->original.y = server->seat.cursor->y - server->grab_y;
toplevel->original.x = server->seat.cursor->x - server->grab_x;
2024-04-01 15:38:57 +02:00
diyac_view_update_geometry(toplevel, true);
2024-03-31 16:09:01 +02:00
/*
2024-03-30 00:18:51 +01:00
wlr_scene_node_set_position(&toplevel->scene_tree->node,
2024-03-31 16:09:01 +02:00
toplevel->current.x ,
toplevel->current.y);
*/
2024-03-30 00:18:51 +01:00
}
static void process_cursor_resize(struct diyac_server *server, uint32_t time)
{
/*
* Resizing the grabbed toplevel can be a little bit complicated, because we
* could be resizing from any corner or edge. This not only resizes the
* toplevel on one or two axes, but can also move the toplevel if you resize
* from the top or left edges (or top-left corner).
*
* Note that some shortcuts are taken here. In a more fleshed-out
* compositor, you'd wait for the client to prepare a buffer at the new
* size, then commit any movement that was prepared.
*/
2024-03-31 16:09:01 +02:00
struct diyac_view *toplevel = server->grabbed_view;
if(toplevel->state.fullscreen)
{
return;
}
2024-03-30 00:18:51 +01:00
double border_x = server->seat.cursor->x - server->grab_x;
double border_y = server->seat.cursor->y - server->grab_y;
int new_left = server->grab_geobox.x;
int new_right = server->grab_geobox.x + server->grab_geobox.width;
int new_top = server->grab_geobox.y;
int new_bottom = server->grab_geobox.y + server->grab_geobox.height;
if (server->resize_edges & WLR_EDGE_TOP)
{
new_top = border_y;
if (new_top >= new_bottom)
{
new_top = new_bottom - 1;
}
}
else if (server->resize_edges & WLR_EDGE_BOTTOM)
{
new_bottom = border_y;
if (new_bottom <= new_top)
{
new_bottom = new_top + 1;
}
}
if (server->resize_edges & WLR_EDGE_LEFT)
{
new_left = border_x;
if (new_left >= new_right)
{
new_left = new_right - 1;
}
}
else if (server->resize_edges & WLR_EDGE_RIGHT)
{
new_right = border_x;
if (new_right <= new_left)
{
new_right = new_left + 1;
}
}
struct wlr_box geo_box;
wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box);
2024-04-01 15:38:57 +02:00
toplevel->original.x = new_left - geo_box.x;
toplevel->original.y = new_top - geo_box.y;
2024-03-30 00:18:51 +01:00
int new_width = new_right - new_left;
int new_height = new_bottom - new_top;
2024-04-01 15:38:57 +02:00
toplevel->original.width = new_width;
toplevel->original.height = new_height;
toplevel->requested.maximized = false;
2024-04-01 15:38:57 +02:00
diyac_view_update_geometry(toplevel, false);
2024-03-31 16:09:01 +02:00
/*
wlr_scene_node_set_position(&toplevel->scene_tree->node,
toplevel->current.x, toplevel->current.y);
2024-03-30 00:18:51 +01:00
wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, new_width, new_height);
2024-03-31 16:09:01 +02:00
*/
2024-03-30 00:18:51 +01:00
}
static void process_cursor_motion(struct diyac_server *server, uint32_t time)
{
/* If the mode is non-passthrough, delegate to those functions. */
if (server->seat.cursor_mode == DIYAC_CURSOR_MOVE)
{
process_cursor_move(server, time);
return;
}
else if (server->seat.cursor_mode == DIYAC_CURSOR_RESIZE)
{
process_cursor_resize(server, time);
return;
}
/* Otherwise, find the toplevel under the pointer and send the event along. */
double sx, sy;
struct wlr_seat *seat = server->seat.wlr_seat;
struct wlr_surface *surface = NULL;
2024-04-01 15:38:57 +02:00
struct diyac_node_descriptor *desc = diyac_node_at(server,
server->seat.cursor->x, server->seat.cursor->y, &surface, &sx, &sy);
2024-04-01 15:38:57 +02:00
if (!desc)
2024-03-30 00:18:51 +01:00
{
/* If there's no toplevel under the cursor, set the cursor image to a
* default. This is what makes the cursor image appear when you move it
* around the screen, not over any toplevels. */
wlr_cursor_set_xcursor(server->seat.cursor, server->seat.cursor_mgr, "default");
}
if (surface)
{
/*
* Send pointer enter and motion events.
*
* The enter event gives the surface "pointer focus", which is distinct
* from keyboard focus. You get pointer focus by moving the pointer over
* a window.
*
* Note that wlroots will avoid sending duplicate enter/motion events if
* the surface has already has pointer focus or if the client is already
* aware of the coordinates passed.
*/
wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
wlr_seat_pointer_notify_motion(seat, time, sx, sy);
}
else
{
/* Clear pointer focus so future button events and such are not sent to
* the last client to have the cursor over it. */
wlr_seat_pointer_clear_focus(seat);
}
}
static void server_cursor_motion(struct wl_listener *listener, void *data)
{
/* This event is forwarded by the cursor when a pointer emits a _relative_
* pointer motion event (i.e. a delta) */
struct diyac_seat *seat =
wl_container_of(listener, seat, cursor_motion);
struct wlr_pointer_motion_event *event = data;
/* 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->pointer->base,
event->delta_x, event->delta_y);
process_cursor_motion(seat->server, event->time_msec);
}
static void server_cursor_motion_absolute(
struct wl_listener *listener, void *data)
{
/* 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. */
struct diyac_seat *seat =
wl_container_of(listener, seat, cursor_motion_absolute);
struct wlr_pointer_motion_absolute_event *event = data;
wlr_cursor_warp_absolute(seat->cursor, &event->pointer->base, event->x,
event->y);
process_cursor_motion(seat->server, event->time_msec);
}
static void server_cursor_button(struct wl_listener *listener, void *data)
{
/* This event is forwarded by the cursor when a pointer emits a button
* event. */
struct diyac_seat *seat =
wl_container_of(listener, seat, cursor_button);
struct wlr_pointer_button_event *event = data;
/* Notify the client with pointer focus that a button press has occurred */
wlr_seat_pointer_notify_button(seat->wlr_seat,
event->time_msec, event->button, event->state);
2024-03-30 00:18:51 +01:00
if (event->state == WLR_BUTTON_RELEASED)
{
/* If you released any buttons, we exit interactive move/resize mode. */
diyac_reset_cursor_mode(seat->server);
}
else
{
2024-04-01 15:38:57 +02:00
diyac_cursor_focus(seat->server);
2024-03-30 00:18:51 +01:00
}
}
static void server_cursor_axis(struct wl_listener *listener, void *data)
{
/* This event is forwarded by the cursor when a pointer emits an axis event,
* for example when you move the scroll wheel. */
struct diyac_seat *seat =
wl_container_of(listener, seat, cursor_axis);
struct wlr_pointer_axis_event *event = data;
/* Notify the client with pointer focus of the axis event. */
wlr_seat_pointer_notify_axis(seat->wlr_seat,
event->time_msec, event->orientation, event->delta,
event->delta_discrete, event->source);
}
static void server_cursor_frame(struct wl_listener *listener, void *data)
{
/* 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. */
struct diyac_seat *seat =
wl_container_of(listener, seat, cursor_frame);
/* Notify the client with pointer focus of the frame event. */
wlr_seat_pointer_notify_frame(seat->wlr_seat);
}
void diyac_init_cursor_manager(struct diyac_server *server)
{
/*
* Creates a cursor, which is a wlroots utility for tracking the cursor
* image shown on screen.
*/
server->seat.cursor = wlr_cursor_create();
wlr_cursor_attach_output_layout(server->seat.cursor, server->output_layout);
/* Creates an xcursor manager, another wlroots utility which loads up
* Xcursor themes to source cursor images from and makes sure that cursor
* images are available at all scale factors on the screen (necessary for
* HiDPI support). */
server->seat.cursor_mgr = wlr_xcursor_manager_create(NULL, 24);
/*
* wlr_cursor *only* displays an image on screen. It does not move around
* when the pointer moves. However, we can attach input devices to it, and
* it will generate aggregate events for all of them. In these events, we
* can choose how we want to process them, forwarding them to clients and
* moving the cursor around. More detail on this process is described in
* https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html.
*
* And more comments are sprinkled throughout the notify functions above.
*/
server->seat.cursor_mode = DIYAC_CURSOR_PASSTHROUGH;
server->seat.cursor_motion.notify = server_cursor_motion;
wl_signal_add(&server->seat.cursor->events.motion, &server->seat.cursor_motion);
server->seat.cursor_motion_absolute.notify = server_cursor_motion_absolute;
wl_signal_add(&server->seat.cursor->events.motion_absolute,
&server->seat.cursor_motion_absolute);
server->seat.cursor_button.notify = server_cursor_button;
wl_signal_add(&server->seat.cursor->events.button, &server->seat.cursor_button);
server->seat.cursor_axis.notify = server_cursor_axis;
wl_signal_add(&server->seat.cursor->events.axis, &server->seat.cursor_axis);
server->seat.cursor_frame.notify = server_cursor_frame;
wl_signal_add(&server->seat.cursor->events.frame, &server->seat.cursor_frame);
}