feat: add support layershell protocol

This commit is contained in:
DanyLE 2024-03-31 16:09:01 +02:00
parent c1960f0438
commit 55719d6dba
15 changed files with 1404 additions and 534 deletions

View File

@ -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

View File

@ -1,23 +1,29 @@
#define _POSIX_C_SOURCE 200112L
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
#include <assert.h>
#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);
}
}

226
desktop.c Normal file
View File

@ -0,0 +1,226 @@
#define _POSIX_C_SOURCE 200112L
#include <wlr/util/log.h>
#include <assert.h>
#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;
}
}

12
desktop.h Normal file
View File

@ -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

386
diyac.c
View File

@ -3,265 +3,177 @@
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
#include <wlr/render/allocator.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_xcursor_manager.h>
#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;
}

234
diyac.h
View File

@ -4,116 +4,194 @@
#include <wlr/backend.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/render/allocator.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_output_layout.h>
#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

542
layer.c
View File

@ -1,177 +1,429 @@
#define _POSIX_C_SOURCE 200112L
#include <wlr/util/log.h>
#include <stdlib.h>
#include <assert.h>
#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;
}

7
layer.h Normal file
View File

@ -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

37
node.c Normal file
View File

@ -0,0 +1,37 @@
#define _POSIX_C_SOURCE 200112L
#include <stdlib.h>
#include <assert.h>
#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;
}

8
node.h Normal file
View File

@ -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

117
output.c
View File

@ -1,11 +1,13 @@
#define _POSIX_C_SOURCE 200112L
#include <time.h>
#include <stdlib.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_output_layout.h>
#include <stdbool.h>
#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;
}

View File

@ -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

84
seat.c
View File

@ -1,9 +1,11 @@
#define _POSIX_C_SOURCE 200112L
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_cursor.h>
#include <stdlib.h>
#include <wlr/util/log.h>
#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;
}
}

2
seat.h
View File

@ -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

226
xdg.c
View File

@ -1,14 +1,16 @@
#define _POSIX_C_SOURCE 200112L
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <assert.h>
#include <stdlib.h>
#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);
}