From 55719d6dbab59a6829279b810eb2d0d70131491b Mon Sep 17 00:00:00 2001 From: DanyLE Date: Sun, 31 Mar 2024 16:09:01 +0200 Subject: [PATCH] feat: add support layershell protocol --- Makefile | 12 +- cursor.c | 41 +++-- desktop.c | 226 +++++++++++++++++++++++ desktop.h | 12 ++ diyac.c | 386 +++++++++++++++----------------------- diyac.h | 234 +++++++++++++++-------- layer.c | 542 +++++++++++++++++++++++++++++++++++++++--------------- layer.h | 7 + node.c | 37 ++++ node.h | 8 + output.c | 117 +++++++++++- output.h | 4 +- seat.c | 84 ++++++++- seat.h | 2 + xdg.c | 226 +++++++++++++++++++---- 15 files changed, 1404 insertions(+), 534 deletions(-) create mode 100644 desktop.c create mode 100644 desktop.h create mode 100644 layer.h create mode 100644 node.c create mode 100644 node.h diff --git a/Makefile b/Makefile index d096bbc..e916472 100644 --- a/Makefile +++ b/Makefile @@ -10,8 +10,12 @@ OBJS=\ cursor.c \ output.c \ seat.c \ + node.c \ + desktop.c \ xdg.c \ - xdg-shell-protocol.c + xdg-shell-protocol.c \ + layer.c \ + wlr-layer-shell-unstable-v1-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 @@ -24,6 +28,10 @@ xdg-shell-protocol.c: xdg-shell-protocol.h $(WAYLAND_SCANNER) private-code \ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ +wlr-layer-shell-unstable-v1-protocol.c: wlr-layer-shell-unstable-v1-protocol.h + $(WAYLAND_SCANNER) private-code \ + protocol/wlr-layer-shell-unstable-v1.xml $@ + wlr-layer-shell-unstable-v1-protocol.h: $(WAYLAND_SCANNER) server-header \ protocol/wlr-layer-shell-unstable-v1.xml $@ @@ -37,7 +45,7 @@ diyac: $(OBJS) $(LIBS) clean: - rm -f diyac xdg-shell-protocol.h xdg-shell-protocol.c + rm -f diyac xdg-shell-protocol.* wlr-layer-shell-unstable-v1-protocol.* .DEFAULT_GOAL=diyac .PHONY: clean \ No newline at end of file diff --git a/cursor.c b/cursor.c index 34b562f..5ab8f55 100644 --- a/cursor.c +++ b/cursor.c @@ -1,23 +1,29 @@ #define _POSIX_C_SOURCE 200112L -#include -#include -#include +#include +#include #include "cursor.h" +#include "desktop.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; + server->grabbed_view = 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; + struct diyac_view *toplevel = server->grabbed_view; + toplevel->current.x = server->seat.cursor->x - server->grab_x; + toplevel->current.y = server->seat.cursor->y - server->grab_y; + toplevel->state = DIYAC_VIEW_NORMAL; + diyac_view_update_geometry(toplevel, false); + /* wlr_scene_node_set_position(&toplevel->scene_tree->node, - server->seat.cursor->x - server->grab_x, - server->seat.cursor->y - server->grab_y); + toplevel->current.x , + toplevel->current.y); + */ } static void process_cursor_resize(struct diyac_server *server, uint32_t time) @@ -32,7 +38,7 @@ static void process_cursor_resize(struct diyac_server *server, uint32_t time) * 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; + struct diyac_view *toplevel = server->grabbed_view; 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; @@ -75,12 +81,19 @@ static void process_cursor_resize(struct diyac_server *server, uint32_t time) 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); - + toplevel->current.x = new_left - geo_box.x; + toplevel->current.y = new_top - geo_box.y; int new_width = new_right - new_left; int new_height = new_bottom - new_top; + toplevel->current.width = new_width; + toplevel->current.height = new_height; + toplevel->state = DIYAC_VIEW_NORMAL; + diyac_view_update_geometry(toplevel, true); + /* + wlr_scene_node_set_position(&toplevel->scene_tree->node, + toplevel->current.x, toplevel->current.y); wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, new_width, new_height); + */ } static void process_cursor_motion(struct diyac_server *server, uint32_t time) @@ -101,7 +114,7 @@ static void process_cursor_motion(struct diyac_server *server, uint32_t time) double sx, sy; struct wlr_seat *seat = server->seat.wlr_seat; struct wlr_surface *surface = NULL; - struct diyac_toplevel *toplevel = diyac_toplevel_at(server, + struct diyac_view *toplevel = diyac_view_at(server, server->seat.cursor->x, server->seat.cursor->y, &surface, &sx, &sy); if (!toplevel) { @@ -180,7 +193,7 @@ static void server_cursor_button(struct wl_listener *listener, void *data) event->time_msec, event->button, event->state); double sx, sy; struct wlr_surface *surface = NULL; - struct diyac_toplevel *toplevel = diyac_toplevel_at(seat->server, + struct diyac_view *toplevel = diyac_view_at(seat->server, seat->cursor->x, seat->cursor->y, &surface, &sx, &sy); if (event->state == WLR_BUTTON_RELEASED) { @@ -190,7 +203,7 @@ static void server_cursor_button(struct wl_listener *listener, void *data) else { /* Focus that client if the button was _pressed_ */ - diyac_focus_toplevel(toplevel, surface); + diyac_focus_view(toplevel); } } diff --git a/desktop.c b/desktop.c new file mode 100644 index 0000000..843a36d --- /dev/null +++ b/desktop.c @@ -0,0 +1,226 @@ +#define _POSIX_C_SOURCE 200112L +#include +#include +#include "desktop.h" +#include "node.h" +#include "seat.h" +#include "output.h" + +void diyac_focus_view(struct diyac_view *toplevel) +{ + /* Note: this function only deals with keyboard focus. */ + if (toplevel == NULL) + { + return; + } + struct diyac_server *server = toplevel->server; + struct wlr_surface *prev_surface = server->seat.wlr_seat->keyboard_state.focused_surface; + if (prev_surface == toplevel->xdg_toplevel->base->surface) + { + /* Don't re-focus an already focused surface. */ + return; + } + 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. + * stop displaying a caret. + */ + struct wlr_xdg_toplevel *prev_toplevel = + wlr_xdg_toplevel_try_from_wlr_surface(prev_surface); + if (prev_toplevel != NULL) + { + wlr_xdg_toplevel_set_activated(prev_toplevel, false); + } + } + /* Move the toplevel to the front */ + wlr_scene_node_raise_to_top(&toplevel->scene_tree->node); + wl_list_remove(&toplevel->link); + wl_list_insert(&server->views, &toplevel->link); + /* Activate the new surface */ + wlr_xdg_toplevel_set_activated(toplevel->xdg_toplevel, true); + /* + * Tell the seat to have the keyboard enter this surface. wlroots will keep + * track of this and automatically send key events to the appropriate + * clients without additional work on your part. + */ + diyac_seat_focus_surface(&server->seat, toplevel->xdg_toplevel->base->surface); +} + +struct diyac_view *diyac_view_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 diyac_view. */ + 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) + { + 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) + { + return NULL; + } + + *surface = scene_surface->surface; + /* Find the node corresponding to the diyac_view 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) + { + tree = tree->node.parent; + } + struct diyac_node_descriptor *node_descriptor = tree->node.data; + if (!node_descriptor || node_descriptor->type != DIYAC_NODE_VIEW) + { + return NULL; + } + + return node_descriptor->data; +} + +void diyac_focus_topmost_view(struct diyac_server *server) +{ + struct diyac_view *view = diyac_topmost_focusable_view(server); + if (view) + { + diyac_focus_view(view); + } + else + { + /* + * Defocus previous focused surface/view if no longer + * focusable (e.g. unmapped or on a different workspace). + */ + diyac_seat_focus_surface(&server->seat, NULL); + } +} + +struct diyac_view *diyac_topmost_focusable_view(struct diyac_server *server) +{ + struct wlr_surface *prev = + server->seat.wlr_seat->keyboard_state.focused_surface; + struct diyac_view *view; + struct wl_list *node_list; + struct wlr_scene_node *node; + node_list = &server->view_tree->children; + wl_list_for_each_reverse(node, node_list, link) + { + if (!node->data) + { + /* We found some non-view, most likely the region overlay */ + continue; + } + view = diyac_view_from_node(node); + return view; + /* + if (view->mapped && view_is_focusable_from(view, prev)) + { + return view; + }*/ + } + return NULL; +} + +void diyac_arrange_all_views(struct diyac_server *server) +{ + /* + * Adjust window positions/sizes. Skip views with no size since + * we can't do anything useful with them; they will presumably + * be initialized with valid positions/sizes later. + * + * We do not simply check view->mapped/been_mapped here because + * views can have maximized/fullscreen geometry applied while + * still unmapped. We do want to adjust the geometry of those + * views. + */ + struct diyac_view *view; + wl_list_for_each(view, &server->views, link) + { + diyac_view_update_geometry(view, true); + } +} +bool diyac_view_update_geometry(struct diyac_view *view, bool resize) +{ + assert(view); + bool adjusted = false; + struct wlr_box geo_box; + struct wlr_box* geometry = &view->current; + // if (wlr_output_layout_intersects(view->server->output_layout, + // view->output->wlr_output, &view->current)) + //{ + struct wlr_box usable = diyac_output_usable_area(view->output); + wlr_log(WLR_INFO, "diyac_view_update_geometry: current: [%d,%d,%d,%d], usable: [%d,%d,%d,%d] ", + geometry->x, geometry->y, geometry->width, geometry->height, + usable.x, usable.y, usable.width, usable.height); + + switch (view->state) + { + case DIYAC_VIEW_MAXIMIZE: + /** + * We dont change the current_view geometry in maximize state + * + */ + wlr_scene_node_set_position(&view->scene_tree->node, usable.x, usable.y); + wlr_xdg_toplevel_set_size(view->xdg_toplevel, usable.width, usable.height); + return true; + case DIYAC_VIEW_MINIMIZE: + wlr_log(WLR_INFO, "diyac_view_update_geometry: minimize ignore"); + return false; + case DIYAC_VIEW_FULL_SCREEN: + wlr_log(WLR_INFO, "diyac_view_update_geometry: full-screen ignore"); + return false; + + default: + wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box); + if(!wlr_box_equal(geometry, &geo_box)) + { + adjusted = true; + } + /**Normal state, recalculate current geometry*/ + if (geometry->x < usable.x) + { + geometry->x = usable.x; + adjusted = true; + } + if((! resize) && (geometry->x + geometry->width > usable.width)) + { + geometry->x = usable.width - geometry->width; + adjusted = true; + } + if (geometry->y < usable.y) + { + geometry->y = usable.y; + adjusted = true; + } + if((! resize) && (geometry->y + geometry->height > usable.height)) + { + geometry->y = usable.height - geometry->height; + adjusted = true; + } + if (resize && geometry->width > usable.width) + { + geometry->width = usable.width; + adjusted = true; + } + if (resize && geometry->height > usable.height) + { + geometry->height = usable.height; + adjusted = true; + } + if (adjusted) + { + wlr_log(WLR_INFO, "diyac_view_update_geometry: updating geometry"); + wlr_scene_node_set_position(&view->scene_tree->node, geometry->x, geometry->y); + wlr_xdg_toplevel_set_size(view->xdg_toplevel, geometry->width, geometry->height); + } + return adjusted; + } +} \ No newline at end of file diff --git a/desktop.h b/desktop.h new file mode 100644 index 0000000..07ca32c --- /dev/null +++ b/desktop.h @@ -0,0 +1,12 @@ +#ifndef DIYAC_DESKTOP_H +#define DIYAC_DESKTOP_H +#include "diyac.h" +void diyac_focus_view(struct diyac_view *toplevel); +struct diyac_view *diyac_view_at( + struct diyac_server *server, double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy); +void diyac_focus_topmost_view(struct diyac_server *server); +struct diyac_view * diyac_topmost_focusable_view(struct diyac_server *server); +bool diyac_view_update_geometry(struct diyac_view *view, bool resize); +void diyac_arrange_all_views(struct diyac_server *server); +#endif \ No newline at end of file diff --git a/diyac.c b/diyac.c index b66b3a8..9057d74 100644 --- a/diyac.c +++ b/diyac.c @@ -3,265 +3,177 @@ #include #include #include -#include #include -#include -#include -#include -#include -#include -#include - -#include "diyac.h" #include "output.h" #include "xdg.h" #include "cursor.h" #include "seat.h" - -void diyac_focus_toplevel(struct diyac_toplevel *toplevel, struct wlr_surface *surface) -{ - /* Note: this function only deals with keyboard focus. */ - if (toplevel == NULL) - { - return; - } - 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) - { - /* Don't re-focus an already focused surface. */ - return; - } - 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. - * stop displaying a caret. - */ - struct wlr_xdg_toplevel *prev_toplevel = - wlr_xdg_toplevel_try_from_wlr_surface(prev_surface); - if (prev_toplevel != NULL) - { - wlr_xdg_toplevel_set_activated(prev_toplevel, false); - } - } - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); - /* Move the toplevel to the front */ - wlr_scene_node_raise_to_top(&toplevel->scene_tree->node); - wl_list_remove(&toplevel->link); - wl_list_insert(&server->toplevels, &toplevel->link); - /* Activate the new surface */ - wlr_xdg_toplevel_set_activated(toplevel->xdg_toplevel, true); - /* - * Tell the seat to have the keyboard enter this surface. wlroots will keep - * track of this and automatically send key events to the appropriate - * clients without additional work on your part. - */ - if (keyboard != NULL) - { - wlr_seat_keyboard_notify_enter(seat, toplevel->xdg_toplevel->base->surface, - keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); - } -} - -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 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) - { - 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) - { - return NULL; - } - - *surface = scene_surface->surface; - /* 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) - { - tree = tree->node.parent; - } - return tree->node.data; -} +#include "layer.h" int main(int argc, char *argv[]) { - wlr_log_init(WLR_INFO, NULL); - char *startup_cmd = NULL; + wlr_log_init(WLR_INFO, NULL); + char *startup_cmd = NULL; - int c; - while ((c = getopt(argc, argv, "s:h")) != -1) - { - switch (c) - { - case 's': - startup_cmd = optarg; - break; - default: - printf("Usage: %s [-s startup command]\n", argv[0]); - return 0; - } - } - if (optind < argc) - { - printf("Usage: %s [-s startup command]\n", argv[0]); - return 0; - } + int c; + while ((c = getopt(argc, argv, "s:h")) != -1) + { + switch (c) + { + case 's': + startup_cmd = optarg; + break; + default: + printf("Usage: %s [-s startup command]\n", argv[0]); + return 0; + } + } + if (optind < argc) + { + printf("Usage: %s [-s startup command]\n", argv[0]); + return 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(); - /* The backend is a wlroots feature which abstracts the underlying input and - * output hardware. The autocreate option will choose the most suitable - * 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) - { - wlr_log(WLR_ERROR, "failed to create wlr_backend"); - return 1; - } + 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(); + /* The backend is a wlroots feature which abstracts the underlying input and + * output hardware. The autocreate option will choose the most suitable + * 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) + { + wlr_log(WLR_ERROR, "failed to create wlr_backend"); + return 1; + } - /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user - * can also specify a renderer using the WLR_RENDERER env var. - * 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) - { - wlr_log(WLR_ERROR, "failed to create wlr_renderer"); - return 1; - } + /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user + * can also specify a renderer using the WLR_RENDERER env var. + * 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) + { + wlr_log(WLR_ERROR, "failed to create wlr_renderer"); + return 1; + } - wlr_renderer_init_wl_display(server.renderer, server.wl_display); + wlr_renderer_init_wl_display(server.renderer, server.wl_display); - /* Autocreates an allocator for us. - * The allocator is the bridge between the renderer and the backend. It - * handles the buffer creation, allowing wlroots to render onto the - * screen */ - server.allocator = wlr_allocator_autocreate(server.backend, - server.renderer); - if (server.allocator == NULL) - { - wlr_log(WLR_ERROR, "failed to create wlr_allocator"); - return 1; - } + /* Autocreates an allocator for us. + * The allocator is the bridge between the renderer and the backend. It + * handles the buffer creation, allowing wlroots to render onto the + * screen */ + server.allocator = wlr_allocator_autocreate(server.backend, + server.renderer); + if (server.allocator == NULL) + { + wlr_log(WLR_ERROR, "failed to create wlr_allocator"); + return 1; + } - /* This creates some hands-off wlroots interfaces. The compositor is - * necessary for clients to allocate surfaces, the subcompositor allows to - * assign the role of subsurfaces to surfaces and the data device manager - * handles the clipboard. Each of these wlroots interfaces has room for you - * to dig your fingers in and play with their behavior if you want. Note that - * the clients cannot set the selection directly without compositor approval, - * see the handling of the request_set_selection event below.*/ - wlr_compositor_create(server.wl_display, 5, server.renderer); - wlr_subcompositor_create(server.wl_display); - wlr_data_device_manager_create(server.wl_display); + /* This creates some hands-off wlroots interfaces. The compositor is + * necessary for clients to allocate surfaces, the subcompositor allows to + * assign the role of subsurfaces to surfaces and the data device manager + * handles the clipboard. Each of these wlroots interfaces has room for you + * to dig your fingers in and play with their behavior if you want. Note that + * the clients cannot set the selection directly without compositor approval, + * see the handling of the request_set_selection event below.*/ + wlr_compositor_create(server.wl_display, 5, server.renderer); + wlr_subcompositor_create(server.wl_display); + wlr_data_device_manager_create(server.wl_display); - /* Creates an output layout, which a wlroots utility for working with an - * arrangement of screens in a physical layout. */ - server.output_layout = wlr_output_layout_create(); + /* Creates an output layout, which a wlroots utility for working with an + * arrangement of screens in a physical layout. */ + server.output_layout = wlr_output_layout_create(); - /* Configure a listener to be notified when new outputs are available on the - * backend. */ - wl_list_init(&server.outputs); - server.new_output.notify = diyac_server_new_output; - wl_signal_add(&server.backend->events.new_output, &server.new_output); + /* Configure a listener to be notified when new outputs are available on the + * backend. */ + wl_list_init(&server.outputs); + 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 - * rendering and damage tracking. All the compositor author needs to do - * is add things that should be rendered to the scene graph at the proper - * positions and then call wlr_scene_output_commit() to render a frame if - * necessary. - */ - server.scene = wlr_scene_create(); - server.scene_layout = wlr_scene_attach_output_layout(server.scene, server.output_layout); + /* Create a scene graph. This is a wlroots abstraction that handles all + * rendering and damage tracking. All the compositor author needs to do + * is add things that should be rendered to the scene graph at the proper + * positions and then call wlr_scene_output_commit() to render a frame if + * necessary. + */ + server.scene = wlr_scene_create(); + server.scene_layout = wlr_scene_attach_output_layout(server.scene, server.output_layout); - /* Set up xdg-shell version 3. The xdg-shell is a Wayland protocol which is - * used for application windows. For more detail on shells, refer to - * https://drewdevault.com/2018/07/29/Wayland-shells.html. - */ - wl_list_init(&server.toplevels); - server.xdg_shell = wlr_xdg_shell_create(server.wl_display, 3); - server.new_xdg_surface.notify = diyac_new_xdg_surface; - wl_signal_add(&server.xdg_shell->events.new_surface, - &server.new_xdg_surface); + /* Set up xdg-shell version 3. The xdg-shell is a Wayland protocol which is + * used for application windows. For more detail on shells, refer to + * https://drewdevault.com/2018/07/29/Wayland-shells.html. + */ + wl_list_init(&server.views); + server.view_tree = wlr_scene_tree_create(&server.scene->tree); + server.xdg_popup_tree = wlr_scene_tree_create(&server.scene->tree); + + server.xdg_shell = wlr_xdg_shell_create(server.wl_display, 3); + server.new_xdg_surface.notify = diyac_new_xdg_surface; + wl_signal_add(&server.xdg_shell->events.new_surface, + &server.new_xdg_surface); - /** - * TODO - * + server.layer_shell = wlr_layer_shell_v1_create(server.wl_display,4); + server.new_layer_surface.notify = diyac_new_layer_surface; + wl_signal_add(&server.layer_shell->events.new_surface, + &server.new_layer_surface); + + diyac_init_cursor_manager(&server); - 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 + * 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. + */ + wl_list_init(&server.seat.keyboards); + diyac_init_seat(&server); - /* - * 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. - */ - 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) + { + wlr_backend_destroy(server.backend); + return 1; + } - /* Add a Unix socket to the Wayland display. */ - const char *socket = wl_display_add_socket_auto(server.wl_display); - 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)) + { + wlr_backend_destroy(server.backend); + wl_display_destroy(server.wl_display); + return 1; + } - /* Start the backend. This will enumerate outputs and inputs, become the DRM - * master, etc */ - if (!wlr_backend_start(server.backend)) - { - wlr_backend_destroy(server.backend); - wl_display_destroy(server.wl_display); - return 1; - } + /* 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) + { + execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL); + } + } + /* Run the Wayland event loop. This does not return until you exit the + * compositor. Starting the backend rigged up all of the necessary event + * loop configuration to listen to libinput events, DRM events, generate + * frame events at the refresh rate, and so on. */ + wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", + socket); + wl_display_run(server.wl_display); - /* 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) - { - execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL); - } - } - /* Run the Wayland event loop. This does not return until you exit the - * compositor. Starting the backend rigged up all of the necessary event - * loop configuration to listen to libinput events, DRM events, generate - * frame events at the refresh rate, and so on. */ - wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", - socket); - wl_display_run(server.wl_display); - - /* Once wl_display_run returns, we destroy all clients then shut down the - * server. */ - wl_display_destroy_clients(server.wl_display); - wlr_scene_node_destroy(&server.scene->tree.node); - wlr_xcursor_manager_destroy(server.seat.cursor_mgr); - wlr_output_layout_destroy(server.output_layout); - wl_display_destroy(server.wl_display); - return 0; + /* Once wl_display_run returns, we destroy all clients then shut down the + * server. */ + wl_display_destroy_clients(server.wl_display); + wlr_scene_node_destroy(&server.scene->tree.node); + 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 index bd6b184..4c1ccc2 100644 --- a/diyac.h +++ b/diyac.h @@ -4,116 +4,194 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LAYER_TREE_SZ 4 /* 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, + DIYAC_CURSOR_PASSTHROUGH, + DIYAC_CURSOR_MOVE, + DIYAC_CURSOR_RESIZE, }; -struct diyac_seat { - struct wlr_seat* wlr_seat; - struct diyac_server *server; +enum diyac_node_descriptor_type +{ + DIYAC_NODE_NONE = 0, + DIYAC_NODE_VIEW, + DIYAC_NODE_XDG_POPUP, + DIYAC_NODE_LAYER_SURFACE, + DIYAC_NODE_LAYER_POPUP, + DIYAC_NODE_TREE, + // DIYAC_NODE_MENUITEM, + // LAB_NODE_TREE, + // LAB_NODE_SSD_BUTTON, +}; +enum diyac_view_state +{ + DIYAC_VIEW_NORMAL = 0, + DIYAC_VIEW_MAXIMIZE, + DIYAC_VIEW_MINIMIZE, + DIYAC_VIEW_FULL_SCREEN, +}; - 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 diyac_seat +{ + struct wlr_seat *wlr_seat; + struct diyac_server *server; - struct wl_listener new_input; - struct wl_listener request_cursor; - struct wl_listener request_set_selection; - struct wl_list keyboards; + struct wlr_cursor *cursor; + enum diyac_cursor_mode cursor_mode; + struct wlr_xcursor_manager *cursor_mgr; + + struct wlr_layer_surface_v1 *focused_layer; + + 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_popup +{ + struct wlr_xdg_popup *wlr_popup; + struct wlr_scene_tree *scene_tree; + struct wl_listener commit; + struct wl_listener destroy; + struct wl_listener new_popup; + struct wlr_box output_toplevel_sx_box; + void *parent; +}; + +struct diyac_layer_scenes +{ + struct wlr_scene_tree *background; + struct wlr_scene_tree *bottom; + struct wlr_scene_tree *top; + struct wlr_scene_tree *overlay; + struct wlr_scene_tree *popup; + struct wlr_scene_tree *session; +}; + +struct diyac_node_descriptor +{ + enum diyac_node_descriptor_type type; + void *data; + struct wl_listener destroy; }; 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 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 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 views; + /* + * Popups need to be rendered above always-on-top views, so we reparent + * them to this dedicated tree + */ + struct wlr_scene_tree *xdg_popup_tree; + struct wlr_scene_tree *view_tree; - struct diyac_seat seat; + struct diyac_seat seat; - struct diyac_toplevel *grabbed_toplevel; - double grab_x, grab_y; - struct wlr_box grab_geobox; - uint32_t resize_edges; + struct diyac_view *grabbed_view; + 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 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 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; + 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; + struct wlr_box usable_area; - // layer output - struct wlr_scene_tree *layer_tree[4]; + // layer output + struct diyac_layer_scenes scenes; + // alias to diyac_layer_scenes elements + struct wlr_scene_tree *layer_tree[LAYER_TREE_SZ]; }; -struct diyac_toplevel +struct diyac_view { - 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 wl_list link; + struct diyac_server *server; + struct wlr_xdg_surface *xdg_surface; + struct wlr_xdg_toplevel *xdg_toplevel; + struct wlr_scene_tree *scene_tree; + enum diyac_view_state state; + /* + * Geometry of the wlr_surface contained within the view, as + * currently displayed. Should be kept in sync with the + * scene-graph at all times. + */ + struct wlr_box current; + + struct diyac_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 wl_listener set_app_id; + struct wl_listener new_popup; }; struct diyac_keyboard { - struct wl_list link; - struct diyac_server *server; - struct wlr_keyboard *wlr_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; + 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 index 3cf5010..cf54c72 100644 --- a/layer.c +++ b/layer.c @@ -1,177 +1,429 @@ +#define _POSIX_C_SOURCE 200112L +#include +#include +#include +#include "layer.h" +#include "node.h" +#include "output.h" +#include "seat.h" + +static void popup_handle_new_popup(struct wl_listener *listener, void *data); +static struct diyac_popup *create_layer_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_tree *parent); +static void process_keyboard_interactivity(struct diyac_layer_surface *layer) +{ + struct wlr_layer_surface_v1 *layer_surface = layer->scene_layer_surface->layer_surface; + struct diyac_seat *seat = &layer->server->seat; + + if (layer_surface->current.keyboard_interactive && layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) + { + /* + * Give keyboard focus to surface if + * - keyboard-interactivity is 'exclusive' or 'on-demand'; and + * - surface is in top/overlay layers; and + * - currently focused layer has a lower precedence + * + * In other words, when dealing with two surfaces with + * exclusive/on-demand keyboard-interactivity (firstly the + * currently focused 'focused_layer' and secondly the + * 'layer_surface' for which we're just responding to a + * map/commit event), the following logic applies: + * + * | focused_layer | layer_surface | who gets keyboard focus | + * |---------------|---------------|-------------------------| + * | overlay | top | focused_layer | + * | overlay | overlay | layer_surface | + * | top | top | layer_surface | + * | top | overlay | layer_surface | + */ + + if (!seat->focused_layer || seat->focused_layer->current.layer <= layer_surface->current.layer) + { + diyac_seat_focus_layer(seat, layer_surface); + } + } + else if (seat->focused_layer && !seat->focused_layer->current.keyboard_interactive) + { + /* + * Clear focus if keyboard-interactivity has been set to + * ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE + */ + diyac_seat_focus_layer(seat, NULL); + } +} 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; + 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; - } + if (!wlr_output) + { + return; + } - uint32_t committed = layer_surface->current.committed; - struct diyac_output *output = (struct diyac_output *)wlr_output->data; + 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); - } + /* 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) + { + wlr_log(WLR_INFO, "Process keyboard"); + 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); - } + if (committed || layer->mapped != layer_surface->surface->mapped) + { + layer->mapped = layer_surface->surface->mapped; + diyac_output_update_usable_area(output); + /* + * Update cursor focus here to ensure we + * enter a new/moved/resized layer surface. + */ + // cursor_update_focus(layer->server); + wlr_log(WLR_INFO, "update focus"); + } } 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); - //} + 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) + { + diyac_output_update_usable_area(layer_surface->output->data); + } + struct diyac_seat *seat = &layer->server->seat; + if (seat->focused_layer == layer_surface) { + diyac_seat_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. - */ + 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) + { + diyac_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); + 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); } static void -layer_surface_node_destroy(struct wl_listener *listener, void *data) +popup_handle_destroy(struct wl_listener *listener, void *data) { - struct diyac_layer_surface *layer = - wl_container_of(listener, layer, node_destroy); + struct diyac_popup *popup = wl_container_of(listener, popup, destroy); + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->new_popup.link); - /* - * 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. - */ + /* Usually already removed unless there was no commit at all */ + if (popup->commit.notify) + { + wl_list_remove(&popup->commit.link); + } - 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); + free(popup); } -static void -layer_surface_output_destroy(struct wl_listener *listener, void *data) + +static void popup_handle_commit(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); + struct diyac_popup *popup = + wl_container_of(listener, popup, commit); + + if (popup->wlr_popup->base->initial_commit) + { + wlr_xdg_popup_unconstrain_from_box(popup->wlr_popup, &popup->output_toplevel_sx_box); + + /* Prevent getting called over and over again */ + wl_list_remove(&popup->commit.link); + popup->commit.notify = NULL; + } } /* This popup's parent is a layer popup */ -static void -layer_surface_new_popup(struct wl_listener *listener, void *data) +static void popup_handle_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;*/ + struct diyac_popup *popup = + wl_container_of(listener, popup, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + struct diyac_popup *new_popup = create_layer_popup(wlr_popup, + popup->scene_tree); + new_popup->output_toplevel_sx_box = popup->output_toplevel_sx_box; } -static void server_new_layer_surface(struct wl_listener *listener, void *data) + +static struct diyac_popup *create_layer_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_tree *parent) { - struct diyac_server *server = wl_container_of(listener, server, new_layer_surface); - struct wlr_layer_surface_v1 *layer_surface = data; + wlr_log(WLR_INFO, + "create_layer_popup: popup create"); + struct diyac_popup *popup = calloc(1, sizeof(*popup)); + popup->wlr_popup = wlr_popup; + popup->scene_tree = + wlr_scene_xdg_surface_create(parent, wlr_popup->base); + if (!popup->scene_tree) + { + free(popup); + return NULL; + } + diyac_node_descriptor_create(&popup->scene_tree->node, + DIYAC_NODE_LAYER_POPUP, popup); - 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; - } + popup->destroy.notify = popup_handle_destroy; + wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); - struct diyac_layer_surface *surface = calloc(1, sizeof(*surface)); + popup->new_popup.notify = popup_handle_new_popup; + wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); - struct diyac_output *output = layer_surface->output->data; + popup->commit.notify = popup_handle_commit; + wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); - 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; + return popup; } + +/* + * We move popups from the bottom to the top layer so that they are + * rendered above views. + */ +static void move_popup_to_top_layer(struct diyac_layer_surface *toplevel, + struct diyac_popup *popup) +{ + wlr_log(WLR_INFO, + "move_popup_to_top_layer: popup notif"); + struct diyac_server *server = toplevel->server; + struct wlr_output *wlr_output = + toplevel->scene_layer_surface->layer_surface->output; + struct diyac_output *output = (struct diyac_output *)wlr_output->data; + struct wlr_box box = {0}; + wlr_output_layout_get_box(server->output_layout, wlr_output, &box); + int lx = toplevel->scene_layer_surface->tree->node.x + box.x; + int ly = toplevel->scene_layer_surface->tree->node.y + box.y; + + struct wlr_scene_node *node = &popup->scene_tree->node; + wlr_scene_node_reparent(node, output->scenes.popup); + /* FIXME: verify the whole tree should be repositioned */ + wlr_scene_node_set_position(&output->scenes.popup->node, lx, ly); +} + +/* This popup's parent is a layer popup */ +static void layer_popup_new(struct wl_listener *listener, void *data) +{ + wlr_log(WLR_INFO, + "layer_popup_new: popup notif"); + struct diyac_layer_surface *toplevel = + wl_container_of(listener, toplevel, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + + struct diyac_server *server = toplevel->server; + struct wlr_scene_layer_surface_v1 *surface = toplevel->scene_layer_surface; + struct diyac_output *output = surface->layer_surface->output->data; + + int lx, ly; + wlr_scene_node_coords(&surface->tree->node, &lx, &ly); + + struct wlr_box output_box = {0}; + wlr_output_layout_get_box(server->output_layout, + output->wlr_output, &output_box); + + /* + * Output geometry expressed in the coordinate system of the toplevel + * parent of popup. We store this struct the lab_layer_popup struct + * to make it easier to unconstrain children when we move popups from + * the bottom to the top layer. + */ + struct wlr_box output_toplevel_sx_box = { + .x = output_box.x - lx, + .y = output_box.y - ly, + .width = output_box.width, + .height = output_box.height, + }; + struct diyac_popup *popup = create_layer_popup(wlr_popup, surface->tree); + popup->output_toplevel_sx_box = output_toplevel_sx_box; + + if (surface->layer_surface->current.layer <= ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) + { + move_popup_to_top_layer(toplevel, popup); + } +} +void diyac_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; + } + + diyac_node_descriptor_create(&surface->scene_layer_surface->tree->node, + DIYAC_NODE_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_popup_new; + 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; + diyac_output_update_usable_area(output); + layer_surface->current = old_state; +} + +static void arrange_one_layer(const struct wlr_box *full_area, struct wlr_box *usable_area, + struct wlr_scene_tree *tree, bool exclusive) +{ + struct wlr_scene_node *node; + wl_list_for_each(node, &tree->children, link) + { + struct diyac_layer_surface *surface = diyac_layer_surface_from_node(node); + struct wlr_scene_layer_surface_v1 *scene = surface->scene_layer_surface; + if (!!scene->layer_surface->current.exclusive_zone != exclusive) + { + continue; + } + wlr_scene_layer_surface_v1_configure(scene, full_area, usable_area); + } +} + +/* + * To ensure outputs/views are left in a consistent state, this + * function should be called ONLY from output_update_usable_area() + * or output_update_all_usable_areas(). + */ +void diyac_layers_arrange(struct diyac_output *output) +{ + assert(output); + struct wlr_box full_area = {0}; + wlr_output_effective_resolution(output->wlr_output, + &full_area.width, &full_area.height); + struct wlr_box usable_area = full_area; + struct diyac_server *server = output->server; + struct wlr_scene_output *scene_output = + wlr_scene_get_scene_output(server->scene, output->wlr_output); + if (!scene_output) + { + wlr_log(WLR_DEBUG, "no wlr_scene_output"); + return; + } + + for (int i = LAYER_TREE_SZ - 1; i >= 0; i--) + { + struct wlr_scene_tree *layer = output->layer_tree[i]; + + /* + * Process exclusive-zone clients before non-exclusive-zone + * clients, so that the latter give way to the former regardless + * of the order in which they were launched. + * + * Also start calculating the usable_area for exclusive-zone + * clients from the Overlay layer down to the Background layer + * to ensure that higher layers have a higher preference for + * placement. + * + * The 'exclusive' boolean also matches -1 which means that + * the layershell client wants to use the full screen rather + * than the usable area. + */ + arrange_one_layer(&full_area, &usable_area, layer, /* exclusive */ true); + } + + for (size_t i = 0; i < LAYER_TREE_SZ; i++) + { + struct wlr_scene_tree *layer = output->layer_tree[i]; + arrange_one_layer(&full_area, &usable_area, layer, /* exclusive */ false); + + /* Set node position to account for output layout change */ + wlr_scene_node_set_position(&layer->node, scene_output->x, scene_output->y); + } + + output->usable_area = usable_area; +} \ No newline at end of file diff --git a/layer.h b/layer.h new file mode 100644 index 0000000..a8fde22 --- /dev/null +++ b/layer.h @@ -0,0 +1,7 @@ +#ifndef DIYAC_LAYER_H +#define DIYAC_LAYER_H +#include "diyac.h" + +void diyac_new_layer_surface(struct wl_listener *listener, void *data); +void diyac_layers_arrange(struct diyac_output *output); +#endif \ No newline at end of file diff --git a/node.c b/node.c new file mode 100644 index 0000000..3aa608b --- /dev/null +++ b/node.c @@ -0,0 +1,37 @@ +#define _POSIX_C_SOURCE 200112L +#include +#include +#include "node.h" + +static void destroy_notify(struct wl_listener *listener, void *data) +{ + struct diyac_node_descriptor *node_descriptor = + wl_container_of(listener, node_descriptor, destroy); + wl_list_remove(&node_descriptor->destroy.link); + free(node_descriptor); +} + +void diyac_node_descriptor_create(struct wlr_scene_node *scene_node, enum diyac_node_descriptor_type type, void *data) +{ + struct diyac_node_descriptor *node_descriptor = calloc(1, sizeof(*node_descriptor)); + node_descriptor->type = type; + node_descriptor->data = data; + node_descriptor->destroy.notify = destroy_notify; + wl_signal_add(&scene_node->events.destroy, &node_descriptor->destroy); + scene_node->data = node_descriptor; +} + +struct diyac_layer_surface *diyac_layer_surface_from_node(struct wlr_scene_node *wlr_scene_node) +{ + assert(wlr_scene_node->data); + struct diyac_node_descriptor *node_descriptor = wlr_scene_node->data; + assert(node_descriptor->type == DIYAC_NODE_LAYER_SURFACE); + return (struct diyac_layer_surface *)node_descriptor->data; +} +struct diyac_view *diyac_view_from_node(struct wlr_scene_node *wlr_scene_node) +{ + assert(wlr_scene_node->data); + struct diyac_node_descriptor *node_descriptor = wlr_scene_node->data; + assert(node_descriptor->type == DIYAC_NODE_VIEW || node_descriptor->type == DIYAC_NODE_XDG_POPUP); + return (struct diyac_view *)node_descriptor->data; +} \ No newline at end of file diff --git a/node.h b/node.h new file mode 100644 index 0000000..09ee32d --- /dev/null +++ b/node.h @@ -0,0 +1,8 @@ +#ifndef DIYAC_NODE_H +#define DIYAC_NODE_H +#include "diyac.h" + +void diyac_node_descriptor_create(struct wlr_scene_node *scene_node,enum diyac_node_descriptor_type type, void *data); +struct diyac_layer_surface * diyac_layer_surface_from_node(struct wlr_scene_node *wlr_scene_node); +struct diyac_view *diyac_view_from_node(struct wlr_scene_node *wlr_scene_node); +#endif diff --git a/output.c b/output.c index dab8788..621020c 100644 --- a/output.c +++ b/output.c @@ -1,11 +1,13 @@ #define _POSIX_C_SOURCE 200112L #include #include -#include -#include +#include #include "output.h" - -static void output_frame(struct wl_listener *listener, void *data) { +#include "layer.h" +#include "node.h" +#include "desktop.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); @@ -22,7 +24,8 @@ static void output_frame(struct wl_listener *listener, void *data) { wlr_scene_output_send_frame_done(scene_output, &now); } -static void output_request_state(struct wl_listener *listener, void *data) { +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. */ @@ -31,7 +34,8 @@ static void output_request_state(struct wl_listener *listener, void *data) { wlr_output_commit_state(output->wlr_output, event->state); } -static void output_destroy(struct wl_listener *listener, void *data) { +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); @@ -41,7 +45,8 @@ static void output_destroy(struct wl_listener *listener, void *data) { free(output); } -void diyac_server_new_output(struct wl_listener *listener, void *data) { +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 = @@ -63,7 +68,8 @@ void diyac_server_new_output(struct wl_listener *listener, void *data) { * 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) { + if (mode != NULL) + { wlr_output_state_set_mode(&state, mode); } @@ -75,7 +81,11 @@ void diyac_server_new_output(struct wl_listener *listener, void *data) { struct diyac_output *output = calloc(1, sizeof(*output)); output->wlr_output = wlr_output; output->server = server; - + wlr_output->data = output; + output->usable_area.x = 0; + output->usable_area.y = 0; + output->usable_area.width = wlr_output->width; + output->usable_area.height = wlr_output->height; /* Sets up a listener for the frame event. */ output->frame.notify = output_frame; wl_signal_add(&wlr_output->events.frame, &output->frame); @@ -90,6 +100,46 @@ void diyac_server_new_output(struct wl_listener *listener, void *data) { wl_list_insert(&server->outputs, &output->link); + /* + * Create layer-trees (background, bottom, top and overlay) and + * a layer-popup-tree. + */ + output->scenes.background = wlr_scene_tree_create(&server->scene->tree); + output->scenes.bottom = wlr_scene_tree_create(&server->scene->tree); + output->scenes.top = wlr_scene_tree_create(&server->scene->tree); + output->scenes.popup = wlr_scene_tree_create(&server->scene->tree); + output->scenes.overlay = wlr_scene_tree_create(&server->scene->tree); + output->scenes.session = wlr_scene_tree_create(&server->scene->tree); + + diyac_node_descriptor_create(&output->scenes.background->node, DIYAC_NODE_TREE, NULL); + diyac_node_descriptor_create(&output->scenes.bottom->node, DIYAC_NODE_TREE, NULL); + diyac_node_descriptor_create(&output->scenes.top->node, DIYAC_NODE_TREE, NULL); + diyac_node_descriptor_create(&output->scenes.popup->node, DIYAC_NODE_TREE, NULL); + diyac_node_descriptor_create(&output->scenes.overlay->node, DIYAC_NODE_TREE, NULL); + diyac_node_descriptor_create(&output->scenes.session->node, DIYAC_NODE_TREE, NULL); + + output->layer_tree[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND] = output->scenes.background; + output->layer_tree[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM] = output->scenes.bottom; + output->layer_tree[ZWLR_LAYER_SHELL_V1_LAYER_TOP] = output->scenes.top; + output->layer_tree[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY] = output->scenes.overlay; + /* + * Set the z-positions to achieve the following order (from top to + * bottom): + * - session lock layer + * - layer-shell popups + * - overlay layer + * - top layer + * - views + * - bottom layer + * - background layer + */ + wlr_scene_node_lower_to_bottom(&output->scenes.bottom->node); + wlr_scene_node_lower_to_bottom(&output->scenes.background->node); + wlr_scene_node_raise_to_top(&output->scenes.top->node); + wlr_scene_node_raise_to_top(&output->scenes.overlay->node); + wlr_scene_node_raise_to_top(&output->scenes.popup->node); + wlr_scene_node_raise_to_top(&output->scenes.session->node); + /* 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 @@ -100,7 +150,54 @@ void diyac_server_new_output(struct wl_listener *listener, void *data) { * 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); + 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); +} + +/* returns true if usable area changed */ +static bool update_usable_area(struct diyac_output *output) +{ + struct wlr_box old = output->usable_area; + diyac_layers_arrange(output); + + return !wlr_box_equal(&old, &output->usable_area); +} + +void diyac_output_update_usable_area(struct diyac_output *output) +{ + if (update_usable_area(output)) + { + // regions_update_geometry(output); + diyac_arrange_all_views(output->server); + // desktop_arrange_all_views(output->server); + } +} + +struct diyac_output *diyac_output_from_cursor(struct diyac_server *server) +{ + double closest_x, closest_y; + wlr_output_layout_closest_point(server->output_layout, NULL, server->seat.cursor->x, server->seat.cursor->y, + &closest_x, &closest_y); + + struct wlr_output *output = wlr_output_layout_output_at(server->output_layout, + closest_x, closest_y); + if (!output) + return NULL; + return output->data; +} + +struct wlr_box diyac_output_usable_area(struct diyac_output *output) +{ + if (!output) + { + return (struct wlr_box){0}; + } + struct wlr_box box = output->usable_area; + double ox = 0, oy = 0; + wlr_output_layout_output_coords(output->server->output_layout, + output->wlr_output, &ox, &oy); + box.x -= ox; + box.y -= oy; + return box; } \ No newline at end of file diff --git a/output.h b/output.h index f438361..0a1d831 100644 --- a/output.h +++ b/output.h @@ -3,5 +3,7 @@ #include "diyac.h" void diyac_server_new_output(struct wl_listener *listener, void *data); - +void diyac_output_update_usable_area(struct diyac_output *output); +struct diyac_output *diyac_output_from_cursor(struct diyac_server *server); +struct wlr_box diyac_output_usable_area(struct diyac_output * output); #endif \ No newline at end of file diff --git a/seat.c b/seat.c index 2328255..29f1ccf 100644 --- a/seat.c +++ b/seat.c @@ -1,9 +1,11 @@ #define _POSIX_C_SOURCE 200112L #include #include -#include #include +#include #include "seat.h" +#include "desktop.h" + static void keyboard_handle_modifiers( struct wl_listener *listener, void *data) @@ -40,13 +42,13 @@ static bool handle_keybinding(struct diyac_server *server, xkb_keysym_t sym) break; case XKB_KEY_F1: /* Cycle to the next toplevel */ - if (wl_list_length(&server->toplevels) < 2) + if (wl_list_length(&server->views) < 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); + struct diyac_view *next_toplevel = + wl_container_of(server->views.prev, next_toplevel, link); + diyac_focus_view(next_toplevel); break; default: return false; @@ -156,7 +158,7 @@ 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 = + struct diyac_seat *seat = wl_container_of(listener, seat, new_input); struct wlr_input_device *device = data; switch (device->type) @@ -222,7 +224,7 @@ static void seat_request_set_selection(struct wl_listener *listener, void *data) */ void diyac_init_seat(struct diyac_server *server) { - //server->seat = calloc(1, sizeof(struct diyac_seat)); + // 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); @@ -233,4 +235,72 @@ void diyac_init_seat(struct diyac_server *server) 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); +} + +static void seat_focus(struct diyac_seat *seat, struct wlr_surface *surface, bool is_lock_surface) +{ + wlr_log(WLR_INFO, "seat_focus"); + /* + * Respect session lock. This check is critical, DO NOT REMOVE. + * It should also come before the !surface condition, or the + * lock screen may lose focus and become impossible to unlock. + */ + struct diyac_server *server = seat->server; + /*if (server->session_lock && !is_lock_surface) + { + return; + }*/ + + if (!surface) + { + wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); + return; + } + + /* Respect input inhibit (also used by some lock screens) */ + /* + if (input_inhibit_blocks_surface(seat, surface->resource)) { + return; + } + */ + + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + if (keyboard) + { + wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, + keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); + } + + /* + struct wlr_pointer_constraint_v1 *constraint = + wlr_pointer_constraints_v1_constraint_for_surface(server->constraints, + surface, seat->seat); + constrain_cursor(server, constraint); + */ +} + +void diyac_seat_focus_surface(struct diyac_seat *seat, struct wlr_surface *surface) +{ + wlr_log(WLR_INFO, "diyac_seat_focus_surface"); + /* Respect layer-shell exclusive keyboard-interactivity. */ + if (seat->focused_layer && seat->focused_layer->current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) + { + return; + } + seat_focus(seat, surface, /*is_lock_surface*/ false); +} + +void diyac_seat_focus_layer(struct diyac_seat *seat, struct wlr_layer_surface_v1 *layer) +{ + if (!layer) + { + seat->focused_layer = NULL; + diyac_focus_topmost_view(seat->server); + return; + } + seat_focus(seat, layer->surface, /*is_lock_surface*/ false); + if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) + { + seat->focused_layer = layer; + } } \ No newline at end of file diff --git a/seat.h b/seat.h index e4a2f48..a485bf8 100644 --- a/seat.h +++ b/seat.h @@ -3,4 +3,6 @@ #include "diyac.h" void diyac_init_seat(struct diyac_server* server); +void diyac_seat_focus_surface(struct diyac_seat *seat, struct wlr_surface *surface); +void diyac_seat_focus_layer(struct diyac_seat *seat, struct wlr_layer_surface_v1 *layer); #endif \ No newline at end of file diff --git a/xdg.c b/xdg.c index 37bef66..87bffed 100644 --- a/xdg.c +++ b/xdg.c @@ -1,14 +1,16 @@ #define _POSIX_C_SOURCE 200112L -#include #include -#include -#include #include #include #include "xdg.h" #include "cursor.h" +#include "node.h" +#include "desktop.h" +#include "output.h" -static void begin_interactive(struct diyac_toplevel *toplevel, +static void xdg_popup_create(struct diyac_view *view, struct wlr_xdg_popup *wlr_popup); + +static void begin_interactive(struct diyac_view *toplevel, enum diyac_cursor_mode mode, uint32_t edges) { /* This function sets up an interactive move or resize operation, where the @@ -23,7 +25,7 @@ static void begin_interactive(struct diyac_toplevel *toplevel, /* Deny move/resize requests from unfocused clients. */ return; } - server->grabbed_toplevel = toplevel; + server->grabbed_view = toplevel; server->seat.cursor_mode = mode; if (mode == DIYAC_CURSOR_MOVE) @@ -54,20 +56,20 @@ static void begin_interactive(struct diyac_toplevel *toplevel, 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); + struct diyac_view *toplevel = wl_container_of(listener, toplevel, map); + wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &toplevel->current); + wl_list_insert(&toplevel->server->views, &toplevel->link); - wl_list_insert(&toplevel->server->toplevels, &toplevel->link); - - diyac_focus_toplevel(toplevel, toplevel->xdg_toplevel->base->surface); + diyac_focus_view(toplevel); } 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); + struct diyac_view *toplevel = wl_container_of(listener, toplevel, unmap); /* Reset the cursor mode if the grabbed toplevel was unmapped. */ - if (toplevel == toplevel->server->grabbed_toplevel) + if (toplevel == toplevel->server->grabbed_view) { diyac_reset_cursor_mode(toplevel->server); } @@ -83,7 +85,7 @@ static void xdg_toplevel_request_move( * 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); + struct diyac_view *toplevel = wl_container_of(listener, toplevel, request_move); begin_interactive(toplevel, DIYAC_CURSOR_MOVE, 0); } @@ -96,7 +98,7 @@ static void xdg_toplevel_request_resize( * 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); + struct diyac_view *toplevel = wl_container_of(listener, toplevel, request_resize); begin_interactive(toplevel, DIYAC_CURSOR_RESIZE, event->edges); } @@ -109,29 +111,58 @@ static void xdg_toplevel_request_maximize( * 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 = + struct diyac_view *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); + /* + struct wlr_output *output = wlr_output_layout_output_at( + toplevel->server->output_layout, toplevel->server->seat.cursor->x, + toplevel->server->seat.cursor->y); + if (!output) + { + wlr_log(WLR_ERROR, + "No output available to assign layer surface"); + wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); + return; + } + struct diyac_output * target_output = output->data; + */ + if(toplevel->state == DIYAC_VIEW_MAXIMIZE) + { + toplevel->state = DIYAC_VIEW_NORMAL; + // restore its default geometry + } + else + { + toplevel->state = DIYAC_VIEW_MAXIMIZE; + } + diyac_view_update_geometry(toplevel, true); + // 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 = + struct diyac_view *toplevel = wl_container_of(listener, toplevel, request_fullscreen); - wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); + if(toplevel->state == DIYAC_VIEW_FULL_SCREEN) + { + toplevel->state = DIYAC_VIEW_NORMAL; + // restore its default geometry + } + else + { + toplevel->state = DIYAC_VIEW_FULL_SCREEN; + } + diyac_view_update_geometry(toplevel, true); } 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); + struct diyac_view *toplevel = wl_container_of(listener, toplevel, destroy); wl_list_remove(&toplevel->map.link); wl_list_remove(&toplevel->unmap.link); @@ -144,6 +175,124 @@ static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) free(toplevel); } +static void handle_xdg_popup_destroy(struct wl_listener *listener, void *data) +{ + struct diyac_popup *popup = wl_container_of(listener, popup, destroy); + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->new_popup.link); + + /* Usually already removed unless there was no commit at all */ + if (popup->commit.notify) + { + wl_list_remove(&popup->commit.link); + } + free(popup); +} + +static void +popup_unconstrain(struct diyac_popup *popup) +{ + struct diyac_view *view = popup->parent; + struct diyac_server *server = view->server; + struct wlr_box *popup_box = &popup->wlr_popup->current.geometry; + struct wlr_output_layout *output_layout = server->output_layout; + struct wlr_output *wlr_output = wlr_output_layout_output_at( + output_layout, view->current.x + popup_box->x, + view->current.y + popup_box->y); + + struct wlr_box output_box; + wlr_output_layout_get_box(output_layout, wlr_output, &output_box); + + struct wlr_box output_toplevel_box = { + .x = output_box.x - view->current.x, + .y = output_box.y - view->current.y, + .width = output_box.width, + .height = output_box.height, + }; + wlr_xdg_popup_unconstrain_from_box(popup->wlr_popup, &output_toplevel_box); +} + +static void handle_xdg_popup_commit(struct wl_listener *listener, void *data) +{ + struct diyac_popup *popup = wl_container_of(listener, popup, commit); + + if (popup->wlr_popup->base->initial_commit) + { + popup_unconstrain(popup); + + /* Prevent getting called over and over again */ + wl_list_remove(&popup->commit.link); + popup->commit.notify = NULL; + } +} + +static void handle_xdg_popup_new(struct wl_listener *listener, void *data) +{ + struct diyac_popup *popup = wl_container_of(listener, popup, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + xdg_popup_create(popup->parent, wlr_popup); +} + +static void xdg_popup_create(struct diyac_view *view, struct wlr_xdg_popup *wlr_popup) +{ + wlr_log(WLR_INFO, "xdg_popup_create: Creating new dialog"); + struct wlr_xdg_surface *parent = + wlr_xdg_surface_try_from_wlr_surface(wlr_popup->parent); + if (!parent) + { + wlr_log(WLR_ERROR, "parent is not a valid XDG surface"); + return; + } + + struct diyac_popup *popup = calloc(1, sizeof(*popup)); + popup->parent = view; + popup->wlr_popup = wlr_popup; + + popup->destroy.notify = handle_xdg_popup_destroy; + wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); + + popup->new_popup.notify = handle_xdg_popup_new; + wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); + + popup->commit.notify = handle_xdg_popup_commit; + wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); + + /* + * 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. + * + * xdg-popups live in server->xdg_popup_tree so that they can be + * rendered above always-on-top windows + */ + struct wlr_scene_tree *parent_tree = NULL; + if (parent->role == WLR_XDG_SURFACE_ROLE_POPUP) + { + parent_tree = parent->surface->data; + } + else + { + parent_tree = view->server->xdg_popup_tree; + wlr_scene_node_set_position(&view->server->xdg_popup_tree->node, + view->current.x, view->current.y); + } + wlr_popup->base->surface->data = + wlr_scene_xdg_surface_create(parent_tree, wlr_popup->base); + diyac_node_descriptor_create(wlr_popup->base->surface->data, + DIYAC_NODE_XDG_POPUP, view); +} + +static void xdg_new_popup_notify(struct wl_listener *listener, void *data) +{ + wlr_log(WLR_INFO, "xdg_new_popup_notify: Creating new dialog"); + struct diyac_view *view = + wl_container_of(listener, view, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + xdg_popup_create(view, wlr_popup); +} + 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 @@ -159,37 +308,32 @@ void diyac_new_xdg_surface(struct wl_listener *listener, void *data) * scene node. */ if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { - struct wlr_xdg_surface *parent = + /*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;*/ + wlr_log(WLR_INFO, "diyac_new_xdg_surface: Creating new dialog using another method"); return; } assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + wlr_log(WLR_INFO, "diyac_new_xdg_surface: Creating new application windows"); + wlr_xdg_surface_ping(xdg_surface); - - - /* Allocate a diyac_toplevel for this surface */ - struct diyac_toplevel *toplevel = calloc(1, sizeof(*toplevel)); + /* Allocate a diyac_view for this surface */ + struct diyac_view *toplevel = calloc(1, sizeof(*toplevel)); + toplevel->state = DIYAC_VIEW_NORMAL; toplevel->server = server; toplevel->xdg_toplevel = xdg_surface->toplevel; + toplevel->xdg_surface = xdg_surface; + toplevel->output = diyac_output_from_cursor(server); 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"); - - } + toplevel->server->view_tree, toplevel->xdg_toplevel->base); + xdg_surface->data = toplevel; + diyac_node_descriptor_create(&toplevel->scene_tree->node, + DIYAC_NODE_VIEW, toplevel); /* Listen to the various events it can emit */ toplevel->map.notify = xdg_toplevel_map; wl_signal_add(&xdg_surface->surface->events.map, &toplevel->map); @@ -210,4 +354,6 @@ void diyac_new_xdg_surface(struct wl_listener *listener, void *data) toplevel->request_fullscreen.notify = xdg_toplevel_request_fullscreen; wl_signal_add(&xdg_toplevel->events.request_fullscreen, &toplevel->request_fullscreen); + toplevel->new_popup.notify = xdg_new_popup_notify; + wl_signal_add(&xdg_surface->events.new_popup, &toplevel->new_popup); } \ No newline at end of file