diff options
author | Drew DeVault <sir@cmpwn.com> | 2018-03-28 15:25:33 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-28 15:25:33 -0400 |
commit | 9070950eecded7bfa64e7bca3bb76b150ccc8b72 (patch) | |
tree | 509a9c669bf2679085e27a1ff1b0c95526abf14c | |
parent | Merge pull request #1641 from swaywm/transformed-events (diff) | |
parent | Address review comments (diff) | |
download | sway-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.build | 16 | ||||
-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.build | 21 | ||||
-rw-r--r-- | include/cairo.h (renamed from include/client/cairo.h) | 10 | ||||
-rw-r--r-- | include/client/buffer.h | 8 | ||||
-rw-r--r-- | include/client/pango.h | 16 | ||||
-rw-r--r-- | include/client/registry.h | 75 | ||||
-rw-r--r-- | include/client/window.h | 67 | ||||
-rw-r--r-- | include/meson.build | 1 | ||||
-rw-r--r-- | include/pool-buffer.h | 21 | ||||
-rw-r--r-- | meson.build | 13 | ||||
-rw-r--r-- | protocols/meson.build | 37 | ||||
-rw-r--r-- | protocols/wlr-layer-shell-unstable-v1.xml | 281 | ||||
-rw-r--r-- | swaybg/main.c | 445 | ||||
-rw-r--r-- | swaybg/meson.build | 18 | ||||
-rw-r--r-- | wayland/pango.c | 73 | ||||
-rw-r--r-- | wayland/registry.c | 293 | ||||
-rw-r--r-- | wayland/window.c | 177 |
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 @@ | |||
1 | lib_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 | ||
15 | static int create_pool_file(size_t size, char **name) { | 14 | static 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 | ||
44 | static void buffer_release(void *data, struct wl_buffer *wl_buffer) { | 43 | static 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 | ||
53 | static struct buffer *create_buffer(struct window *window, struct buffer *buf, | 52 | static 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 | ||
88 | static void destroy_buffer(struct buffer *buffer) { | 82 | static 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 | ||
104 | struct buffer *get_next_buffer(struct window *window) { | 98 | struct 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 | ||
3 | void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { | 8 | void 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 | ||
11 | cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height) { | 16 | cairo_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 | |||
41 | cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) { | 34 | cairo_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 @@ | |||
1 | lib_sway_common = static_library('sway-common', | 1 | deps = [ |
2 | cairo, | ||
3 | wlroots | ||
4 | ] | ||
5 | |||
6 | if gdk_pixbuf.found() | ||
7 | deps += [gdk_pixbuf] | ||
8 | endif | ||
9 | |||
10 | lib_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 | ||
7 | void cairo_set_source_u32(cairo_t *cairo, uint32_t color); | 6 | void cairo_set_source_u32(cairo_t *cairo, uint32_t color); |
8 | 7 | ||
9 | cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height); | 8 | cairo_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 | ||
14 | cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf); | 15 | cairo_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 | |||
6 | struct 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 | |||
10 | PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text, | ||
11 | int32_t scale, bool markup); | ||
12 | void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, | ||
13 | int32_t scale, bool markup, const char *fmt, ...); | ||
14 | void 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 | |||
10 | enum 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 | |||
21 | enum 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 | |||
33 | struct output_state { | ||
34 | struct wl_output *output; | ||
35 | uint32_t flags; | ||
36 | uint32_t width, height; | ||
37 | uint32_t scale; | ||
38 | }; | ||
39 | |||
40 | struct 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 | |||
47 | struct 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 | |||
58 | struct 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 | |||
72 | struct registry *registry_poll(void); | ||
73 | void 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 | |||
12 | struct window; | ||
13 | |||
14 | struct 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 | |||
23 | struct 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 | |||
30 | enum scroll_direction { | ||
31 | SCROLL_UP, | ||
32 | SCROLL_DOWN, | ||
33 | SCROLL_LEFT, | ||
34 | SCROLL_RIGHT, | ||
35 | }; | ||
36 | |||
37 | struct 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 | |||
45 | struct 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 | |||
60 | struct window *window_setup(struct registry *registry, uint32_t width, uint32_t height, | ||
61 | int32_t scale, bool shell_surface); | ||
62 | void window_teardown(struct window *state); | ||
63 | int window_prerender(struct window *state); | ||
64 | int window_render(struct window *state); | ||
65 | void 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 | |||
9 | struct 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 | |||
18 | struct 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') | |||
27 | wayland_egl = dependency('wayland-egl') | 27 | wayland_egl = dependency('wayland-egl') |
28 | wayland_protos = dependency('wayland-protocols') | 28 | wayland_protos = dependency('wayland-protocols') |
29 | xkbcommon = dependency('xkbcommon') | 29 | xkbcommon = dependency('xkbcommon') |
30 | cairo = dependency('cairo') | ||
30 | pango = dependency('pango') | 31 | pango = dependency('pango') |
32 | pangocairo = dependency('pangocairo') | ||
33 | gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false) | ||
31 | pixman = dependency('pixman-1') | 34 | pixman = dependency('pixman-1') |
32 | libcap = dependency('libcap') | 35 | libcap = dependency('libcap') |
33 | libinput = dependency('libinput') | 36 | libinput = dependency('libinput') |
@@ -35,6 +38,12 @@ math = cc.find_library('m') | |||
35 | git = find_program('git', required: false) | 38 | git = find_program('git', required: false) |
36 | a2x = find_program('a2x', required: false) | 39 | a2x = find_program('a2x', required: false) |
37 | 40 | ||
41 | conf_data = configuration_data() | ||
42 | |||
43 | if gdk_pixbuf.found() | ||
44 | conf_data.set('HAVE_GDK_PIXBUF', true) | ||
45 | endif | ||
46 | |||
38 | if a2x.found() | 47 | if 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 | ||
86 | sway_inc = include_directories('include') | 95 | sway_inc = include_directories('include') |
87 | 96 | ||
97 | subdir('include') | ||
98 | subdir('protocols') | ||
88 | subdir('common') | 99 | subdir('common') |
89 | subdir('sway') | 100 | subdir('sway') |
90 | subdir('swaymsg') | 101 | subdir('swaymsg') |
102 | subdir('client') | ||
103 | subdir('swaybg') | ||
91 | 104 | ||
92 | config = configuration_data() | 105 | config = configuration_data() |
93 | config.set('sysconfdir', join_paths(prefix, sysconfdir)) | 106 | 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 @@ | |||
1 | wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') | ||
2 | |||
3 | wayland_scanner = find_program('wayland-scanner') | ||
4 | |||
5 | wayland_scanner_code = generator( | ||
6 | wayland_scanner, | ||
7 | output: '@BASENAME@-protocol.c', | ||
8 | arguments: ['code', '@INPUT@', '@OUTPUT@'], | ||
9 | ) | ||
10 | |||
11 | wayland_scanner_client = generator( | ||
12 | wayland_scanner, | ||
13 | output: '@BASENAME@-client-protocol.h', | ||
14 | arguments: ['client-header', '@INPUT@', '@OUTPUT@'], | ||
15 | ) | ||
16 | |||
17 | protocols = [ | ||
18 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], | ||
19 | ['wlr-layer-shell-unstable-v1.xml'] | ||
20 | ] | ||
21 | |||
22 | wl_protos_src = [] | ||
23 | wl_protos_headers = [] | ||
24 | |||
25 | foreach 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) | ||
29 | endforeach | ||
30 | |||
31 | lib_wl_protos = static_library('wl_protos', wl_protos_src + wl_protos_headers, | ||
32 | dependencies: [wayland_client]) # for the include directory | ||
33 | |||
34 | sway_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 | ||
16 | list_t *surfaces; | 15 | enum background_mode { |
17 | struct 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 | ||
19 | enum scaling_mode { | 24 | struct 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 | ||
27 | void sway_terminate(int exit_code) { | 30 | struct 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 | } | 35 | struct 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 | ||
38 | bool is_valid_color(const char *color) { | 54 | bool 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 | ||
55 | int main(int argc, const char **argv) { | 72 | static 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 | |||
137 | static 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; | 157 | static 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; | 192 | static 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; | 202 | static 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: | 210 | struct 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: | 215 | static 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 | |||
237 | static void handle_global_remove(void *data, struct wl_registry *registry, | ||
238 | uint32_t name) { | ||
239 | // who cares | ||
240 | } | ||
241 | |||
242 | static 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); | 247 | int 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, ®istry_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 @@ | |||
1 | executable( | ||
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 | |||
11 | PangoLayout *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 | |||
34 | void 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 | |||
55 | void 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 | |||
15 | static 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 | |||
27 | static 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 | |||
33 | static void display_handle_done(void *data, struct wl_output *wl_output) { | ||
34 | // this space intentionally left blank | ||
35 | } | ||
36 | |||
37 | static 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 | |||
43 | static 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 | |||
50 | const 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 | |||
61 | const 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 | |||
72 | static 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 | |||
119 | static 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 | |||
124 | static 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 | |||
129 | static 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 | |||
147 | static 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 | |||
168 | static 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 | |||
173 | static 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 | |||
182 | static 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 | |||
202 | static void seat_handle_name(void *data, struct wl_seat *seat, const char *name) { | ||
203 | // this space intentionally left blank | ||
204 | } | ||
205 | |||
206 | static const struct wl_seat_listener seat_listener = { | ||
207 | .capabilities = seat_handle_capabilities, | ||
208 | .name = seat_handle_name, | ||
209 | }; | ||
210 | |||
211 | static 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 | |||
238 | static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) { | ||
239 | // this space intentionally left blank | ||
240 | } | ||
241 | |||
242 | static const struct wl_registry_listener registry_listener = { | ||
243 | .global = registry_global, | ||
244 | .global_remove = registry_global_remove | ||
245 | }; | ||
246 | |||
247 | struct 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, ®istry_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 | |||
270 | void 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 | |||
18 | static 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 | |||
27 | static void pointer_handle_leave(void *data, struct wl_pointer *pointer, | ||
28 | uint32_t serial, struct wl_surface *surface) { | ||
29 | } | ||
30 | |||
31 | static 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 | |||
39 | static 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 | |||
49 | static 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 | |||
71 | static 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 | |||
79 | void 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 | |||
86 | static const struct wl_shell_surface_listener surface_listener = { | ||
87 | .configure = shell_surface_configure | ||
88 | }; | ||
89 | |||
90 | void 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 | |||
96 | struct 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 | |||
144 | static 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 | |||
150 | static const struct wl_callback_listener listener = { | ||
151 | frame_callback | ||
152 | }; | ||
153 | |||
154 | int 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 | |||
163 | int 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 | |||
175 | void window_teardown(struct window *window) { | ||
176 | // TODO | ||
177 | } | ||