void view_focus_next_toplevel(struct tinywl_server *server);
void begin_interactive(struct tinywl_view *view, enum tinywl_cursor_mode mode, uint32_t edges);
bool is_toplevel(struct tinywl_view *view);
+struct tinywl_view *desktop_view_at(struct tinywl_server *server, double lx, double ly,
+ struct wlr_surface **surface, double *sx, double *sy);
/* TODO: try to refactor to remove from header file */
struct tinywl_view *first_toplevel(struct tinywl_server *server);
+void server_cursor_motion(struct wl_listener *listener, void *data);
+void server_cursor_motion_absolute(struct wl_listener *listener, void *data);
+void server_cursor_button(struct wl_listener *listener, void *data);
+void server_cursor_axis(struct wl_listener *listener, void *data);
+void server_cursor_frame(struct wl_listener *listener, void *data);
void server_new_output(struct wl_listener *listener, void *data);
void output_frame(struct wl_listener *listener, void *data);
}
}
-bool view_at(struct tinywl_view *view,
- double lx, double ly, struct wlr_surface **surface,
- double *sx, double *sy) {
- /*
- * XDG toplevels may have nested surfaces, such as popup windows for context
- * menus or tooltips. This function tests if any of those are underneath the
- * coordinates lx and ly (in output Layout Coordinates). If so, it sets the
- * surface pointer to that wlr_surface and the sx and sy coordinates to the
- * coordinates relative to that surface's top-left corner.
- */
- double view_sx = lx - view->x;
- double view_sy = ly - view->y;
- double _sx, _sy;
- struct wlr_surface *_surface = NULL;
-
- switch (view->type) {
- case LAB_XDG_SHELL_VIEW:
- _surface = wlr_xdg_surface_surface_at(
- view->xdg_surface, view_sx, view_sy, &_sx, &_sy);
- break;
- case LAB_XWAYLAND_VIEW:
- if (!view->xwayland_surface->surface)
- return false;
- _surface = wlr_surface_surface_at(
- view->xwayland_surface->surface,
- view_sx, view_sy, &_sx, &_sy);
- break;
- }
-
- if (_surface != NULL) {
- *sx = _sx;
- *sy = _sy;
- *surface = _surface;
- return true;
- }
- return false;
-}
-
-static struct tinywl_view *desktop_view_at(
- struct tinywl_server *server, double lx, double ly,
- struct wlr_surface **surface, double *sx, double *sy) {
- /* This iterates over all of our surfaces and attempts to find one under the
- * cursor. This relies on server->views being ordered from top-to-bottom. */
- struct tinywl_view *view;
- wl_list_for_each(view, &server->views, link) {
- if (view_at(view, lx, ly, surface, sx, sy)) {
- return view;
- }
- }
- return NULL;
-}
-
-static void process_cursor_move(struct tinywl_server *server, uint32_t time) {
- /* Move the grabbed view to the new position. */
- server->grabbed_view->x = server->cursor->x - server->grab_x;
- server->grabbed_view->y = server->cursor->y - server->grab_y;
-}
-
-static void process_cursor_resize(struct tinywl_server *server, uint32_t time) {
- /*
- * Resizing the grabbed view can be a little bit complicated, because we
- * could be resizing from any corner or edge. This not only resizes the view
- * on one or two axes, but can also move the view if you resize from the top
- * or left edges (or top-left corner).
- *
- * Note that I took some shortcuts 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.
- */
- struct tinywl_view *view = server->grabbed_view;
- double dx = server->cursor->x - server->grab_x;
- double dy = server->cursor->y - server->grab_y;
- double x = view->x;
- double y = view->y;
- int width = server->grab_width;
- int height = server->grab_height;
- if (server->resize_edges & WLR_EDGE_TOP) {
- y = server->grab_y + dy;
- height -= dy;
- if (height < 1) {
- y += height;
- }
- } else if (server->resize_edges & WLR_EDGE_BOTTOM) {
- height += dy;
- }
- if (server->resize_edges & WLR_EDGE_LEFT) {
- x = server->grab_x + dx;
- width -= dx;
- if (width < 1) {
- x += width;
- }
- } else if (server->resize_edges & WLR_EDGE_RIGHT) {
- width += dx;
- }
- view->x = x;
- view->y = y;
- wlr_xdg_toplevel_set_size(view->xdg_surface, width, height);
-}
-
-static void process_cursor_motion(struct tinywl_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;
- struct tinywl_view *view = desktop_view_at(server,
- server->cursor->x, server->cursor->y, &surface, &sx, &sy);
- 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);
- }
- 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);
- }
-}
-
-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 tinywl_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);
-}
-
-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 tinywl_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);
-}
-
-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 tinywl_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;
- struct tinywl_view *view = desktop_view_at(server,
- server->cursor->x, server->cursor->y, &surface, &sx, &sy);
- 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_ */
- focus_view(view, surface);
- }
-}
-
-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 tinywl_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);
-}
-
-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 tinywl_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);
-}
-
int main(int argc, char *argv[]) {
wlr_log_init(WLR_ERROR, NULL);
char *startup_cmd = NULL;
#include "labwc.h"
+static void process_cursor_move(struct tinywl_server *server, uint32_t time)
+{
+ /* Move the grabbed view to the new position. */
+ server->grabbed_view->x = server->cursor->x - server->grab_x;
+ server->grabbed_view->y = server->cursor->y - server->grab_y;
+}
+
+static void process_cursor_resize(struct tinywl_server *server, uint32_t time)
+{
+ /*
+ * Resizing the grabbed view can be a little bit complicated, because we
+ * could be resizing from any corner or edge. This not only resizes the view
+ * on one or two axes, but can also move the view if you resize from the top
+ * or left edges (or top-left corner).
+ *
+ * Note that I took some shortcuts 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.
+ */
+ struct tinywl_view *view = server->grabbed_view;
+ double dx = server->cursor->x - server->grab_x;
+ double dy = server->cursor->y - server->grab_y;
+ double x = view->x;
+ double y = view->y;
+ int width = server->grab_width;
+ int height = server->grab_height;
+ if (server->resize_edges & WLR_EDGE_TOP) {
+ y = server->grab_y + dy;
+ height -= dy;
+ if (height < 1) {
+ y += height;
+ }
+ } else if (server->resize_edges & WLR_EDGE_BOTTOM) {
+ height += dy;
+ }
+ if (server->resize_edges & WLR_EDGE_LEFT) {
+ x = server->grab_x + dx;
+ width -= dx;
+ if (width < 1) {
+ x += width;
+ }
+ } else if (server->resize_edges & WLR_EDGE_RIGHT) {
+ width += dx;
+ }
+ view->x = x;
+ view->y = y;
+ wlr_xdg_toplevel_set_size(view->xdg_surface, width, height);
+}
+
+static void process_cursor_motion(struct tinywl_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;
+ struct tinywl_view *view = desktop_view_at(server,
+ server->cursor->x, server->cursor->y, &surface, &sx, &sy);
+ 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);
+ }
+ 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 tinywl_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 tinywl_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 tinywl_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;
+ struct tinywl_view *view = desktop_view_at(server,
+ server->cursor->x, server->cursor->y, &surface, &sx, &sy);
+ 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_ */
+ focus_view(view, surface);
+ }
+}
+
+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 tinywl_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 tinywl_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 or
return NULL;
}
+static bool view_at(struct tinywl_view *view,
+ double lx, double ly, struct wlr_surface **surface,
+ double *sx, double *sy) {
+ /*
+ * XDG toplevels may have nested surfaces, such as popup windows for context
+ * menus or tooltips. This function tests if any of those are underneath the
+ * coordinates lx and ly (in output Layout Coordinates). If so, it sets the
+ * surface pointer to that wlr_surface and the sx and sy coordinates to the
+ * coordinates relative to that surface's top-left corner.
+ */
+ double view_sx = lx - view->x;
+ double view_sy = ly - view->y;
+ double _sx, _sy;
+ struct wlr_surface *_surface = NULL;
+
+ switch (view->type) {
+ case LAB_XDG_SHELL_VIEW:
+ _surface = wlr_xdg_surface_surface_at(
+ view->xdg_surface, view_sx, view_sy, &_sx, &_sy);
+ break;
+ case LAB_XWAYLAND_VIEW:
+ if (!view->xwayland_surface->surface)
+ return false;
+ _surface = wlr_surface_surface_at(
+ view->xwayland_surface->surface,
+ view_sx, view_sy, &_sx, &_sy);
+ break;
+ }
+
+ if (_surface != NULL) {
+ *sx = _sx;
+ *sy = _sy;
+ *surface = _surface;
+ return true;
+ }
+ return false;
+}
+
+struct tinywl_view *desktop_view_at(struct tinywl_server *server, double lx, double ly,
+ struct wlr_surface **surface, double *sx, double *sy) {
+ /* This iterates over all of our surfaces and attempts to find one under the
+ * cursor. This relies on server->views being ordered from top-to-bottom. */
+ struct tinywl_view *view;
+ wl_list_for_each(view, &server->views, link) {
+ if (view_at(view, lx, ly, surface, sx, sy)) {
+ return view;
+ }
+ }
+ return NULL;
+}
+