summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2018-03-28 15:25:33 -0400
committerLibravatar GitHub <noreply@github.com>2018-03-28 15:25:33 -0400
commit9070950eecded7bfa64e7bca3bb76b150ccc8b72 (patch)
tree509a9c669bf2679085e27a1ff1b0c95526abf14c
parentMerge pull request #1641 from swaywm/transformed-events (diff)
parentAddress review comments (diff)
downloadsway-9070950eecded7bfa64e7bca3bb76b150ccc8b72.tar.gz
sway-9070950eecded7bfa64e7bca3bb76b150ccc8b72.tar.zst
sway-9070950eecded7bfa64e7bca3bb76b150ccc8b72.zip
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 @@
1lib_sway_client = static_library(
2 'sway-client',
3 files(
4 'pool-buffer.c',
5 ),
6 dependencies: [
7 cairo,
8 gdk_pixbuf,
9 pango,
10 pangocairo,
11 wlroots,
12 wayland_client,
13 ],
14 link_with: [lib_sway_common],
15 include_directories: sway_inc
16)
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 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <wayland-client.h> 2#include <assert.h>
3#include <cairo/cairo.h> 3#include <cairo/cairo.h>
4#include <pango/pangocairo.h> 4#include <stdio.h>
5#include <stdlib.h> 5#include <stdlib.h>
6#include <string.h> 6#include <string.h>
7#include <stdio.h>
8#include <unistd.h>
9#include <errno.h>
10#include <sys/mman.h> 7#include <sys/mman.h>
11#include "client/buffer.h" 8#include <pango/pangocairo.h>
12#include "list.h" 9#include <unistd.h>
13#include "log.h" 10#include <wayland-client.h>
11#include "config.h"
12#include "pool-buffer.h"
14 13
15static int create_pool_file(size_t size, char **name) { 14static int create_pool_file(size_t size, char **name) {
16 static const char template[] = "sway-client-XXXXXX"; 15 static const char template[] = "sway-client-XXXXXX";
17 const char *path = getenv("XDG_RUNTIME_DIR"); 16 const char *path = getenv("XDG_RUNTIME_DIR");
18 if (!path) { 17 if (!path) {
19 return -1; 18 return -1;
20 } 19 }
21 20
@@ -42,7 +41,7 @@ static int create_pool_file(size_t size, char **name) {
42} 41}
43 42
44static void buffer_release(void *data, struct wl_buffer *wl_buffer) { 43static void buffer_release(void *data, struct wl_buffer *wl_buffer) {
45 struct buffer *buffer = data; 44 struct pool_buffer *buffer = data;
46 buffer->busy = false; 45 buffer->busy = false;
47} 46}
48 47
@@ -50,22 +49,17 @@ static const struct wl_buffer_listener buffer_listener = {
50 .release = buffer_release 49 .release = buffer_release
51}; 50};
52 51
53static struct buffer *create_buffer(struct window *window, struct buffer *buf, 52static struct pool_buffer *create_buffer(struct wl_shm *shm,
54 int32_t width, int32_t height, int32_t scale, uint32_t format) { 53 struct pool_buffer *buf, int32_t width, int32_t height,
55 54 uint32_t format) {
56 width *= scale;
57 height *= scale;
58 uint32_t stride = width * 4; 55 uint32_t stride = width * 4;
59 uint32_t size = stride * height; 56 uint32_t size = stride * height;
60 57
61 char *name; 58 char *name;
62 int fd = create_pool_file(size, &name); 59 int fd = create_pool_file(size, &name);
63 if (fd == -1) { 60 assert(fd);
64 sway_abort("Unable to allocate buffer");
65 return NULL; // never reached
66 }
67 void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 61 void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
68 struct wl_shm_pool *pool = wl_shm_create_pool(window->registry->shm, fd, size); 62 struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
69 buf->buffer = wl_shm_pool_create_buffer(pool, 0, 63 buf->buffer = wl_shm_pool_create_buffer(pool, 0,
70 width, height, stride, format); 64 width, height, stride, format);
71 wl_shm_pool_destroy(pool); 65 wl_shm_pool_destroy(pool);
@@ -85,7 +79,7 @@ static struct buffer *create_buffer(struct window *window, struct buffer *buf,
85 return buf; 79 return buf;
86} 80}
87 81
88static void destroy_buffer(struct buffer *buffer) { 82static void destroy_buffer(struct pool_buffer *buffer) {
89 if (buffer->buffer) { 83 if (buffer->buffer) {
90 wl_buffer_destroy(buffer->buffer); 84 wl_buffer_destroy(buffer->buffer);
91 } 85 }
@@ -98,37 +92,33 @@ static void destroy_buffer(struct buffer *buffer) {
98 if (buffer->pango) { 92 if (buffer->pango) {
99 g_object_unref(buffer->pango); 93 g_object_unref(buffer->pango);
100 } 94 }
101 memset(buffer, 0, sizeof(struct buffer)); 95 memset(buffer, 0, sizeof(struct pool_buffer));
102} 96}
103 97
104struct buffer *get_next_buffer(struct window *window) { 98struct pool_buffer *get_next_buffer(struct wl_shm *shm,
105 struct buffer *buffer = NULL; 99 struct pool_buffer pool[static 2], uint32_t width, uint32_t height) {
100 struct pool_buffer *buffer = NULL;
106 101
107 int i; 102 for (size_t i = 0; i < 2; ++i) {
108 for (i = 0; i < 2; ++i) { 103 if (pool[i].busy) {
109 if (window->buffers[i].busy) {
110 continue; 104 continue;
111 } 105 }
112 buffer = &window->buffers[i]; 106 buffer = &pool[i];
113 } 107 }
114 108
115 if (!buffer) { 109 if (!buffer) {
116 return NULL; 110 return NULL;
117 } 111 }
118 112
119 if (buffer->width != window->width || buffer->height != window->height) { 113 if (buffer->width != width || buffer->height != height) {
120 destroy_buffer(buffer); 114 destroy_buffer(buffer);
121 } 115 }
122 116
123 if (!buffer->buffer) { 117 if (!buffer->buffer) {
124 if (!create_buffer(window, buffer, 118 if (!create_buffer(shm, buffer, width, height,
125 window->width, window->height, window->scale,
126 WL_SHM_FORMAT_ARGB8888)) { 119 WL_SHM_FORMAT_ARGB8888)) {
127 return NULL; 120 return NULL;
128 } 121 }
129 } 122 }
130
131 window->cairo = buffer->cairo;
132 window->buffer = buffer;
133 return buffer; 123 return buffer;
134} 124}
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 @@
1#include "client/cairo.h" 1#include <stdint.h>
2#include <cairo/cairo.h>
3#include "cairo.h"
4#ifdef HAVE_GDK_PIXBUF
5#include <gdk-pixbuf/gdk-pixbuf.h>
6#endif
2 7
3void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { 8void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
4 cairo_set_source_rgba(cairo, 9 cairo_set_source_rgba(cairo,
@@ -8,45 +13,31 @@ void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
8 (color >> (0*8) & 0xFF) / 255.0); 13 (color >> (0*8) & 0xFF) / 255.0);
9} 14}
10 15
11cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height) { 16cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image,
17 int width, int height) {
12 int image_width = cairo_image_surface_get_width(image); 18 int image_width = cairo_image_surface_get_width(image);
13 int image_height = cairo_image_surface_get_height(image); 19 int image_height = cairo_image_surface_get_height(image);
14 20
15 cairo_surface_t *new = 21 cairo_surface_t *new =
16 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); 22 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
17
18 cairo_t *cairo = cairo_create(new); 23 cairo_t *cairo = cairo_create(new);
19 24 cairo_scale(cairo, (double)width / image_width,
20 cairo_scale(cairo, (double) width / image_width, (double) height / image_height); 25 (double)height / image_height);
21
22 cairo_set_source_surface(cairo, image, 0, 0); 26 cairo_set_source_surface(cairo, image, 0, 0);
23 cairo_paint(cairo);
24 27
28 cairo_paint(cairo);
25 cairo_destroy(cairo); 29 cairo_destroy(cairo);
26
27 return new; 30 return new;
28} 31}
29 32
30#ifdef WITH_GDK_PIXBUF 33#ifdef HAVE_GDK_PIXBUF
31#include <gdk-pixbuf/gdk-pixbuf.h>
32
33#ifndef GDK_PIXBUF_CHECK_VERSION
34#define GDK_PIXBUF_CHECK_VERSION(major,minor,micro) \
35 (GDK_PIXBUF_MAJOR > (major) || \
36 (GDK_PIXBUF_MAJOR == (major) && GDK_PIXBUF_MINOR > (minor)) || \
37 (GDK_PIXBUF_MAJOR == (major) && GDK_PIXBUF_MINOR == (minor) && \
38 GDK_PIXBUF_MICRO >= (micro)))
39#endif
40
41cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) { 34cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) {
42 int chan = gdk_pixbuf_get_n_channels(gdkbuf); 35 int chan = gdk_pixbuf_get_n_channels(gdkbuf);
43 if (chan < 3) return NULL; 36 if (chan < 3) {
37 return NULL;
38 }
44 39
45#if GDK_PIXBUF_CHECK_VERSION(2,32,0)
46 const guint8* gdkpix = gdk_pixbuf_read_pixels(gdkbuf); 40 const guint8* gdkpix = gdk_pixbuf_read_pixels(gdkbuf);
47#else
48 const guint8* gdkpix = gdk_pixbuf_get_pixels(gdkbuf);
49#endif
50 if (!gdkpix) { 41 if (!gdkpix) {
51 return NULL; 42 return NULL;
52 } 43 }
@@ -101,7 +92,9 @@ cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdk
101 * ------ 92 * ------
102 * tested as equal to lround(z/255.0) for uint z in [0..0xfe02] 93 * tested as equal to lround(z/255.0) for uint z in [0..0xfe02]
103 */ 94 */
104#define PREMUL_ALPHA(x,a,b,z) G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } G_STMT_END 95#define PREMUL_ALPHA(x,a,b,z) \
96 G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } \
97 G_STMT_END
105 int i; 98 int i;
106 for (i = h; i; --i) { 99 for (i = h; i; --i) {
107 const guint8 *gp = gdkpix; 100 const guint8 *gp = gdkpix;
@@ -131,4 +124,4 @@ cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdk
131 cairo_surface_mark_dirty(cs); 124 cairo_surface_mark_dirty(cs);
132 return cs; 125 return cs;
133} 126}
134#endif //WITH_GDK_PIXBUF 127#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 @@
1lib_sway_common = static_library('sway-common', 1deps = [
2 cairo,
3 wlroots
4]
5
6if gdk_pixbuf.found()
7 deps += [gdk_pixbuf]
8endif
9
10lib_sway_common = static_library(
11 'sway-common',
2 files( 12 files(
13 'cairo.c',
14 'ipc-client.c',
3 'log.c', 15 'log.c',
4 'list.c', 16 'list.c',
5 'util.c',
6 'stringop.c',
7 'readline.c', 17 'readline.c',
8 'ipc-client.c' 18 'stringop.c',
19 'util.c'
9 ), 20 ),
10 dependencies: [ wlroots ], 21 dependencies: deps,
11 include_directories: sway_inc 22 include_directories: sway_inc
12) 23)
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 @@
1#ifndef _SWAY_CAIRO_H 1#ifndef _SWAY_CAIRO_H
2#define _SWAY_CAIRO_H 2#define _SWAY_CAIRO_H
3
4#include <stdint.h> 3#include <stdint.h>
5#include <cairo/cairo.h> 4#include <cairo/cairo.h>
6 5
7void cairo_set_source_u32(cairo_t *cairo, uint32_t color); 6void cairo_set_source_u32(cairo_t *cairo, uint32_t color);
8 7
9cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height); 8cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image,
9 int width, int height);
10 10
11#ifdef WITH_GDK_PIXBUF 11#include "config.h"
12#ifdef HAVE_GDK_PIXBUF
12#include <gdk-pixbuf/gdk-pixbuf.h> 13#include <gdk-pixbuf/gdk-pixbuf.h>
13 14
14cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf); 15cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(
16 const GdkPixbuf *gdkbuf);
15#endif //WITH_GDK_PIXBUF 17#endif //WITH_GDK_PIXBUF
16 18
17#endif 19#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 @@
1#ifndef _BUFFER_H
2#define _BUFFER_H
3
4#include "client/window.h"
5
6struct buffer *get_next_buffer(struct window *state);
7
8#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 @@
1#ifndef _SWAY_CLIENT_PANGO_H
2#define _SWAY_CLIENT_PANGO_H
3
4#include <cairo/cairo.h>
5#include <pango/pangocairo.h>
6#include <stdarg.h>
7#include <stdbool.h>
8#include <stdint.h>
9
10PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text,
11 int32_t scale, bool markup);
12void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
13 int32_t scale, bool markup, const char *fmt, ...);
14void pango_printf(cairo_t *cairo, const char *font, int32_t scale, bool markup, const char *fmt, ...);
15
16#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 @@
1#ifndef _SWAY_CLIENT_REGISTRY_H
2#define _SWAY_CLIENT_REGISTRY_H
3
4#include <wayland-client.h>
5#include <xkbcommon/xkbcommon.h>
6#include "wayland-desktop-shell-client-protocol.h"
7#include "wayland-swaylock-client-protocol.h"
8#include "list.h"
9
10enum mod_bit {
11 MOD_SHIFT = 1<<0,
12 MOD_CAPS = 1<<1,
13 MOD_CTRL = 1<<2,
14 MOD_ALT = 1<<3,
15 MOD_MOD2 = 1<<4,
16 MOD_MOD3 = 1<<5,
17 MOD_LOGO = 1<<6,
18 MOD_MOD5 = 1<<7,
19};
20
21enum mask {
22 MASK_SHIFT,
23 MASK_CAPS,
24 MASK_CTRL,
25 MASK_ALT,
26 MASK_MOD2,
27 MASK_MOD3,
28 MASK_LOGO,
29 MASK_MOD5,
30 MASK_LAST
31};
32
33struct output_state {
34 struct wl_output *output;
35 uint32_t flags;
36 uint32_t width, height;
37 uint32_t scale;
38};
39
40struct xkb {
41 struct xkb_state *state;
42 struct xkb_context *context;
43 struct xkb_keymap *keymap;
44 xkb_mod_mask_t masks[MASK_LAST];
45};
46
47struct input {
48 struct xkb xkb;
49
50 xkb_keysym_t sym;
51 uint32_t code;
52 uint32_t last_code;
53 uint32_t modifiers;
54
55 void (*notify)(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint);
56};
57
58struct registry {
59 struct wl_compositor *compositor;
60 struct wl_display *display;
61 struct wl_pointer *pointer;
62 struct wl_keyboard *keyboard;
63 struct wl_seat *seat;
64 struct wl_shell *shell;
65 struct wl_shm *shm;
66 struct desktop_shell *desktop_shell;
67 struct lock *swaylock;
68 struct input *input;
69 list_t *outputs;
70};
71
72struct registry *registry_poll(void);
73void registry_teardown(struct registry *registry);
74
75#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 @@
1#ifndef _CLIENT_H
2#define _CLIENT_H
3
4#include <wayland-client.h>
5#include "wayland-desktop-shell-client-protocol.h"
6#include <cairo/cairo.h>
7#include <pango/pangocairo.h>
8#include <stdbool.h>
9#include "list.h"
10#include "client/registry.h"
11
12struct window;
13
14struct buffer {
15 struct wl_buffer *buffer;
16 cairo_surface_t *surface;
17 cairo_t *cairo;
18 PangoContext *pango;
19 uint32_t width, height;
20 bool busy;
21};
22
23struct cursor {
24 struct wl_surface *surface;
25 struct wl_cursor_theme *cursor_theme;
26 struct wl_cursor *cursor;
27 struct wl_pointer *pointer;
28};
29
30enum scroll_direction {
31 SCROLL_UP,
32 SCROLL_DOWN,
33 SCROLL_LEFT,
34 SCROLL_RIGHT,
35};
36
37struct pointer_input {
38 int last_x;
39 int last_y;
40
41 void (*notify_button)(struct window *window, int x, int y, uint32_t button, uint32_t state_w);
42 void (*notify_scroll)(struct window *window, enum scroll_direction direction);
43};
44
45struct window {
46 struct registry *registry;
47 struct buffer buffers[2];
48 struct buffer *buffer;
49 struct wl_surface *surface;
50 struct wl_shell_surface *shell_surface;
51 struct wl_callback *frame_cb;
52 struct cursor cursor;
53 uint32_t width, height;
54 int32_t scale;
55 char *font;
56 cairo_t *cairo;
57 struct pointer_input pointer_input;
58};
59
60struct window *window_setup(struct registry *registry, uint32_t width, uint32_t height,
61 int32_t scale, bool shell_surface);
62void window_teardown(struct window *state);
63int window_prerender(struct window *state);
64int window_render(struct window *state);
65void window_make_shell(struct window *window);
66
67#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 @@
1#ifndef _SWAY_BUFFERS_H
2#define _SWAY_BUFFERS_H
3#include <cairo/cairo.h>
4#include <pango/pangocairo.h>
5#include <stdbool.h>
6#include <stdint.h>
7#include <wayland-client.h>
8
9struct pool_buffer {
10 struct wl_buffer *buffer;
11 cairo_surface_t *surface;
12 cairo_t *cairo;
13 PangoContext *pango;
14 uint32_t width, height;
15 bool busy;
16};
17
18struct pool_buffer *get_next_buffer(struct wl_shm *shm,
19 struct pool_buffer pool[static 2], uint32_t width, uint32_t height);
20
21#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')
27wayland_egl = dependency('wayland-egl') 27wayland_egl = dependency('wayland-egl')
28wayland_protos = dependency('wayland-protocols') 28wayland_protos = dependency('wayland-protocols')
29xkbcommon = dependency('xkbcommon') 29xkbcommon = dependency('xkbcommon')
30cairo = dependency('cairo')
30pango = dependency('pango') 31pango = dependency('pango')
32pangocairo = dependency('pangocairo')
33gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false)
31pixman = dependency('pixman-1') 34pixman = dependency('pixman-1')
32libcap = dependency('libcap') 35libcap = dependency('libcap')
33libinput = dependency('libinput') 36libinput = dependency('libinput')
@@ -35,6 +38,12 @@ math = cc.find_library('m')
35git = find_program('git', required: false) 38git = find_program('git', required: false)
36a2x = find_program('a2x', required: false) 39a2x = find_program('a2x', required: false)
37 40
41conf_data = configuration_data()
42
43if gdk_pixbuf.found()
44 conf_data.set('HAVE_GDK_PIXBUF', true)
45endif
46
38if a2x.found() 47if a2x.found()
39 mandir = get_option('mandir') 48 mandir = get_option('mandir')
40 man_files = [ 49 man_files = [
@@ -85,9 +94,13 @@ add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c')
85 94
86sway_inc = include_directories('include') 95sway_inc = include_directories('include')
87 96
97subdir('include')
98subdir('protocols')
88subdir('common') 99subdir('common')
89subdir('sway') 100subdir('sway')
90subdir('swaymsg') 101subdir('swaymsg')
102subdir('client')
103subdir('swaybg')
91 104
92config = configuration_data() 105config = configuration_data()
93config.set('sysconfdir', join_paths(prefix, sysconfdir)) 106config.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 @@
1wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
2
3wayland_scanner = find_program('wayland-scanner')
4
5wayland_scanner_code = generator(
6 wayland_scanner,
7 output: '@BASENAME@-protocol.c',
8 arguments: ['code', '@INPUT@', '@OUTPUT@'],
9)
10
11wayland_scanner_client = generator(
12 wayland_scanner,
13 output: '@BASENAME@-client-protocol.h',
14 arguments: ['client-header', '@INPUT@', '@OUTPUT@'],
15)
16
17protocols = [
18 [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
19 ['wlr-layer-shell-unstable-v1.xml']
20]
21
22wl_protos_src = []
23wl_protos_headers = []
24
25foreach p : protocols
26 xml = join_paths(p)
27 wl_protos_src += wayland_scanner_code.process(xml)
28 wl_protos_headers += wayland_scanner_client.process(xml)
29endforeach
30
31lib_wl_protos = static_library('wl_protos', wl_protos_src + wl_protos_headers,
32 dependencies: [wayland_client]) # for the include directory
33
34sway_protos = declare_dependency(
35 link_with: lib_wl_protos,
36 sources: wl_protos_headers,
37)
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 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<protocol name="wlr_layer_shell_unstable_v1">
3 <copyright>
4 Copyright © 2017 Drew DeVault
5
6 Permission to use, copy, modify, distribute, and sell this
7 software and its documentation for any purpose is hereby granted
8 without fee, provided that the above copyright notice appear in
9 all copies and that both that copyright notice and this permission
10 notice appear in supporting documentation, and that the name of
11 the copyright holders not be used in advertising or publicity
12 pertaining to distribution of the software without specific,
13 written prior permission. The copyright holders make no
14 representations about the suitability of this software for any
15 purpose. It is provided "as is" without express or implied
16 warranty.
17
18 THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
19 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
23 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
24 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
25 THIS SOFTWARE.
26 </copyright>
27
28 <interface name="zwlr_layer_shell_v1" version="1">
29 <description summary="create surfaces that are layers of the desktop">
30 Clients can use this interface to assign the surface_layer role to
31 wl_surfaces. Such surfaces are assigned to a "layer" of the output and
32 rendered with a defined z-depth respective to each other. They may also be
33 anchored to the edges and corners of a screen and specify input handling
34 semantics. This interface should be suitable for the implementation of
35 many desktop shell components, and a broad number of other applications
36 that interact with the desktop.
37 </description>
38
39 <request name="get_layer_surface">
40 <description summary="create a layer_surface from a surface">
41 Create a layer surface for an existing surface. This assigns the role of
42 layer_surface, or raises a protocol error if another role is already
43 assigned.
44
45 Creating a layer surface from a wl_surface which has a buffer attached
46 or committed is a client error, and any attempts by a client to attach
47 or manipulate a buffer prior to the first layer_surface.configure call
48 must also be treated as errors.
49
50 Clients can specify a namespace that defines the purpose of the layer
51 surface.
52 </description>
53 <arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
54 <arg name="surface" type="object" interface="wl_surface"/>
55 <arg name="output" type="object" interface="wl_output"/>
56 <arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
57 <arg name="namespace" type="string" summary="namespace for the layer surface"/>
58 </request>
59
60 <enum name="error">
61 <entry name="role" value="0" summary="wl_surface has another role"/>
62 <entry name="invalid_layer" value="1" summary="layer value is invalid"/>
63 <entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
64 </enum>
65
66 <enum name="layer">
67 <description summary="available layers for surfaces">
68 These values indicate which layers a surface can be rendered in. They
69 are ordered by z depth, bottom-most first. Traditional shell surfaces
70 will typically be rendered between the bottom and top layers.
71 Fullscreen shell surfaces are typically rendered at the top layer.
72 Multiple surfaces can share a single layer, and ordering within a
73 single layer is undefined.
74 </description>
75
76 <entry name="background" value="0"/>
77 <entry name="bottom" value="1"/>
78 <entry name="top" value="2"/>
79 <entry name="overlay" value="3"/>
80 </enum>
81 </interface>
82
83 <interface name="zwlr_layer_surface_v1" version="1">
84 <description summary="layer metadata interface">
85 An interface that may be implemented by a wl_surface, for surfaces that
86 are designed to be rendered as a layer of a stacked desktop-like
87 environment.
88
89 Layer surface state (size, anchor, exclusive zone, margin, interactivity)
90 is double-buffered, and will be applied at the time wl_surface.commit of
91 the corresponding wl_surface is called.
92 </description>
93
94 <request name="set_size">
95 <description summary="sets the size of the surface">
96 Sets the size of the surface in surface-local coordinates. The
97 compositor will display the surface centered with respect to its
98 anchors.
99
100 If you pass 0 for either value, the compositor will assign it and
101 inform you of the assignment in the configure event. You must set your
102 anchor to opposite edges in the dimensions you omit; not doing so is a
103 protocol error. Both values are 0 by default.
104
105 Size is double-buffered, see wl_surface.commit.
106 </description>
107 <arg name="width" type="uint"/>
108 <arg name="height" type="uint"/>
109 </request>
110
111 <request name="set_anchor">
112 <description summary="configures the anchor point of the surface">
113 Requests that the compositor anchor the surface to the specified edges
114 and corners. If two orthoginal edges are specified (e.g. 'top' and
115 'left'), then the anchor point will be the intersection of the edges
116 (e.g. the top left corner of the output); otherwise the anchor point
117 will be centered on that edge, or in the center if none is specified.
118
119 Anchor is double-buffered, see wl_surface.commit.
120 </description>
121 <arg name="anchor" type="uint" enum="anchor"/>
122 </request>
123
124 <request name="set_exclusive_zone">
125 <description summary="configures the exclusive geometry of this surface">
126 Requests that the compositor avoids occluding an area of the surface
127 with other surfaces. The compositor's use of this information is
128 implementation-dependent - do not assume that this region will not
129 actually be occluded.
130
131 A positive value is only meaningful if the surface is anchored to an
132 edge, rather than a corner. The zone is the number of surface-local
133 coordinates from the edge that are considered exclusive.
134
135 Surfaces that do not wish to have an exclusive zone may instead specify
136 how they should interact with surfaces that do. If set to zero, the
137 surface indicates that it would like to be moved to avoid occluding
138 surfaces with a positive excluzive zone. If set to -1, the surface
139 indicates that it would not like to be moved to accomodate for other
140 surfaces, and the compositor should extend it all the way to the edges
141 it is anchored to.
142
143 For example, a panel might set its exclusive zone to 10, so that
144 maximized shell surfaces are not shown on top of it. A notification
145 might set its exclusive zone to 0, so that it is moved to avoid
146 occluding the panel, but shell surfaces are shown underneath it. A
147 wallpaper or lock screen might set their exclusive zone to -1, so that
148 they stretch below or over the panel.
149
150 The default value is 0.
151
152 Exclusive zone is double-buffered, see wl_surface.commit.
153 </description>
154 <arg name="zone" type="int"/>
155 </request>
156
157 <request name="set_margin">
158 <description summary="sets a margin from the anchor point">
159 Requests that the surface be placed some distance away from the anchor
160 point on the output, in surface-local coordinates. Setting this value
161 for edges you are not anchored to has no effect.
162
163 The exclusive zone includes the margin.
164
165 Margin is double-buffered, see wl_surface.commit.
166 </description>
167 <arg name="top" type="int"/>
168 <arg name="right" type="int"/>
169 <arg name="bottom" type="int"/>
170 <arg name="left" type="int"/>
171 </request>
172
173 <request name="set_keyboard_interactivity">
174 <description summary="requests keyboard events">
175 Set to 1 to request that the seat send keyboard events to this layer
176 surface. For layers below the shell surface layer, the seat will use
177 normal focus semantics. For layers above the shell surface layers, the
178 seat will always give exclusive keyboard focus to the top-most layer
179 which has keyboard interactivity set to true.
180
181 Layer surfaces receive pointer, touch, and tablet events normally. If
182 you do not want to receive them, set the input region on your surface
183 to an empty region.
184
185 Events is double-buffered, see wl_surface.commit.
186 </description>
187 <arg name="keyboard_interactivity" type="uint"/>
188 </request>
189
190 <request name="get_popup">
191 <description summary="assign this layer_surface as an xdg_popup parent">
192 This assigns an xdg_popup's parent to this layer_surface. This popup
193 should have been created via xdg_surface::get_popup with the parent set
194 to NULL, and this request must be invoked before committing the popup's
195 initial state.
196
197 See the documentation of xdg_popup for more details about what an
198 xdg_popup is and how it is used.
199 </description>
200 <arg name="popup" type="object" interface="xdg_popup"/>
201 </request>
202
203 <request name="ack_configure">
204 <description summary="ack a configure event">
205 When a configure event is received, if a client commits the
206 surface in response to the configure event, then the client
207 must make an ack_configure request sometime before the commit
208 request, passing along the serial of the configure event.
209
210 If the client receives multiple configure events before it
211 can respond to one, it only has to ack the last configure event.
212
213 A client is not required to commit immediately after sending
214 an ack_configure request - it may even ack_configure several times
215 before its next surface commit.
216
217 A client may send multiple ack_configure requests before committing, but
218 only the last request sent before a commit indicates which configure
219 event the client really is responding to.
220 </description>
221 <arg name="serial" type="uint" summary="the serial from the configure event"/>
222 </request>
223
224 <request name="destroy" type="destructor">
225 <description summary="destroy the layer_surface">
226 This request destroys the layer surface.
227 </description>
228 </request>
229
230 <event name="configure">
231 <description summary="suggest a surface change">
232 The configure event asks the client to resize its surface.
233
234 Clients should arrange their surface for the new states, and then send
235 an ack_configure request with the serial sent in this configure event at
236 some point before committing the new surface.
237
238 The client is free to dismiss all but the last configure event it
239 received.
240
241 The width and height arguments specify the size of the window in
242 surface-local coordinates.
243
244 The size is a hint, in the sense that the client is free to ignore it if
245 it doesn't resize, pick a smaller size (to satisfy aspect ratio or
246 resize in steps of NxM pixels). If the client picks a smaller size and
247 is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
248 surface will be centered on this axis.
249
250 If the width or height arguments are zero, it means the client should
251 decide its own window dimension.
252 </description>
253 <arg name="serial" type="uint"/>
254 <arg name="width" type="uint"/>
255 <arg name="height" type="uint"/>
256 </event>
257
258 <event name="closed">
259 <description summary="surface should be closed">
260 The closed event is sent by the compositor when the surface will no
261 longer be shown. The output may have been destroyed or the user may
262 have asked for it to be removed. Further changes to the surface will be
263 ignored. The client should destroy the resource after receiving this
264 event, and create a new surface if they so choose.
265 </description>
266 </event>
267
268 <enum name="error">
269 <entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
270 <entry name="invalid_size" value="1" summary="size is invalid"/>
271 <entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
272 </enum>
273
274 <enum name="anchor" bitfield="true">
275 <entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
276 <entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
277 <entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
278 <entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
279 </enum>
280 </interface>
281</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 @@
1#include "wayland-desktop-shell-client-protocol.h" 1#include <assert.h>
2#include <ctype.h>
2#include <stdbool.h> 3#include <stdbool.h>
3#include <stdio.h> 4#include <stdio.h>
4#include <stdlib.h> 5#include <stdlib.h>
5#include <ctype.h>
6#include <wayland-client.h>
7#include <time.h>
8#include <string.h> 6#include <string.h>
9#include "client/window.h" 7#include <time.h>
10#include "client/registry.h" 8#include <wayland-client.h>
11#include "client/cairo.h" 9#include <wlr/util/log.h>
12#include "log.h" 10#include "pool-buffer.h"
13#include "list.h" 11#include "cairo.h"
14#include "util.h" 12#include "util.h"
13#include "wlr-layer-shell-unstable-v1-client-protocol.h"
15 14
16list_t *surfaces; 15enum background_mode {
17struct registry *registry; 16 BACKGROUND_MODE_STRETCH,
17 BACKGROUND_MODE_FILL,
18 BACKGROUND_MODE_FIT,
19 BACKGROUND_MODE_CENTER,
20 BACKGROUND_MODE_TILE,
21 BACKGROUND_MODE_SOLID_COLOR,
22};
18 23
19enum scaling_mode { 24struct swaybg_args {
20 SCALING_MODE_STRETCH, 25 int output_idx;
21 SCALING_MODE_FILL, 26 const char *path;
22 SCALING_MODE_FIT, 27 enum background_mode mode;
23 SCALING_MODE_CENTER,
24 SCALING_MODE_TILE,
25}; 28};
26 29
27void sway_terminate(int exit_code) { 30struct swaybg_context {
28 int i; 31 uint32_t color;
29 for (i = 0; i < surfaces->length; ++i) { 32 cairo_surface_t *image;
30 struct window *window = surfaces->items[i]; 33};
31 window_teardown(window); 34
32 } 35struct swaybg_state {
33 list_free(surfaces); 36 const struct swaybg_args *args;
34 registry_teardown(registry); 37 struct swaybg_context context;
35 exit(exit_code); 38
36} 39 struct wl_display *display;
40 struct wl_compositor *compositor;
41 struct zwlr_layer_shell_v1 *layer_shell;
42 struct wl_shm *shm;
43
44 struct wl_output *output;
45 struct wl_surface *surface;
46 struct zwlr_layer_surface_v1 *layer_surface;
47
48 bool run_display;
49 uint32_t width, height;
50 struct pool_buffer buffers[2];
51 struct pool_buffer *current_buffer;
52};
37 53
38bool is_valid_color(const char *color) { 54bool is_valid_color(const char *color) {
39 int len = strlen(color); 55 int len = strlen(color);
40 if (len != 7 || color[0] != '#') { 56 if (len != 7 || color[0] != '#') {
41 sway_log(L_ERROR, "%s is not a valid color for swaybg. Color should be specified as #rrggbb (no alpha).", color); 57 wlr_log(L_ERROR, "%s is not a valid color for swaybg. "
58 "Color should be specified as #rrggbb (no alpha).", color);
42 return false; 59 return false;
43 } 60 }
44 61
@@ -52,163 +69,245 @@ bool is_valid_color(const char *color) {
52 return true; 69 return true;
53} 70}
54 71
55int main(int argc, const char **argv) { 72static void render_image(struct swaybg_state *state) {
56 init_log(L_INFO); 73 cairo_t *cairo = state->current_buffer->cairo;
57 surfaces = create_list(); 74 cairo_surface_t *image = state->context.image;
58 registry = registry_poll(); 75 double width = cairo_image_surface_get_width(image);
76 double height = cairo_image_surface_get_height(image);
77 int wwidth = state->width;
78 int wheight = state->height;
59 79
60 if (argc != 4) { 80 switch (state->args->mode) {
61 sway_abort("Do not run this program manually. See man 5 sway and look for output options."); 81 case BACKGROUND_MODE_STRETCH:
62 } 82 cairo_scale(cairo, (double)wwidth / width, (double)wheight / height);
83 cairo_set_source_surface(cairo, image, 0, 0);
84 break;
85 case BACKGROUND_MODE_FILL: {
86 double window_ratio = (double)wwidth / wheight;
87 double bg_ratio = width / height;
63 88
64 if (!registry->desktop_shell) { 89 if (window_ratio > bg_ratio) {
65 sway_abort("swaybg requires the compositor to support the desktop-shell extension."); 90 double scale = (double)wwidth / width;
91 cairo_scale(cairo, scale, scale);
92 cairo_set_source_surface(cairo, image,
93 0, (double)wheight / 2 / scale - height / 2);
94 } else {
95 double scale = (double)wheight / height;
96 cairo_scale(cairo, scale, scale);
97 cairo_set_source_surface(cairo, image,
98 (double)wwidth / 2 / scale - width / 2, 0);
99 }
100 break;
66 } 101 }
102 case BACKGROUND_MODE_FIT: {
103 double window_ratio = (double)wwidth / wheight;
104 double bg_ratio = width / height;
67 105
68 int desired_output = atoi(argv[1]); 106 if (window_ratio > bg_ratio) {
69 sway_log(L_INFO, "Using output %d of %d", desired_output, registry->outputs->length); 107 double scale = (double)wheight / height;
70 int i; 108 cairo_scale(cairo, scale, scale);
71 struct output_state *output = registry->outputs->items[desired_output]; 109 cairo_set_source_surface(cairo, image,
72 struct window *window = window_setup(registry, 110 (double)wwidth / 2 / scale - width / 2, 0);
73 output->width, output->height, output->scale, false);
74 if (!window) {
75 sway_abort("Failed to create surfaces.");
76 }
77 desktop_shell_set_background(registry->desktop_shell, output->output, window->surface);
78 window_make_shell(window);
79 list_add(surfaces, window);
80
81 if (strcmp(argv[3], "solid_color") == 0 && is_valid_color(argv[2])) {
82 cairo_set_source_u32(window->cairo, parse_color(argv[2]));
83 cairo_paint(window->cairo);
84 window_render(window);
85 } else {
86#ifdef WITH_GDK_PIXBUF
87 GError *err = NULL;
88 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(argv[2], &err);
89 if (!pixbuf) {
90 sway_abort("Failed to load background image.");
91 }
92 cairo_surface_t *image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
93 g_object_unref(pixbuf);
94#else
95 cairo_surface_t *image = cairo_image_surface_create_from_png(argv[2]);
96#endif //WITH_GDK_PIXBUF
97 if (!image) {
98 sway_abort("Failed to read background image.");
99 }
100 if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
101 sway_abort("Failed to read background image: %s."
102#ifndef WITH_GDK_PIXBUF
103 "\nSway was compiled without gdk_pixbuf support, so only"
104 "\nPNG images can be loaded. This is the likely cause."
105#endif //WITH_GDK_PIXBUF
106 , cairo_status_to_string(cairo_surface_status(image)));
107 }
108 double width = cairo_image_surface_get_width(image);
109 double height = cairo_image_surface_get_height(image);
110
111 const char *scaling_mode_str = argv[3];
112 enum scaling_mode scaling_mode = SCALING_MODE_STRETCH;
113 if (strcmp(scaling_mode_str, "stretch") == 0) {
114 scaling_mode = SCALING_MODE_STRETCH;
115 } else if (strcmp(scaling_mode_str, "fill") == 0) {
116 scaling_mode = SCALING_MODE_FILL;
117 } else if (strcmp(scaling_mode_str, "fit") == 0) {
118 scaling_mode = SCALING_MODE_FIT;
119 } else if (strcmp(scaling_mode_str, "center") == 0) {
120 scaling_mode = SCALING_MODE_CENTER;
121 } else if (strcmp(scaling_mode_str, "tile") == 0) {
122 scaling_mode = SCALING_MODE_TILE;
123 } else { 111 } else {
124 sway_abort("Unsupported scaling mode: %s", scaling_mode_str); 112 double scale = (double)wwidth / width;
113 cairo_scale(cairo, scale, scale);
114 cairo_set_source_surface(cairo, image,
115 0, (double)wheight / 2 / scale - height / 2);
125 } 116 }
117 break;
118 }
119 case BACKGROUND_MODE_CENTER:
120 cairo_set_source_surface(cairo, image,
121 (double)wwidth / 2 - width / 2,
122 (double)wheight / 2 - height / 2);
123 break;
124 case BACKGROUND_MODE_TILE: {
125 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
126 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
127 cairo_set_source(cairo, pattern);
128 break;
129 }
130 case BACKGROUND_MODE_SOLID_COLOR:
131 assert(0);
132 break;
133 }
134 cairo_paint(cairo);
135}
136
137static void render_frame(struct swaybg_state *state) {
138 state->current_buffer = get_next_buffer(state->shm,
139 state->buffers, state->width, state->height);
140 cairo_t *cairo = state->current_buffer->cairo;
141
142 switch (state->args->mode) {
143 case BACKGROUND_MODE_SOLID_COLOR:
144 cairo_set_source_u32(cairo, state->context.color);
145 cairo_paint(cairo);
146 break;
147 default:
148 render_image(state);
149 break;
150 }
151
152 wl_surface_attach(state->surface, state->current_buffer->buffer, 0, 0);
153 wl_surface_damage(state->surface, 0, 0, state->width, state->height);
154 wl_surface_commit(state->surface);
155}
126 156
127 int wwidth = window->width * window->scale; 157static bool prepare_context(struct swaybg_state *state) {
128 int wheight = window->height * window->scale; 158 if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) {
129 159 state->context.color = parse_color(state->args->path);
130 for (i = 0; i < surfaces->length; ++i) { 160 return is_valid_color(state->args->path);
131 struct window *window = surfaces->items[i]; 161 }
132 if (window_prerender(window) && window->cairo) { 162#ifdef HAVE_GDK_PIXBUF
133 switch (scaling_mode) { 163 GError *err = NULL;
134 case SCALING_MODE_STRETCH: 164 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(state->args->path, &err);
135 cairo_scale(window->cairo, 165 if (!pixbuf) {
136 (double) wwidth / width, 166 wlr_log(L_ERROR, "Failed to load background image.");
137 (double) wheight / height); 167 return false;
138 cairo_set_source_surface(window->cairo, image, 0, 0); 168 }
139 break; 169 state->context.image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
140 case SCALING_MODE_FILL: 170 g_object_unref(pixbuf);
141 { 171#else
142 double window_ratio = (double) wwidth / wheight; 172 state->context.image = cairo_image_surface_create_from_png(
143 double bg_ratio = width / height; 173 state->args->path);
144 174#endif //HAVE_GDK_PIXBUF
145 if (window_ratio > bg_ratio) { 175 if (!state->context.image) {
146 double scale = (double) wwidth / width; 176 wlr_log(L_ERROR, "Failed to read background image.");
147 cairo_scale(window->cairo, scale, scale); 177 return false;
148 cairo_set_source_surface(window->cairo, image, 178 }
149 0, 179 if (cairo_surface_status(state->context.image) != CAIRO_STATUS_SUCCESS) {
150 (double) wheight/2 / scale - height/2); 180 wlr_log(L_ERROR, "Failed to read background image: %s."
151 } else { 181#ifndef HAVE_GDK_PIXBUF
152 double scale = (double) wheight / height; 182 "\nSway was compiled without gdk_pixbuf support, so only"
153 cairo_scale(window->cairo, scale, scale); 183 "\nPNG images can be loaded. This is the likely cause."
154 cairo_set_source_surface(window->cairo, image, 184#endif //HAVE_GDK_PIXBUF
155 (double) wwidth/2 / scale - width/2, 185 , cairo_status_to_string(
156 0); 186 cairo_surface_status(state->context.image)));
157 } 187 return false;
158 break; 188 }
159 } 189 return true;
160 case SCALING_MODE_FIT: 190}
161 { 191
162 double window_ratio = (double) wwidth / wheight; 192static void layer_surface_configure(void *data,
163 double bg_ratio = width / height; 193 struct zwlr_layer_surface_v1 *surface,
164 194 uint32_t serial, uint32_t width, uint32_t height) {
165 if (window_ratio > bg_ratio) { 195 struct swaybg_state *state = data;
166 double scale = (double) wheight / height; 196 state->width = width;
167 cairo_scale(window->cairo, scale, scale); 197 state->height = height;
168 cairo_set_source_surface(window->cairo, image, 198 zwlr_layer_surface_v1_ack_configure(surface, serial);
169 (double) wwidth/2 / scale - width/2, 199 render_frame(state);
170 0); 200}
171 } else { 201
172 double scale = (double) wwidth / width; 202static void layer_surface_closed(void *data,
173 cairo_scale(window->cairo, scale, scale); 203 struct zwlr_layer_surface_v1 *surface) {
174 cairo_set_source_surface(window->cairo, image, 204 struct swaybg_state *state = data;
175 0, 205 zwlr_layer_surface_v1_destroy(state->layer_surface);
176 (double) wheight/2 / scale - height/2); 206 wl_surface_destroy(state->surface);
177 } 207 state->run_display = false;
178 break; 208}
179 } 209
180 case SCALING_MODE_CENTER: 210struct zwlr_layer_surface_v1_listener layer_surface_listener = {
181 cairo_set_source_surface(window->cairo, image, 211 .configure = layer_surface_configure,
182 (double) wwidth/2 - width/2, 212 .closed = layer_surface_closed,
183 (double) wheight/2 - height/2); 213};
184 break; 214
185 case SCALING_MODE_TILE: 215static void handle_global(void *data, struct wl_registry *registry,
186 { 216 uint32_t name, const char *interface, uint32_t version) {
187 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); 217 struct swaybg_state *state = data;
188 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); 218 if (strcmp(interface, wl_compositor_interface.name) == 0) {
189 cairo_set_source(window->cairo, pattern); 219 state->compositor = wl_registry_bind(registry, name,
190 break; 220 &wl_compositor_interface, 1);
191 } 221 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
192 default: 222 state->shm = wl_registry_bind(registry, name,
193 sway_abort("Scaling mode '%s' not implemented yet!", scaling_mode_str); 223 &wl_shm_interface, 1);
194 } 224 } else if (strcmp(interface, wl_output_interface.name) == 0) {
195 225 static int output_idx = 0;
196 cairo_paint(window->cairo); 226 if (output_idx == state->args->output_idx) {
197 227 state->output = wl_registry_bind(registry, name,
198 window_render(window); 228 &wl_output_interface, 1);
199 }
200 } 229 }
230 output_idx++;
231 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
232 state->layer_shell = wl_registry_bind(
233 registry, name, &zwlr_layer_shell_v1_interface, 1);
234 }
235}
236
237static void handle_global_remove(void *data, struct wl_registry *registry,
238 uint32_t name) {
239 // who cares
240}
241
242static const struct wl_registry_listener registry_listener = {
243 .global = handle_global,
244 .global_remove = handle_global_remove,
245};
201 246
202 cairo_surface_destroy(image); 247int main(int argc, const char **argv) {
248 struct swaybg_args args = {0};
249 struct swaybg_state state = {0};
250 state.args = &args;
251 wlr_log_init(L_DEBUG, NULL);
252
253 if (argc != 4) {
254 wlr_log(L_ERROR, "Do not run this program manually. "
255 "See man 5 sway and look for output options.");
256 return 1;
203 } 257 }
258 args.output_idx = atoi(argv[1]);
259 args.path = argv[2];
260
261 args.mode = BACKGROUND_MODE_STRETCH;
262 if (strcmp(argv[3], "stretch") == 0) {
263 args.mode = BACKGROUND_MODE_STRETCH;
264 } else if (strcmp(argv[3], "fill") == 0) {
265 args.mode = BACKGROUND_MODE_FILL;
266 } else if (strcmp(argv[3], "fit") == 0) {
267 args.mode = BACKGROUND_MODE_FIT;
268 } else if (strcmp(argv[3], "center") == 0) {
269 args.mode = BACKGROUND_MODE_CENTER;
270 } else if (strcmp(argv[3], "tile") == 0) {
271 args.mode = BACKGROUND_MODE_TILE;
272 } else if (strcmp(argv[3], "solid_color") == 0) {
273 args.mode = BACKGROUND_MODE_SOLID_COLOR;
274 } else {
275 wlr_log(L_ERROR, "Unsupported background mode: %s", argv[3]);
276 return 1;
277 }
278
279 if (!prepare_context(&state)) {
280 return 1;
281 }
282
283 assert(state.display = wl_display_connect(NULL));
284
285 struct wl_registry *registry = wl_display_get_registry(state.display);
286 wl_registry_add_listener(registry, &registry_listener, &state);
287 wl_display_roundtrip(state.display);
288 assert(state.compositor && state.layer_shell && state.output && state.shm);
289
290 assert(state.surface = wl_compositor_create_surface(state.compositor));
291
292 state.layer_surface = zwlr_layer_shell_v1_get_layer_surface(
293 state.layer_shell, state.surface, state.output,
294 ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper");
295 assert(state.layer_surface);
204 296
205 while (wl_display_dispatch(registry->display) != -1); 297 zwlr_layer_surface_v1_set_size(state.layer_surface, 0, 0);
298 zwlr_layer_surface_v1_set_anchor(state.layer_surface,
299 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
300 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
301 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
302 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
303 zwlr_layer_surface_v1_add_listener(state.layer_surface,
304 &layer_surface_listener, &state);
305 state.run_display = true;
306 wl_surface_commit(state.surface);
307 wl_display_roundtrip(state.display);
206 308
207 for (i = 0; i < surfaces->length; ++i) { 309 while (wl_display_dispatch(state.display) != -1 && state.run_display) {
208 struct window *window = surfaces->items[i]; 310 // This space intentionally left blank
209 window_teardown(window);
210 } 311 }
211 list_free(surfaces);
212 registry_teardown(registry);
213 return 0; 312 return 0;
214} 313}
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 @@
1executable(
2 'swaybg',
3 'main.c',
4 include_directories: [sway_inc],
5 dependencies: [
6 cairo,
7 gdk_pixbuf,
8 jsonc,
9 math,
10 pango,
11 pangocairo,
12 sway_protos,
13 wayland_client,
14 wlroots,
15 ],
16 link_with: [lib_sway_common, lib_sway_client],
17 install: true
18)
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 @@
1#include <cairo/cairo.h>
2#include <pango/pangocairo.h>
3#include <stdarg.h>
4#include <stdlib.h>
5#include <string.h>
6#include <stdio.h>
7#include <stdbool.h>
8#include <stdint.h>
9#include "log.h"
10
11PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text,
12 int32_t scale, bool markup) {
13 PangoLayout *layout = pango_cairo_create_layout(cairo);
14 PangoAttrList *attrs;
15 if (markup) {
16 char *buf;
17 pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, NULL);
18 pango_layout_set_markup(layout, buf, -1);
19 free(buf);
20 } else {
21 attrs = pango_attr_list_new();
22 pango_layout_set_text(layout, text, -1);
23 }
24 pango_attr_list_insert(attrs, pango_attr_scale_new(scale));
25 PangoFontDescription *desc = pango_font_description_from_string(font);
26 pango_layout_set_font_description(layout, desc);
27 pango_layout_set_single_paragraph_mode(layout, 1);
28 pango_layout_set_attributes(layout, attrs);
29 pango_attr_list_unref(attrs);
30 pango_font_description_free(desc);
31 return layout;
32}
33
34void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
35 int32_t scale, bool markup, const char *fmt, ...) {
36 char *buf = malloc(2048);
37
38 va_list args;
39 va_start(args, fmt);
40 if (vsnprintf(buf, 2048, fmt, args) >= 2048) {
41 strcpy(buf, "[buffer overflow]");
42 }
43 va_end(args);
44
45 PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
46 pango_cairo_update_layout(cairo, layout);
47
48 pango_layout_get_pixel_size(layout, width, height);
49
50 g_object_unref(layout);
51
52 free(buf);
53}
54
55void pango_printf(cairo_t *cairo, const char *font, int32_t scale, bool markup, const char *fmt, ...) {
56 char *buf = malloc(2048);
57
58 va_list args;
59 va_start(args, fmt);
60 if (vsnprintf(buf, 2048, fmt, args) >= 2048) {
61 strcpy(buf, "[buffer overflow]");
62 }
63 va_end(args);
64
65 PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
66 pango_cairo_update_layout(cairo, layout);
67
68 pango_cairo_show_layout(cairo, layout);
69
70 g_object_unref(layout);
71
72 free(buf);
73}
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 @@
1#include <wayland-client.h>
2#include <xkbcommon/xkbcommon.h>
3#include <stdlib.h>
4#include <string.h>
5#include <unistd.h>
6#include <sys/mman.h>
7#include <sys/types.h>
8#include <sys/timerfd.h>
9#include "wayland-desktop-shell-client-protocol.h"
10#include "wayland-swaylock-client-protocol.h"
11#include "client/registry.h"
12#include "stringop.h"
13#include "log.h"
14
15static void display_handle_mode(void *data, struct wl_output *wl_output,
16 uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
17 struct output_state *state = data;
18 if (flags & WL_OUTPUT_MODE_CURRENT) {
19 state->flags = flags;
20 state->width = width;
21 state->height = height;
22 sway_log(L_DEBUG, "Got mode %dx%d:0x%X for output %p",
23 width, height, flags, data);
24 }
25}
26
27static void display_handle_geometry(void *data, struct wl_output *wl_output,
28 int32_t x, int32_t y, int32_t physical_width, int32_t physical_height,
29 int32_t subpixel, const char *make, const char *model, int32_t transform) {
30 // this space intentionally left blank
31}
32
33static void display_handle_done(void *data, struct wl_output *wl_output) {
34 // this space intentionally left blank
35}
36
37static void display_handle_scale(void *data, struct wl_output *wl_output, int32_t factor) {
38 struct output_state *state = data;
39 state->scale = factor;
40 sway_log(L_DEBUG, "Got scale factor %d for output %p", factor, data);
41}
42
43static const struct wl_output_listener output_listener = {
44 .mode = display_handle_mode,
45 .geometry = display_handle_geometry,
46 .done = display_handle_done,
47 .scale = display_handle_scale
48};
49
50const char *XKB_MASK_NAMES[MASK_LAST] = {
51 XKB_MOD_NAME_SHIFT,
52 XKB_MOD_NAME_CAPS,
53 XKB_MOD_NAME_CTRL,
54 XKB_MOD_NAME_ALT,
55 "Mod2",
56 "Mod3",
57 XKB_MOD_NAME_LOGO,
58 "Mod5",
59};
60
61const enum mod_bit XKB_MODS[MASK_LAST] = {
62 MOD_SHIFT,
63 MOD_CAPS,
64 MOD_CTRL,
65 MOD_ALT,
66 MOD_MOD2,
67 MOD_MOD3,
68 MOD_LOGO,
69 MOD_MOD5
70};
71
72static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
73 uint32_t format, int fd, uint32_t size) {
74 // Keyboard errors are abort-worthy because you wouldn't be able to unlock your screen otherwise.
75
76 struct registry *registry = data;
77 if (!data) {
78 close(fd);
79 return;
80 }
81
82 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
83 close(fd);
84 sway_abort("Unknown keymap format %d, aborting", format);
85 }
86
87 char *map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
88 if (map_str == MAP_FAILED) {
89 close(fd);
90 sway_abort("Unable to initialized shared keyboard memory, aborting");
91 }
92
93 struct xkb_keymap *keymap = xkb_keymap_new_from_string(registry->input->xkb.context,
94 map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
95 munmap(map_str, size);
96 close(fd);
97
98 if (!keymap) {
99 sway_abort("Failed to compile keymap, aborting");
100 }
101
102 struct xkb_state *state = xkb_state_new(keymap);
103 if (!state) {
104 xkb_keymap_unref(keymap);
105 sway_abort("Failed to create xkb state, aborting");
106 }
107
108 xkb_keymap_unref(registry->input->xkb.keymap);
109 xkb_state_unref(registry->input->xkb.state);
110 registry->input->xkb.keymap = keymap;
111 registry->input->xkb.state = state;
112
113 int i;
114 for (i = 0; i < MASK_LAST; ++i) {
115 registry->input->xkb.masks[i] = 1 << xkb_keymap_mod_get_index(registry->input->xkb.keymap, XKB_MASK_NAMES[i]);
116 }
117}
118
119static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
120 uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
121 // this space intentionally left blank
122}
123
124static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
125 uint32_t serial, struct wl_surface *surface) {
126 // this space intentionally left blank
127}
128
129static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
130 uint32_t serial, uint32_t time, uint32_t key, uint32_t state_w) {
131 struct registry *registry = data;
132 enum wl_keyboard_key_state state = state_w;
133
134 if (!registry->input->xkb.state) {
135 return;
136 }
137
138 xkb_keysym_t sym = xkb_state_key_get_one_sym(registry->input->xkb.state, key + 8);
139 registry->input->sym = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? sym : XKB_KEY_NoSymbol);
140 registry->input->code = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? key + 8 : 0);
141 uint32_t codepoint = xkb_state_key_get_utf32(registry->input->xkb.state, registry->input->code);
142 if (registry->input->notify) {
143 registry->input->notify(state, sym, key, codepoint);
144 }
145}
146
147static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
148 uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
149 uint32_t mods_locked, uint32_t group) {
150 struct registry *registry = data;
151
152 if (!registry->input->xkb.keymap) {
153 return;
154 }
155
156 xkb_state_update_mask(registry->input->xkb.state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
157 xkb_mod_mask_t mask = xkb_state_serialize_mods(registry->input->xkb.state,
158 XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED);
159
160 registry->input->modifiers = 0;
161 for (uint32_t i = 0; i < MASK_LAST; ++i) {
162 if (mask & registry->input->xkb.masks[i]) {
163 registry->input->modifiers |= XKB_MODS[i];
164 }
165 }
166}
167
168static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *keyboard,
169 int32_t rate, int32_t delay) {
170 // this space intentionally left blank
171}
172
173static const struct wl_keyboard_listener keyboard_listener = {
174 .keymap = keyboard_handle_keymap,
175 .enter = keyboard_handle_enter,
176 .leave = keyboard_handle_leave,
177 .key = keyboard_handle_key,
178 .modifiers = keyboard_handle_modifiers,
179 .repeat_info = keyboard_handle_repeat_info
180};
181
182static void seat_handle_capabilities(void *data, struct wl_seat *seat,
183 enum wl_seat_capability caps) {
184 struct registry *reg = data;
185
186 if ((caps & WL_SEAT_CAPABILITY_POINTER) && !reg->pointer) {
187 reg->pointer = wl_seat_get_pointer(reg->seat);
188 } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && reg->pointer) {
189 wl_pointer_destroy(reg->pointer);
190 reg->pointer = NULL;
191 }
192
193 if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !reg->keyboard) {
194 reg->keyboard = wl_seat_get_keyboard(reg->seat);
195 wl_keyboard_add_listener(reg->keyboard, &keyboard_listener, reg);
196 } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && reg->keyboard) {
197 wl_keyboard_destroy(reg->keyboard);
198 reg->keyboard = NULL;
199 }
200}
201
202static void seat_handle_name(void *data, struct wl_seat *seat, const char *name) {
203 // this space intentionally left blank
204}
205
206static const struct wl_seat_listener seat_listener = {
207 .capabilities = seat_handle_capabilities,
208 .name = seat_handle_name,
209};
210
211static void registry_global(void *data, struct wl_registry *registry,
212 uint32_t name, const char *interface, uint32_t version) {
213 struct registry *reg = data;
214
215 if (strcmp(interface, wl_compositor_interface.name) == 0) {
216 reg->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, version);
217 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
218 reg->shm = wl_registry_bind(registry, name, &wl_shm_interface, version);
219 } else if (strcmp(interface, wl_shell_interface.name) == 0) {
220 reg->shell = wl_registry_bind(registry, name, &wl_shell_interface, version);
221 } else if (strcmp(interface, wl_seat_interface.name) == 0) {
222 reg->seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
223 wl_seat_add_listener(reg->seat, &seat_listener, reg);
224 } else if (strcmp(interface, wl_output_interface.name) == 0) {
225 struct wl_output *output = wl_registry_bind(registry, name, &wl_output_interface, version);
226 struct output_state *ostate = malloc(sizeof(struct output_state));
227 ostate->output = output;
228 ostate->scale = 1;
229 wl_output_add_listener(output, &output_listener, ostate);
230 list_add(reg->outputs, ostate);
231 } else if (strcmp(interface, desktop_shell_interface.name) == 0) {
232 reg->desktop_shell = wl_registry_bind(registry, name, &desktop_shell_interface, version);
233 } else if (strcmp(interface, lock_interface.name) == 0) {
234 reg->swaylock = wl_registry_bind(registry, name, &lock_interface, version);
235 }
236}
237
238static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) {
239 // this space intentionally left blank
240}
241
242static const struct wl_registry_listener registry_listener = {
243 .global = registry_global,
244 .global_remove = registry_global_remove
245};
246
247struct registry *registry_poll(void) {
248 struct registry *registry = malloc(sizeof(struct registry));
249 memset(registry, 0, sizeof(struct registry));
250 registry->outputs = create_list();
251 registry->input = calloc(sizeof(struct input), 1);
252 registry->input->xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
253
254 registry->display = wl_display_connect(NULL);
255 if (!registry->display) {
256 sway_log(L_ERROR, "Error opening display");
257 registry_teardown(registry);
258 return NULL;
259 }
260
261 struct wl_registry *reg = wl_display_get_registry(registry->display);
262 wl_registry_add_listener(reg, &registry_listener, registry);
263 wl_display_dispatch(registry->display);
264 wl_display_roundtrip(registry->display);
265 wl_registry_destroy(reg);
266
267 return registry;
268}
269
270void registry_teardown(struct registry *registry) {
271 if (registry->pointer) {
272 wl_pointer_destroy(registry->pointer);
273 }
274 if (registry->seat) {
275 wl_seat_destroy(registry->seat);
276 }
277 if (registry->shell) {
278 wl_shell_destroy(registry->shell);
279 }
280 if (registry->shm) {
281 wl_shm_destroy(registry->shm);
282 }
283 if (registry->compositor) {
284 wl_compositor_destroy(registry->compositor);
285 }
286 if (registry->display) {
287 wl_display_disconnect(registry->display);
288 }
289 if (registry->outputs) {
290 free_flat_list(registry->outputs);
291 }
292 free(registry);
293}
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 @@
1#include <wayland-client.h>
2#include <wayland-cursor.h>
3#include "wayland-xdg-shell-client-protocol.h"
4#include "wayland-desktop-shell-client-protocol.h"
5#include <cairo/cairo.h>
6#include <pango/pangocairo.h>
7#include <stdlib.h>
8#include <string.h>
9#include <stdio.h>
10#include <unistd.h>
11#include <errno.h>
12#include <sys/mman.h>
13#include "client/window.h"
14#include "client/buffer.h"
15#include "list.h"
16#include "log.h"
17
18static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
19 uint32_t serial, struct wl_surface *surface, wl_fixed_t sx_w, wl_fixed_t sy_w) {
20 struct window *window = data;
21 if (window->registry->pointer) {
22 struct wl_cursor_image *image = window->cursor.cursor->images[0];
23 wl_pointer_set_cursor(pointer, serial, window->cursor.surface, image->hotspot_x, image->hotspot_y);
24 }
25}
26
27static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
28 uint32_t serial, struct wl_surface *surface) {
29}
30
31static void pointer_handle_motion(void *data, struct wl_pointer *pointer,
32 uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w) {
33 struct window *window = data;
34
35 window->pointer_input.last_x = wl_fixed_to_int(sx_w);
36 window->pointer_input.last_y = wl_fixed_to_int(sy_w);
37}
38
39static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
40 uint32_t time, uint32_t button, uint32_t state_w) {
41 struct window *window = data;
42 struct pointer_input *input = &window->pointer_input;
43
44 if (window->pointer_input.notify_button) {
45 window->pointer_input.notify_button(window, input->last_x, input->last_y, button, state_w);
46 }
47}
48
49static void pointer_handle_axis(void *data, struct wl_pointer *pointer,
50 uint32_t time, uint32_t axis, wl_fixed_t value) {
51 struct window *window = data;
52 enum scroll_direction direction;
53
54 switch (axis) {
55 case 0:
56 direction = wl_fixed_to_double(value) < 0 ? SCROLL_UP : SCROLL_DOWN;
57 break;
58 case 1:
59 direction = wl_fixed_to_double(value) < 0 ? SCROLL_LEFT : SCROLL_RIGHT;
60 break;
61 default:
62 sway_log(L_DEBUG, "Unexpected axis value on mouse scroll");
63 return;
64 }
65
66 if (window->pointer_input.notify_scroll) {
67 window->pointer_input.notify_scroll(window, direction);
68 }
69}
70
71static const struct wl_pointer_listener pointer_listener = {
72 .enter = pointer_handle_enter,
73 .leave = pointer_handle_leave,
74 .motion = pointer_handle_motion,
75 .button = pointer_handle_button,
76 .axis = pointer_handle_axis
77};
78
79void shell_surface_configure(void *data, struct wl_shell_surface *wl_shell_surface,
80 uint32_t edges, int32_t width, int32_t height) {
81 struct window *window = data;
82 window->width = width;
83 window->height = height;
84}
85
86static const struct wl_shell_surface_listener surface_listener = {
87 .configure = shell_surface_configure
88};
89
90void window_make_shell(struct window *window) {
91 window->shell_surface = wl_shell_get_shell_surface(window->registry->shell, window->surface);
92 wl_shell_surface_add_listener(window->shell_surface, &surface_listener, window);
93 wl_shell_surface_set_toplevel(window->shell_surface);
94}
95
96struct window *window_setup(struct registry *registry, uint32_t width, uint32_t height,
97 int32_t scale, bool shell_surface) {
98 struct window *window = malloc(sizeof(struct window));
99 memset(window, 0, sizeof(struct window));
100 window->width = width;
101 window->height = height;
102 window->scale = scale;
103 window->registry = registry;
104 window->font = "monospace 10";
105
106 window->surface = wl_compositor_create_surface(registry->compositor);
107 if (shell_surface) {
108 window_make_shell(window);
109 }
110 if (registry->pointer) {
111 wl_pointer_add_listener(registry->pointer, &pointer_listener, window);
112 }
113
114 get_next_buffer(window);
115
116 if (registry->pointer) {
117 char *cursor_theme = getenv("SWAY_CURSOR_THEME");
118 if (!cursor_theme) {
119 cursor_theme = "default";
120 }
121 char *cursor_size = getenv("SWAY_CURSOR_SIZE");
122 if (!cursor_size) {
123 cursor_size = "16";
124 }
125
126 sway_log(L_DEBUG, "Cursor scale: %d", scale);
127 window->cursor.cursor_theme = wl_cursor_theme_load(cursor_theme,
128 atoi(cursor_size) * scale, registry->shm);
129 window->cursor.cursor = wl_cursor_theme_get_cursor(window->cursor.cursor_theme, "left_ptr");
130 window->cursor.surface = wl_compositor_create_surface(registry->compositor);
131
132 struct wl_cursor_image *image = window->cursor.cursor->images[0];
133 struct wl_buffer *cursor_buf = wl_cursor_image_get_buffer(image);
134 wl_surface_attach(window->cursor.surface, cursor_buf, 0, 0);
135 wl_surface_set_buffer_scale(window->cursor.surface, scale);
136 wl_surface_damage(window->cursor.surface, 0, 0,
137 image->width, image->height);
138 wl_surface_commit(window->cursor.surface);
139 }
140
141 return window;
142}
143
144static void frame_callback(void *data, struct wl_callback *callback, uint32_t time) {
145 struct window *window = data;
146 wl_callback_destroy(callback);
147 window->frame_cb = NULL;
148}
149
150static const struct wl_callback_listener listener = {
151 frame_callback
152};
153
154int window_prerender(struct window *window) {
155 if (window->frame_cb) {
156 return 0;
157 }
158
159 get_next_buffer(window);
160 return 1;
161}
162
163int window_render(struct window *window) {
164 window->frame_cb = wl_surface_frame(window->surface);
165 wl_callback_add_listener(window->frame_cb, &listener, window);
166
167 wl_surface_attach(window->surface, window->buffer->buffer, 0, 0);
168 wl_surface_set_buffer_scale(window->surface, window->scale);
169 wl_surface_damage(window->surface, 0, 0, window->width, window->height);
170 wl_surface_commit(window->surface);
171
172 return 1;
173}
174
175void window_teardown(struct window *window) {
176 // TODO
177}