aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2018-03-28 15:25:33 -0400
committerGitHub <noreply@github.com>2018-03-28 15:25:33 -0400
commit9070950eecded7bfa64e7bca3bb76b150ccc8b72 (patch)
tree509a9c669bf2679085e27a1ff1b0c95526abf14c
parent45a50d5afe013b31d9c94090d990bca49448d396 (diff)
parentd39bda76c4007c42452a81883fefc671b816a74b (diff)
Merge pull request #1638 from swaywm/swaybg-layers
Reimplement swaybg using surface layers
-rw-r--r--client/meson.build16
-rw-r--r--client/pool-buffer.c (renamed from wayland/buffers.c)58
-rw-r--r--common/cairo.c (renamed from wayland/cairo.c)45
-rw-r--r--common/meson.build21
-rw-r--r--include/cairo.h (renamed from include/client/cairo.h)10
-rw-r--r--include/client/buffer.h8
-rw-r--r--include/client/pango.h16
-rw-r--r--include/client/registry.h75
-rw-r--r--include/client/window.h67
-rw-r--r--include/meson.build1
-rw-r--r--include/pool-buffer.h21
-rw-r--r--meson.build13
-rw-r--r--protocols/meson.build37
-rw-r--r--protocols/wlr-layer-shell-unstable-v1.xml281
-rw-r--r--swaybg/main.c445
-rw-r--r--swaybg/meson.build18
-rw-r--r--wayland/pango.c73
-rw-r--r--wayland/registry.c293
-rw-r--r--wayland/window.c177
19 files changed, 724 insertions, 951 deletions
diff --git a/client/meson.build b/client/meson.build
new file mode 100644
index 00000000..2bdda457
--- /dev/null
+++ b/client/meson.build
@@ -0,0 +1,16 @@
+lib_sway_client = static_library(
+ 'sway-client',
+ files(
+ 'pool-buffer.c',
+ ),
+ dependencies: [
+ cairo,
+ gdk_pixbuf,
+ pango,
+ pangocairo,
+ wlroots,
+ wayland_client,
+ ],
+ link_with: [lib_sway_common],
+ include_directories: sway_inc
+)
diff --git a/wayland/buffers.c b/client/pool-buffer.c
index e9780997..93cfcfc5 100644
--- a/wayland/buffers.c
+++ b/client/pool-buffer.c
@@ -1,21 +1,20 @@
#define _XOPEN_SOURCE 500
-#include <wayland-client.h>
+#include <assert.h>
#include <cairo/cairo.h>
-#include <pango/pangocairo.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <errno.h>
#include <sys/mman.h>
-#include "client/buffer.h"
-#include "list.h"
-#include "log.h"
+#include <pango/pangocairo.h>
+#include <unistd.h>
+#include <wayland-client.h>
+#include "config.h"
+#include "pool-buffer.h"
static int create_pool_file(size_t size, char **name) {
static const char template[] = "sway-client-XXXXXX";
const char *path = getenv("XDG_RUNTIME_DIR");
- if (!path) {
+ if (!path) {
return -1;
}
@@ -42,7 +41,7 @@ static int create_pool_file(size_t size, char **name) {
}
static void buffer_release(void *data, struct wl_buffer *wl_buffer) {
- struct buffer *buffer = data;
+ struct pool_buffer *buffer = data;
buffer->busy = false;
}
@@ -50,22 +49,17 @@ static const struct wl_buffer_listener buffer_listener = {
.release = buffer_release
};
-static struct buffer *create_buffer(struct window *window, struct buffer *buf,
- int32_t width, int32_t height, int32_t scale, uint32_t format) {
-
- width *= scale;
- height *= scale;
+static struct pool_buffer *create_buffer(struct wl_shm *shm,
+ struct pool_buffer *buf, int32_t width, int32_t height,
+ uint32_t format) {
uint32_t stride = width * 4;
uint32_t size = stride * height;
char *name;
int fd = create_pool_file(size, &name);
- if (fd == -1) {
- sway_abort("Unable to allocate buffer");
- return NULL; // never reached
- }
+ assert(fd);
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- struct wl_shm_pool *pool = wl_shm_create_pool(window->registry->shm, fd, size);
+ struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
buf->buffer = wl_shm_pool_create_buffer(pool, 0,
width, height, stride, format);
wl_shm_pool_destroy(pool);
@@ -85,7 +79,7 @@ static struct buffer *create_buffer(struct window *window, struct buffer *buf,
return buf;
}
-static void destroy_buffer(struct buffer *buffer) {
+static void destroy_buffer(struct pool_buffer *buffer) {
if (buffer->buffer) {
wl_buffer_destroy(buffer->buffer);
}
@@ -98,37 +92,33 @@ static void destroy_buffer(struct buffer *buffer) {
if (buffer->pango) {
g_object_unref(buffer->pango);
}
- memset(buffer, 0, sizeof(struct buffer));
+ memset(buffer, 0, sizeof(struct pool_buffer));
}
-struct buffer *get_next_buffer(struct window *window) {
- struct buffer *buffer = NULL;
+struct pool_buffer *get_next_buffer(struct wl_shm *shm,
+ struct pool_buffer pool[static 2], uint32_t width, uint32_t height) {
+ struct pool_buffer *buffer = NULL;
- int i;
- for (i = 0; i < 2; ++i) {
- if (window->buffers[i].busy) {
+ for (size_t i = 0; i < 2; ++i) {
+ if (pool[i].busy) {
continue;
}
- buffer = &window->buffers[i];
+ buffer = &pool[i];
}
if (!buffer) {
return NULL;
}
- if (buffer->width != window->width || buffer->height != window->height) {
+ if (buffer->width != width || buffer->height != height) {
destroy_buffer(buffer);
}
if (!buffer->buffer) {
- if (!create_buffer(window, buffer,
- window->width, window->height, window->scale,
+ if (!create_buffer(shm, buffer, width, height,
WL_SHM_FORMAT_ARGB8888)) {
return NULL;
}
}
-
- window->cairo = buffer->cairo;
- window->buffer = buffer;
return buffer;
}
diff --git a/wayland/cairo.c b/common/cairo.c
index 193205b1..c267c77c 100644
--- a/wayland/cairo.c
+++ b/common/cairo.c
@@ -1,4 +1,9 @@
-#include "client/cairo.h"
+#include <stdint.h>
+#include <cairo/cairo.h>
+#include "cairo.h"
+#ifdef HAVE_GDK_PIXBUF
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#endif
void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
cairo_set_source_rgba(cairo,
@@ -8,45 +13,31 @@ void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
(color >> (0*8) & 0xFF) / 255.0);
}
-cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height) {
+cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image,
+ int width, int height) {
int image_width = cairo_image_surface_get_width(image);
int image_height = cairo_image_surface_get_height(image);
cairo_surface_t *new =
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
-
cairo_t *cairo = cairo_create(new);
-
- cairo_scale(cairo, (double) width / image_width, (double) height / image_height);
-
+ cairo_scale(cairo, (double)width / image_width,
+ (double)height / image_height);
cairo_set_source_surface(cairo, image, 0, 0);
- cairo_paint(cairo);
+ cairo_paint(cairo);
cairo_destroy(cairo);
-
return new;
}
-#ifdef WITH_GDK_PIXBUF
-#include <gdk-pixbuf/gdk-pixbuf.h>
-
-#ifndef GDK_PIXBUF_CHECK_VERSION
-#define GDK_PIXBUF_CHECK_VERSION(major,minor,micro) \
- (GDK_PIXBUF_MAJOR > (major) || \
- (GDK_PIXBUF_MAJOR == (major) && GDK_PIXBUF_MINOR > (minor)) || \
- (GDK_PIXBUF_MAJOR == (major) && GDK_PIXBUF_MINOR == (minor) && \
- GDK_PIXBUF_MICRO >= (micro)))
-#endif
-
+#ifdef HAVE_GDK_PIXBUF
cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) {
int chan = gdk_pixbuf_get_n_channels(gdkbuf);
- if (chan < 3) return NULL;
+ if (chan < 3) {
+ return NULL;
+ }
-#if GDK_PIXBUF_CHECK_VERSION(2,32,0)
const guint8* gdkpix = gdk_pixbuf_read_pixels(gdkbuf);
-#else
- const guint8* gdkpix = gdk_pixbuf_get_pixels(gdkbuf);
-#endif
if (!gdkpix) {
return NULL;
}
@@ -101,7 +92,9 @@ cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdk
* ------
* tested as equal to lround(z/255.0) for uint z in [0..0xfe02]
*/
-#define PREMUL_ALPHA(x,a,b,z) G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } G_STMT_END
+#define PREMUL_ALPHA(x,a,b,z) \
+ G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } \
+ G_STMT_END
int i;
for (i = h; i; --i) {
const guint8 *gp = gdkpix;
@@ -131,4 +124,4 @@ cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdk
cairo_surface_mark_dirty(cs);
return cs;
}
-#endif //WITH_GDK_PIXBUF
+#endif //HAVE_GDK_PIXBUF
diff --git a/common/meson.build b/common/meson.build
index abe0cdcf..01736ca6 100644
--- a/common/meson.build
+++ b/common/meson.build
@@ -1,12 +1,23 @@
-lib_sway_common = static_library('sway-common',
+deps = [
+ cairo,
+ wlroots
+]
+
+if gdk_pixbuf.found()
+ deps += [gdk_pixbuf]
+endif
+
+lib_sway_common = static_library(
+ 'sway-common',
files(
+ 'cairo.c',
+ 'ipc-client.c',
'log.c',
'list.c',
- 'util.c',
- 'stringop.c',
'readline.c',
- 'ipc-client.c'
+ 'stringop.c',
+ 'util.c'
),
- dependencies: [ wlroots ],
+ dependencies: deps,
include_directories: sway_inc
)
diff --git a/include/client/cairo.h b/include/cairo.h
index e7ef7c7e..31672705 100644
--- a/include/client/cairo.h
+++ b/include/cairo.h
@@ -1,17 +1,19 @@
#ifndef _SWAY_CAIRO_H
#define _SWAY_CAIRO_H
-
#include <stdint.h>
#include <cairo/cairo.h>
void cairo_set_source_u32(cairo_t *cairo, uint32_t color);
-cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height);
+cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image,
+ int width, int height);
-#ifdef WITH_GDK_PIXBUF
+#include "config.h"
+#ifdef HAVE_GDK_PIXBUF
#include <gdk-pixbuf/gdk-pixbuf.h>
-cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf);
+cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(
+ const GdkPixbuf *gdkbuf);
#endif //WITH_GDK_PIXBUF
#endif
diff --git a/include/client/buffer.h b/include/client/buffer.h
deleted file mode 100644
index eb9973ed..00000000
--- a/include/client/buffer.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef _BUFFER_H
-#define _BUFFER_H
-
-#include "client/window.h"
-
-struct buffer *get_next_buffer(struct window *state);
-
-#endif
diff --git a/include/client/pango.h b/include/client/pango.h
deleted file mode 100644
index dd2f53c3..00000000
--- a/include/client/pango.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef _SWAY_CLIENT_PANGO_H
-#define _SWAY_CLIENT_PANGO_H
-
-#include <cairo/cairo.h>
-#include <pango/pangocairo.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text,
- int32_t scale, bool markup);
-void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
- int32_t scale, bool markup, const char *fmt, ...);
-void pango_printf(cairo_t *cairo, const char *font, int32_t scale, bool markup, const char *fmt, ...);
-
-#endif
diff --git a/include/client/registry.h b/include/client/registry.h
deleted file mode 100644
index 9dfbd835..00000000
--- a/include/client/registry.h
+++ /dev/null
@@ -1,75 +0,0 @@
-#ifndef _SWAY_CLIENT_REGISTRY_H
-#define _SWAY_CLIENT_REGISTRY_H
-
-#include <wayland-client.h>
-#include <xkbcommon/xkbcommon.h>
-#include "wayland-desktop-shell-client-protocol.h"
-#include "wayland-swaylock-client-protocol.h"
-#include "list.h"
-
-enum mod_bit {
- MOD_SHIFT = 1<<0,
- MOD_CAPS = 1<<1,
- MOD_CTRL = 1<<2,
- MOD_ALT = 1<<3,
- MOD_MOD2 = 1<<4,
- MOD_MOD3 = 1<<5,
- MOD_LOGO = 1<<6,
- MOD_MOD5 = 1<<7,
-};
-
-enum mask {
- MASK_SHIFT,
- MASK_CAPS,
- MASK_CTRL,
- MASK_ALT,
- MASK_MOD2,
- MASK_MOD3,
- MASK_LOGO,
- MASK_MOD5,
- MASK_LAST
-};
-
-struct output_state {
- struct wl_output *output;
- uint32_t flags;
- uint32_t width, height;
- uint32_t scale;
-};
-
-struct xkb {
- struct xkb_state *state;
- struct xkb_context *context;
- struct xkb_keymap *keymap;
- xkb_mod_mask_t masks[MASK_LAST];
-};
-
-struct input {
- struct xkb xkb;
-
- xkb_keysym_t sym;
- uint32_t code;
- uint32_t last_code;
- uint32_t modifiers;
-
- void (*notify)(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint);
-};
-
-struct registry {
- struct wl_compositor *compositor;
- struct wl_display *display;
- struct wl_pointer *pointer;
- struct wl_keyboard *keyboard;
- struct wl_seat *seat;
- struct wl_shell *shell;
- struct wl_shm *shm;
- struct desktop_shell *desktop_shell;
- struct lock *swaylock;
- struct input *input;
- list_t *outputs;
-};
-
-struct registry *registry_poll(void);
-void registry_teardown(struct registry *registry);
-
-#endif
diff --git a/include/client/window.h b/include/client/window.h
deleted file mode 100644
index 8af8225c..00000000
--- a/include/client/window.h
+++ /dev/null
@@ -1,67 +0,0 @@
-#ifndef _CLIENT_H
-#define _CLIENT_H
-
-#include <wayland-client.h>
-#include "wayland-desktop-shell-client-protocol.h"
-#include <cairo/cairo.h>
-#include <pango/pangocairo.h>
-#include <stdbool.h>
-#include "list.h"
-#include "client/registry.h"
-
-struct window;
-
-struct buffer {
- struct wl_buffer *buffer;
- cairo_surface_t *surface;
- cairo_t *cairo;
- PangoContext *pango;
- uint32_t width, height;
- bool busy;
-};
-
-struct cursor {
- struct wl_surface *surface;
- struct wl_cursor_theme *cursor_theme;
- struct wl_cursor *cursor;
- struct wl_pointer *pointer;
-};
-
-enum scroll_direction {
- SCROLL_UP,
- SCROLL_DOWN,
- SCROLL_LEFT,
- SCROLL_RIGHT,
-};
-
-struct pointer_input {
- int last_x;
- int last_y;
-
- void (*notify_button)(struct window *window, int x, int y, uint32_t button, uint32_t state_w);
- void (*notify_scroll)(struct window *window, enum scroll_direction direction);
-};
-
-struct window {
- struct registry *registry;
- struct buffer buffers[2];
- struct buffer *buffer;
- struct wl_surface *surface;
- struct wl_shell_surface *shell_surface;
- struct wl_callback *frame_cb;
- struct cursor cursor;
- uint32_t width, height;
- int32_t scale;
- char *font;
- cairo_t *cairo;
- struct pointer_input pointer_input;
-};
-
-struct window *window_setup(struct registry *registry, uint32_t width, uint32_t height,
- int32_t scale, bool shell_surface);
-void window_teardown(struct window *state);
-int window_prerender(struct window *state);
-int window_render(struct window *state);
-void window_make_shell(struct window *window);
-
-#endif
diff --git a/include/meson.build b/include/meson.build
new file mode 100644
index 00000000..65ed027a
--- /dev/null
+++ b/include/meson.build
@@ -0,0 +1 @@
+configure_file(output: 'config.h', configuration: conf_data)
diff --git a/include/pool-buffer.h b/include/pool-buffer.h
new file mode 100644
index 00000000..cdebd64d
--- /dev/null
+++ b/include/pool-buffer.h
@@ -0,0 +1,21 @@
+#ifndef _SWAY_BUFFERS_H
+#define _SWAY_BUFFERS_H
+#include <cairo/cairo.h>
+#include <pango/pangocairo.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <wayland-client.h>
+
+struct pool_buffer {
+ struct wl_buffer *buffer;
+ cairo_surface_t *surface;
+ cairo_t *cairo;
+ PangoContext *pango;
+ uint32_t width, height;
+ bool busy;
+};
+
+struct pool_buffer *get_next_buffer(struct wl_shm *shm,
+ struct pool_buffer pool[static 2], uint32_t width, uint32_t height);
+
+#endif
diff --git a/meson.build b/meson.build
index f27ac451..b681f43a 100644
--- a/meson.build
+++ b/meson.build
@@ -27,7 +27,10 @@ wayland_client = dependency('wayland-client')
wayland_egl = dependency('wayland-egl')
wayland_protos = dependency('wayland-protocols')
xkbcommon = dependency('xkbcommon')
+cairo = dependency('cairo')
pango = dependency('pango')
+pangocairo = dependency('pangocairo')
+gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false)
pixman = dependency('pixman-1')
libcap = dependency('libcap')
libinput = dependency('libinput')
@@ -35,6 +38,12 @@ math = cc.find_library('m')
git = find_program('git', required: false)
a2x = find_program('a2x', required: false)
+conf_data = configuration_data()
+
+if gdk_pixbuf.found()
+ conf_data.set('HAVE_GDK_PIXBUF', true)
+endif
+
if a2x.found()
mandir = get_option('mandir')
man_files = [
@@ -85,9 +94,13 @@ add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c')
sway_inc = include_directories('include')
+subdir('include')
+subdir('protocols')
subdir('common')
subdir('sway')
subdir('swaymsg')
+subdir('client')
+subdir('swaybg')
config = configuration_data()
config.set('sysconfdir', join_paths(prefix, sysconfdir))
diff --git a/protocols/meson.build b/protocols/meson.build
new file mode 100644
index 00000000..1fda600e
--- /dev/null
+++ b/protocols/meson.build
@@ -0,0 +1,37 @@
+wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
+
+wayland_scanner = find_program('wayland-scanner')
+
+wayland_scanner_code = generator(
+ wayland_scanner,
+ output: '@BASENAME@-protocol.c',
+ arguments: ['code', '@INPUT@', '@OUTPUT@'],
+)
+
+wayland_scanner_client = generator(
+ wayland_scanner,
+ output: '@BASENAME@-client-protocol.h',
+ arguments: ['client-header', '@INPUT@', '@OUTPUT@'],
+)
+
+protocols = [
+ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
+ ['wlr-layer-shell-unstable-v1.xml']
+]
+
+wl_protos_src = []
+wl_protos_headers = []
+
+foreach p : protocols
+ xml = join_paths(p)
+ wl_protos_src += wayland_scanner_code.process(xml)
+ wl_protos_headers += wayland_scanner_client.process(xml)
+endforeach
+
+lib_wl_protos = static_library('wl_protos', wl_protos_src + wl_protos_headers,
+ dependencies: [wayland_client]) # for the include directory
+
+sway_protos = declare_dependency(
+ link_with: lib_wl_protos,
+ sources: wl_protos_headers,
+)
diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml
new file mode 100644
index 00000000..3181c0bb
--- /dev/null
+++ b/protocols/wlr-layer-shell-unstable-v1.xml
@@ -0,0 +1,281 @@
+<?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="1">
+ <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.
+
+ 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"/>
+ <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>
+ </interface>
+
+ <interface name="zwlr_layer_surface_v1" version="1">
+ <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 (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.
+ </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 orthoginal 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 of the surface
+ 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 an
+ edge, rather than a corner. The zone is the number of surface-local
+ coordinates from the edge that are considered 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 excluzive zone. If set to -1, the surface
+ indicates that it would not like to be moved to accomodate 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>
+
+ <request name="set_keyboard_interactivity">
+ <description summary="requests keyboard events">
+ Set to 1 to request that the seat send keyboard events to this layer
+ surface. For layers below the shell surface layer, the seat will use
+ normal focus semantics. For layers above the shell surface layers, the
+ seat will always give exclusive keyboard focus to the top-most layer
+ which has keyboard interactivity set to true.
+
+ 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.
+
+ Events is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="keyboard_interactivity" type="uint"/>
+ </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"/>
+ </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>
+ </interface>
+</protocol>
diff --git a/swaybg/main.c b/swaybg/main.c
index 2fdd4220..f431526c 100644
--- a/swaybg/main.c
+++ b/swaybg/main.c
@@ -1,44 +1,61 @@
-#include "wayland-desktop-shell-client-protocol.h"
+#include <assert.h>
+#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
-#include <ctype.h>
-#include <wayland-client.h>
-#include <time.h>
#include <string.h>
-#include "client/window.h"
-#include "client/registry.h"
-#include "client/cairo.h"
-#include "log.h"
-#include "list.h"
+#include <time.h>
+#include <wayland-client.h>
+#include <wlr/util/log.h>
+#include "pool-buffer.h"
+#include "cairo.h"
#include "util.h"
+#include "wlr-layer-shell-unstable-v1-client-protocol.h"
-list_t *surfaces;
-struct registry *registry;
+enum background_mode {
+ BACKGROUND_MODE_STRETCH,
+ BACKGROUND_MODE_FILL,
+ BACKGROUND_MODE_FIT,
+ BACKGROUND_MODE_CENTER,
+ BACKGROUND_MODE_TILE,
+ BACKGROUND_MODE_SOLID_COLOR,
+};
-enum scaling_mode {
- SCALING_MODE_STRETCH,
- SCALING_MODE_FILL,
- SCALING_MODE_FIT,
- SCALING_MODE_CENTER,
- SCALING_MODE_TILE,
+struct swaybg_args {
+ int output_idx;
+ const char *path;
+ enum background_mode mode;
};
-void sway_terminate(int exit_code) {
- int i;
- for (i = 0; i < surfaces->length; ++i) {
- struct window *window = surfaces->items[i];
- window_teardown(window);
- }
- list_free(surfaces);
- registry_teardown(registry);
- exit(exit_code);
-}
+struct swaybg_context {
+ uint32_t color;
+ cairo_surface_t *image;
+};
+
+struct swaybg_state {
+ const struct swaybg_args *args;
+ struct swaybg_context context;
+
+ struct wl_display *display;
+ struct wl_compositor *compositor;
+ struct zwlr_layer_shell_v1 *layer_shell;
+ struct wl_shm *shm;
+
+ struct wl_output *output;
+ struct wl_surface *surface;
+ struct zwlr_layer_surface_v1 *layer_surface;
+
+ bool run_display;
+ uint32_t width, height;
+ struct pool_buffer buffers[2];
+ struct pool_buffer *current_buffer;
+};
bool is_valid_color(const char *color) {
int len = strlen(color);
if (len != 7 || color[0] != '#') {
- sway_log(L_ERROR, "%s is not a valid color for swaybg. Color should be specified as #rrggbb (no alpha).", color);
+ wlr_log(L_ERROR, "%s is not a valid color for swaybg. "
+ "Color should be specified as #rrggbb (no alpha).", color);
return false;
}
@@ -52,163 +69,245 @@ bool is_valid_color(const char *color) {
return true;
}
-int main(int argc, const char **argv) {
- init_log(L_INFO);
- surfaces = create_list();
- registry = registry_poll();
+static void render_image(struct swaybg_state *state) {
+ cairo_t *cairo = state->current_buffer->cairo;
+ cairo_surface_t *image = state->context.image;
+ double width = cairo_image_surface_get_width(image);
+ double height = cairo_image_surface_get_height(image);
+ int wwidth = state->width;
+ int wheight = state->height;
- if (argc != 4) {
- sway_abort("Do not run this program manually. See man 5 sway and look for output options.");
- }
+ switch (state->args->mode) {
+ case BACKGROUND_MODE_STRETCH:
+ cairo_scale(cairo, (double)wwidth / width, (double)wheight / height);
+ cairo_set_source_surface(cairo, image, 0, 0);
+ break;
+ case BACKGROUND_MODE_FILL: {
+ double window_ratio = (double)wwidth / wheight;
+ double bg_ratio = width / height;
- if (!registry->desktop_shell) {
- sway_abort("swaybg requires the compositor to support the desktop-shell extension.");
+ if (window_ratio > bg_ratio) {
+ double scale = (double)wwidth / width;
+ cairo_scale(cairo, scale, scale);
+ cairo_set_source_surface(cairo, image,
+ 0, (double)wheight / 2 / scale - height / 2);
+ } else {
+ double scale = (double)wheight / height;
+ cairo_scale(cairo, scale, scale);
+ cairo_set_source_surface(cairo, image,
+ (double)wwidth / 2 / scale - width / 2, 0);
+ }
+ break;
}
+ case BACKGROUND_MODE_FIT: {
+ double window_ratio = (double)wwidth / wheight;
+ double bg_ratio = width / height;
- int desired_output = atoi(argv[1]);
- sway_log(L_INFO, "Using output %d of %d", desired_output, registry->outputs->length);
- int i;
- struct output_state *output = registry->outputs->items[desired_output];
- struct window *window = window_setup(registry,
- output->width, output->height, output->scale, false);
- if (!window) {
- sway_abort("Failed to create surfaces.");
- }
- desktop_shell_set_background(registry->desktop_shell, output->output, window->surface);
- window_make_shell(window);
- list_add(surfaces, window);
-
- if (strcmp(argv[3], "solid_color") == 0 && is_valid_color(argv[2])) {
- cairo_set_source_u32(window->cairo, parse_color(argv[2]));
- cairo_paint(window->cairo);
- window_render(window);
- } else {
-#ifdef WITH_GDK_PIXBUF
- GError *err = NULL;
- GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(argv[2], &err);
- if (!pixbuf) {
- sway_abort("Failed to load background image.");
- }
- cairo_surface_t *image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
- g_object_unref(pixbuf);
-#else
- cairo_surface_t *image = cairo_image_surface_create_from_png(argv[2]);
-#endif //WITH_GDK_PIXBUF
- if (!image) {
- sway_abort("Failed to read background image.");
- }
- if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
- sway_abort("Failed to read background image: %s."
-#ifndef WITH_GDK_PIXBUF
- "\nSway was compiled without gdk_pixbuf support, so only"
- "\nPNG images can be loaded. This is the likely cause."
-#endif //WITH_GDK_PIXBUF
- , cairo_status_to_string(cairo_surface_status(image)));
- }
- double width = cairo_image_surface_get_width(image);
- double height = cairo_image_surface_get_height(image);
-
- const char *scaling_mode_str = argv[3];
- enum scaling_mode scaling_mode = SCALING_MODE_STRETCH;
- if (strcmp(scaling_mode_str, "stretch") == 0) {
- scaling_mode = SCALING_MODE_STRETCH;
- } else if (strcmp(scaling_mode_str, "fill") == 0) {
- scaling_mode = SCALING_MODE_FILL;
- } else if (strcmp(scaling_mode_str, "fit") == 0) {
- scaling_mode = SCALING_MODE_FIT;
- } else if (strcmp(scaling_mode_str, "center") == 0) {
- scaling_mode = SCALING_MODE_CENTER;
- } else if (strcmp(scaling_mode_str, "tile") == 0) {
- scaling_mode = SCALING_MODE_TILE;
+ if (window_ratio > bg_ratio) {
+ double scale = (double)wheight / height;
+ cairo_scale(cairo, scale, scale);
+ cairo_set_source_surface(cairo, image,
+ (double)wwidth / 2 / scale - width / 2, 0);
} else {
- sway_abort("Unsupported scaling mode: %s", scaling_mode_str);
+ double scale = (double)wwidth / width;
+ cairo_scale(cairo, scale, scale);
+ cairo_set_source_surface(cairo, image,
+ 0, (double)wheight / 2 / scale - height / 2);
}
+ break;
+ }
+ case BACKGROUND_MODE_CENTER:
+ cairo_set_source_surface(cairo, image,
+ (double)wwidth / 2 - width / 2,
+ (double)wheight / 2 - height / 2);
+ break;
+ case BACKGROUND_MODE_TILE: {
+ cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
+ cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
+ cairo_set_source(cairo, pattern);
+ break;
+ }
+ case BACKGROUND_MODE_SOLID_COLOR:
+ assert(0);
+ break;
+ }
+ cairo_paint(cairo);
+}
+
+static void render_frame(struct swaybg_state *state) {
+ state->current_buffer = get_next_buffer(state->shm,
+ state->buffers, state->width, state->height);
+ cairo_t *cairo = state->current_buffer->cairo;
+
+ switch (state->args->mode) {
+ case BACKGROUND_MODE_SOLID_COLOR:
+ cairo_set_source_u32(cairo, state->context.color);
+ cairo_paint(cairo);
+ break;
+ default:
+ render_image(state);
+ break;
+ }
+
+ wl_surface_attach(state->surface, state->current_buffer->buffer, 0, 0);
+ wl_surface_damage(state->surface, 0, 0, state->width, state->height);
+ wl_surface_commit(state->surface);
+}
- int wwidth = window->width * window->scale;
- int wheight = window->height * window->scale;
-
- for (i = 0; i < surfaces->length; ++i) {
- struct window *window = surfaces->items[i];
- if (window_prerender(window) && window->cairo) {
- switch (scaling_mode) {
- case SCALING_MODE_STRETCH:
- cairo_scale(window->cairo,
- (double) wwidth / width,
- (double) wheight / height);
- cairo_set_source_surface(window->cairo, image, 0, 0);
- break;
- case SCALING_MODE_FILL:
- {
- double window_ratio = (double) wwidth / wheight;
- double bg_ratio = width / height;
-
- if (window_ratio > bg_ratio) {
- double scale = (double) wwidth / width;
- cairo_scale(window->cairo, scale, scale);
- cairo_set_source_surface(window->cairo, image,
- 0,
- (double) wheight/2 / scale - height/2);
- } else {
- double scale = (double) wheight / height;
- cairo_scale(window->cairo, scale, scale);
- cairo_set_source_surface(window->cairo, image,
- (double) wwidth/2 / scale - width/2,
- 0);
- }
- break;
- }
- case SCALING_MODE_FIT:
- {
- double window_ratio = (double) wwidth / wheight;
- double bg_ratio = width / height;
-
- if (window_ratio > bg_ratio) {
- double scale = (double) wheight / height;
- cairo_scale(window->cairo, scale, scale);
- cairo_set_source_surface(window->cairo, image,
- (double) wwidth/2 / scale - width/2,
- 0);
- } else {
- double scale = (double) wwidth / width;
- cairo_scale(window->cairo, scale, scale);
- cairo_set_source_surface(window->cairo, image,
- 0,
- (double) wheight/2 / scale - height/2);
- }
- break;
- }
- case SCALING_MODE_CENTER:
- cairo_set_source_surface(window->cairo, image,
- (double) wwidth/2 - width/2,
- (double) wheight/2 - height/2);
- break;
- case SCALING_MODE_TILE:
- {
- cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
- cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
- cairo_set_source(window->cairo, pattern);
- break;
- }
- default:
- sway_abort("Scaling mode '%s' not implemented yet!", scaling_mode_str);
- }
-
- cairo_paint(window->cairo);
-
- window_render(window);
- }
+static bool prepare_context(struct swaybg_state *state) {
+ if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) {
+ state->context.color = parse_color(state->args->path);
+ return is_valid_color(state->args->path);
+ }
+#ifdef HAVE_GDK_PIXBUF
+ GError *err = NULL;
+ GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(state->args->path, &err);
+ if (!pixbuf) {
+ wlr_log(L_ERROR, "Failed to load background image.");
+ return false;
+ }
+ state->context.image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
+ g_object_unref(pixbuf);
+#else
+ state->context.image = cairo_image_surface_create_from_png(
+ state->args->path);
+#endif //HAVE_GDK_PIXBUF
+ if (!state->context.image) {
+ wlr_log(L_ERROR, "Failed to read background image.");
+ return false;
+ }
+ if (cairo_surface_status(state->context.image) != CAIRO_STATUS_SUCCESS) {
+ wlr_log(L_ERROR, "Failed to read background image: %s."
+#ifndef HAVE_GDK_PIXBUF
+ "\nSway was compiled without gdk_pixbuf support, so only"
+ "\nPNG images can be loaded. This is the likely cause."
+#endif //HAVE_GDK_PIXBUF
+ , cairo_status_to_string(
+ cairo_surface_status(state->context.image)));
+ return false;
+ }
+ return true;
+}
+
+static void layer_surface_configure(void *data,
+ struct zwlr_layer_surface_v1 *surface,
+ uint32_t serial, uint32_t width, uint32_t height) {
+ struct swaybg_state *state = data;
+ state->width = width;
+ state->height = height;
+ zwlr_layer_surface_v1_ack_configure(surface, serial);
+ render_frame(state);
+}
+
+static void layer_surface_closed(void *data,
+ struct zwlr_layer_surface_v1 *surface) {
+ struct swaybg_state *state = data;
+ zwlr_layer_surface_v1_destroy(state->layer_surface);
+ wl_surface_destroy(state->surface);
+ state->run_display = false;
+}
+
+struct zwlr_layer_surface_v1_listener layer_surface_listener = {
+ .configure = layer_surface_configure,
+ .closed = layer_surface_closed,
+};
+
+static void handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version) {
+ struct swaybg_state *state = data;
+ if (strcmp(interface, wl_compositor_interface.name) == 0) {
+ state->compositor = wl_registry_bind(registry, name,
+ &wl_compositor_interface, 1);
+ } else if (strcmp(interface, wl_shm_interface.name) == 0) {
+ state->shm = wl_registry_bind(registry, name,
+ &wl_shm_interface, 1);
+ } else if (strcmp(interface, wl_output_interface.name) == 0) {
+ static int output_idx = 0;
+ if (output_idx == state->args->output_idx) {
+ state->output = wl_registry_bind(registry, name,
+ &wl_output_interface, 1);
}
+ output_idx++;
+ } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
+ state->layer_shell = wl_registry_bind(
+ registry, name, &zwlr_layer_shell_v1_interface, 1);
+ }
+}
+
+static void handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name) {
+ // who cares
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = handle_global,
+ .global_remove = handle_global_remove,
+};
- cairo_surface_destroy(image);
+int main(int argc, const char **argv) {
+ struct swaybg_args args = {0};
+ struct swaybg_state state = {0};
+ state.args = &args;
+ wlr_log_init(L_DEBUG, NULL);
+
+ if (argc != 4) {
+ wlr_log(L_ERROR, "Do not run this program manually. "
+ "See man 5 sway and look for output options.");
+ return 1;
}
+ args.output_idx = atoi(argv[1]);
+ args.path = argv[2];
+
+ args.mode = BACKGROUND_MODE_STRETCH;
+ if (strcmp(argv[3], "stretch") == 0) {
+ args.mode = BACKGROUND_MODE_STRETCH;
+ } else if (strcmp(argv[3], "fill") == 0) {
+ args.mode = BACKGROUND_MODE_FILL;
+ } else if (strcmp(argv[3], "fit") == 0) {
+ args.mode = BACKGROUND_MODE_FIT;
+ } else if (strcmp(argv[3], "center") == 0) {
+ args.mode = BACKGROUND_MODE_CENTER;
+ } else if (strcmp(argv[3], "tile") == 0) {
+ args.mode = BACKGROUND_MODE_TILE;
+ } else if (strcmp(argv[3], "solid_color") == 0) {
+ args.mode = BACKGROUND_MODE_SOLID_COLOR;
+ } else {
+ wlr_log(L_ERROR, "Unsupported background mode: %s", argv[3]);
+ return 1;
+ }
+
+ if (!prepare_context(&state)) {
+ return 1;
+ }
+
+ assert(state.display = wl_display_connect(NULL));
+
+ struct wl_registry *registry = wl_display_get_registry(state.display);
+ wl_registry_add_listener(registry, &registry_listener, &state);
+ wl_display_roundtrip(state.display);
+ assert(state.compositor && state.layer_shell && state.output && state.shm);
+
+ assert(state.surface = wl_compositor_create_surface(state.compositor));
+
+ state.layer_surface = zwlr_layer_shell_v1_get_layer_surface(
+ state.layer_shell, state.surface, state.output,
+ ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper");
+ assert(state.layer_surface);
- while (wl_display_dispatch(registry->display) != -1);
+ zwlr_layer_surface_v1_set_size(state.layer_surface, 0, 0);
+ zwlr_layer_surface_v1_set_anchor(state.layer_surface,
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
+ zwlr_layer_surface_v1_add_listener(state.layer_surface,
+ &layer_surface_listener, &state);
+ state.run_display = true;
+ wl_surface_commit(state.surface);
+ wl_display_roundtrip(state.display);
- for (i = 0; i < surfaces->length; ++i) {
- struct window *window = surfaces->items[i];
- window_teardown(window);
+ while (wl_display_dispatch(state.display) != -1 && state.run_display) {
+ // This space intentionally left blank
}
- list_free(surfaces);
- registry_teardown(registry);
return 0;
}
diff --git a/swaybg/meson.build b/swaybg/meson.build
new file mode 100644
index 00000000..5e10f3c7
--- /dev/null
+++ b/swaybg/meson.build
@@ -0,0 +1,18 @@
+executable(
+ 'swaybg',
+ 'main.c',
+ include_directories: [sway_inc],
+ dependencies: [
+ cairo,
+ gdk_pixbuf,
+ jsonc,
+ math,
+ pango,
+ pangocairo,
+ sway_protos,
+ wayland_client,
+ wlroots,
+ ],
+ link_with: [lib_sway_common, lib_sway_client],
+ install: true
+)
diff --git a/wayland/pango.c b/wayland/pango.c
deleted file mode 100644
index f9eec98c..00000000
--- a/wayland/pango.c
+++ /dev/null
@@ -1,73 +0,0 @@
-#include <cairo/cairo.h>
-#include <pango/pangocairo.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include "log.h"
-
-PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text,
- int32_t scale, bool markup) {
- PangoLayout *layout = pango_cairo_create_layout(cairo);
- PangoAttrList *attrs;
- if (markup) {
- char *buf;
- pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, NULL);
- pango_layout_set_markup(layout, buf, -1);
- free(buf);
- } else {
- attrs = pango_attr_list_new();
- pango_layout_set_text(layout, text, -1);
- }
- pango_attr_list_insert(attrs, pango_attr_scale_new(scale));
- PangoFontDescription *desc = pango_font_description_from_string(font);
- pango_layout_set_font_description(layout, desc);
- pango_layout_set_single_paragraph_mode(layout, 1);
- pango_layout_set_attributes(layout, attrs);
- pango_attr_list_unref(attrs);
- pango_font_description_free(desc);
- return layout;
-}
-
-void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
- int32_t scale, bool markup, const char *fmt, ...) {
- char *buf = malloc(2048);
-
- va_list args;
- va_start(args, fmt);
- if (vsnprintf(buf, 2048, fmt, args) >= 2048) {
- strcpy(buf, "[buffer overflow]");
- }
- va_end(args);
-
- PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
- pango_cairo_update_layout(cairo, layout);
-
- pango_layout_get_pixel_size(layout, width, height);
-
- g_object_unref(layout);
-
- free(buf);
-}
-
-void pango_printf(cairo_t *cairo, const char *font, int32_t scale, bool markup, const char *fmt, ...) {
- char *buf = malloc(2048);
-
- va_list args;
- va_start(args, fmt);
- if (vsnprintf(buf, 2048, fmt, args) >= 2048) {
- strcpy(buf, "[buffer overflow]");
- }
- va_end(args);
-
- PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
- pango_cairo_update_layout(cairo, layout);
-
- pango_cairo_show_layout(cairo, layout);
-
- g_object_unref(layout);
-
- free(buf);
-}
diff --git a/wayland/registry.c b/wayland/registry.c
deleted file mode 100644
index bbb43ad9..00000000
--- a/wayland/registry.c
+++ /dev/null
@@ -1,293 +0,0 @@
-#include <wayland-client.h>
-#include <xkbcommon/xkbcommon.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/timerfd.h>
-#include "wayland-desktop-shell-client-protocol.h"
-#include "wayland-swaylock-client-protocol.h"
-#include "client/registry.h"
-#include "stringop.h"
-#include "log.h"
-
-static void display_handle_mode(void *data, struct wl_output *wl_output,
- uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
- struct output_state *state = data;
- if (flags & WL_OUTPUT_MODE_CURRENT) {
- state->flags = flags;
- state->width = width;
- state->height = height;
- sway_log(L_DEBUG, "Got mode %dx%d:0x%X for output %p",
- width, height, flags, data);
- }
-}
-
-static void display_handle_geometry(void *data, struct wl_output *wl_output,
- int32_t x, int32_t y, int32_t physical_width, int32_t physical_height,
- int32_t subpixel, const char *make, const char *model, int32_t transform) {
- // this space intentionally left blank
-}
-
-static void display_handle_done(void *data, struct wl_output *wl_output) {
- // this space intentionally left blank
-}
-
-static void display_handle_scale(void *data, struct wl_output *wl_output, int32_t factor) {
- struct output_state *state = data;
- state->scale = factor;
- sway_log(L_DEBUG, "Got scale factor %d for output %p", factor, data);
-}
-
-static const struct wl_output_listener output_listener = {
- .mode = display_handle_mode,
- .geometry = display_handle_geometry,
- .done = display_handle_done,
- .scale = display_handle_scale
-};
-
-const char *XKB_MASK_NAMES[MASK_LAST] = {
- XKB_MOD_NAME_SHIFT,
- XKB_MOD_NAME_CAPS,
- XKB_MOD_NAME_CTRL,
- XKB_MOD_NAME_ALT,
- "Mod2",
- "Mod3",
- XKB_MOD_NAME_LOGO,
- "Mod5",
-};
-
-const enum mod_bit XKB_MODS[MASK_LAST] = {
- MOD_SHIFT,
- MOD_CAPS,
- MOD_CTRL,
- MOD_ALT,
- MOD_MOD2,
- MOD_MOD3,
- MOD_LOGO,
- MOD_MOD5
-};
-
-static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
- uint32_t format, int fd, uint32_t size) {
- // Keyboard errors are abort-worthy because you wouldn't be able to unlock your screen otherwise.
-
- struct registry *registry = data;
- if (!data) {
- close(fd);
- return;
- }
-
- if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
- close(fd);
- sway_abort("Unknown keymap format %d, aborting", format);
- }
-
- char *map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
- if (map_str == MAP_FAILED) {
- close(fd);
- sway_abort("Unable to initialized shared keyboard memory, aborting");
- }
-
- struct xkb_keymap *keymap = xkb_keymap_new_from_string(registry->input->xkb.context,
- map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
- munmap(map_str, size);
- close(fd);
-
- if (!keymap) {
- sway_abort("Failed to compile keymap, aborting");
- }
-
- struct xkb_state *state = xkb_state_new(keymap);
- if (!state) {
- xkb_keymap_unref(keymap);
- sway_abort("Failed to create xkb state, aborting");
- }
-
- xkb_keymap_unref(registry->input->xkb.keymap);
- xkb_state_unref(registry->input->xkb.state);
- registry->input->xkb.keymap = keymap;
- registry->input->xkb.state = state;
-
- int i;
- for (i = 0; i < MASK_LAST; ++i) {
- registry->input->xkb.masks[i] = 1 << xkb_keymap_mod_get_index(registry->input->xkb.keymap, XKB_MASK_NAMES[i]);
- }
-}
-
-static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
- // this space intentionally left blank
-}
-
-static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, struct wl_surface *surface) {
- // this space intentionally left blank
-}
-
-static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, uint32_t time, uint32_t key, uint32_t state_w) {
- struct registry *registry = data;
- enum wl_keyboard_key_state state = state_w;
-
- if (!registry->input->xkb.state) {
- return;
- }
-
- xkb_keysym_t sym = xkb_state_key_get_one_sym(registry->input->xkb.state, key + 8);
- registry->input->sym = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? sym : XKB_KEY_NoSymbol);
- registry->input->code = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? key + 8 : 0);
- uint32_t codepoint = xkb_state_key_get_utf32(registry->input->xkb.state, registry->input->code);
- if (registry->input->notify) {
- registry->input->notify(state, sym, key, codepoint);
- }
-}
-
-static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
- uint32_t mods_locked, uint32_t group) {
- struct registry *registry = data;
-
- if (!registry->input->xkb.keymap) {
- return;
- }
-
- xkb_state_update_mask(registry->input->xkb.state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
- xkb_mod_mask_t mask = xkb_state_serialize_mods(registry->input->xkb.state,
- XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED);
-
- registry->input->modifiers = 0;
- for (uint32_t i = 0; i < MASK_LAST; ++i) {
- if (mask & registry->input->xkb.masks[i]) {
- registry->input->modifiers |= XKB_MODS[i];
- }
- }
-}
-
-static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *keyboard,
- int32_t rate, int32_t delay) {
- // this space intentionally left blank
-}
-
-static const struct wl_keyboard_listener keyboard_listener = {
- .keymap = keyboard_handle_keymap,
- .enter = keyboard_handle_enter,
- .leave = keyboard_handle_leave,
- .key = keyboard_handle_key,
- .modifiers = keyboard_handle_modifiers,
- .repeat_info = keyboard_handle_repeat_info
-};
-
-static void seat_handle_capabilities(void *data, struct wl_seat *seat,
- enum wl_seat_capability caps) {
- struct registry *reg = data;
-
- if ((caps & WL_SEAT_CAPABILITY_POINTER) && !reg->pointer) {
- reg->pointer = wl_seat_get_pointer(reg->seat);
- } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && reg->pointer) {
- wl_pointer_destroy(reg->pointer);
- reg->pointer = NULL;
- }
-
- if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !reg->keyboard) {
- reg->keyboard = wl_seat_get_keyboard(reg->seat);
- wl_keyboard_add_listener(reg->keyboard, &keyboard_listener, reg);
- } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && reg->keyboard) {
- wl_keyboard_destroy(reg->keyboard);
- reg->keyboard = NULL;
- }
-}
-
-static void seat_handle_name(void *data, struct wl_seat *seat, const char *name) {
- // this space intentionally left blank
-}
-
-static const struct wl_seat_listener seat_listener = {
- .capabilities = seat_handle_capabilities,
- .name = seat_handle_name,
-};
-
-static void registry_global(void *data, struct wl_registry *registry,
- uint32_t name, const char *interface, uint32_t version) {
- struct registry *reg = data;
-
- if (strcmp(interface, wl_compositor_interface.name) == 0) {
- reg->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, version);
- } else if (strcmp(interface, wl_shm_interface.name) == 0) {
- reg->shm = wl_registry_bind(registry, name, &wl_shm_interface, version);
- } else if (strcmp(interface, wl_shell_interface.name) == 0) {
- reg->shell = wl_registry_bind(registry, name, &wl_shell_interface, version);
- } else if (strcmp(interface, wl_seat_interface.name) == 0) {
- reg->seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
- wl_seat_add_listener(reg->seat, &seat_listener, reg);
- } else if (strcmp(interface, wl_output_interface.name) == 0) {
- struct wl_output *output = wl_registry_bind(registry, name, &wl_output_interface, version);
- struct output_state *ostate = malloc(sizeof(struct output_state));
- ostate->output = output;
- ostate->scale = 1;
- wl_output_add_listener(output, &output_listener, ostate);
- list_add(reg->outputs, ostate);
- } else if (strcmp(interface, desktop_shell_interface.name) == 0) {
- reg->desktop_shell = wl_registry_bind(registry, name, &desktop_shell_interface, version);
- } else if (strcmp(interface, lock_interface.name) == 0) {
- reg->swaylock = wl_registry_bind(registry, name, &lock_interface, version);
- }
-}
-
-static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) {
- // this space intentionally left blank
-}
-
-static const struct wl_registry_listener registry_listener = {
- .global = registry_global,
- .global_remove = registry_global_remove
-};
-
-struct registry *registry_poll(void) {
- struct registry *registry = malloc(sizeof(struct registry));
- memset(registry, 0, sizeof(struct registry));
- registry->outputs = create_list();
- registry->input = calloc(sizeof(struct input), 1);
- registry->input->xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
-
- registry->display = wl_display_connect(NULL);
- if (!registry->display) {
- sway_log(L_ERROR, "Error opening display");
- registry_teardown(registry);
- return NULL;
- }
-
- struct wl_registry *reg = wl_display_get_registry(registry->display);
- wl_registry_add_listener(reg, &registry_listener, registry);
- wl_display_dispatch(registry->display);
- wl_display_roundtrip(registry->display);
- wl_registry_destroy(reg);
-
- return registry;
-}
-
-void registry_teardown(struct registry *registry) {
- if (registry->pointer) {
- wl_pointer_destroy(registry->pointer);
- }
- if (registry->seat) {
- wl_seat_destroy(registry->seat);
- }
- if (registry->shell) {
- wl_shell_destroy(registry->shell);
- }
- if (registry->shm) {
- wl_shm_destroy(registry->shm);
- }
- if (registry->compositor) {
- wl_compositor_destroy(registry->compositor);
- }
- if (registry->display) {
- wl_display_disconnect(registry->display);
- }
- if (registry->outputs) {
- free_flat_list(registry->outputs);
- }
- free(registry);
-}
diff --git a/wayland/window.c b/wayland/window.c
deleted file mode 100644
index 8a506656..00000000
--- a/wayland/window.c
+++ /dev/null
@@ -1,177 +0,0 @@
-#include <wayland-client.h>
-#include <wayland-cursor.h>
-#include "wayland-xdg-shell-client-protocol.h"
-#include "wayland-desktop-shell-client-protocol.h"
-#include <cairo/cairo.h>
-#include <pango/pangocairo.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <errno.h>
-#include <sys/mman.h>
-#include "client/window.h"
-#include "client/buffer.h"
-#include "list.h"
-#include "log.h"
-
-static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
- uint32_t serial, struct wl_surface *surface, wl_fixed_t sx_w, wl_fixed_t sy_w) {
- struct window *window = data;
- if (window->registry->pointer) {
- struct wl_cursor_image *image = window->cursor.cursor->images[0];
- wl_pointer_set_cursor(pointer, serial, window->cursor.surface, image->hotspot_x, image->hotspot_y);
- }
-}
-
-static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
- uint32_t serial, struct wl_surface *surface) {
-}
-
-static void pointer_handle_motion(void *data, struct wl_pointer *pointer,
- uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w) {
- struct window *window = data;
-
- window->pointer_input.last_x = wl_fixed_to_int(sx_w);
- window->pointer_input.last_y = wl_fixed_to_int(sy_w);
-}
-
-static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
- uint32_t time, uint32_t button, uint32_t state_w) {
- struct window *window = data;
- struct pointer_input *input = &window->pointer_input;
-
- if (window->pointer_input.notify_button) {
- window->pointer_input.notify_button(window, input->last_x, input->last_y, button, state_w);
- }
-}
-
-static void pointer_handle_axis(void *data, struct wl_pointer *pointer,
- uint32_t time, uint32_t axis, wl_fixed_t value) {
- struct window *window = data;
- enum scroll_direction direction;
-
- switch (axis) {
- case 0:
- direction = wl_fixed_to_double(value) < 0 ? SCROLL_UP : SCROLL_DOWN;
- break;
- case 1:
- direction = wl_fixed_to_double(value) < 0 ? SCROLL_LEFT : SCROLL_RIGHT;
- break;
- default:
- sway_log(L_DEBUG, "Unexpected axis value on mouse scroll");
- return;
- }
-
- if (window->pointer_input.notify_scroll) {
- window->pointer_input.notify_scroll(window, direction);
- }
-}
-
-static const struct wl_pointer_listener pointer_listener = {
- .enter = pointer_handle_enter,
- .leave = pointer_handle_leave,
- .motion = pointer_handle_motion,
- .button = pointer_handle_button,
- .axis = pointer_handle_axis
-};
-
-void shell_surface_configure(void *data, struct wl_shell_surface *wl_shell_surface,
- uint32_t edges, int32_t width, int32_t height) {
- struct window *window = data;
- window->width = width;
- window->height = height;
-}
-
-static const struct wl_shell_surface_listener surface_listener = {
- .configure = shell_surface_configure
-};
-
-void window_make_shell(struct window *window) {
- window->shell_surface = wl_shell_get_shell_surface(window->registry->shell, window->surface);
- wl_shell_surface_add_listener(window->shell_surface, &surface_listener, window);
- wl_shell_surface_set_toplevel(window->shell_surface);
-}
-
-struct window *window_setup(struct registry *registry, uint32_t width, uint32_t height,
- int32_t scale, bool shell_surface) {
- struct window *window = malloc(sizeof(struct window));
- memset(window, 0, sizeof(struct window));
- window->width = width;
- window->height = height;
- window->scale = scale;
- window->registry = registry;
- window->font = "monospace 10";
-
- window->surface = wl_compositor_create_surface(registry->compositor);
- if (shell_surface) {
- window_make_shell(window);
- }
- if (registry->pointer) {
- wl_pointer_add_listener(registry->pointer, &pointer_listener, window);
- }
-
- get_next_buffer(window);
-
- if (registry->pointer) {
- char *cursor_theme = getenv("SWAY_CURSOR_THEME");
- if (!cursor_theme) {
- cursor_theme = "default";
- }
- char *cursor_size = getenv("SWAY_CURSOR_SIZE");
- if (!cursor_size) {
- cursor_size = "16";
- }
-
- sway_log(L_DEBUG, "Cursor scale: %d", scale);
- window->cursor.cursor_theme = wl_cursor_theme_load(cursor_theme,
- atoi(cursor_size) * scale, registry->shm);
- window->cursor.cursor = wl_cursor_theme_get_cursor(window->cursor.cursor_theme, "left_ptr");
- window->cursor.surface = wl_compositor_create_surface(registry->compositor);
-
- struct wl_cursor_image *image = window->cursor.cursor->images[0];
- struct wl_buffer *cursor_buf = wl_cursor_image_get_buffer(image);
- wl_surface_attach(window->cursor.surface, cursor_buf, 0, 0);
- wl_surface_set_buffer_scale(window->cursor.surface, scale);
- wl_surface_damage(window->cursor.surface, 0, 0,
- image->width, image->height);
- wl_surface_commit(window->cursor.surface);
- }
-
- return window;
-}
-
-static void frame_callback(void *data, struct wl_callback *callback, uint32_t time) {
- struct window *window = data;
- wl_callback_destroy(callback);
- window->frame_cb = NULL;
-}
-
-static const struct wl_callback_listener listener = {
- frame_callback
-};
-
-int window_prerender(struct window *window) {
- if (window->frame_cb) {
- return 0;
- }
-
- get_next_buffer(window);
- return 1;
-}
-
-int window_render(struct window *window) {
- window->frame_cb = wl_surface_frame(window->surface);
- wl_callback_add_listener(window->frame_cb, &listener, window);
-
- wl_surface_attach(window->surface, window->buffer->buffer, 0, 0);
- wl_surface_set_buffer_scale(window->surface, window->scale);
- wl_surface_damage(window->surface, 0, 0, window->width, window->height);
- wl_surface_commit(window->surface);
-
- return 1;
-}
-
-void window_teardown(struct window *window) {
- // TODO
-}