Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement layer_shell_v1 #345

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion cage.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* Cage: A Wayland kiosk.
*
* Copyright (C) 2018-2020 Jente Hidskes
* Copyright (C) 2018-2021 Jente Hidskes
*
* See the LICENSE file accompanying this file.
*/
Expand All @@ -27,6 +27,7 @@
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output_management_v1.h>
#include <wlr/types/wlr_presentation_time.h>
Expand All @@ -52,6 +53,7 @@
#endif

#include "idle_inhibit_v1.h"
#include "layer_shell_v1.h"
#include "output.h"
#include "seat.h"
#include "server.h"
Expand Down Expand Up @@ -462,6 +464,15 @@ main(int argc, char *argv[])
goto end;
}

server.layer_shell_v1 = wlr_layer_shell_v1_create(server.wl_display, 4);
if (!server.layer_shell_v1) {
wlr_log(WLR_ERROR, "Unable to create the layer shell");
ret = 1;
goto end;
}
server.new_layer_shell_v1_surface.notify = handle_layer_shell_v1_surface_new;
wl_signal_add(&server.layer_shell_v1->events.new_surface, &server.new_layer_shell_v1_surface);

if (!wlr_export_dmabuf_manager_v1_create(server.wl_display)) {
wlr_log(WLR_ERROR, "Unable to create the export DMABUF manager");
ret = 1;
Expand Down
207 changes: 207 additions & 0 deletions layer_shell_v1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
/*
* Cage: A Wayland kiosk.
*
* Copyright (C) 2021 Jente Hidskes
*
* See the LICENSE file accompanying this file.
*/

#include "layer_shell_v1.h"
#include "output.h"
#include "seat.h"
#include "server.h"

#include <stdlib.h>
#include <assert.h>
#include <complex.h>
#include <wayland-server-core.h>
#include <wayland-util.h>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>

#define NUM_LAYERS (4)

static void
arrange_surface(struct cg_output *output, const struct wlr_box *full_area, struct wlr_box *usable_area,
struct wlr_scene_tree *tree)
{
struct wlr_scene_node *node;
wl_list_for_each (node, &tree->children, link) {
struct cg_view *view = node->data;
if (!view) {
continue;
}
struct cg_layer_surface *surface = (struct cg_layer_surface *) view;

if (!surface->scene->layer_surface->initialized) {
continue;
}

wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area);
}
}

void
arrange_layers(struct cg_output *output)
{
struct wlr_box usable_area = {0};
wlr_output_effective_resolution(output->wlr_output, &usable_area.width, &usable_area.height);
const struct wlr_box full_area = usable_area;

arrange_surface(output, &full_area, &usable_area, output->layers.shell_background);
arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom);
arrange_surface(output, &full_area, &usable_area, output->layers.shell_top);
arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay);

if (!wlr_box_equal(&usable_area, &output->usable_area)) {
wlr_log(WLR_DEBUG, "Usable area changed, rearranging output");
output->usable_area = usable_area;
// arrange_output(output);
} else {
// arrange_popups(root->layers.popup);
// FIXME: popup is not implemented
}
}

static struct wlr_scene_tree *
cg_layer_get_scene(struct cg_output *output, enum zwlr_layer_shell_v1_layer layer_type)
{
assert(layer_type <= NUM_LAYERS);
switch (layer_type) {
case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND:
return output->layers.shell_background;
case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM:
return output->layers.shell_bottom;
case ZWLR_LAYER_SHELL_V1_LAYER_TOP:
return output->layers.shell_top;
case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY:
return output->layers.shell_overlay;
}
return NULL;
}

