aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2023-04-22 18:40:22 +0200
committerNathan Reiner <nathan@nathanreiner.xyz>2023-04-22 18:40:22 +0200
commitf14acf7306b022c1d0dd98f5fd654dbdc38e64e5 (patch)
tree562e30fdad694f11fc1075b353be6d22d9094171
create ffbg
-rw-r--r--.gitignore4
-rw-r--r--LICENSE19
-rw-r--r--Makefile45
-rw-r--r--README.md18
-rw-r--r--config.def.h4
-rw-r--r--drw.c98
-rw-r--r--drw.h45
-rw-r--r--ffbg.c357
-rw-r--r--ffbg.ffbin0 -> 96272 bytes
-rw-r--r--protocols/wlr-layer-shell-unstable.xml390
-rw-r--r--util.c11
-rw-r--r--util.h6
-rw-r--r--wayland.c66
-rw-r--r--wayland.h33
14 files changed, 1096 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..08ccd06
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.o
+config.h
+ffbg
+*-protocol.*
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..43d5d33
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+© 2023- Nathan Reiner <nathan@nathanreiner.xyz>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c172b55
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,45 @@
+PROTOCOLS = xdg-shell-client-protocol.c wlr-layer-shell-protocol.c xdg-output-unstable-v1-client-protocol.c
+SRC = ${PROTOCOLS} ffbg.c drw.c util.c wayland.c
+OBJ = ${SRC:.c=.o}
+CFLAGS =
+LIBS = -lwayland-client -lrt -lm
+
+all: ffbg
+
+config.h:
+ cp config.def.h config.h
+
+xdg-shell-client-protocol.h:
+ wayland-scanner client-header < /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml > xdg-shell-client-protocol.h
+
+xdg-shell-client-protocol.c: xdg-shell-client-protocol.h
+ wayland-scanner private-code < /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml > xdg-shell-client-protocol.c
+
+xdg-output-unstable-v1-client-protocol.h:
+ wayland-scanner client-header < /usr/share/wayland-protocols/unstable/xdg-output/xdg-output-unstable-v1.xml > xdg-output-unstable-v1-client-protocol.h
+
+xdg-output-unstable-v1-client-protocol.c: xdg-output-unstable-v1-client-protocol.h
+ wayland-scanner private-code < /usr/share/wayland-protocols/unstable/xdg-output/xdg-output-unstable-v1.xml > xdg-output-unstable-v1-client-protocol.c
+
+wlr-layer-shell-protocol.h:
+ wayland-scanner client-header < protocols/wlr-layer-shell-unstable.xml > wlr-layer-shell-protocol.h
+
+wlr-layer-shell-protocol.c: wlr-layer-shell-protocol.h
+ wayland-scanner private-code < protocols/wlr-layer-shell-unstable.xml > wlr-layer-shell-protocol.c
+
+.c.o:
+ gcc -c $< ${CFLAGS}
+
+${OBJ}: ${PROTOCOLS} config.h
+
+ffbg: ${OBJ} ${SRC}
+ gcc ${OBJ} -o ffbg ${LIBS}
+
+install: ffbg
+ install -D ./ffbg /usr/local/bin/ffbg
+
+run: ffbg
+ ./ffbg
+
+clean:
+ rm *.o ffbg ${PROTOCOLS} ${PROTOCOLS:.c=.h}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..96cb61d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+# ffbg - wayland wallpaper using farbfeld
+
+ffbg displays a ff pixmap centered of each monitor as wallpaper
+
+## Usage
+
+To display your wallpaper use:
+```sh
+cat /path/to/your/wallpaper.ff | ffbg
+```
+
+## Install
+
+To install use
+
+```sh
+make install
+```
diff --git a/config.def.h b/config.def.h
new file mode 100644
index 0000000..9e40f0f
--- /dev/null
+++ b/config.def.h
@@ -0,0 +1,4 @@
+#include <stdint.h>
+
+/* The colors are in ARGB format */
+static uint32_t background = 0xff282828;
diff --git a/drw.c b/drw.c
new file mode 100644
index 0000000..b78d10a
--- /dev/null
+++ b/drw.c
@@ -0,0 +1,98 @@
+#include <sys/mman.h>
+#include <unistd.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "drw.h"
+#include "util.h"
+
+/* static function declarations */
+static Color to_color(uint32_t color);
+static uint32_t to_uint32_t(Color color);
+
+
+Canvas*
+create_drw(struct wl_shm *shm, unsigned width, unsigned height)
+{
+ struct wl_shm_pool *pool;
+ Canvas *canvas;
+ int stride;
+ int fd;
+
+ canvas = malloc(sizeof(Canvas));
+ canvas->width = width;
+ canvas->height = height;
+
+ stride = width * sizeof(uint32_t);
+ canvas->size = stride * height;
+
+ fd = allocate_shm_file(canvas->size);
+ if (fd == -1) {
+ return NULL;
+ }
+
+ canvas->data = mmap(
+ NULL,
+ canvas->size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ fd,
+ 0
+ );
+
+ if (canvas->data == MAP_FAILED) {
+ close(fd);
+ return NULL;
+ }
+
+ pool = wl_shm_create_pool(
+ shm,
+ fd,
+ canvas->size
+ );
+
+ canvas->buffer = wl_shm_pool_create_buffer(
+ pool,
+ 0,
+ width,
+ height,
+ stride,
+ WL_SHM_FORMAT_XRGB8888
+ );
+
+ wl_shm_pool_destroy(pool);
+ close(fd);
+
+ return canvas;
+}
+
+
+void
+free_drw(Canvas *canvas)
+{
+ munmap(canvas->data, canvas->size);
+ free(canvas);
+}
+
+
+void
+draw_point(Canvas *canvas, unsigned x, unsigned y, uint32_t color)
+{
+ if (!(x < canvas->width && y < canvas->height))
+ return;
+
+ canvas->data[x + canvas->width * y] = color;
+}
+
+
+void
+draw_rect(Canvas *canvas, unsigned x, unsigned y, unsigned width, unsigned height, uint32_t color) {
+ int py;
+ int px;
+
+ for (py = y; py < y + height; ++py) {
+ for (px = x; px < x + width; ++px) {
+ draw_point(canvas, px, py, color);
+ }
+ }
+}
diff --git a/drw.h b/drw.h
new file mode 100644
index 0000000..37e44c9
--- /dev/null
+++ b/drw.h
@@ -0,0 +1,45 @@
+#ifndef DRW_H
+#define DRW_H
+
+#include <stdint.h>
+#include "wayland.h"
+
+/* type definitions */
+typedef struct {
+ uint32_t *data;
+ unsigned width;
+ unsigned height;
+ unsigned size;
+ struct wl_buffer *buffer;
+} Canvas;
+
+typedef struct {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+ uint8_t a;
+} Color;
+
+/* exported functions */
+Canvas* create_drw(struct wl_shm *shm, unsigned width, unsigned height);
+void free_drw(Canvas *canvas);
+
+void draw_rect(Canvas *canvas, unsigned x, unsigned y, unsigned width, unsigned height, uint32_t color);
+void draw_point(Canvas *canvas, unsigned x, unsigned y, uint32_t color);
+
+
+
+static Color
+to_color(uint32_t color)
+{
+ return *(Color*)(&color);
+}
+
+
+static uint32_t
+to_uint32_t(Color color)
+{
+ return *(uint32_t*)(&color);
+}
+
+#endif
diff --git a/ffbg.c b/ffbg.c
new file mode 100644
index 0000000..4fd5e25
--- /dev/null
+++ b/ffbg.c
@@ -0,0 +1,357 @@
+#include <wayland-client-protocol.h>
+#include <assert.h>
+#include <string.h>
+#include <wayland-client.h>
+#include <xkbcommon/xkbcommon.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <malloc.h>
+#include <arpa/inet.h>
+
+#include "drw.h"
+#include "util.h"
+#include "wayland.h"
+
+/* type definitions */
+typedef struct BG BG;
+struct BG {
+ unsigned width;
+ unsigned height;
+ uint32_t registry_name;
+ Canvas *canvas;
+ struct wl_output *wl_output;
+ struct wl_surface *wl_surface;
+ struct zwlr_layer_surface_v1 *zwlr_surface;
+ BG *next;
+};
+
+typedef struct {
+ uint32_t width;
+ uint32_t height;
+ uint32_t *buffer;
+} Image;
+
+/* function declarations */
+static void update_bg(BG *bg);
+static void wl_buffer_release(void *data, struct wl_buffer *wl_buffer);
+static void remove_bg(BG *bg);
+static void registry_global(void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version);
+static void remove_global(void *data, struct wl_registry *wl_registry, uint32_t name);
+static void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t w, uint32_t h);
+static void layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface);
+static void dummy();
+static void wayland_flush();
+static void register_bg(BG *bg);
+static void readimage();
+static void setup();
+struct wl_buffer *draw_frame(BG *bg);
+static BG *add_bg();
+
+
+/* global variables */
+static const struct wl_buffer_listener wl_buffer_listener = {
+ .release = wl_buffer_release,
+};
+
+static const struct wl_registry_listener wl_registry_listener = {
+ .global = registry_global,
+ .global_remove = remove_global,
+};
+
+static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
+ .configure = layer_surface_configure,
+ .closed = layer_surface_closed
+};
+
+static BG *bgs = NULL;
+static char statusline[1024] = "";
+struct client_state bg_state = { 0 };
+struct pollfd pollfds[2];
+int displayfd;
+int ready = 0;
+Image img;
+
+/* configuration */
+#include "config.h"
+
+/* function implementations */
+void
+wl_buffer_release(void *data, struct wl_buffer *wl_buffer)
+{
+ wl_buffer_destroy(wl_buffer);
+}
+
+
+struct wl_buffer *
+draw_frame(BG *bg)
+{
+
+ Canvas *canvas = bg->canvas;
+ unsigned tx;
+ unsigned ty;
+ unsigned x;
+ unsigned y;
+ Color c;
+ Color colbg = to_color(background);
+ double strength;
+
+ if (canvas == 0)
+ return 0;
+
+ draw_rect(canvas, 0, 0, bg->width, bg->height, background);
+
+ tx = (bg->width - img.width) / 2;
+ ty = (bg->height - img.height) / 2;
+
+ for (x = 0; x < img.width; ++x) {
+ for (y = 0; y < img.height; ++y) {
+ c = to_color(img.buffer[x + img.width * y]);
+ strength = (double)(c.a) / 255;
+
+ c.r = (strength * c.r) + ((1 - strength) * colbg.r);
+ c.g = (strength * c.g) + ((1 - strength) * colbg.g);
+ c.b = (strength * c.b) + ((1 - strength) * colbg.b);
+ c.a = 0xff;
+
+ draw_point(canvas, x + tx, y + ty, to_uint32_t(c));
+ }
+ }
+
+ return canvas->buffer;
+}
+
+void
+update_bg(BG *bg)
+{
+ struct wl_buffer *buffer = draw_frame(bg);
+
+ if (!buffer)
+ return;
+
+ wl_surface_attach(bg->wl_surface, buffer, 0, 0);
+ wl_surface_damage_buffer(bg->wl_surface, 0, 0, bg->width, bg->height);
+ wl_surface_commit(bg->wl_surface);
+}
+
+
+void
+registry_global(void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version)
+{
+ struct client_state *state = data;
+ BG *bg;
+
+ if (strcmp(interface, wl_shm_interface.name) == 0) {
+ state->wl_shm = wl_registry_bind(
+ wl_registry, name, &wl_shm_interface, 1);
+ } else if (strcmp(interface, wl_compositor_interface.name) == 0) {
+ state->wl_compositor = wl_registry_bind(
+ wl_registry, name, &wl_compositor_interface, 4);
+ } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
+ state->zwlr_layer = wl_registry_bind(wl_registry, name, &zwlr_layer_shell_v1_interface, 1);
+ } else if (strcmp(interface, wl_output_interface.name) == 0) {
+ bg = add_bg();
+ bg->wl_output = wl_registry_bind(wl_registry, name, &wl_output_interface, 1);
+ bg->registry_name = name;
+
+ if (ready) {
+ register_bg(bg);
+ }
+ }
+}
+
+
+void
+remove_global(void *data, struct wl_registry *wl_registry, uint32_t name)
+{
+ BG *bg = bgs;
+ BG *prev = 0;
+
+ for (; bg; bg = bg->next) {
+ if (bg->registry_name == name) {
+ if (prev) {
+ prev->next = bg->next;
+ } else {
+ bgs = bg->next;
+ }
+
+ remove_bg(bg);
+ break;
+ }
+
+ prev = bg;
+ }
+}
+
+
+void
+layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t w, uint32_t h)
+{
+ BG *bg = data;
+
+ bg->width = w;
+ bg->height = h;
+
+ zwlr_layer_surface_v1_set_exclusive_zone(surface, -1);
+ zwlr_layer_surface_v1_ack_configure(surface, serial);
+
+ bg->canvas = create_drw(bg_state.wl_shm, bg->width, bg->height);
+
+ update_bg(bg);
+}
+
+
+void
+layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface)
+{
+ BG *bg = data;
+ zwlr_layer_surface_v1_destroy(surface);
+ wl_surface_destroy(bg->wl_surface);
+}
+
+
+void
+dummy()
+{
+ /* Who cares */
+}
+
+
+BG *
+add_bg()
+{
+ BG *bg = bgs;
+
+ if (!bg) {
+ bgs = malloc(sizeof(BG));
+ memset(bgs, 0, sizeof(BG));
+ return bgs;
+ }
+
+ for (; bg->next; bg = bg->next);
+
+ bg->next = malloc(sizeof(BG));
+ memset(bg->next, 0, sizeof(BG));
+
+ return bg->next;
+}
+
+
+void
+remove_bg(BG *bg)
+{
+ wl_surface_destroy(bg->wl_surface);
+ zwlr_layer_surface_v1_destroy(bg->zwlr_surface);
+ wl_output_destroy(bg->wl_output);
+ free_drw(bg->canvas);
+ free(bg);
+}
+
+
+void
+register_bg(BG *bg)
+{
+ char *namespace = "dbg";
+ uint32_t layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND;
+ uint32_t anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
+
+ bg->wl_surface = wl_compositor_create_surface(bg_state.wl_compositor);
+ bg->zwlr_surface = zwlr_layer_shell_v1_get_layer_surface(bg_state.zwlr_layer, bg->wl_surface, bg->wl_output, layer, namespace);
+
+ zwlr_layer_surface_v1_add_listener(bg->zwlr_surface, &layer_surface_listener, bg);
+
+ zwlr_layer_surface_v1_set_size(bg->zwlr_surface, bg->width, bg->height);
+ zwlr_layer_surface_v1_set_anchor(bg->zwlr_surface, anchor);
+
+ wl_surface_commit(bg->wl_surface);
+ wl_display_roundtrip(bg_state.wl_display);
+}
+
+void
+readimage()
+{
+ uint32_t buf[4];
+ size_t i;
+ size_t s = 0;
+ uint8_t r, g, b, a;
+ uint64_t *tmpbuf;
+ uint64_t color;
+ read(STDIN_FILENO, buf, sizeof(buf));
+
+ if (memcmp(buf, "farbfeld", sizeof("farbfeld") - 1)) {
+ die("input not farbfeld image");
+ }
+
+ img.width = ntohl(buf[2]);
+ img.height = ntohl(buf[3]);
+ img.buffer = malloc(img.width * img.height * sizeof(uint32_t));
+ tmpbuf = malloc(img.width * img.height * sizeof(uint64_t));
+
+ for (i = 0; i < ((size_t)img.width * img.height * sizeof(uint64_t));) {
+ i += read(STDIN_FILENO, (char*)(tmpbuf) + i, 1);
+ }
+
+ for (i = 0; i < img.width * img.height; ++i) {
+ color = tmpbuf[i];
+ a = ((color & (0xffffl << 48)) >> 48) / 257;
+ b = ((color & (0xffffl << 32)) >> 32) / 257;
+ g = ((color & (0xffffl << 16)) >> 16) / 257;
+ r = (color & 0xffffl) / 257;
+ img.buffer[i] = (a << 24) | (r << 16) | (g << 8) | b;
+ }
+
+ free(tmpbuf);
+}
+
+
+void
+setup()
+{
+ BG *bg;
+
+ bg_state.wl_display = wl_display_connect(NULL);
+ bg_state.wl_registry = wl_display_get_registry(bg_state.wl_display);
+ wl_registry_add_listener(bg_state.wl_registry, &wl_registry_listener, &bg_state);
+ wl_display_roundtrip(bg_state.wl_display);
+
+ for (bg = bgs; bg; bg = bg->next) {
+ register_bg(bg);
+ }
+
+ ready = 1;
+}
+
+
+void
+wayland_flush()
+{
+ int i;
+
+ wl_display_dispatch_pending(bg_state.wl_display);
+
+ if (wl_display_flush(bg_state.wl_display) < 0 && errno == EAGAIN) {
+ for (i = 0; i < 2; ++i) {
+ if (pollfds[i].fd == displayfd) {
+ pollfds[i].events |= POLLOUT;
+ }
+ }
+ }
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ readimage();
+ setup();
+
+ while (wl_display_dispatch(bg_state.wl_display)) {
+ }
+
+ return 0;
+}
diff --git a/ffbg.ff b/ffbg.ff
new file mode 100644
index 0000000..f599384
--- /dev/null
+++ b/ffbg.ff
Binary files differ
diff --git a/protocols/wlr-layer-shell-unstable.xml b/protocols/wlr-layer-shell-unstable.xml
new file mode 100644
index 0000000..d62fd51
--- /dev/null
+++ b/protocols/wlr-layer-shell-unstable.xml
@@ -0,0 +1,390 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wlr_layer_shell_unstable_v1">
+ <copyright>
+ Copyright © 2017 Drew DeVault
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+ </copyright>
+
+ <interface name="zwlr_layer_shell_v1" version="4">
+ <description summary="create surfaces that are layers of the desktop">
+ Clients can use this interface to assign the surface_layer role to
+ wl_surfaces. Such surfaces are assigned to a "layer" of the output and
+ rendered with a defined z-depth respective to each other. They may also be
+ anchored to the edges and corners of a screen and specify input handling
+ semantics. This interface should be suitable for the implementation of
+ many desktop shell components, and a broad number of other applications
+ that interact with the desktop.
+ </description>
+
+ <request name="get_layer_surface">
+ <description summary="create a layer_surface from a surface">
+ Create a layer surface for an existing surface. This assigns the role of
+ layer_surface, or raises a protocol error if another role is already
+ assigned.
+
+ Creating a layer surface from a wl_surface which has a buffer attached
+ or committed is a client error, and any attempts by a client to attach
+ or manipulate a buffer prior to the first layer_surface.configure call
+ must also be treated as errors.
+
+ After creating a layer_surface object and setting it up, the client
+ must perform an initial commit without any buffer attached.
+ The compositor will reply with a layer_surface.configure event.
+ The client must acknowledge it and is then allowed to attach a buffer
+ to map the surface.
+
+ You may pass NULL for output to allow the compositor to decide which
+ output to use. Generally this will be the one that the user most
+ recently interacted with.
+
+ Clients can specify a namespace that defines the purpose of the layer
+ surface.
+ </description>
+ <arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="output" type="object" interface="wl_output" allow-null="true"/>
+ <arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
+ <arg name="namespace" type="string" summary="namespace for the layer surface"/>
+ </request>
+
+ <enum name="error">
+ <entry name="role" value="0" summary="wl_surface has another role"/>
+ <entry name="invalid_layer" value="1" summary="layer value is invalid"/>
+ <entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
+ </enum>
+
+ <enum name="layer">
+ <description summary="available layers for surfaces">
+ These values indicate which layers a surface can be rendered in. They
+ are ordered by z depth, bottom-most first. Traditional shell surfaces
+ will typically be rendered between the bottom and top layers.
+ Fullscreen shell surfaces are typically rendered at the top layer.
+ Multiple surfaces can share a single layer, and ordering within a
+ single layer is undefined.
+ </description>
+
+ <entry name="background" value="0"/>
+ <entry name="bottom" value="1"/>
+ <entry name="top" value="2"/>
+ <entry name="overlay" value="3"/>
+ </enum>
+
+ <!-- Version 3 additions -->
+
+ <request name="destroy" type="destructor" since="3">
+ <description summary="destroy the layer_shell object">
+ This request indicates that the client will not use the layer_shell
+ object any more. Objects that have been created through this instance
+ are not affected.
+ </description>
+ </request>
+ </interface>
+
+ <interface name="zwlr_layer_surface_v1" version="4">
+ <description summary="layer metadata interface">
+ An interface that may be implemented by a wl_surface, for surfaces that
+ are designed to be rendered as a layer of a stacked desktop-like
+ environment.
+
+ Layer surface state (layer, size, anchor, exclusive zone,
+ margin, interactivity) is double-buffered, and will be applied at the
+ time wl_surface.commit of the corresponding wl_surface is called.
+
+ Attaching a null buffer to a layer surface unmaps it.
+
+ Unmapping a layer_surface means that the surface cannot be shown by the
+ compositor until it is explicitly mapped again. The layer_surface
+ returns to the state it had right after layer_shell.get_layer_surface.
+ The client can re-map the surface by performing a commit without any
+ buffer attached, waiting for a configure event and handling it as usual.
+ </description>
+
+ <request name="set_size">
+ <description summary="sets the size of the surface">
+ Sets the size of the surface in surface-local coordinates. The
+ compositor will display the surface centered with respect to its
+ anchors.
+
+ If you pass 0 for either value, the compositor will assign it and
+ inform you of the assignment in the configure event. You must set your
+ anchor to opposite edges in the dimensions you omit; not doing so is a
+ protocol error. Both values are 0 by default.
+
+ Size is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="width" type="uint"/>
+ <arg name="height" type="uint"/>
+ </request>
+
+ <request name="set_anchor">
+ <description summary="configures the anchor point of the surface">
+ Requests that the compositor anchor the surface to the specified edges
+ and corners. If two orthogonal edges are specified (e.g. 'top' and
+ 'left'), then the anchor point will be the intersection of the edges
+ (e.g. the top left corner of the output); otherwise the anchor point
+ will be centered on that edge, or in the center if none is specified.
+
+ Anchor is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="anchor" type="uint" enum="anchor"/>
+ </request>
+
+ <request name="set_exclusive_zone">
+ <description summary="configures the exclusive geometry of this surface">
+ Requests that the compositor avoids occluding an area with other
+ surfaces. The compositor's use of this information is
+ implementation-dependent - do not assume that this region will not
+ actually be occluded.
+
+ A positive value is only meaningful if the surface is anchored to one
+ edge or an edge and both perpendicular edges. If the surface is not
+ anchored, anchored to only two perpendicular edges (a corner), anchored
+ to only two parallel edges or anchored to all edges, a positive value
+ will be treated the same as zero.
+
+ A positive zone is the distance from the edge in surface-local
+ coordinates to consider exclusive.
+
+ Surfaces that do not wish to have an exclusive zone may instead specify
+ how they should interact with surfaces that do. If set to zero, the
+ surface indicates that it would like to be moved to avoid occluding
+ surfaces with a positive exclusive zone. If set to -1, the surface
+ indicates that it would not like to be moved to accommodate for other
+ surfaces, and the compositor should extend it all the way to the edges
+ it is anchored to.
+
+ For example, a panel might set its exclusive zone to 10, so that
+ maximized shell surfaces are not shown on top of it. A notification
+ might set its exclusive zone to 0, so that it is moved to avoid
+ occluding the panel, but shell surfaces are shown underneath it. A
+ wallpaper or lock screen might set their exclusive zone to -1, so that
+ they stretch below or over the panel.
+
+ The default value is 0.
+
+ Exclusive zone is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="zone" type="int"/>
+ </request>
+
+ <request name="set_margin">
+ <description summary="sets a margin from the anchor point">
+ Requests that the surface be placed some distance away from the anchor
+ point on the output, in surface-local coordinates. Setting this value
+ for edges you are not anchored to has no effect.
+
+ The exclusive zone includes the margin.
+
+ Margin is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="top" type="int"/>
+ <arg name="right" type="int"/>
+ <arg name="bottom" type="int"/>
+ <arg name="left" type="int"/>
+ </request>
+
+ <enum name="keyboard_interactivity">
+ <description summary="types of keyboard interaction possible for a layer shell surface">
+ Types of keyboard interaction possible for layer shell surfaces. The
+ rationale for this is twofold: (1) some applications are not interested
+ in keyboard events and not allowing them to be focused can improve the
+ desktop experience; (2) some applications will want to take exclusive
+ keyboard focus.
+ </description>
+
+ <entry name="none" value="0">
+ <description summary="no keyboard focus is possible">
+ This value indicates that this surface is not interested in keyboard
+ events and the compositor should never assign it the keyboard focus.
+
+ This is the default value, set for newly created layer shell surfaces.
+
+ This is useful for e.g. desktop widgets that display information or
+ only have interaction with non-keyboard input devices.
+ </description>
+ </entry>
+ <entry name="exclusive" value="1">
+ <description summary="request exclusive keyboard focus">
+ Request exclusive keyboard focus if this surface is above the shell surface layer.
+
+ For the top and overlay layers, the seat will always give
+ exclusive keyboard focus to the top-most layer which has keyboard
+ interactivity set to exclusive. If this layer contains multiple
+ surfaces with keyboard interactivity set to exclusive, the compositor
+ determines the one receiving keyboard events in an implementation-
+ defined manner. In this case, no guarantee is made when this surface
+ will receive keyboard focus (if ever).
+
+ For the bottom and background layers, the compositor is allowed to use
+ normal focus semantics.
+
+ This setting is mainly intended for applications that need to ensure
+ they receive all keyboard events, such as a lock screen or a password
+ prompt.
+ </description>
+ </entry>
+ <entry name="on_demand" value="2" since="4">
+ <description summary="request regular keyboard focus semantics">
+ This requests the compositor to allow this surface to be focused and
+ unfocused by the user in an implementation-defined manner. The user
+ should be able to unfocus this surface even regardless of the layer
+ it is on.
+
+ Typically, the compositor will want to use its normal mechanism to
+ manage keyboard focus between layer shell surfaces with this setting
+ and regular toplevels on the desktop layer (e.g. click to focus).
+ Nevertheless, it is possible for a compositor to require a special
+ interaction to focus or unfocus layer shell surfaces (e.g. requiring
+ a click even if focus follows the mouse normally, or providing a
+ keybinding to switch focus between layers).
+
+ This setting is mainly intended for desktop shell components (e.g.
+ panels) that allow keyboard interaction. Using this option can allow
+ implementing a desktop shell that can be fully usable without the
+ mouse.
+ </description>
+ </entry>
+ </enum>
+
+ <request name="set_keyboard_interactivity">
+ <description summary="requests keyboard events">
+ Set how keyboard events are delivered to this surface. By default,
+ layer shell surfaces do not receive keyboard events; this request can
+ be used to change this.
+
+ This setting is inherited by child surfaces set by the get_popup
+ request.
+
+ Layer surfaces receive pointer, touch, and tablet events normally. If
+ you do not want to receive them, set the input region on your surface
+ to an empty region.
+
+ Keyboard interactivity is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="keyboard_interactivity" type="uint" enum="keyboard_interactivity"/>
+ </request>
+
+ <request name="get_popup">
+ <description summary="assign this layer_surface as an xdg_popup parent">
+ This assigns an xdg_popup's parent to this layer_surface. This popup
+ should have been created via xdg_surface::get_popup with the parent set
+ to NULL, and this request must be invoked before committing the popup's
+ initial state.
+
+ See the documentation of xdg_popup for more details about what an
+ xdg_popup is and how it is used.
+ </description>
+ <arg name="popup" type="object" interface="xdg_popup"/>
+ </request>
+
+ <request name="ack_configure">
+ <description summary="ack a configure event">
+ When a configure event is received, if a client commits the
+ surface in response to the configure event, then the client
+ must make an ack_configure request sometime before the commit
+ request, passing along the serial of the configure event.
+
+ If the client receives multiple configure events before it
+ can respond to one, it only has to ack the last configure event.
+
+ A client is not required to commit immediately after sending
+ an ack_configure request - it may even ack_configure several times
+ before its next surface commit.
+
+ A client may send multiple ack_configure requests before committing, but
+ only the last request sent before a commit indicates which configure
+ event the client really is responding to.
+ </description>
+ <arg name="serial" type="uint" summary="the serial from the configure event"/>
+ </request>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the layer_surface">
+ This request destroys the layer surface.
+ </description>
+ </request>
+
+ <event name="configure">
+ <description summary="suggest a surface change">
+ The configure event asks the client to resize its surface.
+
+ Clients should arrange their surface for the new states, and then send
+ an ack_configure request with the serial sent in this configure event at
+ some point before committing the new surface.
+
+ The client is free to dismiss all but the last configure event it
+ received.
+
+ The width and height arguments specify the size of the window in
+ surface-local coordinates.
+
+ The size is a hint, in the sense that the client is free to ignore it if
+ it doesn't resize, pick a smaller size (to satisfy aspect ratio or
+ resize in steps of NxM pixels). If the client picks a smaller size and
+ is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
+ surface will be centered on this axis.
+
+ If the width or height arguments are zero, it means the client should
+ decide its own window dimension.
+ </description>
+ <arg name="serial" type="uint"/>
+ <arg name="width" type="uint"/>
+ <arg name="height" type="uint"/>
+ </event>
+
+ <event name="closed">
+ <description summary="surface should be closed">
+ The closed event is sent by the compositor when the surface will no
+ longer be shown. The output may have been destroyed or the user may
+ have asked for it to be removed. Further changes to the surface will be
+ ignored. The client should destroy the resource after receiving this
+ event, and create a new surface if they so choose.
+ </description>
+ </event>
+
+ <enum name="error">
+ <entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
+ <entry name="invalid_size" value="1" summary="size is invalid"/>
+ <entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
+ <entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity is invalid"/>
+ </enum>
+
+ <enum name="anchor" bitfield="true">
+ <entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
+ <entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
+ <entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
+ <entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
+ </enum>
+
+ <!-- Version 2 additions -->
+
+ <request name="set_layer" since="2">
+ <description summary="change the layer of the surface">
+ Change the layer that the surface is rendered on.
+
+ Layer is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
+ </request>
+ </interface>
+</protocol>
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..2e8a1c8
--- /dev/null
+++ b/util.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+void
+die(const char *error)
+{
+ fprintf(stderr, "error: %s\n", error);
+ exit(-1);
+}
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..0feb54a
--- /dev/null
+++ b/util.h
@@ -0,0 +1,6 @@
+#ifndef UTIL_H
+#define UTIL_H
+
+void die(const char *error);
+
+#endif
diff --git a/wayland.c b/wayland.c
new file mode 100644
index 0000000..0787ec3
--- /dev/null
+++ b/wayland.c
@@ -0,0 +1,66 @@
+#include <errno.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "wayland.h"
+
+static void randname(char *buf);
+static int create_shm_file(void);
+
+
+void
+randname(char *buf)
+{
+ struct timespec ts;
+ long r;
+ int i;
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ r = ts.tv_nsec;
+ for (i = 0; i < 6; ++i) {
+ buf[i] = 'A'+(r&15)+(r&16)*2;
+ r >>= 5;
+ }
+}
+
+int
+create_shm_file(void)
+{
+ int fd;
+ int retries = 100;
+ char name[] = "/wl_shm-XXXXXX";
+
+ do {
+ randname(name + sizeof(name) - 7);
+ --retries;
+ fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
+ if (fd >= 0) {
+ shm_unlink(name);
+ return fd;
+ }
+ } while (retries > 0 && errno == EEXIST);
+ return -1;
+}
+
+int
+allocate_shm_file(size_t size)
+{
+ int ret;
+ int fd = create_shm_file();
+
+ if (fd < 0)
+ return -1;
+
+ do {
+ ret = ftruncate(fd, size);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
diff --git a/wayland.h b/wayland.h
new file mode 100644
index 0000000..bf9ebb0
--- /dev/null
+++ b/wayland.h
@@ -0,0 +1,33 @@
+#ifndef WAYLAND_H
+#define WAYLAND_H
+
+#include "xdg-shell-client-protocol.h"
+#include "wlr-layer-shell-protocol.h"
+
+typedef struct ListElement ListElement;
+struct ListElement {
+ const char *name;
+ ListElement *next;
+ ListElement *previous;
+};
+
+typedef struct {
+ ListElement *first;
+ ListElement *last;
+ uint32_t size;
+} List;
+
+struct client_state {
+ struct wl_display *wl_display;
+ struct wl_registry *wl_registry;
+ struct wl_shm *wl_shm;
+ struct wl_compositor *wl_compositor;
+ struct zwlr_layer_shell_v1 *zwlr_layer;
+ struct zdwl_manager_v1 *dwl_manager;
+ List tags;
+ List layouts;
+};
+
+int allocate_shm_file(size_t size);
+
+#endif