From 40d3e1b17541c9f03d693a5dd1727ca466e658b4 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 29 May 2020 21:36:12 +0100 Subject: [PATCH] Add cursor.c --- src/cursor.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++ src/interactive.c | 1 - src/meson.build | 1 + src/server.c | 210 ------------------------------------------ 4 files changed, 228 insertions(+), 211 deletions(-) create mode 100644 src/cursor.c diff --git a/src/cursor.c b/src/cursor.c new file mode 100644 index 00000000..b0b29d4f --- /dev/null +++ b/src/cursor.c @@ -0,0 +1,227 @@ +#include "labwc.h" + +static void process_cursor_move(struct server *server, uint32_t time) +{ + /* Move the grabbed view to the new position. */ + double dx = server->cursor->x - server->grab_x; + double dy = server->cursor->y - server->grab_y; + server->grabbed_view->x = server->grab_box.x + dx; + server->grabbed_view->y = server->grab_box.y + dy; + + if (server->grabbed_view->type != LAB_XWAYLAND_VIEW) + return; + + struct view *view = server->grabbed_view; + wlr_xwayland_surface_configure(view->xwayland_surface, view->x, view->y, + view->xwayland_surface->width, + view->xwayland_surface->height); +} + +#define MIN_VIEW_WIDTH (100) +#define MIN_VIEW_HEIGHT (60) + +static void process_cursor_resize(struct server *server, uint32_t time) +{ + /* + * TODO: Wait for the client to prepare a buffer at the new size, then + * commit any movement that was prepared. + */ + double dx = server->cursor->x - server->grab_x; + double dy = server->cursor->y - server->grab_y; + + struct view *view = server->grabbed_view; + struct wlr_box new_view_geo = view_geometry(view); + + if (server->resize_edges & WLR_EDGE_TOP) { + 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) { + new_view_geo.height = server->grab_box.height + dy; + } + if (server->resize_edges & WLR_EDGE_LEFT) { + 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) { + new_view_geo.width = server->grab_box.width + dx; + } + if ((new_view_geo.height < MIN_VIEW_HEIGHT) || + (new_view_geo.width < MIN_VIEW_WIDTH)) + return; + + /* Move */ + view->x = new_view_geo.x; + view->y = new_view_geo.y; + + /* Resize */ + view_resize(view, new_view_geo); +} + +static void process_cursor_motion(struct server *server, uint32_t time) +{ + /* If the mode is non-passthrough, delegate to those functions. */ + if (server->cursor_mode == TINYWL_CURSOR_MOVE) { + process_cursor_move(server, time); + return; + } else if (server->cursor_mode == TINYWL_CURSOR_RESIZE) { + process_cursor_resize(server, time); + return; + } + + /* Otherwise, find the view under the pointer and send the event along. + */ + double sx, sy; + struct wlr_seat *seat = server->seat; + struct wlr_surface *surface = NULL; + int view_area; + struct view *view = view_at(server, server->cursor->x, + server->cursor->y, &surface, &sx, &sy, + &view_area); + if (!view) { + /* If there's no view 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 views. */ + wlr_xcursor_manager_set_cursor_image( + server->cursor_mgr, "left_ptr", server->cursor); + } + switch (view_area) { + case LAB_DECO_PART_TOP: + wlr_xcursor_manager_set_cursor_image( + server->cursor_mgr, "left_ptr", server->cursor); + break; + case LAB_DECO_PART_LEFT: + wlr_xcursor_manager_set_cursor_image( + server->cursor_mgr, "left_side", server->cursor); + break; + } + if (surface) { + bool focus_changed = seat->pointer_state.focused_surface != + surface; + /* + * "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. + */ + wlr_seat_pointer_notify_enter(seat, surface, sx, sy); + if (!focus_changed) { + /* The enter event contains coordinates, so we only need + * to notify on motion if the focus did not change. */ + 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); + } +} + +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 server *server = + wl_container_of(listener, server, cursor_motion); + struct wlr_event_pointer_motion *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(server->cursor, event->device, event->delta_x, + event->delta_y); + process_cursor_motion(server, event->time_msec); +} + +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 server *server = + wl_container_of(listener, server, cursor_motion_absolute); + struct wlr_event_pointer_motion_absolute *event = data; + wlr_cursor_warp_absolute(server->cursor, event->device, event->x, + event->y); + process_cursor_motion(server, event->time_msec); +} + +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 server *server = + wl_container_of(listener, server, cursor_button); + struct wlr_event_pointer_button *event = data; + + /* + * Notify the client with pointer focus that a button press has + * occurred. + */ + wlr_seat_pointer_notify_button(server->seat, event->time_msec, + event->button, event->state); + double sx, sy; + struct wlr_surface *surface; + int view_area; + struct view *view = view_at(server, server->cursor->x, + server->cursor->y, &surface, &sx, &sy, + &view_area); + if (event->state == WLR_BUTTON_RELEASED) { + /* Exit interactive move/resize mode. */ + server->cursor_mode = TINYWL_CURSOR_PASSTHROUGH; + } else { + /* Focus that client if the button was _pressed_ */ + view_focus(view); + switch (view_area) { + case LAB_DECO_PART_TOP: + interactive_begin(view, TINYWL_CURSOR_MOVE, 0); + break; + case LAB_DECO_PART_LEFT: + interactive_begin(view, TINYWL_CURSOR_RESIZE, + WLR_EDGE_LEFT); + break; + } + } +} + +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 server *server = wl_container_of(listener, server, cursor_axis); + struct wlr_event_pointer_axis *event = data; + + /* Notify the client with pointer focus of the axis event. */ + wlr_seat_pointer_notify_axis(server->seat, event->time_msec, + event->orientation, event->delta, + event->delta_discrete, event->source); +} + +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 server *server = wl_container_of(listener, server, cursor_frame); + /* Notify the client with pointer focus of the frame event. */ + wlr_seat_pointer_notify_frame(server->seat); +} diff --git a/src/interactive.c b/src/interactive.c index 7022ead9..c702e4c9 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -17,4 +17,3 @@ void interactive_begin(struct view *view, enum cursor_mode mode, uint32_t edges) server->grab_box = view_geometry(view); server->resize_edges = edges; } - diff --git a/src/meson.build b/src/meson.build index 1f1aa33a..fcbce31d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,4 +1,5 @@ labwc_sources = files( + 'cursor.c', 'deco.c', 'interactive.c', 'main.c', diff --git a/src/server.c b/src/server.c index 69a2f490..be047f44 100644 --- a/src/server.c +++ b/src/server.c @@ -1,8 +1,5 @@ #include "labwc.h" -#define MIN_VIEW_WIDTH (100) -#define MIN_VIEW_HEIGHT (60) - static void keyboard_handle_modifiers(struct wl_listener *listener, void *data) { /* @@ -212,213 +209,6 @@ void seat_request_set_selection(struct wl_listener *listener, void *data) wlr_seat_set_selection(server->seat, event->source, event->serial); } -static void process_cursor_move(struct server *server, uint32_t time) -{ - /* Move the grabbed view to the new position. */ - double dx = server->cursor->x - server->grab_x; - double dy = server->cursor->y - server->grab_y; - server->grabbed_view->x = server->grab_box.x + dx; - server->grabbed_view->y = server->grab_box.y + dy; - - if (server->grabbed_view->type != LAB_XWAYLAND_VIEW) - return; - - struct view *view = server->grabbed_view; - wlr_xwayland_surface_configure(view->xwayland_surface, view->x, view->y, - view->xwayland_surface->width, - view->xwayland_surface->height); -} - -static void process_cursor_resize(struct server *server, uint32_t time) -{ - /* - * TODO: Wait for the client to prepare a buffer at the new size, then - * commit any movement that was prepared. - */ - double dx = server->cursor->x - server->grab_x; - double dy = server->cursor->y - server->grab_y; - - struct view *view = server->grabbed_view; - struct wlr_box new_view_geo = view_geometry(view); - - if (server->resize_edges & WLR_EDGE_TOP) { - 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) { - new_view_geo.height = server->grab_box.height + dy; - } - if (server->resize_edges & WLR_EDGE_LEFT) { - 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) { - new_view_geo.width = server->grab_box.width + dx; - } - if ((new_view_geo.height < MIN_VIEW_HEIGHT) || - (new_view_geo.width < MIN_VIEW_WIDTH)) - return; - - /* Move */ - view->x = new_view_geo.x; - view->y = new_view_geo.y; - - /* Resize */ - view_resize(view, new_view_geo); -} - -static void process_cursor_motion(struct server *server, uint32_t time) -{ - /* If the mode is non-passthrough, delegate to those functions. */ - if (server->cursor_mode == TINYWL_CURSOR_MOVE) { - process_cursor_move(server, time); - return; - } else if (server->cursor_mode == TINYWL_CURSOR_RESIZE) { - process_cursor_resize(server, time); - return; - } - - /* Otherwise, find the view under the pointer and send the event along. - */ - double sx, sy; - struct wlr_seat *seat = server->seat; - struct wlr_surface *surface = NULL; - int view_area; - struct view *view = view_at(server, server->cursor->x, - server->cursor->y, &surface, &sx, &sy, - &view_area); - if (!view) { - /* If there's no view 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 views. */ - wlr_xcursor_manager_set_cursor_image( - server->cursor_mgr, "left_ptr", server->cursor); - } - switch (view_area) { - case LAB_DECO_PART_TOP: - wlr_xcursor_manager_set_cursor_image( - server->cursor_mgr, "left_ptr", server->cursor); - break; - case LAB_DECO_PART_LEFT: - wlr_xcursor_manager_set_cursor_image( - server->cursor_mgr, "left_side", server->cursor); - break; - } - if (surface) { - bool focus_changed = seat->pointer_state.focused_surface != - surface; - /* - * "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. - */ - wlr_seat_pointer_notify_enter(seat, surface, sx, sy); - if (!focus_changed) { - /* The enter event contains coordinates, so we only need - * to notify on motion if the focus did not change. */ - 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); - } -} - -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 server *server = - wl_container_of(listener, server, cursor_motion); - struct wlr_event_pointer_motion *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(server->cursor, event->device, event->delta_x, - event->delta_y); - process_cursor_motion(server, event->time_msec); -} - -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 server *server = - wl_container_of(listener, server, cursor_motion_absolute); - struct wlr_event_pointer_motion_absolute *event = data; - wlr_cursor_warp_absolute(server->cursor, event->device, event->x, - event->y); - process_cursor_motion(server, event->time_msec); -} - -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 server *server = - wl_container_of(listener, server, cursor_button); - struct wlr_event_pointer_button *event = data; - /* Notify the client with pointer focus that a button press has occurred - */ - wlr_seat_pointer_notify_button(server->seat, event->time_msec, - event->button, event->state); - double sx, sy; - struct wlr_surface *surface; - int view_area; - struct view *view = view_at(server, server->cursor->x, - server->cursor->y, &surface, &sx, &sy, - &view_area); - if (event->state == WLR_BUTTON_RELEASED) { - /* If you released any buttons, we exit interactive move/resize - * mode. */ - server->cursor_mode = TINYWL_CURSOR_PASSTHROUGH; - } else { - /* Focus that client if the button was _pressed_ */ - view_focus(view); - switch (view_area) { - case LAB_DECO_PART_TOP: - interactive_begin(view, TINYWL_CURSOR_MOVE, 0); - break; - case LAB_DECO_PART_LEFT: - interactive_begin(view, TINYWL_CURSOR_RESIZE, - WLR_EDGE_LEFT); - break; - } - } -} - -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 server *server = wl_container_of(listener, server, cursor_axis); - struct wlr_event_pointer_axis *event = data; - /* Notify the client with pointer focus of the axis event. */ - wlr_seat_pointer_notify_axis(server->seat, event->time_msec, - event->orientation, event->delta, - event->delta_discrete, event->source); -} - -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 server *server = wl_container_of(listener, server, cursor_frame); - /* Notify the client with pointer focus of the frame event. */ - wlr_seat_pointer_notify_frame(server->seat); -} - void server_new_output(struct wl_listener *listener, void *data) { /* This event is rasied by the backend when a new output (aka a display