static void
handle_map(struct wl_listener *listener, void *data)
{
struct cg_layer_surface *surface = wl_container_of(listener, surface, map);
struct wlr_layer_surface_v1 *layer_surface = surface->scene->layer_surface;
struct cg_server *server = surface->server;

// focus on new surface
if (layer_surface->current.keyboard_interactive &&
(layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY ||
layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) {
struct cg_seat *seat = server->seat;
if (!seat->focused_layer || seat->focused_layer->current.layer >= layer_surface->current.layer) {
// seat_set_focus_layer(seat, layer_surface);
}
arrange_layers(surface->output);
}
}

static void
handle_unmap(struct wl_listener *listener, void *data)
{
}

static void
handle_surface_commit(struct wl_listener *listener, void *data)
{
struct cg_layer_surface *surface = wl_container_of(listener, surface, surface_commit);
struct wlr_layer_surface_v1 *layer_surface = surface->scene->layer_surface;
if (!layer_surface->initialized) {
return;
}

uint32_t committed = layer_surface->current.committed;
if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) {
enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer;
struct wlr_scene_tree *output_layer = cg_layer_get_scene(surface->output, layer_type);
wlr_scene_node_reparent(&surface->scene->tree->node, output_layer);
}

if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) {
surface->mapped = layer_surface->surface->mapped;
arrange_layers(surface->output);
}
}

static void
handle_output_destroy(struct wl_listener *listener, void *data)
{
}

void
handle_layer_shell_v1_surface_new(struct wl_listener *listener, void *data)
{
struct cg_server *server = wl_container_of(listener, server, new_layer_shell_v1_surface);
struct cg_seat *seat = server->seat;
struct wlr_layer_surface_v1 *layer_surface = data;

wlr_log(WLR_DEBUG,
"New layer shell surface: namespace %s layer %d anchor %" PRIu32 " size %" PRIu32 "x%" PRIu32
" margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",",
layer_surface->namespace, layer_surface->pending.layer, layer_surface->pending.anchor,
layer_surface->pending.desired_width, layer_surface->pending.desired_height,
layer_surface->pending.margin.top, layer_surface->pending.margin.right,
layer_surface->pending.margin.bottom, layer_surface->pending.margin.left);

/*
if (layer_surface->output) {
struct wlr_output *wlr_output =
wlr_output_layout_output_at(server->output_layout, seat->cursor->x, seat->cursor->y);
if (wlr_output) {
layer_surface->output = wlr_output;
} else {
struct cg_output *output = wl_container_of(server->outputs.prev, output, link);
layer_surface->output = output->wlr_output;
}
}
struct cg_output *output = layer_surface->output->data;
*/
struct cg_output *output;
if (layer_surface->output) {
output = layer_surface->output->data;
} else {
struct wlr_output *wlr_output =
wlr_output_layout_output_at(server->output_layout, seat->cursor->x, seat->cursor->y);
layer_surface->output = wlr_output;
output = wlr_output->data;
}

enum zwlr_layer_shell_v1_layer layer_type = layer_surface->pending.layer;
struct wlr_scene_tree *output_layer = cg_layer_get_scene(output, layer_type);

struct cg_layer_surface *cg_surface = calloc(1, sizeof(*cg_surface));
if (!cg_surface) {
wlr_layer_surface_v1_destroy(layer_surface);
wlr_log(WLR_ERROR, "Failed to allocate layer shell");
return;
}

struct wlr_scene_layer_surface_v1 *scene = wlr_scene_layer_surface_v1_create(output_layer, layer_surface);
if (!scene) {
wlr_log(WLR_ERROR, "Could not allocate a layer_surface_v1");
return;
}

cg_surface->server = server;
cg_surface->layer_surface = scene->layer_surface;
cg_surface->scene = scene;
cg_surface->tree = scene->tree;
cg_surface->tree->node.data =cg_surface;
cg_surface->layer_surface->data = cg_surface;
cg_surface->output = output;

cg_surface->map.notify = handle_map;
wl_signal_add(&layer_surface->surface->events.map, &cg_surface->map);
cg_surface->unmap.notify = handle_unmap;
wl_signal_add(&layer_surface->surface->events.unmap, &cg_surface->unmap);
cg_surface->surface_commit.notify = handle_surface_commit;
wl_signal_add(&layer_surface->surface->events.commit, &cg_surface->surface_commit);

cg_surface->output_destroy.notify = handle_output_destroy;
wl_signal_add(&layer_surface->surface->events.destroy, &cg_surface->output_destroy);
}
29 changes: 29 additions & 0 deletions layer_shell_v1.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef CG_LAYER_SHELL_V1_H
#define CG_LAYER_SHELL_V1_H

