From c1960f043853d1d4764b430855172b591ecb468b Mon Sep 17 00:00:00 2001 From: DanyLE Date: Sat, 30 Mar 2024 00:18:51 +0100 Subject: [PATCH] refactor code to multiple modules --- .gitignore | 4 +- Makefile | 17 +- cursor.c | 259 ++++++ cursor.h | 8 + diyac.c | 904 ++----------------- diyac.h | 119 +++ layer.c | 177 ++++ output.c | 106 +++ output.h | 7 + protocol/server-decoration.xml | 94 ++ protocol/virtual-keyboard-unstable-v1.xml | 113 +++ protocol/wlr-input-inhibitor-unstable-v1.xml | 67 ++ protocol/wlr-layer-shell-unstable-v1.xml | 390 ++++++++ seat.c | 236 +++++ seat.h | 6 + xdg.c | 213 +++++ xdg.h | 7 + 17 files changed, 1899 insertions(+), 828 deletions(-) create mode 100644 cursor.c create mode 100644 cursor.h create mode 100644 diyac.h create mode 100644 layer.c create mode 100644 output.c create mode 100644 output.h create mode 100644 protocol/server-decoration.xml create mode 100644 protocol/virtual-keyboard-unstable-v1.xml create mode 100644 protocol/wlr-input-inhibitor-unstable-v1.xml create mode 100644 protocol/wlr-layer-shell-unstable-v1.xml create mode 100644 seat.c create mode 100644 seat.h create mode 100644 xdg.c create mode 100644 xdg.h diff --git a/.gitignore b/.gitignore index da68945..27380c2 100644 --- a/.gitignore +++ b/.gitignore @@ -140,4 +140,6 @@ m4/lt~obsolete.m4 # (which is called by configure script)) # Makefile xdg-shell-protocol.* -diyac \ No newline at end of file +diyac +.vscode +wlr-layer-shell-unstable-v1-protocol* \ No newline at end of file diff --git a/Makefile b/Makefile index bac2c97..d096bbc 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,14 @@ LIBS=\ $(shell pkg-config --cflags --libs wayland-server) \ $(shell pkg-config --cflags --libs xkbcommon) +OBJS=\ + diyac.c \ + cursor.c \ + output.c \ + seat.c \ + xdg.c \ + xdg-shell-protocol.c + # wayland-scanner is a tool which generates C headers and rigging for Wayland # protocols, which are specified in XML. wlroots requires you to rig these up # to your build system yourself and provide them in the include path. @@ -16,11 +24,16 @@ xdg-shell-protocol.c: xdg-shell-protocol.h $(WAYLAND_SCANNER) private-code \ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ -diyac: diyac.c xdg-shell-protocol.h xdg-shell-protocol.c +wlr-layer-shell-unstable-v1-protocol.h: + $(WAYLAND_SCANNER) server-header \ + protocol/wlr-layer-shell-unstable-v1.xml $@ + +diyac: $(OBJS) + echo "Object is $(OBJS)" $(CC) $(CFLAGS) \ -g -Werror -I. \ -DWLR_USE_UNSTABLE \ - -o $@ $< \ + -o $@ $(OBJS) \ $(LIBS) clean: diff --git a/cursor.c b/cursor.c new file mode 100644 index 0000000..34b562f --- /dev/null +++ b/cursor.c @@ -0,0 +1,259 @@ +#define _POSIX_C_SOURCE 200112L +#include +#include +#include +#include "cursor.h" + +void diyac_reset_cursor_mode(struct diyac_server *server) +{ + /* Reset the cursor mode to passthrough. */ + server->seat.cursor_mode = DIYAC_CURSOR_PASSTHROUGH; + server->grabbed_toplevel = NULL; +} + +static void process_cursor_move(struct diyac_server *server, uint32_t time) +{ + /* Move the grabbed toplevel to the new position. */ + struct diyac_toplevel *toplevel = server->grabbed_toplevel; + wlr_scene_node_set_position(&toplevel->scene_tree->node, + server->seat.cursor->x - server->grab_x, + server->seat.cursor->y - server->grab_y); +} + +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. + */ + struct diyac_toplevel *toplevel = server->grabbed_toplevel; + 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); + wlr_scene_node_set_position(&toplevel->scene_tree->node, + new_left - geo_box.x, new_top - geo_box.y); + + int new_width = new_right - new_left; + int new_height = new_bottom - new_top; + wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, new_width, new_height); +} + +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; + struct diyac_toplevel *toplevel = diyac_toplevel_at(server, + server->seat.cursor->x, server->seat.cursor->y, &surface, &sx, &sy); + if (!toplevel) + { + /* 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); + double sx, sy; + struct wlr_surface *surface = NULL; + struct diyac_toplevel *toplevel = diyac_toplevel_at(seat->server, + seat->cursor->x, seat->cursor->y, &surface, &sx, &sy); + if (event->state == WLR_BUTTON_RELEASED) + { + /* If you released any buttons, we exit interactive move/resize mode. */ + diyac_reset_cursor_mode(seat->server); + } + else + { + /* Focus that client if the button was _pressed_ */ + diyac_focus_toplevel(toplevel, 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 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); +} \ No newline at end of file diff --git a/cursor.h b/cursor.h new file mode 100644 index 0000000..334e74c --- /dev/null +++ b/cursor.h @@ -0,0 +1,8 @@ +#ifndef DIYAC_CURSOR_H +#define DIYAC_CURSOR_H + +#include "diyac.h" + +void diyac_init_cursor_manager(struct diyac_server * server); +void diyac_reset_cursor_mode(struct diyac_server *server); +#endif \ No newline at end of file diff --git a/diyac.c b/diyac.c index e3e8f23..b66b3a8 100644 --- a/diyac.c +++ b/diyac.c @@ -1,120 +1,40 @@ #define _POSIX_C_SOURCE 200112L -#include -#include -#include -#include #include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include #include -#include +#include +#include +#include +#include +#include +#include -/* For brevity's sake, struct members are annotated where they are used. */ -enum tinywl_cursor_mode { - TINYWL_CURSOR_PASSTHROUGH, - TINYWL_CURSOR_MOVE, - TINYWL_CURSOR_RESIZE, -}; +#include "diyac.h" +#include "output.h" +#include "xdg.h" +#include "cursor.h" +#include "seat.h" -struct tinywl_server { - struct wl_display *wl_display; - struct wlr_backend *backend; - struct wlr_renderer *renderer; - struct wlr_allocator *allocator; - struct wlr_scene *scene; - struct wlr_scene_output_layout *scene_layout; - - struct wlr_xdg_shell *xdg_shell; - struct wl_listener new_xdg_surface; - struct wl_list toplevels; - - struct wlr_cursor *cursor; - struct wlr_xcursor_manager *cursor_mgr; - struct wl_listener cursor_motion; - struct wl_listener cursor_motion_absolute; - struct wl_listener cursor_button; - struct wl_listener cursor_axis; - struct wl_listener cursor_frame; - - struct wlr_seat *seat; - struct wl_listener new_input; - struct wl_listener request_cursor; - struct wl_listener request_set_selection; - struct wl_list keyboards; - enum tinywl_cursor_mode cursor_mode; - struct tinywl_toplevel *grabbed_toplevel; - double grab_x, grab_y; - struct wlr_box grab_geobox; - uint32_t resize_edges; - - struct wlr_output_layout *output_layout; - struct wl_list outputs; - struct wl_listener new_output; -}; - -struct tinywl_output { - struct wl_list link; - struct tinywl_server *server; - struct wlr_output *wlr_output; - struct wl_listener frame; - struct wl_listener request_state; - struct wl_listener destroy; -}; - -struct tinywl_toplevel { - struct wl_list link; - struct tinywl_server *server; - struct wlr_xdg_toplevel *xdg_toplevel; - struct wlr_scene_tree *scene_tree; - struct wl_listener map; - struct wl_listener unmap; - struct wl_listener destroy; - struct wl_listener request_move; - struct wl_listener request_resize; - struct wl_listener request_maximize; - struct wl_listener request_fullscreen; -}; - -struct tinywl_keyboard { - struct wl_list link; - struct tinywl_server *server; - struct wlr_keyboard *wlr_keyboard; - - struct wl_listener modifiers; - struct wl_listener key; - struct wl_listener destroy; -}; - -static void focus_toplevel(struct tinywl_toplevel *toplevel, struct wlr_surface *surface) { +void diyac_focus_toplevel(struct diyac_toplevel *toplevel, struct wlr_surface *surface) +{ /* Note: this function only deals with keyboard focus. */ - if (toplevel == NULL) { + if (toplevel == NULL) + { return; } - struct tinywl_server *server = toplevel->server; - struct wlr_seat *seat = server->seat; + struct diyac_server *server = toplevel->server; + struct wlr_seat *seat = server->seat.wlr_seat; struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface; - if (prev_surface == surface) { + if (prev_surface == surface) + { /* Don't re-focus an already focused surface. */ return; } - if (prev_surface) { + if (prev_surface) + { /* * Deactivate the previously focused surface. This lets the client know * it no longer has focus and the client will repaint accordingly, e.g. @@ -122,7 +42,8 @@ static void focus_toplevel(struct tinywl_toplevel *toplevel, struct wlr_surface */ struct wlr_xdg_toplevel *prev_toplevel = wlr_xdg_toplevel_try_from_wlr_surface(prev_surface); - if (prev_toplevel != NULL) { + if (prev_toplevel != NULL) + { wlr_xdg_toplevel_set_activated(prev_toplevel, false); } } @@ -138,696 +59,55 @@ static void focus_toplevel(struct tinywl_toplevel *toplevel, struct wlr_surface * track of this and automatically send key events to the appropriate * clients without additional work on your part. */ - if (keyboard != NULL) { + if (keyboard != NULL) + { wlr_seat_keyboard_notify_enter(seat, toplevel->xdg_toplevel->base->surface, - keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); + keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); } } -static void keyboard_handle_modifiers( - struct wl_listener *listener, void *data) { - /* This event is raised when a modifier key, such as shift or alt, is - * pressed. We simply communicate this to the client. */ - struct tinywl_keyboard *keyboard = - wl_container_of(listener, keyboard, modifiers); - /* - * A seat can only have one keyboard, but this is a limitation of the - * Wayland protocol - not wlroots. We assign all connected keyboards to the - * same seat. You can swap out the underlying wlr_keyboard like this and - * wlr_seat handles this transparently. - */ - wlr_seat_set_keyboard(keyboard->server->seat, keyboard->wlr_keyboard); - /* Send modifiers to the client. */ - wlr_seat_keyboard_notify_modifiers(keyboard->server->seat, - &keyboard->wlr_keyboard->modifiers); -} - -static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym) { - /* - * Here we handle compositor keybindings. This is when the compositor is - * processing keys, rather than passing them on to the client for its own - * processing. - * - * This function assumes Alt is held down. - */ - switch (sym) { - case XKB_KEY_Escape: - wl_display_terminate(server->wl_display); - break; - case XKB_KEY_F1: - /* Cycle to the next toplevel */ - if (wl_list_length(&server->toplevels) < 2) { - break; - } - struct tinywl_toplevel *next_toplevel = - wl_container_of(server->toplevels.prev, next_toplevel, link); - focus_toplevel(next_toplevel, next_toplevel->xdg_toplevel->base->surface); - break; - default: - return false; - } - return true; -} - -static void keyboard_handle_key( - struct wl_listener *listener, void *data) { - /* This event is raised when a key is pressed or released. */ - struct tinywl_keyboard *keyboard = - wl_container_of(listener, keyboard, key); - struct tinywl_server *server = keyboard->server; - struct wlr_keyboard_key_event *event = data; - struct wlr_seat *seat = server->seat; - - /* Translate libinput keycode -> xkbcommon */ - uint32_t keycode = event->keycode + 8; - /* Get a list of keysyms based on the keymap for this keyboard */ - const xkb_keysym_t *syms; - int nsyms = xkb_state_key_get_syms( - keyboard->wlr_keyboard->xkb_state, keycode, &syms); - - bool handled = false; - uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr_keyboard); - if ((modifiers & WLR_MODIFIER_ALT) && - event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { - /* If alt is held down and this button was _pressed_, we attempt to - * process it as a compositor keybinding. */ - for (int i = 0; i < nsyms; i++) { - handled = handle_keybinding(server, syms[i]); - } - } - - if (!handled) { - /* Otherwise, we pass it along to the client. */ - wlr_seat_set_keyboard(seat, keyboard->wlr_keyboard); - wlr_seat_keyboard_notify_key(seat, event->time_msec, - event->keycode, event->state); - } -} - -static void keyboard_handle_destroy(struct wl_listener *listener, void *data) { - /* This event is raised by the keyboard base wlr_input_device to signal - * the destruction of the wlr_keyboard. It will no longer receive events - * and should be destroyed. - */ - struct tinywl_keyboard *keyboard = - wl_container_of(listener, keyboard, destroy); - wl_list_remove(&keyboard->modifiers.link); - wl_list_remove(&keyboard->key.link); - wl_list_remove(&keyboard->destroy.link); - wl_list_remove(&keyboard->link); - free(keyboard); -} - -static void server_new_keyboard(struct tinywl_server *server, - struct wlr_input_device *device) { - struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(device); - - struct tinywl_keyboard *keyboard = calloc(1, sizeof(*keyboard)); - keyboard->server = server; - keyboard->wlr_keyboard = wlr_keyboard; - - /* We need to prepare an XKB keymap and assign it to the keyboard. This - * assumes the defaults (e.g. layout = "us"). */ - struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, - XKB_KEYMAP_COMPILE_NO_FLAGS); - - wlr_keyboard_set_keymap(wlr_keyboard, keymap); - xkb_keymap_unref(keymap); - xkb_context_unref(context); - wlr_keyboard_set_repeat_info(wlr_keyboard, 25, 600); - - /* Here we set up listeners for keyboard events. */ - keyboard->modifiers.notify = keyboard_handle_modifiers; - wl_signal_add(&wlr_keyboard->events.modifiers, &keyboard->modifiers); - keyboard->key.notify = keyboard_handle_key; - wl_signal_add(&wlr_keyboard->events.key, &keyboard->key); - keyboard->destroy.notify = keyboard_handle_destroy; - wl_signal_add(&device->events.destroy, &keyboard->destroy); - - wlr_seat_set_keyboard(server->seat, keyboard->wlr_keyboard); - - /* And add the keyboard to our list of keyboards */ - wl_list_insert(&server->keyboards, &keyboard->link); -} - -static void server_new_pointer(struct tinywl_server *server, - struct wlr_input_device *device) { - /* We don't do anything special with pointers. All of our pointer handling - * is proxied through wlr_cursor. On another compositor, you might take this - * opportunity to do libinput configuration on the device to set - * acceleration, etc. */ - wlr_cursor_attach_input_device(server->cursor, device); -} - -static void server_new_input(struct wl_listener *listener, void *data) { - /* This event is raised by the backend when a new input device becomes - * available. */ - struct tinywl_server *server = - wl_container_of(listener, server, new_input); - struct wlr_input_device *device = data; - switch (device->type) { - case WLR_INPUT_DEVICE_KEYBOARD: - server_new_keyboard(server, device); - break; - case WLR_INPUT_DEVICE_POINTER: - server_new_pointer(server, device); - break; - default: - break; - } - /* We need to let the wlr_seat know what our capabilities are, which is - * communiciated to the client. In TinyWL we always have a cursor, even if - * there are no pointer devices, so we always include that capability. */ - uint32_t caps = WL_SEAT_CAPABILITY_POINTER; - if (!wl_list_empty(&server->keyboards)) { - caps |= WL_SEAT_CAPABILITY_KEYBOARD; - } - wlr_seat_set_capabilities(server->seat, caps); -} - -static void seat_request_cursor(struct wl_listener *listener, void *data) { - struct tinywl_server *server = wl_container_of( - listener, server, request_cursor); - /* This event is raised 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 = - server->seat->pointer_state.focused_client; - /* This can be sent by any client, so we check to make sure this one is - * actually has pointer focus first. */ - if (focused_client == event->seat_client) { - /* Once we've vetted the client, we can tell the cursor to use the - * provided surface as the cursor image. It will set the hardware cursor - * on the output that it's currently on and continue to do so as the - * cursor moves between outputs. */ - wlr_cursor_set_surface(server->cursor, event->surface, - event->hotspot_x, event->hotspot_y); - } -} - -static void seat_request_set_selection(struct wl_listener *listener, void *data) { - /* This event is raised by the seat when a client wants to set the selection, - * usually when the user copies something. wlroots allows compositors to - * ignore such requests if they so choose, but in tinywl we always honor - */ - struct tinywl_server *server = wl_container_of( - listener, server, request_set_selection); - struct wlr_seat_request_set_selection_event *event = data; - wlr_seat_set_selection(server->seat, event->source, event->serial); -} - -static struct tinywl_toplevel *desktop_toplevel_at( - struct tinywl_server *server, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { +struct diyac_toplevel *diyac_toplevel_at( + struct diyac_server *server, double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy) +{ /* This returns the topmost node in the scene at the given layout coords. * We only care about surface nodes as we are specifically looking for a - * surface in the surface tree of a tinywl_toplevel. */ + * surface in the surface tree of a diyac_toplevel. */ struct wlr_scene_node *node = wlr_scene_node_at( &server->scene->tree.node, lx, ly, sx, sy); - if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) { + if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) + { return NULL; } struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(scene_buffer); - if (!scene_surface) { + if (!scene_surface) + { return NULL; } *surface = scene_surface->surface; - /* Find the node corresponding to the tinywl_toplevel at the root of this + /* Find the node corresponding to the diyac_toplevel at the root of this * surface tree, it is the only one for which we set the data field. */ struct wlr_scene_tree *tree = node->parent; - while (tree != NULL && tree->node.data == NULL) { + while (tree != NULL && tree->node.data == NULL) + { tree = tree->node.parent; } return tree->node.data; } -static void reset_cursor_mode(struct tinywl_server *server) { - /* Reset the cursor mode to passthrough. */ - server->cursor_mode = TINYWL_CURSOR_PASSTHROUGH; - server->grabbed_toplevel = NULL; -} - -static void process_cursor_move(struct tinywl_server *server, uint32_t time) { - /* Move the grabbed toplevel to the new position. */ - struct tinywl_toplevel *toplevel = server->grabbed_toplevel; - wlr_scene_node_set_position(&toplevel->scene_tree->node, - server->cursor->x - server->grab_x, - server->cursor->y - server->grab_y); -} - -static void process_cursor_resize(struct tinywl_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. - */ - struct tinywl_toplevel *toplevel = server->grabbed_toplevel; - double border_x = server->cursor->x - server->grab_x; - double border_y = server->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); - wlr_scene_node_set_position(&toplevel->scene_tree->node, - new_left - geo_box.x, new_top - geo_box.y); - - int new_width = new_right - new_left; - int new_height = new_bottom - new_top; - wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, new_width, new_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 toplevel under the pointer and send the event along. */ - double sx, sy; - struct wlr_seat *seat = server->seat; - struct wlr_surface *surface = NULL; - struct tinywl_toplevel *toplevel = desktop_toplevel_at(server, - server->cursor->x, server->cursor->y, &surface, &sx, &sy); - if (!toplevel) { - /* 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->cursor, server->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 tinywl_server *server = - wl_container_of(listener, server, 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(server->cursor, &event->pointer->base, - 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_pointer_motion_absolute_event *event = data; - wlr_cursor_warp_absolute(server->cursor, &event->pointer->base, 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_pointer_button_event *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 = NULL; - struct tinywl_toplevel *toplevel = desktop_toplevel_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. */ - reset_cursor_mode(server); - } else { - /* Focus that client if the button was _pressed_ */ - focus_toplevel(toplevel, 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_pointer_axis_event *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); -} - -static void output_frame(struct wl_listener *listener, void *data) { - /* This function is called every time an output is ready to display a frame, - * generally at the output's refresh rate (e.g. 60Hz). */ - struct tinywl_output *output = wl_container_of(listener, output, frame); - struct wlr_scene *scene = output->server->scene; - - struct wlr_scene_output *scene_output = wlr_scene_get_scene_output( - scene, output->wlr_output); - - /* Render the scene if needed and commit the output */ - wlr_scene_output_commit(scene_output, NULL); - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - wlr_scene_output_send_frame_done(scene_output, &now); -} - -static void output_request_state(struct wl_listener *listener, void *data) { - /* This function is called when the backend requests a new state for - * the output. For example, Wayland and X11 backends request a new mode - * when the output window is resized. */ - struct tinywl_output *output = wl_container_of(listener, output, request_state); - const struct wlr_output_event_request_state *event = data; - wlr_output_commit_state(output->wlr_output, event->state); -} - -static void output_destroy(struct wl_listener *listener, void *data) { - struct tinywl_output *output = wl_container_of(listener, output, destroy); - - wl_list_remove(&output->frame.link); - wl_list_remove(&output->request_state.link); - wl_list_remove(&output->destroy.link); - wl_list_remove(&output->link); - free(output); -} - -static void server_new_output(struct wl_listener *listener, void *data) { - /* This event is raised by the backend when a new output (aka a display or - * monitor) becomes available. */ - struct tinywl_server *server = - wl_container_of(listener, server, new_output); - struct wlr_output *wlr_output = data; - - /* Configures the output created by the backend to use our allocator - * and our renderer. Must be done once, before commiting the output */ - wlr_output_init_render(wlr_output, server->allocator, server->renderer); - - /* The output may be disabled, switch it on. */ - struct wlr_output_state state; - wlr_output_state_init(&state); - wlr_output_state_set_enabled(&state, true); - - /* Some backends don't have modes. DRM+KMS does, and we need to set a mode - * before we can use the output. The mode is a tuple of (width, height, - * refresh rate), and each monitor supports only a specific set of modes. We - * just pick the monitor's preferred mode, a more sophisticated compositor - * would let the user configure it. */ - struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); - if (mode != NULL) { - wlr_output_state_set_mode(&state, mode); - } - - /* Atomically applies the new output state. */ - wlr_output_commit_state(wlr_output, &state); - wlr_output_state_finish(&state); - - /* Allocates and configures our state for this output */ - struct tinywl_output *output = calloc(1, sizeof(*output)); - output->wlr_output = wlr_output; - output->server = server; - - /* Sets up a listener for the frame event. */ - output->frame.notify = output_frame; - wl_signal_add(&wlr_output->events.frame, &output->frame); - - /* Sets up a listener for the state request event. */ - output->request_state.notify = output_request_state; - wl_signal_add(&wlr_output->events.request_state, &output->request_state); - - /* Sets up a listener for the destroy event. */ - output->destroy.notify = output_destroy; - wl_signal_add(&wlr_output->events.destroy, &output->destroy); - - wl_list_insert(&server->outputs, &output->link); - - /* Adds this to the output layout. The add_auto function arranges outputs - * from left-to-right in the order they appear. A more sophisticated - * compositor would let the user configure the arrangement of outputs in the - * layout. - * - * The output layout utility automatically adds a wl_output global to the - * display, which Wayland clients can see to find out information about the - * output (such as DPI, scale factor, manufacturer, etc). - */ - struct wlr_output_layout_output *l_output = wlr_output_layout_add_auto(server->output_layout, - wlr_output); - struct wlr_scene_output *scene_output = wlr_scene_output_create(server->scene, wlr_output); - wlr_scene_output_layout_add_output(server->scene_layout, l_output, scene_output); -} - -static void xdg_toplevel_map(struct wl_listener *listener, void *data) { - /* Called when the surface is mapped, or ready to display on-screen. */ - struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, map); - - wl_list_insert(&toplevel->server->toplevels, &toplevel->link); - - focus_toplevel(toplevel, toplevel->xdg_toplevel->base->surface); -} - -static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { - /* Called when the surface is unmapped, and should no longer be shown. */ - struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, unmap); - - /* Reset the cursor mode if the grabbed toplevel was unmapped. */ - if (toplevel == toplevel->server->grabbed_toplevel) { - reset_cursor_mode(toplevel->server); - } - - wl_list_remove(&toplevel->link); -} - -static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) { - /* Called when the xdg_toplevel is destroyed. */ - struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, destroy); - - wl_list_remove(&toplevel->map.link); - wl_list_remove(&toplevel->unmap.link); - wl_list_remove(&toplevel->destroy.link); - wl_list_remove(&toplevel->request_move.link); - wl_list_remove(&toplevel->request_resize.link); - wl_list_remove(&toplevel->request_maximize.link); - wl_list_remove(&toplevel->request_fullscreen.link); - - free(toplevel); -} - -static void begin_interactive(struct tinywl_toplevel *toplevel, - enum tinywl_cursor_mode mode, uint32_t edges) { - /* This function sets up an interactive move or resize operation, where the - * compositor stops propegating pointer events to clients and instead - * consumes them itself, to move or resize windows. */ - struct tinywl_server *server = toplevel->server; - struct wlr_surface *focused_surface = - server->seat->pointer_state.focused_surface; - if (toplevel->xdg_toplevel->base->surface != - wlr_surface_get_root_surface(focused_surface)) { - /* Deny move/resize requests from unfocused clients. */ - return; - } - server->grabbed_toplevel = toplevel; - server->cursor_mode = mode; - - if (mode == TINYWL_CURSOR_MOVE) { - server->grab_x = server->cursor->x - toplevel->scene_tree->node.x; - server->grab_y = server->cursor->y - toplevel->scene_tree->node.y; - } else { - struct wlr_box geo_box; - wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box); - - double border_x = (toplevel->scene_tree->node.x + geo_box.x) + - ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0); - double border_y = (toplevel->scene_tree->node.y + geo_box.y) + - ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0); - server->grab_x = server->cursor->x - border_x; - server->grab_y = server->cursor->y - border_y; - - server->grab_geobox = geo_box; - server->grab_geobox.x += toplevel->scene_tree->node.x; - server->grab_geobox.y += toplevel->scene_tree->node.y; - - server->resize_edges = edges; - } -} - -static void xdg_toplevel_request_move( - struct wl_listener *listener, void *data) { - /* This event is raised when a client would like to begin an interactive - * move, typically because the user clicked on their client-side - * decorations. Note that a more sophisticated compositor should check the - * provided serial against a list of button press serials sent to this - * client, to prevent the client from requesting this whenever they want. */ - struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, request_move); - begin_interactive(toplevel, TINYWL_CURSOR_MOVE, 0); -} - -static void xdg_toplevel_request_resize( - struct wl_listener *listener, void *data) { - /* This event is raised when a client would like to begin an interactive - * resize, typically because the user clicked on their client-side - * decorations. Note that a more sophisticated compositor should check the - * provided serial against a list of button press serials sent to this - * client, to prevent the client from requesting this whenever they want. */ - struct wlr_xdg_toplevel_resize_event *event = data; - struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, request_resize); - begin_interactive(toplevel, TINYWL_CURSOR_RESIZE, event->edges); -} - -static void xdg_toplevel_request_maximize( - struct wl_listener *listener, void *data) { - /* This event is raised when a client would like to maximize itself, - * typically because the user clicked on the maximize button on - * client-side decorations. tinywl doesn't support maximization, but - * to conform to xdg-shell protocol we still must send a configure. - * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ - struct tinywl_toplevel *toplevel = - wl_container_of(listener, toplevel, request_maximize); - wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); -} - -static void xdg_toplevel_request_fullscreen( - struct wl_listener *listener, void *data) { - /* Just as with request_maximize, we must send a configure here. */ - struct tinywl_toplevel *toplevel = - wl_container_of(listener, toplevel, request_fullscreen); - wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); -} - -static void server_new_xdg_surface(struct wl_listener *listener, void *data) { - /* This event is raised when wlr_xdg_shell receives a new xdg surface from a - * client, either a toplevel (application window) or popup. */ - struct tinywl_server *server = - wl_container_of(listener, server, new_xdg_surface); - struct wlr_xdg_surface *xdg_surface = data; - - /* We must add xdg popups to the scene graph so they get rendered. The - * wlroots scene graph provides a helper for this, but to use it we must - * provide the proper parent scene node of the xdg popup. To enable this, - * we always set the user data field of xdg_surfaces to the corresponding - * scene node. */ - if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { - struct wlr_xdg_surface *parent = - wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent); - assert(parent != NULL); - struct wlr_scene_tree *parent_tree = parent->data; - xdg_surface->data = wlr_scene_xdg_surface_create( - parent_tree, xdg_surface); - return; - } - assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - - /* Allocate a tinywl_toplevel for this surface */ - struct tinywl_toplevel *toplevel = calloc(1, sizeof(*toplevel)); - toplevel->server = server; - toplevel->xdg_toplevel = xdg_surface->toplevel; - toplevel->scene_tree = wlr_scene_xdg_surface_create( - &toplevel->server->scene->tree, toplevel->xdg_toplevel->base); - toplevel->scene_tree->node.data = toplevel; - xdg_surface->data = toplevel->scene_tree; - - /* Listen to the various events it can emit */ - toplevel->map.notify = xdg_toplevel_map; - wl_signal_add(&xdg_surface->surface->events.map, &toplevel->map); - toplevel->unmap.notify = xdg_toplevel_unmap; - wl_signal_add(&xdg_surface->surface->events.unmap, &toplevel->unmap); - toplevel->destroy.notify = xdg_toplevel_destroy; - wl_signal_add(&xdg_surface->events.destroy, &toplevel->destroy); - - /* cotd */ - struct wlr_xdg_toplevel *xdg_toplevel = xdg_surface->toplevel; - toplevel->request_move.notify = xdg_toplevel_request_move; - wl_signal_add(&xdg_toplevel->events.request_move, &toplevel->request_move); - toplevel->request_resize.notify = xdg_toplevel_request_resize; - wl_signal_add(&xdg_toplevel->events.request_resize, &toplevel->request_resize); - toplevel->request_maximize.notify = xdg_toplevel_request_maximize; - wl_signal_add(&xdg_toplevel->events.request_maximize, - &toplevel->request_maximize); - toplevel->request_fullscreen.notify = xdg_toplevel_request_fullscreen; - wl_signal_add(&xdg_toplevel->events.request_fullscreen, - &toplevel->request_fullscreen); -} - -int main(int argc, char *argv[]) { - wlr_log_init(WLR_DEBUG, NULL); +int main(int argc, char *argv[]) +{ + wlr_log_init(WLR_INFO, NULL); char *startup_cmd = NULL; int c; - while ((c = getopt(argc, argv, "s:h")) != -1) { - switch (c) { + while ((c = getopt(argc, argv, "s:h")) != -1) + { + switch (c) + { case 's': startup_cmd = optarg; break; @@ -836,12 +116,13 @@ int main(int argc, char *argv[]) { return 0; } } - if (optind < argc) { + if (optind < argc) + { printf("Usage: %s [-s startup command]\n", argv[0]); return 0; } - struct tinywl_server server = {0}; + struct diyac_server server = {0}; /* The Wayland display is managed by libwayland. It handles accepting * clients from the Unix socket, manging Wayland globals, and so on. */ server.wl_display = wl_display_create(); @@ -850,7 +131,8 @@ int main(int argc, char *argv[]) { * backend based on the current environment, such as opening an X11 window * if an X11 server is running. */ server.backend = wlr_backend_autocreate(server.wl_display, NULL); - if (server.backend == NULL) { + if (server.backend == NULL) + { wlr_log(WLR_ERROR, "failed to create wlr_backend"); return 1; } @@ -860,7 +142,8 @@ int main(int argc, char *argv[]) { * The renderer is responsible for defining the various pixel formats it * supports for shared memory, this configures that for clients. */ server.renderer = wlr_renderer_autocreate(server.backend); - if (server.renderer == NULL) { + if (server.renderer == NULL) + { wlr_log(WLR_ERROR, "failed to create wlr_renderer"); return 1; } @@ -872,8 +155,9 @@ int main(int argc, char *argv[]) { * handles the buffer creation, allowing wlroots to render onto the * screen */ server.allocator = wlr_allocator_autocreate(server.backend, - server.renderer); - if (server.allocator == NULL) { + server.renderer); + if (server.allocator == NULL) + { wlr_log(WLR_ERROR, "failed to create wlr_allocator"); return 1; } @@ -896,7 +180,7 @@ int main(int argc, char *argv[]) { /* Configure a listener to be notified when new outputs are available on the * backend. */ wl_list_init(&server.outputs); - server.new_output.notify = server_new_output; + server.new_output.notify = diyac_server_new_output; wl_signal_add(&server.backend->events.new_output, &server.new_output); /* Create a scene graph. This is a wlroots abstraction that handles all @@ -914,45 +198,19 @@ int main(int argc, char *argv[]) { */ wl_list_init(&server.toplevels); server.xdg_shell = wlr_xdg_shell_create(server.wl_display, 3); - server.new_xdg_surface.notify = server_new_xdg_surface; + server.new_xdg_surface.notify = diyac_new_xdg_surface; wl_signal_add(&server.xdg_shell->events.new_surface, - &server.new_xdg_surface); + &server.new_xdg_surface); - /* - * Creates a cursor, which is a wlroots utility for tracking the cursor - * image shown on screen. - */ - server.cursor = wlr_cursor_create(); - wlr_cursor_attach_output_layout(server.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.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. + /** + * TODO * - * And more comments are sprinkled throughout the notify functions above. - */ - server.cursor_mode = TINYWL_CURSOR_PASSTHROUGH; - server.cursor_motion.notify = server_cursor_motion; - wl_signal_add(&server.cursor->events.motion, &server.cursor_motion); - server.cursor_motion_absolute.notify = server_cursor_motion_absolute; - wl_signal_add(&server.cursor->events.motion_absolute, - &server.cursor_motion_absolute); - server.cursor_button.notify = server_cursor_button; - wl_signal_add(&server.cursor->events.button, &server.cursor_button); - server.cursor_axis.notify = server_cursor_axis; - wl_signal_add(&server.cursor->events.axis, &server.cursor_axis); - server.cursor_frame.notify = server_cursor_frame; - wl_signal_add(&server.cursor->events.frame, &server.cursor_frame); + + server.new_layer_surface.notify = server_new_layer_surface; + wl_signal_add(&server.layer_shell->events.new_surface, + &server.new_layer_surface); + */ + diyac_init_cursor_manager(&server); /* * Configures a seat, which is a single "seat" at which a user sits and @@ -960,27 +218,21 @@ int main(int argc, char *argv[]) { * pointer, touch, and drawing tablet device. We also rig up a listener to * let us know when new input devices are available on the backend. */ - wl_list_init(&server.keyboards); - server.new_input.notify = server_new_input; - wl_signal_add(&server.backend->events.new_input, &server.new_input); - server.seat = wlr_seat_create(server.wl_display, "seat0"); - server.request_cursor.notify = seat_request_cursor; - wl_signal_add(&server.seat->events.request_set_cursor, - &server.request_cursor); - server.request_set_selection.notify = seat_request_set_selection; - wl_signal_add(&server.seat->events.request_set_selection, - &server.request_set_selection); + wl_list_init(&server.seat.keyboards); + diyac_init_seat(&server); /* Add a Unix socket to the Wayland display. */ const char *socket = wl_display_add_socket_auto(server.wl_display); - if (!socket) { + if (!socket) + { wlr_backend_destroy(server.backend); return 1; } /* Start the backend. This will enumerate outputs and inputs, become the DRM * master, etc */ - if (!wlr_backend_start(server.backend)) { + if (!wlr_backend_start(server.backend)) + { wlr_backend_destroy(server.backend); wl_display_destroy(server.wl_display); return 1; @@ -989,8 +241,10 @@ int main(int argc, char *argv[]) { /* Set the WAYLAND_DISPLAY environment variable to our socket and run the * startup command if requested. */ setenv("WAYLAND_DISPLAY", socket, true); - if (startup_cmd) { - if (fork() == 0) { + if (startup_cmd) + { + if (fork() == 0) + { execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL); } } @@ -1006,7 +260,7 @@ int main(int argc, char *argv[]) { * server. */ wl_display_destroy_clients(server.wl_display); wlr_scene_node_destroy(&server.scene->tree.node); - wlr_xcursor_manager_destroy(server.cursor_mgr); + wlr_xcursor_manager_destroy(server.seat.cursor_mgr); wlr_output_layout_destroy(server.output_layout); wl_display_destroy(server.wl_display); return 0; diff --git a/diyac.h b/diyac.h new file mode 100644 index 0000000..bd6b184 --- /dev/null +++ b/diyac.h @@ -0,0 +1,119 @@ +#ifndef DIYAC_H +#define DIYAC_H +#include +#include +#include +#include +/* For brevity's sake, struct members are annotated where they are used. */ +enum diyac_cursor_mode +{ + DIYAC_CURSOR_PASSTHROUGH, + DIYAC_CURSOR_MOVE, + DIYAC_CURSOR_RESIZE, +}; + +struct diyac_seat { + struct wlr_seat* wlr_seat; + struct diyac_server *server; + + struct wlr_cursor *cursor; + enum diyac_cursor_mode cursor_mode; + struct wlr_xcursor_manager *cursor_mgr; + struct wl_listener cursor_motion; + struct wl_listener cursor_motion_absolute; + struct wl_listener cursor_button; + struct wl_listener cursor_axis; + struct wl_listener cursor_frame; + + struct wl_listener new_input; + struct wl_listener request_cursor; + struct wl_listener request_set_selection; + struct wl_list keyboards; +}; + +struct diyac_server +{ + struct wl_display *wl_display; + struct wlr_backend *backend; + struct wlr_renderer *renderer; + struct wlr_allocator *allocator; + struct wlr_scene *scene; + struct wlr_scene_output_layout *scene_layout; + + struct wlr_xdg_shell *xdg_shell; + struct wlr_layer_shell_v1 *layer_shell; + struct wl_listener new_xdg_surface; + struct wl_listener new_layer_surface; + struct wl_list toplevels; + + struct diyac_seat seat; + + struct diyac_toplevel *grabbed_toplevel; + double grab_x, grab_y; + struct wlr_box grab_geobox; + uint32_t resize_edges; + + struct wlr_output_layout *output_layout; + struct wl_list outputs; + struct wl_listener new_output; +}; + +struct diyac_layer_surface +{ + struct wlr_scene_layer_surface_v1 *scene_layer_surface; + struct diyac_server *server; + bool mapped; + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener surface_commit; + struct wl_listener output_destroy; + struct wl_listener node_destroy; + struct wl_listener new_popup; +}; + +struct diyac_output +{ + struct wl_list link; + struct diyac_server *server; + struct wlr_output *wlr_output; + struct wl_listener frame; + struct wl_listener request_state; + struct wl_listener destroy; + + // layer output + struct wlr_scene_tree *layer_tree[4]; +}; + +struct diyac_toplevel +{ + struct wl_list link; + struct diyac_server *server; + struct wlr_xdg_toplevel *xdg_toplevel; + struct wlr_scene_tree *scene_tree; + struct wlr_output *output; + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener destroy; + struct wl_listener request_move; + struct wl_listener request_resize; + struct wl_listener request_maximize; + struct wl_listener request_fullscreen; +}; + +struct diyac_keyboard +{ + struct wl_list link; + struct diyac_server *server; + struct wlr_keyboard *wlr_keyboard; + + struct wl_listener modifiers; + struct wl_listener key; + struct wl_listener destroy; +}; + +void diyac_focus_toplevel(struct diyac_toplevel *toplevel, struct wlr_surface *surface); +struct diyac_toplevel *diyac_toplevel_at( + struct diyac_server *server, double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy); + +#endif \ No newline at end of file diff --git a/layer.c b/layer.c new file mode 100644 index 0000000..3cf5010 --- /dev/null +++ b/layer.c @@ -0,0 +1,177 @@ +static void layer_surface_commit(struct wl_listener *listener, void *data) +{ + struct diyac_layer_surface *layer = + wl_container_of(listener, layer, surface_commit); + struct wlr_layer_surface_v1 *layer_surface = + layer->scene_layer_surface->layer_surface; + struct wlr_output *wlr_output = + layer->scene_layer_surface->layer_surface->output; + + if (!wlr_output) { + return; + } + + uint32_t committed = layer_surface->current.committed; + struct diyac_output *output = (struct diyac_output *)wlr_output->data; + + /* Process layer change */ + if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) { + wlr_scene_node_reparent(&layer->scene_layer_surface->tree->node, + output->layer_tree[layer_surface->current.layer]); + } + /* Process keyboard-interactivity change */ + if (committed & WLR_LAYER_SURFACE_V1_STATE_KEYBOARD_INTERACTIVITY) { + //process_keyboard_interactivity(layer); + } + + if (committed || layer->mapped != layer_surface->surface->mapped) { + layer->mapped = layer_surface->surface->mapped; + //output_update_usable_area(output); + /* + * Update cursor focus here to ensure we + * enter a new/moved/resized layer surface. + */ + //cursor_update_focus(layer->server); + } +} +static void +layer_surface_unmap(struct wl_listener *listener, void *data) +{ + struct diyac_layer_surface *layer = wl_container_of(listener, layer, unmap); + struct wlr_layer_surface_v1 *layer_surface = + layer->scene_layer_surface->layer_surface; + if (layer_surface->output) { + //output_update_usable_area(layer_surface->output->data); + } + struct wlr_seat *seat = layer->server->seat; + //if (seat->focused_layer == layer_surface) { + //seat_set_focus_layer(seat, NULL); + //} +} + +static void layer_surface_map(struct wl_listener *listener, void *data) +{ + struct diyac_layer_surface *layer = wl_container_of(listener, layer, map); + struct wlr_output *wlr_output = + layer->scene_layer_surface->layer_surface->output; + if (wlr_output) { + //output_update_usable_area(wlr_output->data); + } + /* + * Since moving to the wlroots scene-graph API, there is no need to + * call wlr_surface_send_enter() from here since that will be done + * automatically based on the position of the surface and outputs in + * the scene. See wlr_scene_surface_create() documentation. + */ + + //process_keyboard_interactivity(layer); +} +static void +layer_surface_node_destroy(struct wl_listener *listener, void *data) +{ + struct diyac_layer_surface *layer = + wl_container_of(listener, layer, node_destroy); + + /* + * TODO: Determine if this layer is being used by an exclusive client. + * If it is, try and find another layer owned by this client to pass + * focus to. + */ + + wl_list_remove(&layer->map.link); + wl_list_remove(&layer->unmap.link); + wl_list_remove(&layer->surface_commit.link); + wl_list_remove(&layer->new_popup.link); + wl_list_remove(&layer->output_destroy.link); + wl_list_remove(&layer->node_destroy.link); + free(layer); +} +static void +layer_surface_output_destroy(struct wl_listener *listener, void *data) +{ + struct diyac_layer_surface *layer = + wl_container_of(listener, layer, output_destroy); + layer->scene_layer_surface->layer_surface->output = NULL; + wlr_layer_surface_v1_destroy(layer->scene_layer_surface->layer_surface); +} +/* This popup's parent is a layer popup */ +static void +layer_surface_new_popup(struct wl_listener *listener, void *data) +{ + /*struct lab_layer_popup *lab_layer_popup = + wl_container_of(listener, lab_layer_popup, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + struct lab_layer_popup *new_popup = create_popup(wlr_popup, + lab_layer_popup->scene_tree); + new_popup->output_toplevel_sx_box = + lab_layer_popup->output_toplevel_sx_box;*/ +} +static void server_new_layer_surface(struct wl_listener *listener, void *data) +{ + struct diyac_server *server = wl_container_of(listener, server, new_layer_surface); + struct wlr_layer_surface_v1 *layer_surface = data; + + if (!layer_surface->output) { + struct wlr_output *output = wlr_output_layout_output_at( + server->output_layout, server->seat.cursor->x, + server->seat.cursor->y); + if (!output) { + wlr_log(WLR_INFO, + "No output available to assign layer surface"); + wlr_layer_surface_v1_destroy(layer_surface); + return; + } + layer_surface->output = output; + } + + struct diyac_layer_surface *surface = calloc(1, sizeof(*surface)); + + struct diyac_output *output = layer_surface->output->data; + + struct wlr_scene_tree *selected_layer = + output->layer_tree[layer_surface->current.layer]; + + surface->scene_layer_surface = wlr_scene_layer_surface_v1_create( + selected_layer, layer_surface); + if (!surface->scene_layer_surface) { + wlr_layer_surface_v1_destroy(layer_surface); + wlr_log(WLR_ERROR, "could not create layer surface"); + return; + } + + //node_descriptor_create(&surface->scene_layer_surface->tree->node, + // LAB_NODE_DESC_LAYER_SURFACE, surface); + + surface->server = server; + surface->scene_layer_surface->layer_surface = layer_surface; + + surface->surface_commit.notify = layer_surface_commit; + wl_signal_add(&layer_surface->surface->events.commit, + &surface->surface_commit); + + surface->map.notify = layer_surface_map; + wl_signal_add(&layer_surface->surface->events.map, &surface->map); + + surface->unmap.notify = layer_surface_unmap; + wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap); + + surface->new_popup.notify = layer_surface_new_popup; + wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); + + surface->output_destroy.notify = layer_surface_output_destroy; + wl_signal_add(&layer_surface->output->events.destroy, + &surface->output_destroy); + + surface->node_destroy.notify = layer_surface_node_destroy; + wl_signal_add(&surface->scene_layer_surface->tree->node.events.destroy, + &surface->node_destroy); + + /* + * Temporarily set the layer's current state to pending so that + * it can easily be arranged. + */ + //struct wlr_layer_surface_v1_state old_state = layer_surface->current; + //layer_surface->current = layer_surface->pending; + //output_update_usable_area(output); + //layer_surface->current = old_state; +} diff --git a/output.c b/output.c new file mode 100644 index 0000000..dab8788 --- /dev/null +++ b/output.c @@ -0,0 +1,106 @@ +#define _POSIX_C_SOURCE 200112L +#include +#include +#include +#include +#include "output.h" + +static void output_frame(struct wl_listener *listener, void *data) { + /* This function is called every time an output is ready to display a frame, + * generally at the output's refresh rate (e.g. 60Hz). */ + struct diyac_output *output = wl_container_of(listener, output, frame); + struct wlr_scene *scene = output->server->scene; + + struct wlr_scene_output *scene_output = wlr_scene_get_scene_output( + scene, output->wlr_output); + + /* Render the scene if needed and commit the output */ + wlr_scene_output_commit(scene_output, NULL); + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + wlr_scene_output_send_frame_done(scene_output, &now); +} + +static void output_request_state(struct wl_listener *listener, void *data) { + /* This function is called when the backend requests a new state for + * the output. For example, Wayland and X11 backends request a new mode + * when the output window is resized. */ + struct diyac_output *output = wl_container_of(listener, output, request_state); + const struct wlr_output_event_request_state *event = data; + wlr_output_commit_state(output->wlr_output, event->state); +} + +static void output_destroy(struct wl_listener *listener, void *data) { + struct diyac_output *output = wl_container_of(listener, output, destroy); + + wl_list_remove(&output->frame.link); + wl_list_remove(&output->request_state.link); + wl_list_remove(&output->destroy.link); + wl_list_remove(&output->link); + free(output); +} + +void diyac_server_new_output(struct wl_listener *listener, void *data) { + /* This event is raised by the backend when a new output (aka a display or + * monitor) becomes available. */ + struct diyac_server *server = + wl_container_of(listener, server, new_output); + struct wlr_output *wlr_output = data; + + /* Configures the output created by the backend to use our allocator + * and our renderer. Must be done once, before commiting the output */ + wlr_output_init_render(wlr_output, server->allocator, server->renderer); + + /* The output may be disabled, switch it on. */ + struct wlr_output_state state; + wlr_output_state_init(&state); + wlr_output_state_set_enabled(&state, true); + + /* Some backends don't have modes. DRM+KMS does, and we need to set a mode + * before we can use the output. The mode is a tuple of (width, height, + * refresh rate), and each monitor supports only a specific set of modes. We + * just pick the monitor's preferred mode, a more sophisticated compositor + * would let the user configure it. */ + struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); + if (mode != NULL) { + wlr_output_state_set_mode(&state, mode); + } + + /* Atomically applies the new output state. */ + wlr_output_commit_state(wlr_output, &state); + wlr_output_state_finish(&state); + + /* Allocates and configures our state for this output */ + struct diyac_output *output = calloc(1, sizeof(*output)); + output->wlr_output = wlr_output; + output->server = server; + + /* Sets up a listener for the frame event. */ + output->frame.notify = output_frame; + wl_signal_add(&wlr_output->events.frame, &output->frame); + + /* Sets up a listener for the state request event. */ + output->request_state.notify = output_request_state; + wl_signal_add(&wlr_output->events.request_state, &output->request_state); + + /* Sets up a listener for the destroy event. */ + output->destroy.notify = output_destroy; + wl_signal_add(&wlr_output->events.destroy, &output->destroy); + + wl_list_insert(&server->outputs, &output->link); + + /* Adds this to the output layout. The add_auto function arranges outputs + * from left-to-right in the order they appear. A more sophisticated + * compositor would let the user configure the arrangement of outputs in the + * layout. + * + * The output layout utility automatically adds a wl_output global to the + * display, which Wayland clients can see to find out information about the + * output (such as DPI, scale factor, manufacturer, etc). + */ + struct wlr_output_layout_output *l_output = wlr_output_layout_add_auto(server->output_layout, + wlr_output); + struct wlr_scene_output *scene_output = wlr_scene_output_create(server->scene, wlr_output); + wlr_scene_output_layout_add_output(server->scene_layout, l_output, scene_output); +} \ No newline at end of file diff --git a/output.h b/output.h new file mode 100644 index 0000000..f438361 --- /dev/null +++ b/output.h @@ -0,0 +1,7 @@ +#ifndef DIYAC_OUTPUT_H +#define DIYAC_OUTPUT_H +#include "diyac.h" + +void diyac_server_new_output(struct wl_listener *listener, void *data); + +#endif \ No newline at end of file diff --git a/protocol/server-decoration.xml b/protocol/server-decoration.xml new file mode 100644 index 0000000..45f1128 --- /dev/null +++ b/protocol/server-decoration.xml @@ -0,0 +1,94 @@ + + + . + ]]> + + + This interface allows to coordinate whether the server should create + a server-side window decoration around a wl_surface representing a + shell surface (wl_shell_surface or similar). By announcing support + for this interface the server indicates that it supports server + side decorations. + + + + When a client creates a server-side decoration object it indicates + that it supports the protocol. The client is supposed to tell the + server whether it wants server-side decorations or will provide + client-side decorations. + + If the client does not create a server-side decoration object for + a surface the server interprets this as lack of support for this + protocol and considers it as client-side decorated. Nevertheless a + client-side decorated surface should use this protocol to indicate + to the server that it does not want a server-side deco. + + + + + + + + + + + + + This event is emitted directly after binding the interface. It contains + the default mode for the decoration. When a new server decoration object + is created this new object will be in the default mode until the first + request_mode is requested. + + The server may change the default mode at any time. + + + + + + + + + + + + + + + + + + + + + This event is emitted directly after the decoration is created and + represents the base decoration policy by the server. E.g. a server + which wants all surfaces to be client-side decorated will send Client, + a server which wants server-side decoration will send Server. + + The client can request a different mode through the decoration request. + The server will acknowledge this by another event with the same mode. So + even if a server prefers server-side decoration it's possible to force a + client-side decoration. + + The server may emit this event at any time. In this case the client can + again request a different mode. It's the responsibility of the server to + prevent a feedback loop. + + + + + diff --git a/protocol/virtual-keyboard-unstable-v1.xml b/protocol/virtual-keyboard-unstable-v1.xml new file mode 100644 index 0000000..5095c91 --- /dev/null +++ b/protocol/virtual-keyboard-unstable-v1.xml @@ -0,0 +1,113 @@ + + + + Copyright © 2008-2011 Kristian Høgsberg + Copyright © 2010-2013 Intel Corporation + Copyright © 2012-2013 Collabora, Ltd. + Copyright © 2018 Purism SPC + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + The virtual keyboard provides an application with requests which emulate + the behaviour of a physical keyboard. + + This interface can be used by clients on its own to provide raw input + events, or it can accompany the input method protocol. + + + + + Provide a file descriptor to the compositor which can be + memory-mapped to provide a keyboard mapping description. + + Format carries a value from the keymap_format enumeration. + + + + + + + + + + + + + A key was pressed or released. + The time argument is a timestamp with millisecond granularity, with an + undefined base. All requests regarding a single object must share the + same clock. + + Keymap must be set before issuing this request. + + State carries a value from the key_state enumeration. + + + + + + + + + Notifies the compositor that the modifier and/or group state has + changed, and it should update state. + + The client should use wl_keyboard.modifiers event to synchronize its + internal state with seat state. + + Keymap must be set before issuing this request. + + + + + + + + + + + + + + + A virtual keyboard manager allows an application to provide keyboard + input events as if they came from a physical keyboard. + + + + + + + + + Creates a new virtual keyboard associated to a seat. + + If the compositor enables a keyboard to perform arbitrary actions, it + should present an error when an untrusted client requests a new + keyboard. + + + + + + diff --git a/protocol/wlr-input-inhibitor-unstable-v1.xml b/protocol/wlr-input-inhibitor-unstable-v1.xml new file mode 100644 index 0000000..b62d1bb --- /dev/null +++ b/protocol/wlr-input-inhibitor-unstable-v1.xml @@ -0,0 +1,67 @@ + + + + Copyright © 2018 Drew DeVault + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Clients can use this interface to prevent input events from being sent to + any surfaces but its own, which is useful for example in lock screen + software. It is assumed that access to this interface will be locked down + to whitelisted clients by the compositor. + + + + + Activates the input inhibitor. As long as the inhibitor is active, the + compositor will not send input events to other clients. + + + + + + + + + + + + While this resource exists, input to clients other than the owner of the + inhibitor resource will not receive input events. The client that owns + this resource will receive all input events normally. The compositor will + also disable all of its own input processing (such as keyboard shortcuts) + while the inhibitor is active. + + The compositor may continue to send input events to selected clients, + such as an on-screen keyboard (via the input-method protocol). + + + + + Destroy the inhibitor and allow other clients to receive input. + + + + diff --git a/protocol/wlr-layer-shell-unstable-v1.xml b/protocol/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 0000000..d62fd51 --- /dev/null +++ b/protocol/wlr-layer-shell-unstable-v1.xml @@ -0,0 +1,390 @@ + + + + Copyright © 2017 Drew DeVault + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Clients can use this interface to assign the surface_layer role to + wl_surfaces. Such surfaces are assigned to a "layer" of the output and + rendered with a defined z-depth respective to each other. They may also be + anchored to the edges and corners of a screen and specify input handling + semantics. This interface should be suitable for the implementation of + many desktop shell components, and a broad number of other applications + that interact with the desktop. + + + + + Create a layer surface for an existing surface. This assigns the role of + layer_surface, or raises a protocol error if another role is already + assigned. + + Creating a layer surface from a wl_surface which has a buffer attached + or committed is a client error, and any attempts by a client to attach + or manipulate a buffer prior to the first layer_surface.configure call + must also be treated as errors. + + After creating a layer_surface object and setting it up, the client + must perform an initial commit without any buffer attached. + The compositor will reply with a layer_surface.configure event. + The client must acknowledge it and is then allowed to attach a buffer + to map the surface. + + You may pass NULL for output to allow the compositor to decide which + output to use. Generally this will be the one that the user most + recently interacted with. + + Clients can specify a namespace that defines the purpose of the layer + surface. + + + + + + + + + + + + + + + + + These values indicate which layers a surface can be rendered in. They + are ordered by z depth, bottom-most first. Traditional shell surfaces + will typically be rendered between the bottom and top layers. + Fullscreen shell surfaces are typically rendered at the top layer. + Multiple surfaces can share a single layer, and ordering within a + single layer is undefined. + + + + + + + + + + + + + This request indicates that the client will not use the layer_shell + object any more. Objects that have been created through this instance + are not affected. + + + + + + + An interface that may be implemented by a wl_surface, for surfaces that + are designed to be rendered as a layer of a stacked desktop-like + environment. + + Layer surface state (layer, size, anchor, exclusive zone, + margin, interactivity) is double-buffered, and will be applied at the + time wl_surface.commit of the corresponding wl_surface is called. + + Attaching a null buffer to a layer surface unmaps it. + + Unmapping a layer_surface means that the surface cannot be shown by the + compositor until it is explicitly mapped again. The layer_surface + returns to the state it had right after layer_shell.get_layer_surface. + The client can re-map the surface by performing a commit without any + buffer attached, waiting for a configure event and handling it as usual. + + + + + Sets the size of the surface in surface-local coordinates. The + compositor will display the surface centered with respect to its + anchors. + + If you pass 0 for either value, the compositor will assign it and + inform you of the assignment in the configure event. You must set your + anchor to opposite edges in the dimensions you omit; not doing so is a + protocol error. Both values are 0 by default. + + Size is double-buffered, see wl_surface.commit. + + + + + + + + Requests that the compositor anchor the surface to the specified edges + and corners. If two orthogonal edges are specified (e.g. 'top' and + 'left'), then the anchor point will be the intersection of the edges + (e.g. the top left corner of the output); otherwise the anchor point + will be centered on that edge, or in the center if none is specified. + + Anchor is double-buffered, see wl_surface.commit. + + + + + + + Requests that the compositor avoids occluding an area with other + surfaces. The compositor's use of this information is + implementation-dependent - do not assume that this region will not + actually be occluded. + + A positive value is only meaningful if the surface is anchored to one + edge or an edge and both perpendicular edges. If the surface is not + anchored, anchored to only two perpendicular edges (a corner), anchored + to only two parallel edges or anchored to all edges, a positive value + will be treated the same as zero. + + A positive zone is the distance from the edge in surface-local + coordinates to consider exclusive. + + Surfaces that do not wish to have an exclusive zone may instead specify + how they should interact with surfaces that do. If set to zero, the + surface indicates that it would like to be moved to avoid occluding + surfaces with a positive exclusive zone. If set to -1, the surface + indicates that it would not like to be moved to accommodate for other + surfaces, and the compositor should extend it all the way to the edges + it is anchored to. + + For example, a panel might set its exclusive zone to 10, so that + maximized shell surfaces are not shown on top of it. A notification + might set its exclusive zone to 0, so that it is moved to avoid + occluding the panel, but shell surfaces are shown underneath it. A + wallpaper or lock screen might set their exclusive zone to -1, so that + they stretch below or over the panel. + + The default value is 0. + + Exclusive zone is double-buffered, see wl_surface.commit. + + + + + + + Requests that the surface be placed some distance away from the anchor + point on the output, in surface-local coordinates. Setting this value + for edges you are not anchored to has no effect. + + The exclusive zone includes the margin. + + Margin is double-buffered, see wl_surface.commit. + + + + + + + + + + Types of keyboard interaction possible for layer shell surfaces. The + rationale for this is twofold: (1) some applications are not interested + in keyboard events and not allowing them to be focused can improve the + desktop experience; (2) some applications will want to take exclusive + keyboard focus. + + + + + This value indicates that this surface is not interested in keyboard + events and the compositor should never assign it the keyboard focus. + + This is the default value, set for newly created layer shell surfaces. + + This is useful for e.g. desktop widgets that display information or + only have interaction with non-keyboard input devices. + + + + + Request exclusive keyboard focus if this surface is above the shell surface layer. + + For the top and overlay layers, the seat will always give + exclusive keyboard focus to the top-most layer which has keyboard + interactivity set to exclusive. If this layer contains multiple + surfaces with keyboard interactivity set to exclusive, the compositor + determines the one receiving keyboard events in an implementation- + defined manner. In this case, no guarantee is made when this surface + will receive keyboard focus (if ever). + + For the bottom and background layers, the compositor is allowed to use + normal focus semantics. + + This setting is mainly intended for applications that need to ensure + they receive all keyboard events, such as a lock screen or a password + prompt. + + + + + This requests the compositor to allow this surface to be focused and + unfocused by the user in an implementation-defined manner. The user + should be able to unfocus this surface even regardless of the layer + it is on. + + Typically, the compositor will want to use its normal mechanism to + manage keyboard focus between layer shell surfaces with this setting + and regular toplevels on the desktop layer (e.g. click to focus). + Nevertheless, it is possible for a compositor to require a special + interaction to focus or unfocus layer shell surfaces (e.g. requiring + a click even if focus follows the mouse normally, or providing a + keybinding to switch focus between layers). + + This setting is mainly intended for desktop shell components (e.g. + panels) that allow keyboard interaction. Using this option can allow + implementing a desktop shell that can be fully usable without the + mouse. + + + + + + + Set how keyboard events are delivered to this surface. By default, + layer shell surfaces do not receive keyboard events; this request can + be used to change this. + + This setting is inherited by child surfaces set by the get_popup + request. + + Layer surfaces receive pointer, touch, and tablet events normally. If + you do not want to receive them, set the input region on your surface + to an empty region. + + Keyboard interactivity is double-buffered, see wl_surface.commit. + + + + + + + This assigns an xdg_popup's parent to this layer_surface. This popup + should have been created via xdg_surface::get_popup with the parent set + to NULL, and this request must be invoked before committing the popup's + initial state. + + See the documentation of xdg_popup for more details about what an + xdg_popup is and how it is used. + + + + + + + When a configure event is received, if a client commits the + surface in response to the configure event, then the client + must make an ack_configure request sometime before the commit + request, passing along the serial of the configure event. + + If the client receives multiple configure events before it + can respond to one, it only has to ack the last configure event. + + A client is not required to commit immediately after sending + an ack_configure request - it may even ack_configure several times + before its next surface commit. + + A client may send multiple ack_configure requests before committing, but + only the last request sent before a commit indicates which configure + event the client really is responding to. + + + + + + + This request destroys the layer surface. + + + + + + The configure event asks the client to resize its surface. + + Clients should arrange their surface for the new states, and then send + an ack_configure request with the serial sent in this configure event at + some point before committing the new surface. + + The client is free to dismiss all but the last configure event it + received. + + The width and height arguments specify the size of the window in + surface-local coordinates. + + The size is a hint, in the sense that the client is free to ignore it if + it doesn't resize, pick a smaller size (to satisfy aspect ratio or + resize in steps of NxM pixels). If the client picks a smaller size and + is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the + surface will be centered on this axis. + + If the width or height arguments are zero, it means the client should + decide its own window dimension. + + + + + + + + + The closed event is sent by the compositor when the surface will no + longer be shown. The output may have been destroyed or the user may + have asked for it to be removed. Further changes to the surface will be + ignored. The client should destroy the resource after receiving this + event, and create a new surface if they so choose. + + + + + + + + + + + + + + + + + + + + + + Change the layer that the surface is rendered on. + + Layer is double-buffered, see wl_surface.commit. + + + + + diff --git a/seat.c b/seat.c new file mode 100644 index 0000000..2328255 --- /dev/null +++ b/seat.c @@ -0,0 +1,236 @@ +#define _POSIX_C_SOURCE 200112L +#include +#include +#include +#include +#include "seat.h" + +static void keyboard_handle_modifiers( + struct wl_listener *listener, void *data) +{ + /* This event is raised when a modifier key, such as shift or alt, is + * pressed. We simply communicate this to the client. */ + struct diyac_keyboard *keyboard = + wl_container_of(listener, keyboard, modifiers); + /* + * A seat can only have one keyboard, but this is a limitation of the + * Wayland protocol - not wlroots. We assign all connected keyboards to the + * same seat. You can swap out the underlying wlr_keyboard like this and + * wlr_seat handles this transparently. + */ + wlr_seat_set_keyboard(keyboard->server->seat.wlr_seat, keyboard->wlr_keyboard); + /* Send modifiers to the client. */ + wlr_seat_keyboard_notify_modifiers(keyboard->server->seat.wlr_seat, + &keyboard->wlr_keyboard->modifiers); +} + +static bool handle_keybinding(struct diyac_server *server, xkb_keysym_t sym) +{ + /* + * Here we handle compositor keybindings. This is when the compositor is + * processing keys, rather than passing them on to the client for its own + * processing. + * + * This function assumes Alt is held down. + */ + switch (sym) + { + case XKB_KEY_Escape: + wl_display_terminate(server->wl_display); + break; + case XKB_KEY_F1: + /* Cycle to the next toplevel */ + if (wl_list_length(&server->toplevels) < 2) + { + break; + } + struct diyac_toplevel *next_toplevel = + wl_container_of(server->toplevels.prev, next_toplevel, link); + diyac_focus_toplevel(next_toplevel, next_toplevel->xdg_toplevel->base->surface); + break; + default: + return false; + } + return true; +} + +static void keyboard_handle_key( + struct wl_listener *listener, void *data) +{ + /* This event is raised when a key is pressed or released. */ + struct diyac_keyboard *keyboard = + wl_container_of(listener, keyboard, key); + struct diyac_server *server = keyboard->server; + struct wlr_keyboard_key_event *event = data; + struct wlr_seat *seat = server->seat.wlr_seat; + + /* Translate libinput keycode -> xkbcommon */ + uint32_t keycode = event->keycode + 8; + /* Get a list of keysyms based on the keymap for this keyboard */ + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms( + keyboard->wlr_keyboard->xkb_state, keycode, &syms); + + bool handled = false; + uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr_keyboard); + if ((modifiers & WLR_MODIFIER_ALT) && + event->state == WL_KEYBOARD_KEY_STATE_PRESSED) + { + /* If alt is held down and this button was _pressed_, we attempt to + * process it as a compositor keybinding. */ + for (int i = 0; i < nsyms; i++) + { + handled = handle_keybinding(server, syms[i]); + } + } + + if (!handled) + { + /* Otherwise, we pass it along to the client. */ + wlr_seat_set_keyboard(seat, keyboard->wlr_keyboard); + wlr_seat_keyboard_notify_key(seat, event->time_msec, + event->keycode, event->state); + } +} + +static void keyboard_handle_destroy(struct wl_listener *listener, void *data) +{ + /* This event is raised by the keyboard base wlr_input_device to signal + * the destruction of the wlr_keyboard. It will no longer receive events + * and should be destroyed. + */ + struct diyac_keyboard *keyboard = + wl_container_of(listener, keyboard, destroy); + wl_list_remove(&keyboard->modifiers.link); + wl_list_remove(&keyboard->key.link); + wl_list_remove(&keyboard->destroy.link); + wl_list_remove(&keyboard->link); + free(keyboard); +} + +static void server_new_keyboard(struct diyac_server *server, + struct wlr_input_device *device) +{ + struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(device); + + struct diyac_keyboard *keyboard = calloc(1, sizeof(*keyboard)); + keyboard->server = server; + keyboard->wlr_keyboard = wlr_keyboard; + + /* We need to prepare an XKB keymap and assign it to the keyboard. This + * assumes the defaults (e.g. layout = "us"). */ + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, + XKB_KEYMAP_COMPILE_NO_FLAGS); + + wlr_keyboard_set_keymap(wlr_keyboard, keymap); + xkb_keymap_unref(keymap); + xkb_context_unref(context); + wlr_keyboard_set_repeat_info(wlr_keyboard, 25, 600); + + /* Here we set up listeners for keyboard events. */ + keyboard->modifiers.notify = keyboard_handle_modifiers; + wl_signal_add(&wlr_keyboard->events.modifiers, &keyboard->modifiers); + keyboard->key.notify = keyboard_handle_key; + wl_signal_add(&wlr_keyboard->events.key, &keyboard->key); + keyboard->destroy.notify = keyboard_handle_destroy; + wl_signal_add(&device->events.destroy, &keyboard->destroy); + + wlr_seat_set_keyboard(server->seat.wlr_seat, keyboard->wlr_keyboard); + + /* And add the keyboard to our list of keyboards */ + wl_list_insert(&server->seat.keyboards, &keyboard->link); +} + +static void server_new_pointer(struct diyac_server *server, + struct wlr_input_device *device) +{ + /* We don't do anything special with pointers. All of our pointer handling + * is proxied through wlr_cursor. On another compositor, you might take this + * opportunity to do libinput configuration on the device to set + * acceleration, etc. */ + wlr_cursor_attach_input_device(server->seat.cursor, device); +} + +static void server_new_input(struct wl_listener *listener, void *data) +{ + /* This event is raised by the backend when a new input device becomes + * available. */ + struct diyac_seat* seat = + wl_container_of(listener, seat, new_input); + struct wlr_input_device *device = data; + switch (device->type) + { + case WLR_INPUT_DEVICE_KEYBOARD: + server_new_keyboard(seat->server, device); + break; + case WLR_INPUT_DEVICE_POINTER: + server_new_pointer(seat->server, device); + break; + default: + break; + } + /* We need to let the wlr_seat know what our capabilities are, which is + * communiciated to the client. In Diyac we always have a cursor, even if + * there are no pointer devices, so we always include that capability. */ + uint32_t caps = WL_SEAT_CAPABILITY_POINTER; + if (!wl_list_empty(&seat->keyboards)) + { + caps |= WL_SEAT_CAPABILITY_KEYBOARD; + } + wlr_seat_set_capabilities(seat->wlr_seat, caps); +} + +static void seat_request_cursor(struct wl_listener *listener, void *data) +{ + struct diyac_seat *seat = wl_container_of( + listener, seat, request_cursor); + /* This event is raised 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->wlr_seat->pointer_state.focused_client; + /* This can be sent by any client, so we check to make sure this one is + * actually has pointer focus first. */ + if (focused_client == event->seat_client) + { + /* Once we've vetted the client, we can tell the cursor to use the + * provided surface as the cursor image. It will set the hardware cursor + * on the output that it's currently on and continue to do so as the + * cursor moves between outputs. */ + wlr_cursor_set_surface(seat->cursor, event->surface, + event->hotspot_x, event->hotspot_y); + } +} + +static void seat_request_set_selection(struct wl_listener *listener, void *data) +{ + /* This event is raised by the seat when a client wants to set the selection, + * usually when the user copies something. wlroots allows compositors to + * ignore such requests if they so choose, but in diyac we always honor + */ + struct diyac_seat *seat = wl_container_of( + listener, seat, request_set_selection); + struct wlr_seat_request_set_selection_event *event = data; + wlr_seat_set_selection(seat->wlr_seat, event->source, event->serial); +} + +/* + * Configures a seat, which is a single "seat" at which a user sits and + * operates the computer. This conceptually includes up to one keyboard, + * pointer, touch, and drawing tablet device. We also rig up a listener to + * let us know when new input devices are available on the backend. + */ +void diyac_init_seat(struct diyac_server *server) +{ + //server->seat = calloc(1, sizeof(struct diyac_seat)); + server->seat.server = server; + server->seat.new_input.notify = server_new_input; + wl_signal_add(&server->backend->events.new_input, &server->seat.new_input); + server->seat.wlr_seat = wlr_seat_create(server->wl_display, "seat0"); + server->seat.request_cursor.notify = seat_request_cursor; + wl_signal_add(&server->seat.wlr_seat->events.request_set_cursor, + &server->seat.request_cursor); + server->seat.request_set_selection.notify = seat_request_set_selection; + wl_signal_add(&server->seat.wlr_seat->events.request_set_selection, + &server->seat.request_set_selection); +} \ No newline at end of file diff --git a/seat.h b/seat.h new file mode 100644 index 0000000..e4a2f48 --- /dev/null +++ b/seat.h @@ -0,0 +1,6 @@ +#ifndef DIYAC_SEAT_H +#define DIYAC_SEAT_H +#include "diyac.h" + +void diyac_init_seat(struct diyac_server* server); +#endif \ No newline at end of file diff --git a/xdg.c b/xdg.c new file mode 100644 index 0000000..37bef66 --- /dev/null +++ b/xdg.c @@ -0,0 +1,213 @@ +#define _POSIX_C_SOURCE 200112L +#include +#include +#include +#include +#include +#include +#include "xdg.h" +#include "cursor.h" + +static void begin_interactive(struct diyac_toplevel *toplevel, + enum diyac_cursor_mode mode, uint32_t edges) +{ + /* This function sets up an interactive move or resize operation, where the + * compositor stops propegating pointer events to clients and instead + * consumes them itself, to move or resize windows. */ + struct diyac_server *server = toplevel->server; + struct wlr_surface *focused_surface = + server->seat.wlr_seat->pointer_state.focused_surface; + if (toplevel->xdg_toplevel->base->surface != + wlr_surface_get_root_surface(focused_surface)) + { + /* Deny move/resize requests from unfocused clients. */ + return; + } + server->grabbed_toplevel = toplevel; + server->seat.cursor_mode = mode; + + if (mode == DIYAC_CURSOR_MOVE) + { + server->grab_x = server->seat.cursor->x - toplevel->scene_tree->node.x; + server->grab_y = server->seat.cursor->y - toplevel->scene_tree->node.y; + } + else + { + struct wlr_box geo_box; + wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box); + + double border_x = (toplevel->scene_tree->node.x + geo_box.x) + + ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0); + double border_y = (toplevel->scene_tree->node.y + geo_box.y) + + ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0); + server->grab_x = server->seat.cursor->x - border_x; + server->grab_y = server->seat.cursor->y - border_y; + + server->grab_geobox = geo_box; + server->grab_geobox.x += toplevel->scene_tree->node.x; + server->grab_geobox.y += toplevel->scene_tree->node.y; + + server->resize_edges = edges; + } +} + +static void xdg_toplevel_map(struct wl_listener *listener, void *data) +{ + /* Called when the surface is mapped, or ready to display on-screen. */ + struct diyac_toplevel *toplevel = wl_container_of(listener, toplevel, map); + + wl_list_insert(&toplevel->server->toplevels, &toplevel->link); + + diyac_focus_toplevel(toplevel, toplevel->xdg_toplevel->base->surface); +} + +static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) +{ + /* Called when the surface is unmapped, and should no longer be shown. */ + struct diyac_toplevel *toplevel = wl_container_of(listener, toplevel, unmap); + + /* Reset the cursor mode if the grabbed toplevel was unmapped. */ + if (toplevel == toplevel->server->grabbed_toplevel) + { + diyac_reset_cursor_mode(toplevel->server); + } + + wl_list_remove(&toplevel->link); +} + +static void xdg_toplevel_request_move( + struct wl_listener *listener, void *data) +{ + /* This event is raised when a client would like to begin an interactive + * move, typically because the user clicked on their client-side + * decorations. Note that a more sophisticated compositor should check the + * provided serial against a list of button press serials sent to this + * client, to prevent the client from requesting this whenever they want. */ + struct diyac_toplevel *toplevel = wl_container_of(listener, toplevel, request_move); + begin_interactive(toplevel, DIYAC_CURSOR_MOVE, 0); +} + +static void xdg_toplevel_request_resize( + struct wl_listener *listener, void *data) +{ + /* This event is raised when a client would like to begin an interactive + * resize, typically because the user clicked on their client-side + * decorations. Note that a more sophisticated compositor should check the + * provided serial against a list of button press serials sent to this + * client, to prevent the client from requesting this whenever they want. */ + struct wlr_xdg_toplevel_resize_event *event = data; + struct diyac_toplevel *toplevel = wl_container_of(listener, toplevel, request_resize); + begin_interactive(toplevel, DIYAC_CURSOR_RESIZE, event->edges); +} + +static void xdg_toplevel_request_maximize( + struct wl_listener *listener, void *data) +{ + /* This event is raised when a client would like to maximize itself, + * typically because the user clicked on the maximize button on + * client-side decorations. diyac doesn't support maximization, but + * to conform to xdg-shell protocol we still must send a configure. + * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ + + struct diyac_toplevel *toplevel = + wl_container_of(listener, toplevel, request_maximize); + // wlr_wl_output* output = get_wl_output_from_surface(struct wlr_wl_backend *wl, + // struct wl_surface *surface); + wlr_log(WLR_ERROR, "request maximize %dx%d", toplevel->output->width, toplevel->output->height); + wlr_scene_node_set_position(&toplevel->scene_tree->node,0,0); + wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, toplevel->output->width, toplevel->output->height); + //wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); +} + +static void xdg_toplevel_request_fullscreen( + struct wl_listener *listener, void *data) +{ + /* Just as with request_maximize, we must send a configure here. */ + struct diyac_toplevel *toplevel = + wl_container_of(listener, toplevel, request_fullscreen); + wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); +} + +static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) +{ + /* Called when the xdg_toplevel is destroyed. */ + struct diyac_toplevel *toplevel = wl_container_of(listener, toplevel, destroy); + + wl_list_remove(&toplevel->map.link); + wl_list_remove(&toplevel->unmap.link); + wl_list_remove(&toplevel->destroy.link); + wl_list_remove(&toplevel->request_move.link); + wl_list_remove(&toplevel->request_resize.link); + wl_list_remove(&toplevel->request_maximize.link); + wl_list_remove(&toplevel->request_fullscreen.link); + + free(toplevel); +} + +void diyac_new_xdg_surface(struct wl_listener *listener, void *data) +{ + /* This event is raised when wlr_xdg_shell receives a new xdg surface from a + * client, either a toplevel (application window) or popup. */ + struct diyac_server *server = + wl_container_of(listener, server, new_xdg_surface); + struct wlr_xdg_surface *xdg_surface = data; + + /* We must add xdg popups to the scene graph so they get rendered. The + * wlroots scene graph provides a helper for this, but to use it we must + * provide the proper parent scene node of the xdg popup. To enable this, + * we always set the user data field of xdg_surfaces to the corresponding + * scene node. */ + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) + { + struct wlr_xdg_surface *parent = + wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent); + assert(parent != NULL); + struct wlr_scene_tree *parent_tree = parent->data; + xdg_surface->data = wlr_scene_xdg_surface_create( + parent_tree, xdg_surface); + return; + } + assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + + + + /* Allocate a diyac_toplevel for this surface */ + struct diyac_toplevel *toplevel = calloc(1, sizeof(*toplevel)); + toplevel->server = server; + toplevel->xdg_toplevel = xdg_surface->toplevel; + toplevel->scene_tree = wlr_scene_xdg_surface_create( + &toplevel->server->scene->tree, toplevel->xdg_toplevel->base); + toplevel->scene_tree->node.data = toplevel; + xdg_surface->data = toplevel->scene_tree; + + struct wlr_output *output = wlr_output_layout_output_at( + server->output_layout, server->seat.cursor->x, + server->seat.cursor->y); + toplevel->output = output; + if (!output) + { + wlr_log(WLR_ERROR, + "No output available to assign layer surface"); + + } + /* Listen to the various events it can emit */ + toplevel->map.notify = xdg_toplevel_map; + wl_signal_add(&xdg_surface->surface->events.map, &toplevel->map); + toplevel->unmap.notify = xdg_toplevel_unmap; + wl_signal_add(&xdg_surface->surface->events.unmap, &toplevel->unmap); + toplevel->destroy.notify = xdg_toplevel_destroy; + wl_signal_add(&xdg_surface->events.destroy, &toplevel->destroy); + + /* cotd */ + struct wlr_xdg_toplevel *xdg_toplevel = xdg_surface->toplevel; + toplevel->request_move.notify = xdg_toplevel_request_move; + wl_signal_add(&xdg_toplevel->events.request_move, &toplevel->request_move); + toplevel->request_resize.notify = xdg_toplevel_request_resize; + wl_signal_add(&xdg_toplevel->events.request_resize, &toplevel->request_resize); + toplevel->request_maximize.notify = xdg_toplevel_request_maximize; + wl_signal_add(&xdg_toplevel->events.request_maximize, + &toplevel->request_maximize); + toplevel->request_fullscreen.notify = xdg_toplevel_request_fullscreen; + wl_signal_add(&xdg_toplevel->events.request_fullscreen, + &toplevel->request_fullscreen); +} \ No newline at end of file diff --git a/xdg.h b/xdg.h new file mode 100644 index 0000000..294f0c6 --- /dev/null +++ b/xdg.h @@ -0,0 +1,7 @@ +#ifndef DIYAC_XDG_H +#define DIYAC_XDG_H +#include "diyac.h" + +void diyac_new_xdg_surface(struct wl_listener *listener, void *data); + +#endif \ No newline at end of file