#include <wayland-server-core.h>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_scene.h>

void handle_layer_shell_v1_surface_new(struct wl_listener *listener, void *data);

struct cg_layer_surface {
struct cg_server *server;
struct wl_list link; // cg_output::layers

struct wl_listener map;
struct wl_listener unmap;
struct wl_listener surface_commit;
// struct wl_listener destroy;

struct wl_listener output_destroy;

bool mapped;

struct cg_output *output;
struct wlr_scene_tree *tree;
struct wlr_scene_layer_surface_v1 *scene;
struct wlr_layer_surface_v1 *layer_surface;
};

#endif
25 changes: 3 additions & 22 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -41,28 +41,7 @@ wayland_server = dependency('wayland-server')
xkbcommon = dependency('xkbcommon')
math = cc.find_library('m')

wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
wayland_scanner = find_program('wayland-scanner')
wayland_scanner_server = generator(
wayland_scanner,
output: '@[email protected]',
arguments: ['server-header', '@INPUT@', '@OUTPUT@'],
)

server_protocols = [
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
]

server_protos_headers = []

foreach p : server_protocols
xml = join_paths(p)
server_protos_headers += wayland_scanner_server.process(xml)
endforeach

server_protos = declare_dependency(
sources: server_protos_headers,
)
subdir('protocols')

have_xwayland = wlroots.get_variable(pkgconfig: 'have_xwayland', internal: 'have_xwayland') == 'true'

Expand Down Expand Up @@ -113,6 +92,7 @@ endif
cage_sources = [
'cage.c',
'idle_inhibit_v1.c',
'layer_shell_v1.c',
'output.c',
'seat.c',
'view.c',
Expand All @@ -124,6 +104,7 @@ cage_headers = [
output: 'config.h',
configuration: conf_data),
'idle_inhibit_v1.h',
'layer_shell_v1.h',
'output.h',
'seat.h',
'server.h',
Expand Down
22 changes: 22 additions & 0 deletions output.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,11 @@ output_destroy(struct cg_output *output)
wl_list_remove(&output->frame.link);
wl_list_remove(&output->link);

wlr_scene_node_destroy(&output->layers.shell_background->node);
wlr_scene_node_destroy(&output->layers.shell_bottom->node);
wlr_scene_node_destroy(&output->layers.shell_top->node);
wlr_scene_node_destroy(&output->layers.shell_overlay->node);

output_layout_remove(output);

free(output);
Expand All @@ -255,6 +260,18 @@ handle_output_destroy(struct wl_listener *listener, void *data)
output_destroy(output);
}

static struct wlr_scene_tree *
create_layer_for_output(struct cg_output *output)
{
struct cg_server *server = output->server;
struct wlr_scene_tree *layer = wlr_scene_tree_create(&server->scene->tree);
if (layer == NULL) {
return NULL;
}
layer->node.data = output->wlr_output;
return layer;
}

void
handle_new_output(struct wl_listener *listener, void *data)
{
Expand Down Expand Up @@ -320,6 +337,11 @@ handle_new_output(struct wl_listener *listener, void *data)
output_disable(next);
}

output->layers.shell_background = create_layer_for_output(output);
output->layers.shell_bottom = create_layer_for_output(output);
output->layers.shell_top = create_layer_for_output(output);
output->layers.shell_overlay = create_layer_for_output(output);

if (!wlr_xcursor_manager_load(server->seat->xcursor_manager, wlr_output->scale)) {
wlr_log(WLR_ERROR, "Cannot load XCursor theme for output '%s' with scale %f", wlr_output->name,
wlr_output->scale);
Expand Down
9 changes: 9 additions & 0 deletions output.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ struct cg_output {
struct wl_listener destroy;
struct wl_listener frame;

struct {
struct wlr_scene_tree *shell_background;
struct wlr_scene_tree *shell_bottom;
struct wlr_scene_tree *shell_top;
struct wlr_scene_tree *shell_overlay;
} layers;

struct wlr_box usable_area;

struct wl_list link; // cg_server::outputs
};

Expand Down
Loading
Loading