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 | 45a50d5afe013b31d9c94090d990bca49448d396 (diff) | |
parent | d39bda76c4007c42452a81883fefc671b816a74b (diff) |
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 @@ +lib_sway_client = static_library( + 'sway-client', + files( + 'pool-buffer.c', + ), + dependencies: [ + cairo, + gdk_pixbuf, + pango, + pangocairo, + wlroots, + wayland_client, + ], + link_with: [lib_sway_common], + include_directories: sway_inc +) diff --git a/wayland/buffers.c b/client/pool-buffer.c index e9780997..93cfcfc5 100644 --- a/wayland/buffers.c +++ b/client/pool-buffer.c @@ -1,21 +1,20 @@ #define _XOPEN_SOURCE 500 -#include <wayland-client.h> +#include <assert.h> #include <cairo/cairo.h> -#include <pango/pangocairo.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> -#include <stdio.h> -#include <unistd.h> -#include <errno.h> #include <sys/mman.h> -#include "client/buffer.h" -#include "list.h" -#include "log.h" +#include <pango/pangocairo.h> +#include <unistd.h> +#include <wayland-client.h> +#include "config.h" +#include "pool-buffer.h" static int create_pool_file(size_t size, char **name) { static const char template[] = "sway-client-XXXXXX"; const char *path = getenv("XDG_RUNTIME_DIR"); - if (!path) { + if (!path) { return -1; } @@ -42,7 +41,7 @@ static int create_pool_file(size_t size, char **name) { } static void buffer_release(void *data, struct wl_buffer *wl_buffer) { - struct buffer *buffer = data; + struct pool_buffer *buffer = data; buffer->busy = false; } @@ -50,22 +49,17 @@ static const struct wl_buffer_listener buffer_listener = { .release = buffer_release }; -static struct buffer *create_buffer(struct window *window, struct buffer *buf, - int32_t width, int32_t height, int32_t scale, uint32_t format) { - - width *= scale; - height *= scale; +static struct pool_buffer *create_buffer(struct wl_shm *shm, + struct pool_buffer *buf, int32_t width, int32_t height, + uint32_t format) { uint32_t stride = width * 4; uint32_t size = stride * height; char *name; int fd = create_pool_file(size, &name); - if (fd == -1) { - sway_abort("Unable to allocate buffer"); - return NULL; // never reached - } + assert(fd); void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - struct wl_shm_pool *pool = wl_shm_create_pool(window->registry->shm, fd, size); + struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); buf->buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, format); wl_shm_pool_destroy(pool); @@ -85,7 +79,7 @@ static struct buffer *create_buffer(struct window *window, struct buffer *buf, return buf; } -static void destroy_buffer(struct buffer *buffer) { +static void destroy_buffer(struct pool_buffer *buffer) { if (buffer->buffer) { wl_buffer_destroy(buffer->buffer); } @@ -98,37 +92,33 @@ static void destroy_buffer(struct buffer *buffer) { if (buffer->pango) { g_object_unref(buffer->pango); } - memset(buffer, 0, sizeof(struct buffer)); + memset(buffer, 0, sizeof(struct pool_buffer)); } -struct buffer *get_next_buffer(struct window *window) { - struct buffer *buffer = NULL; +struct pool_buffer *get_next_buffer(struct wl_shm *shm, + struct pool_buffer pool[static 2], uint32_t width, uint32_t height) { + struct pool_buffer *buffer = NULL; - int i; - for (i = 0; i < 2; ++i) { - if (window->buffers[i].busy) { + for (size_t i = 0; i < 2; ++i) { + if (pool[i].busy) { continue; } - buffer = &window->buffers[i]; + buffer = &pool[i]; } if (!buffer) { return NULL; } - if (buffer->width != window->width || buffer->height != window->height) { + if (buffer->width != width || buffer->height != height) { destroy_buffer(buffer); } if (!buffer->buffer) { - if (!create_buffer(window, buffer, - window->width, window->height, window->scale, + if (!create_buffer(shm, buffer, width, height, WL_SHM_FORMAT_ARGB8888)) { return NULL; } } - - window->cairo = buffer->cairo; - window->buffer = buffer; return buffer; } diff --git a/wayland/cairo.c b/common/cairo.c index 193205b1..c267c77c 100644 --- a/wayland/cairo.c +++ b/common/cairo.c @@ -1,4 +1,9 @@ -#include "client/cairo.h" +#include <stdint.h> +#include <cairo/cairo.h> +#include "cairo.h" +#ifdef HAVE_GDK_PIXBUF +#include <gdk-pixbuf/gdk-pixbuf.h> +#endif void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { cairo_set_source_rgba(cairo, @@ -8,45 +13,31 @@ void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { (color >> (0*8) & 0xFF) / 255.0); } -cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height) { +cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, + int width, int height) { int image_width = cairo_image_surface_get_width(image); int image_height = cairo_image_surface_get_height(image); cairo_surface_t *new = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - cairo_t *cairo = cairo_create(new); - - cairo_scale(cairo, (double) width / image_width, (double) height / image_height); - + cairo_scale(cairo, (double)width / image_width, + (double)height / image_height); cairo_set_source_surface(cairo, image, 0, 0); - cairo_paint(cairo); + cairo_paint(cairo); cairo_destroy(cairo); - return new; } -#ifdef WITH_GDK_PIXBUF -#include <gdk-pixbuf/gdk-pixbuf.h> - -#ifndef GDK_PIXBUF_CHECK_VERSION -#define GDK_PIXBUF_CHECK_VERSION(major,minor,micro) \ - (GDK_PIXBUF_MAJOR > (major) || \ - (GDK_PIXBUF_MAJOR == (major) && GDK_PIXBUF_MINOR > (minor)) || \ - (GDK_PIXBUF_MAJOR == (major) && GDK_PIXBUF_MINOR == (minor) && \ - GDK_PIXBUF_MICRO >= (micro))) -#endif - +#ifdef HAVE_GDK_PIXBUF cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) { int chan = gdk_pixbuf_get_n_channels(gdkbuf); - if (chan < 3) return NULL; + if (chan < 3) { + return NULL; + } -#if GDK_PIXBUF_CHECK_VERSION(2,32,0) const guint8* gdkpix = gdk_pixbuf_read_pixels(gdkbuf); -#else - const guint8* gdkpix = gdk_pixbuf_get_pixels(gdkbuf); -#endif if (!gdkpix) { return NULL; } @@ -101,7 +92,9 @@ cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdk * ------ * tested as equal to lround(z/255.0) for uint z in [0..0xfe02] */ -#define PREMUL_ALPHA(x,a,b,z) G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } G_STMT_END +#define PREMUL_ALPHA(x,a,b,z) \ + G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } \ + G_STMT_END int i; for (i = h; i; --i) { const guint8 *gp = gdkpix; @@ -131,4 +124,4 @@ cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdk cairo_surface_mark_dirty(cs); return cs; } -#endif //WITH_GDK_PIXBUF +#endif //HAVE_GDK_PIXBUF diff --git a/common/meson.build b/common/meson.build index abe0cdcf..01736ca6 100644 --- a/common/meson.build +++ b/common/meson.build @@ -1,12 +1,23 @@ -lib_sway_common = static_library('sway-common', +deps = [ + cairo, + wlroots +] + +if gdk_pixbuf.found() + deps += [gdk_pixbuf] +endif + +lib_sway_common = static_library( + 'sway-common', files( + 'cairo.c', + 'ipc-client.c', 'log.c', 'list.c', - 'util.c', - 'stringop.c', 'readline.c', - 'ipc-client.c' + 'stringop.c', + 'util.c' ), - dependencies: [ wlroots ], + dependencies: deps, include_directories: sway_inc ) diff --git a/include/client/cairo.h b/include/cairo.h index e7ef7c7e..31672705 100644 --- a/include/client/cairo.h +++ b/include/cairo.h @@ -1,17 +1,19 @@ #ifndef _SWAY_CAIRO_H #define _SWAY_CAIRO_H - #include <stdint.h> #include <cairo/cairo.h> void cairo_set_source_u32(cairo_t *cairo, uint32_t color); -cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height); +cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, + int width, int height); -#ifdef WITH_GDK_PIXBUF +#include "config.h" +#ifdef HAVE_GDK_PIXBUF #include <gdk-pixbuf/gdk-pixbuf.h> -cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf); +cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( + const GdkPixbuf *gdkbuf); #endif //WITH_GDK_PIXBUF #endif diff --git a/include/client/buffer.h b/include/client/buffer.h deleted file mode 100644 index eb9973ed..00000000 --- a/include/client/buffer.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _BUFFER_H -#define _BUFFER_H - -#include "client/window.h" - -struct buffer *get_next_buffer(struct window *state); - -#endif diff --git a/include/client/pango.h b/include/client/pango.h deleted file mode 100644 index dd2f53c3..00000000 --- a/include/client/pango.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _SWAY_CLIENT_PANGO_H -#define _SWAY_CLIENT_PANGO_H - -#include <cairo/cairo.h> -#include <pango/pangocairo.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdint.h> - -PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text, - int32_t scale, bool markup); -void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, - int32_t scale, bool markup, const char *fmt, ...); -void pango_printf(cairo_t *cairo, const char *font, int32_t scale, bool markup, const char *fmt, ...); - -#endif diff --git a/include/client/registry.h b/include/client/registry.h deleted file mode 100644 index 9dfbd835..00000000 --- a/include/client/registry.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef _SWAY_CLIENT_REGISTRY_H -#define _SWAY_CLIENT_REGISTRY_H - -#include <wayland-client.h> -#include <xkbcommon/xkbcommon.h> -#include "wayland-desktop-shell-client-protocol.h" -#include "wayland-swaylock-client-protocol.h" -#include "list.h" - -enum mod_bit { - MOD_SHIFT = 1<<0, - MOD_CAPS = 1<<1, - MOD_CTRL = 1<<2, - MOD_ALT = 1<<3, - MOD_MOD2 = 1<<4, - MOD_MOD3 = 1<<5, - MOD_LOGO = 1<<6, - MOD_MOD5 = 1<<7, -}; - -enum mask { - MASK_SHIFT, - MASK_CAPS, - MASK_CTRL, - MASK_ALT, - MASK_MOD2, - MASK_MOD3, - MASK_LOGO, - MASK_MOD5, - MASK_LAST -}; - -struct output_state { - struct wl_output *output; - uint32_t flags; - uint32_t width, height; - uint32_t scale; -}; - -struct xkb { - struct xkb_state *state; - struct xkb_context *context; - struct xkb_keymap *keymap; - xkb_mod_mask_t masks[MASK_LAST]; -}; - -struct input { - struct xkb xkb; - - xkb_keysym_t sym; - uint32_t code; - uint32_t last_code; - uint32_t modifiers; - - void (*notify)(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint); -}; - -struct registry { - struct wl_compositor *compositor; - struct wl_display *display; - struct wl_pointer *pointer; - struct wl_keyboard *keyboard; - struct wl_seat *seat; - struct wl_shell *shell; - struct wl_shm *shm; - struct desktop_shell *desktop_shell; - struct lock *swaylock; - struct input *input; - list_t *outputs; -}; - -struct registry *registry_poll(void); -void registry_teardown(struct registry *registry); - -#endif diff --git a/include/client/window.h b/include/client/window.h deleted file mode 100644 index 8af8225c..00000000 --- a/include/client/window.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef _CLIENT_H -#define _CLIENT_H - -#include <wayland-client.h> -#include "wayland-desktop-shell-client-protocol.h" -#include <cairo/cairo.h> -#include <pango/pangocairo.h> -#include <stdbool.h> -#include "list.h" -#include "client/registry.h" - -struct window; - -struct buffer { - struct wl_buffer *buffer; - cairo_surface_t *surface; - cairo_t *cairo; - PangoContext *pango; - uint32_t width, height; - bool busy; -}; - -struct cursor { - struct wl_surface *surface; - struct wl_cursor_theme *cursor_theme; - struct wl_cursor *cursor; - struct wl_pointer *pointer; -}; - -enum scroll_direction { - SCROLL_UP, - SCROLL_DOWN, - SCROLL_LEFT, - SCROLL_RIGHT, -}; - -struct pointer_input { - int last_x; - int last_y; - - void (*notify_button)(struct window *window, int x, int y, uint32_t button, uint32_t state_w); - void (*notify_scroll)(struct window *window, enum scroll_direction direction); -}; - -struct window { - struct registry *registry; - struct buffer buffers[2]; - struct buffer *buffer; - struct wl_surface *surface; - struct wl_shell_surface *shell_surface; - struct wl_callback *frame_cb; - struct cursor cursor; - uint32_t width, height; - int32_t scale; - char *font; - cairo_t *cairo; - struct pointer_input pointer_input; -}; - -struct window *window_setup(struct registry *registry, uint32_t width, uint32_t height, - int32_t scale, bool shell_surface); -void window_teardown(struct window *state); -int window_prerender(struct window *state); -int window_render(struct window *state); -void window_make_shell(struct window *window); - -#endif diff --git a/include/meson.build b/include/meson.build new file mode 100644 index 00000000..65ed027a --- /dev/null +++ b/include/meson.build @@ -0,0 +1 @@ +configure_file(output: 'config.h', configuration: conf_data) diff --git a/include/pool-buffer.h b/include/pool-buffer.h new file mode 100644 index 00000000..cdebd64d --- /dev/null +++ b/include/pool-buffer.h @@ -0,0 +1,21 @@ +#ifndef _SWAY_BUFFERS_H +#define _SWAY_BUFFERS_H +#include <cairo/cairo.h> +#include <pango/pangocairo.h> +#include <stdbool.h> +#include <stdint.h> +#include <wayland-client.h> + +struct pool_buffer { + struct wl_buffer *buffer; + cairo_surface_t *surface; + cairo_t *cairo; + PangoContext *pango; + uint32_t width, height; + bool busy; +}; + +struct pool_buffer *get_next_buffer(struct wl_shm *shm, + struct pool_buffer pool[static 2], uint32_t width, uint32_t height); + +#endif diff --git a/meson.build b/meson.build index f27ac451..b681f43a 100644 --- a/meson.build +++ b/meson.build @@ -27,7 +27,10 @@ wayland_client = dependency('wayland-client') wayland_egl = dependency('wayland-egl') wayland_protos = dependency('wayland-protocols') xkbcommon = dependency('xkbcommon') +cairo = dependency('cairo') pango = dependency('pango') +pangocairo = dependency('pangocairo') +gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false) pixman = dependency('pixman-1') libcap = dependency('libcap') libinput = dependency('libinput') @@ -35,6 +38,12 @@ math = cc.find_library('m') git = find_program('git', required: false) a2x = find_program('a2x', required: false) +conf_data = configuration_data() + +if gdk_pixbuf.found() + conf_data.set('HAVE_GDK_PIXBUF', true) +endif + if a2x.found() mandir = get_option('mandir') man_files = [ @@ -85,9 +94,13 @@ add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') sway_inc = include_directories('include') +subdir('include') +subdir('protocols') subdir('common') subdir('sway') subdir('swaymsg') +subdir('client') +subdir('swaybg') config = configuration_data() config.set('sysconfdir', join_paths(prefix, sysconfdir)) diff --git a/protocols/meson.build b/protocols/meson.build new file mode 100644 index 00000000..1fda600e --- /dev/null +++ b/protocols/meson.build @@ -0,0 +1,37 @@ +wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') + +wayland_scanner = find_program('wayland-scanner') + +wayland_scanner_code = generator( + wayland_scanner, + output: '@BASENAME@-protocol.c', + arguments: ['code', '@INPUT@', '@OUTPUT@'], +) + +wayland_scanner_client = generator( + wayland_scanner, + output: '@BASENAME@-client-protocol.h', + arguments: ['client-header', '@INPUT@', '@OUTPUT@'], +) + +protocols = [ + [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], + ['wlr-layer-shell-unstable-v1.xml'] +] + +wl_protos_src = [] +wl_protos_headers = [] + +foreach p : protocols + xml = join_paths(p) + wl_protos_src += wayland_scanner_code.process(xml) + wl_protos_headers += wayland_scanner_client.process(xml) +endforeach + +lib_wl_protos = static_library('wl_protos', wl_protos_src + wl_protos_headers, + dependencies: [wayland_client]) # for the include directory + +sway_protos = declare_dependency( + link_with: lib_wl_protos, + sources: wl_protos_headers, +) diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 00000000..3181c0bb --- /dev/null +++ b/protocols/wlr-layer-shell-unstable-v1.xml @@ -0,0 +1,281 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="wlr_layer_shell_unstable_v1"> + <copyright> + Copyright © 2017 Drew DeVault + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + </copyright> + + <interface name="zwlr_layer_shell_v1" version="1"> + <description summary="create surfaces that are layers of the desktop"> + Clients can use this interface to assign the surface_layer role to + wl_surfaces. Such surfaces are assigned to a "layer" of the output and + rendered with a defined z-depth respective to each other. They may also be + anchored to the edges and corners of a screen and specify input handling + semantics. This interface should be suitable for the implementation of + many desktop shell components, and a broad number of other applications + that interact with the desktop. + </description> + + <request name="get_layer_surface"> + <description summary="create a layer_surface from a surface"> + Create a layer surface for an existing surface. This assigns the role of + layer_surface, or raises a protocol error if another role is already + assigned. + + Creating a layer surface from a wl_surface which has a buffer attached + or committed is a client error, and any attempts by a client to attach + or manipulate a buffer prior to the first layer_surface.configure call + must also be treated as errors. + + Clients can specify a namespace that defines the purpose of the layer + surface. + </description> + <arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/> + <arg name="surface" type="object" interface="wl_surface"/> + <arg name="output" type="object" interface="wl_output"/> + <arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/> + <arg name="namespace" type="string" summary="namespace for the layer surface"/> + </request> + + <enum name="error"> + <entry name="role" value="0" summary="wl_surface has another role"/> + <entry name="invalid_layer" value="1" summary="layer value is invalid"/> + <entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/> + </enum> + + <enum name="layer"> + <description summary="available layers for surfaces"> + These values indicate which layers a surface can be rendered in. They + are ordered by z depth, bottom-most first. Traditional shell surfaces + will typically be rendered between the bottom and top layers. + Fullscreen shell surfaces are typically rendered at the top layer. + Multiple surfaces can share a single layer, and ordering within a + single layer is undefined. + </description> + + <entry name="background" value="0"/> + <entry name="bottom" value="1"/> + <entry name="top" value="2"/> + <entry name="overlay" value="3"/> + </enum> + </interface> + + <interface name="zwlr_layer_surface_v1" version="1"> + <description summary="layer metadata interface"> + An interface that may be implemented by a wl_surface, for surfaces that + are designed to be rendered as a layer of a stacked desktop-like + environment. + + Layer surface state (size, anchor, exclusive zone, margin, interactivity) + is double-buffered, and will be applied at the time wl_surface.commit of + the corresponding wl_surface is called. + </description> + + <request name="set_size"> + <description summary="sets the size of the surface"> + Sets the size of the surface in surface-local coordinates. The + compositor will display the surface centered with respect to its + anchors. + + If you pass 0 for either value, the compositor will assign it and + inform you of the assignment in the configure event. You must set your + anchor to opposite edges in the dimensions you omit; not doing so is a + protocol error. Both values are 0 by default. + + Size is double-buffered, see wl_surface.commit. + </description> + <arg name="width" type="uint"/> + <arg name="height" type="uint"/> + </request> + + <request name="set_anchor"> + <description summary="configures the anchor point of the surface"> + Requests that the compositor anchor the surface to the specified edges + and corners. If two orthoginal edges are specified (e.g. 'top' and + 'left'), then the anchor point will be the intersection of the edges + (e.g. the top left corner of the output); otherwise the anchor point + will be centered on that edge, or in the center if none is specified. + + Anchor is double-buffered, see wl_surface.commit. + </description> + <arg name="anchor" type="uint" enum="anchor"/> + </request> + + <request name="set_exclusive_zone"> + <description summary="configures the exclusive geometry of this surface"> + Requests that the compositor avoids occluding an area of the surface + with other surfaces. The compositor's use of this information is + implementation-dependent - do not assume that this region will not + actually be occluded. + + A positive value is only meaningful if the surface is anchored to an + edge, rather than a corner. The zone is the number of surface-local + coordinates from the edge that are considered exclusive. + + Surfaces that do not wish to have an exclusive zone may instead specify + how they should interact with surfaces that do. If set to zero, the + surface indicates that it would like to be moved to avoid occluding + surfaces with a positive excluzive zone. If set to -1, the surface + indicates that it would not like to be moved to accomodate for other + surfaces, and the compositor should extend it all the way to the edges + it is anchored to. + + For example, a panel might set its exclusive zone to 10, so that + maximized shell surfaces are not shown on top of it. A notification + might set its exclusive zone to 0, so that it is moved to avoid + occluding the panel, but shell surfaces are shown underneath it. A + wallpaper or lock screen might set their exclusive zone to -1, so that + they stretch below or over the panel. + + The default value is 0. + + Exclusive zone is double-buffered, see wl_surface.commit. + </description> + <arg name="zone" type="int"/> + </request> + + <request name="set_margin"> + <description summary="sets a margin from the anchor point"> + Requests that the surface be placed some distance away from the anchor + point on the output, in surface-local coordinates. Setting this value + for edges you are not anchored to has no effect. + + The exclusive zone includes the margin. + + Margin is double-buffered, see wl_surface.commit. + </description> + <arg name="top" type="int"/> + <arg name="right" type="int"/> + <arg name="bottom" type="int"/> + <arg name="left" type="int"/> + </request> + + <request name="set_keyboard_interactivity"> + <description summary="requests keyboard events"> + Set to 1 to request that the seat send keyboard events to this layer + surface. For layers below the shell surface layer, the seat will use + normal focus semantics. For layers above the shell surface layers, the + seat will always give exclusive keyboard focus to the top-most layer + which has keyboard interactivity set to true. + + Layer surfaces receive pointer, touch, and tablet events normally. If + you do not want to receive them, set the input region on your surface + to an empty region. + + Events is double-buffered, see wl_surface.commit. + </description> + <arg name="keyboard_interactivity" type="uint"/> + </request> + + <request name="get_popup"> + <description summary="assign this layer_surface as an xdg_popup parent"> + This assigns an xdg_popup's parent to this layer_surface. This popup + should have been created via xdg_surface::get_popup with the parent set + to NULL, and this request must be invoked before committing the popup's + initial state. + + See the documentation of xdg_popup for more details about what an + xdg_popup is and how it is used. + </description> + <arg name="popup" type="object" interface="xdg_popup"/> + </request> + + <request name="ack_configure"> + <description summary="ack a configure event"> + When a configure event is received, if a client commits the + surface in response to the configure event, then the client + must make an ack_configure request sometime before the commit + request, passing along the serial of the configure event. + + If the client receives multiple configure events before it + can respond to one, it only has to ack the last configure event. + + A client is not required to commit immediately after sending + an ack_configure request - it may even ack_configure several times + before its next surface commit. + + A client may send multiple ack_configure requests before committing, but + only the last request sent before a commit indicates which configure + event the client really is responding to. + </description> + <arg name="serial" type="uint" summary="the serial from the configure event"/> + </request> + + <request name="destroy" type="destructor"> + <description summary="destroy the layer_surface"> + This request destroys the layer surface. + </description> + </request> + + <event name="configure"> + <description summary="suggest a surface change"> + The configure event asks the client to resize its surface. + + Clients should arrange their surface for the new states, and then send + an ack_configure request with the serial sent in this configure event at + some point before committing the new surface. + + The client is free to dismiss all but the last configure event it + received. + + The width and height arguments specify the size of the window in + surface-local coordinates. + + The size is a hint, in the sense that the client is free to ignore it if + it doesn't resize, pick a smaller size (to satisfy aspect ratio or + resize in steps of NxM pixels). If the client picks a smaller size and + is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the + surface will be centered on this axis. + + If the width or height arguments are zero, it means the client should + decide its own window dimension. + </description> + <arg name="serial" type="uint"/> + <arg name="width" type="uint"/> + <arg name="height" type="uint"/> + </event> + + <event name="closed"> + <description summary="surface should be closed"> + The closed event is sent by the compositor when the surface will no + longer be shown. The output may have been destroyed or the user may + have asked for it to be removed. Further changes to the surface will be + ignored. The client should destroy the resource after receiving this + event, and create a new surface if they so choose. + </description> + </event> + + <enum name="error"> + <entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/> + <entry name="invalid_size" value="1" summary="size is invalid"/> + <entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/> + </enum> + + <enum name="anchor" bitfield="true"> + <entry name="top" value="1" summary="the top edge of the anchor rectangle"/> + <entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/> + <entry name="left" value="4" summary="the left edge of the anchor rectangle"/> + <entry name="right" value="8" summary="the right edge of the anchor rectangle"/> + </enum> + </interface> +</protocol> diff --git a/swaybg/main.c b/swaybg/main.c index 2fdd4220..f431526c 100644 --- a/swaybg/main.c +++ b/swaybg/main.c @@ -1,44 +1,61 @@ -#include "wayland-desktop-shell-client-protocol.h" +#include <assert.h> +#include <ctype.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> -#include <ctype.h> -#include <wayland-client.h> -#include <time.h> #include <string.h> -#include "client/window.h" -#include "client/registry.h" -#include "client/cairo.h" -#include "log.h" -#include "list.h" +#include <time.h> +#include <wayland-client.h> +#include <wlr/util/log.h> +#include "pool-buffer.h" +#include "cairo.h" #include "util.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" -list_t *surfaces; -struct registry *registry; +enum background_mode { + BACKGROUND_MODE_STRETCH, + BACKGROUND_MODE_FILL, + BACKGROUND_MODE_FIT, + BACKGROUND_MODE_CENTER, + BACKGROUND_MODE_TILE, + BACKGROUND_MODE_SOLID_COLOR, +}; -enum scaling_mode { - SCALING_MODE_STRETCH, - SCALING_MODE_FILL, - SCALING_MODE_FIT, - SCALING_MODE_CENTER, - SCALING_MODE_TILE, +struct swaybg_args { + int output_idx; + const char *path; + enum background_mode mode; }; -void sway_terminate(int exit_code) { - int i; - for (i = 0; i < surfaces->length; ++i) { - struct window *window = surfaces->items[i]; - window_teardown(window); - } - list_free(surfaces); - registry_teardown(registry); - exit(exit_code); -} +struct swaybg_context { + uint32_t color; + cairo_surface_t *image; +}; + +struct swaybg_state { + const struct swaybg_args *args; + struct swaybg_context context; + + struct wl_display *display; + struct wl_compositor *compositor; + struct zwlr_layer_shell_v1 *layer_shell; + struct wl_shm *shm; + + struct wl_output *output; + struct wl_surface *surface; + struct zwlr_layer_surface_v1 *layer_surface; + + bool run_display; + uint32_t width, height; + struct pool_buffer buffers[2]; + struct pool_buffer *current_buffer; +}; bool is_valid_color(const char *color) { int len = strlen(color); if (len != 7 || color[0] != '#') { - sway_log(L_ERROR, "%s is not a valid color for swaybg. Color should be specified as #rrggbb (no alpha).", color); + wlr_log(L_ERROR, "%s is not a valid color for swaybg. " + "Color should be specified as #rrggbb (no alpha).", color); return false; } @@ -52,163 +69,245 @@ bool is_valid_color(const char *color) { return true; } -int main(int argc, const char **argv) { - init_log(L_INFO); - surfaces = create_list(); - registry = registry_poll(); +static void render_image(struct swaybg_state *state) { + cairo_t *cairo = state->current_buffer->cairo; + cairo_surface_t *image = state->context.image; + double width = cairo_image_surface_get_width(image); + double height = cairo_image_surface_get_height(image); + int wwidth = state->width; + int wheight = state->height; - if (argc != 4) { - sway_abort("Do not run this program manually. See man 5 sway and look for output options."); - } + switch (state->args->mode) { + case BACKGROUND_MODE_STRETCH: + cairo_scale(cairo, (double)wwidth / width, (double)wheight / height); + cairo_set_source_surface(cairo, image, 0, 0); + break; + case BACKGROUND_MODE_FILL: { + double window_ratio = (double)wwidth / wheight; + double bg_ratio = width / height; - if (!registry->desktop_shell) { - sway_abort("swaybg requires the compositor to support the desktop-shell extension."); + if (window_ratio > bg_ratio) { + double scale = (double)wwidth / width; + cairo_scale(cairo, scale, scale); + cairo_set_source_surface(cairo, image, + 0, (double)wheight / 2 / scale - height / 2); + } else { + double scale = (double)wheight / height; + cairo_scale(cairo, scale, scale); + cairo_set_source_surface(cairo, image, + (double)wwidth / 2 / scale - width / 2, 0); + } + break; } + case BACKGROUND_MODE_FIT: { + double window_ratio = (double)wwidth / wheight; + double bg_ratio = width / height; - int desired_output = atoi(argv[1]); - sway_log(L_INFO, "Using output %d of %d", desired_output, registry->outputs->length); - int i; - struct output_state *output = registry->outputs->items[desired_output]; - struct window *window = window_setup(registry, - output->width, output->height, output->scale, false); - if (!window) { - sway_abort("Failed to create surfaces."); - } - desktop_shell_set_background(registry->desktop_shell, output->output, window->surface); - window_make_shell(window); - list_add(surfaces, window); - - if (strcmp(argv[3], "solid_color") == 0 && is_valid_color(argv[2])) { - cairo_set_source_u32(window->cairo, parse_color(argv[2])); - cairo_paint(window->cairo); - window_render(window); - } else { -#ifdef WITH_GDK_PIXBUF - GError *err = NULL; - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(argv[2], &err); - if (!pixbuf) { - sway_abort("Failed to load background image."); - } - cairo_surface_t *image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); - g_object_unref(pixbuf); -#else - cairo_surface_t *image = cairo_image_surface_create_from_png(argv[2]); -#endif //WITH_GDK_PIXBUF - if (!image) { - sway_abort("Failed to read background image."); - } - if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) { - sway_abort("Failed to read background image: %s." -#ifndef WITH_GDK_PIXBUF - "\nSway was compiled without gdk_pixbuf support, so only" - "\nPNG images can be loaded. This is the likely cause." -#endif //WITH_GDK_PIXBUF - , cairo_status_to_string(cairo_surface_status(image))); - } - double width = cairo_image_surface_get_width(image); - double height = cairo_image_surface_get_height(image); - - const char *scaling_mode_str = argv[3]; - enum scaling_mode scaling_mode = SCALING_MODE_STRETCH; - if (strcmp(scaling_mode_str, "stretch") == 0) { - scaling_mode = SCALING_MODE_STRETCH; - } else if (strcmp(scaling_mode_str, "fill") == 0) { - scaling_mode = SCALING_MODE_FILL; - } else if (strcmp(scaling_mode_str, "fit") == 0) { - scaling_mode = SCALING_MODE_FIT; - } else if (strcmp(scaling_mode_str, "center") == 0) { - scaling_mode = SCALING_MODE_CENTER; - } else if (strcmp(scaling_mode_str, "tile") == 0) { - scaling_mode = SCALING_MODE_TILE; + if (window_ratio > bg_ratio) { + double scale = (double)wheight / height; + cairo_scale(cairo, scale, scale); + cairo_set_source_surface(cairo, image, + (double)wwidth / 2 / scale - width / 2, 0); } else { - sway_abort("Unsupported scaling mode: %s", scaling_mode_str); + double scale = (double)wwidth / width; + cairo_scale(cairo, scale, scale); + cairo_set_source_surface(cairo, image, + 0, (double)wheight / 2 / scale - height / 2); } + break; + } + case BACKGROUND_MODE_CENTER: + cairo_set_source_surface(cairo, image, + (double)wwidth / 2 - width / 2, + (double)wheight / 2 - height / 2); + break; + case BACKGROUND_MODE_TILE: { + cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + cairo_set_source(cairo, pattern); + break; + } + case BACKGROUND_MODE_SOLID_COLOR: + assert(0); + break; + } + cairo_paint(cairo); +} + +static void render_frame(struct swaybg_state *state) { + state->current_buffer = get_next_buffer(state->shm, + state->buffers, state->width, state->height); + cairo_t *cairo = state->current_buffer->cairo; + + switch (state->args->mode) { + case BACKGROUND_MODE_SOLID_COLOR: + cairo_set_source_u32(cairo, state->context.color); + cairo_paint(cairo); + break; + default: + render_image(state); + break; + } + + wl_surface_attach(state->surface, state->current_buffer->buffer, 0, 0); + wl_surface_damage(state->surface, 0, 0, state->width, state->height); + wl_surface_commit(state->surface); +} - int wwidth = window->width * window->scale; - int wheight = window->height * window->scale; - - for (i = 0; i < surfaces->length; ++i) { - struct window *window = surfaces->items[i]; - if (window_prerender(window) && window->cairo) { - switch (scaling_mode) { - case SCALING_MODE_STRETCH: - cairo_scale(window->cairo, - (double) wwidth / width, - (double) wheight / height); - cairo_set_source_surface(window->cairo, image, 0, 0); - break; - case SCALING_MODE_FILL: - { - double window_ratio = (double) wwidth / wheight; - double bg_ratio = width / height; - - if (window_ratio > bg_ratio) { - double scale = (double) wwidth / width; - cairo_scale(window->cairo, scale, scale); - cairo_set_source_surface(window->cairo, image, - 0, - (double) wheight/2 / scale - height/2); - } else { - double scale = (double) wheight / height; - cairo_scale(window->cairo, scale, scale); - cairo_set_source_surface(window->cairo, image, - (double) wwidth/2 / scale - width/2, - 0); - } - break; - } - case SCALING_MODE_FIT: - { - double window_ratio = (double) wwidth / wheight; - double bg_ratio = width / height; - - if (window_ratio > bg_ratio) { - double scale = (double) wheight / height; - cairo_scale(window->cairo, scale, scale); - cairo_set_source_surface(window->cairo, image, - (double) wwidth/2 / scale - width/2, - 0); - } else { - double scale = (double) wwidth / width; - cairo_scale(window->cairo, scale, scale); - cairo_set_source_surface(window->cairo, image, - 0, - (double) wheight/2 / scale - height/2); - } - break; - } - case SCALING_MODE_CENTER: - cairo_set_source_surface(window->cairo, image, - (double) wwidth/2 - width/2, - (double) wheight/2 - height/2); - break; - case SCALING_MODE_TILE: - { - cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); - cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); - cairo_set_source(window->cairo, pattern); - break; - } - default: - sway_abort("Scaling mode '%s' not implemented yet!", scaling_mode_str); - } - - cairo_paint(window->cairo); - - window_render(window); - } +static bool prepare_context(struct swaybg_state *state) { + if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) { + state->context.color = parse_color(state->args->path); + return is_valid_color(state->args->path); + } +#ifdef HAVE_GDK_PIXBUF + GError *err = NULL; + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(state->args->path, &err); + if (!pixbuf) { + wlr_log(L_ERROR, "Failed to load background image."); + return false; + } + state->context.image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); + g_object_unref(pixbuf); +#else + state->context.image = cairo_image_surface_create_from_png( + state->args->path); +#endif //HAVE_GDK_PIXBUF + if (!state->context.image) { + wlr_log(L_ERROR, "Failed to read background image."); + return false; + } + if (cairo_surface_status(state->context.image) != CAIRO_STATUS_SUCCESS) { + wlr_log(L_ERROR, "Failed to read background image: %s." +#ifndef HAVE_GDK_PIXBUF + "\nSway was compiled without gdk_pixbuf support, so only" + "\nPNG images can be loaded. This is the likely cause." +#endif //HAVE_GDK_PIXBUF + , cairo_status_to_string( + cairo_surface_status(state->context.image))); + return false; + } + return true; +} + +static void layer_surface_configure(void *data, + struct zwlr_layer_surface_v1 *surface, + uint32_t serial, uint32_t width, uint32_t height) { + struct swaybg_state *state = data; + state->width = width; + state->height = height; + zwlr_layer_surface_v1_ack_configure(surface, serial); + render_frame(state); +} + +static void layer_surface_closed(void *data, + struct zwlr_layer_surface_v1 *surface) { + struct swaybg_state *state = data; + zwlr_layer_surface_v1_destroy(state->layer_surface); + wl_surface_destroy(state->surface); + state->run_display = false; +} + +struct zwlr_layer_surface_v1_listener layer_surface_listener = { + .configure = layer_surface_configure, + .closed = layer_surface_closed, +}; + +static void handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) { + struct swaybg_state *state = data; + if (strcmp(interface, wl_compositor_interface.name) == 0) { + state->compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, 1); + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + state->shm = wl_registry_bind(registry, name, + &wl_shm_interface, 1); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + static int output_idx = 0; + if (output_idx == state->args->output_idx) { + state->output = wl_registry_bind(registry, name, + &wl_output_interface, 1); } + output_idx++; + } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { + state->layer_shell = wl_registry_bind( + registry, name, &zwlr_layer_shell_v1_interface, 1); + } +} + +static void handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) { + // who cares +} + +static const struct wl_registry_listener registry_listener = { + .global = handle_global, + .global_remove = handle_global_remove, +}; - cairo_surface_destroy(image); +int main(int argc, const char **argv) { + struct swaybg_args args = {0}; + struct swaybg_state state = {0}; + state.args = &args; + wlr_log_init(L_DEBUG, NULL); + + if (argc != 4) { + wlr_log(L_ERROR, "Do not run this program manually. " + "See man 5 sway and look for output options."); + return 1; } + args.output_idx = atoi(argv[1]); + args.path = argv[2]; + + args.mode = BACKGROUND_MODE_STRETCH; + if (strcmp(argv[3], "stretch") == 0) { + args.mode = BACKGROUND_MODE_STRETCH; + } else if (strcmp(argv[3], "fill") == 0) { + args.mode = BACKGROUND_MODE_FILL; + } else if (strcmp(argv[3], "fit") == 0) { + args.mode = BACKGROUND_MODE_FIT; + } else if (strcmp(argv[3], "center") == 0) { + args.mode = BACKGROUND_MODE_CENTER; + } else if (strcmp(argv[3], "tile") == 0) { + args.mode = BACKGROUND_MODE_TILE; + } else if (strcmp(argv[3], "solid_color") == 0) { + args.mode = BACKGROUND_MODE_SOLID_COLOR; + } else { + wlr_log(L_ERROR, "Unsupported background mode: %s", argv[3]); + return 1; + } + + if (!prepare_context(&state)) { + return 1; + } + + assert(state.display = wl_display_connect(NULL)); + + struct wl_registry *registry = wl_display_get_registry(state.display); + wl_registry_add_listener(registry, ®istry_listener, &state); + wl_display_roundtrip(state.display); + assert(state.compositor && state.layer_shell && state.output && state.shm); + + assert(state.surface = wl_compositor_create_surface(state.compositor)); + + state.layer_surface = zwlr_layer_shell_v1_get_layer_surface( + state.layer_shell, state.surface, state.output, + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper"); + assert(state.layer_surface); - while (wl_display_dispatch(registry->display) != -1); + zwlr_layer_surface_v1_set_size(state.layer_surface, 0, 0); + zwlr_layer_surface_v1_set_anchor(state.layer_surface, + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); + zwlr_layer_surface_v1_add_listener(state.layer_surface, + &layer_surface_listener, &state); + state.run_display = true; + wl_surface_commit(state.surface); + wl_display_roundtrip(state.display); - for (i = 0; i < surfaces->length; ++i) { - struct window *window = surfaces->items[i]; - window_teardown(window); + while (wl_display_dispatch(state.display) != -1 && state.run_display) { + // This space intentionally left blank } - list_free(surfaces); - registry_teardown(registry); return 0; } diff --git a/swaybg/meson.build b/swaybg/meson.build new file mode 100644 index 00000000..5e10f3c7 --- /dev/null +++ b/swaybg/meson.build @@ -0,0 +1,18 @@ +executable( + 'swaybg', + 'main.c', + include_directories: [sway_inc], + dependencies: [ + cairo, + gdk_pixbuf, + jsonc, + math, + pango, + pangocairo, + sway_protos, + wayland_client, + wlroots, + ], + link_with: [lib_sway_common, lib_sway_client], + install: true +) diff --git a/wayland/pango.c b/wayland/pango.c deleted file mode 100644 index f9eec98c..00000000 --- a/wayland/pango.c +++ /dev/null @@ -1,73 +0,0 @@ -#include <cairo/cairo.h> -#include <pango/pangocairo.h> -#include <stdarg.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <stdbool.h> -#include <stdint.h> -#include "log.h" - -PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text, - int32_t scale, bool markup) { - PangoLayout *layout = pango_cairo_create_layout(cairo); - PangoAttrList *attrs; - if (markup) { - char *buf; - pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, NULL); - pango_layout_set_markup(layout, buf, -1); - free(buf); - } else { - attrs = pango_attr_list_new(); - pango_layout_set_text(layout, text, -1); - } - pango_attr_list_insert(attrs, pango_attr_scale_new(scale)); - PangoFontDescription *desc = pango_font_description_from_string(font); - pango_layout_set_font_description(layout, desc); - pango_layout_set_single_paragraph_mode(layout, 1); - pango_layout_set_attributes(layout, attrs); - pango_attr_list_unref(attrs); - pango_font_description_free(desc); - return layout; -} - -void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, - int32_t scale, bool markup, const char *fmt, ...) { - char *buf = malloc(2048); - - va_list args; - va_start(args, fmt); - if (vsnprintf(buf, 2048, fmt, args) >= 2048) { - strcpy(buf, "[buffer overflow]"); - } - va_end(args); - - PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); - pango_cairo_update_layout(cairo, layout); - - pango_layout_get_pixel_size(layout, width, height); - - g_object_unref(layout); - - free(buf); -} - -void pango_printf(cairo_t *cairo, const char *font, int32_t scale, bool markup, const char *fmt, ...) { - char *buf = malloc(2048); - - va_list args; - va_start(args, fmt); - if (vsnprintf(buf, 2048, fmt, args) >= 2048) { - strcpy(buf, "[buffer overflow]"); - } - va_end(args); - - PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); - pango_cairo_update_layout(cairo, layout); - - pango_cairo_show_layout(cairo, layout); - - g_object_unref(layout); - - free(buf); -} diff --git a/wayland/registry.c b/wayland/registry.c deleted file mode 100644 index bbb43ad9..00000000 --- a/wayland/registry.c +++ /dev/null @@ -1,293 +0,0 @@ -#include <wayland-client.h> -#include <xkbcommon/xkbcommon.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <sys/mman.h> -#include <sys/types.h> -#include <sys/timerfd.h> -#include "wayland-desktop-shell-client-protocol.h" -#include "wayland-swaylock-client-protocol.h" -#include "client/registry.h" -#include "stringop.h" -#include "log.h" - -static void display_handle_mode(void *data, struct wl_output *wl_output, - uint32_t flags, int32_t width, int32_t height, int32_t refresh) { - struct output_state *state = data; - if (flags & WL_OUTPUT_MODE_CURRENT) { - state->flags = flags; - state->width = width; - state->height = height; - sway_log(L_DEBUG, "Got mode %dx%d:0x%X for output %p", - width, height, flags, data); - } -} - -static void display_handle_geometry(void *data, struct wl_output *wl_output, - int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, - int32_t subpixel, const char *make, const char *model, int32_t transform) { - // this space intentionally left blank -} - -static void display_handle_done(void *data, struct wl_output *wl_output) { - // this space intentionally left blank -} - -static void display_handle_scale(void *data, struct wl_output *wl_output, int32_t factor) { - struct output_state *state = data; - state->scale = factor; - sway_log(L_DEBUG, "Got scale factor %d for output %p", factor, data); -} - -static const struct wl_output_listener output_listener = { - .mode = display_handle_mode, - .geometry = display_handle_geometry, - .done = display_handle_done, - .scale = display_handle_scale -}; - -const char *XKB_MASK_NAMES[MASK_LAST] = { - XKB_MOD_NAME_SHIFT, - XKB_MOD_NAME_CAPS, - XKB_MOD_NAME_CTRL, - XKB_MOD_NAME_ALT, - "Mod2", - "Mod3", - XKB_MOD_NAME_LOGO, - "Mod5", -}; - -const enum mod_bit XKB_MODS[MASK_LAST] = { - MOD_SHIFT, - MOD_CAPS, - MOD_CTRL, - MOD_ALT, - MOD_MOD2, - MOD_MOD3, - MOD_LOGO, - MOD_MOD5 -}; - -static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, - uint32_t format, int fd, uint32_t size) { - // Keyboard errors are abort-worthy because you wouldn't be able to unlock your screen otherwise. - - struct registry *registry = data; - if (!data) { - close(fd); - return; - } - - if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { - close(fd); - sway_abort("Unknown keymap format %d, aborting", format); - } - - char *map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - if (map_str == MAP_FAILED) { - close(fd); - sway_abort("Unable to initialized shared keyboard memory, aborting"); - } - - struct xkb_keymap *keymap = xkb_keymap_new_from_string(registry->input->xkb.context, - map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0); - munmap(map_str, size); - close(fd); - - if (!keymap) { - sway_abort("Failed to compile keymap, aborting"); - } - - struct xkb_state *state = xkb_state_new(keymap); - if (!state) { - xkb_keymap_unref(keymap); - sway_abort("Failed to create xkb state, aborting"); - } - - xkb_keymap_unref(registry->input->xkb.keymap); - xkb_state_unref(registry->input->xkb.state); - registry->input->xkb.keymap = keymap; - registry->input->xkb.state = state; - - int i; - for (i = 0; i < MASK_LAST; ++i) { - registry->input->xkb.masks[i] = 1 << xkb_keymap_mod_get_index(registry->input->xkb.keymap, XKB_MASK_NAMES[i]); - } -} - -static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, - uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { - // this space intentionally left blank -} - -static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, - uint32_t serial, struct wl_surface *surface) { - // this space intentionally left blank -} - -static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard, - uint32_t serial, uint32_t time, uint32_t key, uint32_t state_w) { - struct registry *registry = data; - enum wl_keyboard_key_state state = state_w; - - if (!registry->input->xkb.state) { - return; - } - - xkb_keysym_t sym = xkb_state_key_get_one_sym(registry->input->xkb.state, key + 8); - registry->input->sym = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? sym : XKB_KEY_NoSymbol); - registry->input->code = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? key + 8 : 0); - uint32_t codepoint = xkb_state_key_get_utf32(registry->input->xkb.state, registry->input->code); - if (registry->input->notify) { - registry->input->notify(state, sym, key, codepoint); - } -} - -static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, - uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, - uint32_t mods_locked, uint32_t group) { - struct registry *registry = data; - - if (!registry->input->xkb.keymap) { - return; - } - - xkb_state_update_mask(registry->input->xkb.state, mods_depressed, mods_latched, mods_locked, 0, 0, group); - xkb_mod_mask_t mask = xkb_state_serialize_mods(registry->input->xkb.state, - XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED); - - registry->input->modifiers = 0; - for (uint32_t i = 0; i < MASK_LAST; ++i) { - if (mask & registry->input->xkb.masks[i]) { - registry->input->modifiers |= XKB_MODS[i]; - } - } -} - -static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *keyboard, - int32_t rate, int32_t delay) { - // this space intentionally left blank -} - -static const struct wl_keyboard_listener keyboard_listener = { - .keymap = keyboard_handle_keymap, - .enter = keyboard_handle_enter, - .leave = keyboard_handle_leave, - .key = keyboard_handle_key, - .modifiers = keyboard_handle_modifiers, - .repeat_info = keyboard_handle_repeat_info -}; - -static void seat_handle_capabilities(void *data, struct wl_seat *seat, - enum wl_seat_capability caps) { - struct registry *reg = data; - - if ((caps & WL_SEAT_CAPABILITY_POINTER) && !reg->pointer) { - reg->pointer = wl_seat_get_pointer(reg->seat); - } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && reg->pointer) { - wl_pointer_destroy(reg->pointer); - reg->pointer = NULL; - } - - if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !reg->keyboard) { - reg->keyboard = wl_seat_get_keyboard(reg->seat); - wl_keyboard_add_listener(reg->keyboard, &keyboard_listener, reg); - } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && reg->keyboard) { - wl_keyboard_destroy(reg->keyboard); - reg->keyboard = NULL; - } -} - -static void seat_handle_name(void *data, struct wl_seat *seat, const char *name) { - // this space intentionally left blank -} - -static const struct wl_seat_listener seat_listener = { - .capabilities = seat_handle_capabilities, - .name = seat_handle_name, -}; - -static void registry_global(void *data, struct wl_registry *registry, - uint32_t name, const char *interface, uint32_t version) { - struct registry *reg = data; - - if (strcmp(interface, wl_compositor_interface.name) == 0) { - reg->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, version); - } else if (strcmp(interface, wl_shm_interface.name) == 0) { - reg->shm = wl_registry_bind(registry, name, &wl_shm_interface, version); - } else if (strcmp(interface, wl_shell_interface.name) == 0) { - reg->shell = wl_registry_bind(registry, name, &wl_shell_interface, version); - } else if (strcmp(interface, wl_seat_interface.name) == 0) { - reg->seat = wl_registry_bind(registry, name, &wl_seat_interface, version); - wl_seat_add_listener(reg->seat, &seat_listener, reg); - } else if (strcmp(interface, wl_output_interface.name) == 0) { - struct wl_output *output = wl_registry_bind(registry, name, &wl_output_interface, version); - struct output_state *ostate = malloc(sizeof(struct output_state)); - ostate->output = output; - ostate->scale = 1; - wl_output_add_listener(output, &output_listener, ostate); - list_add(reg->outputs, ostate); - } else if (strcmp(interface, desktop_shell_interface.name) == 0) { - reg->desktop_shell = wl_registry_bind(registry, name, &desktop_shell_interface, version); - } else if (strcmp(interface, lock_interface.name) == 0) { - reg->swaylock = wl_registry_bind(registry, name, &lock_interface, version); - } -} - -static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) { - // this space intentionally left blank -} - -static const struct wl_registry_listener registry_listener = { - .global = registry_global, - .global_remove = registry_global_remove -}; - -struct registry *registry_poll(void) { - struct registry *registry = malloc(sizeof(struct registry)); - memset(registry, 0, sizeof(struct registry)); - registry->outputs = create_list(); - registry->input = calloc(sizeof(struct input), 1); - registry->input->xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - - registry->display = wl_display_connect(NULL); - if (!registry->display) { - sway_log(L_ERROR, "Error opening display"); - registry_teardown(registry); - return NULL; - } - - struct wl_registry *reg = wl_display_get_registry(registry->display); - wl_registry_add_listener(reg, ®istry_listener, registry); - wl_display_dispatch(registry->display); - wl_display_roundtrip(registry->display); - wl_registry_destroy(reg); - - return registry; -} - -void registry_teardown(struct registry *registry) { - if (registry->pointer) { - wl_pointer_destroy(registry->pointer); - } - if (registry->seat) { - wl_seat_destroy(registry->seat); - } - if (registry->shell) { - wl_shell_destroy(registry->shell); - } - if (registry->shm) { - wl_shm_destroy(registry->shm); - } - if (registry->compositor) { - wl_compositor_destroy(registry->compositor); - } - if (registry->display) { - wl_display_disconnect(registry->display); - } - if (registry->outputs) { - free_flat_list(registry->outputs); - } - free(registry); -} diff --git a/wayland/window.c b/wayland/window.c deleted file mode 100644 index 8a506656..00000000 --- a/wayland/window.c +++ /dev/null @@ -1,177 +0,0 @@ -#include <wayland-client.h> -#include <wayland-cursor.h> -#include "wayland-xdg-shell-client-protocol.h" -#include "wayland-desktop-shell-client-protocol.h" -#include <cairo/cairo.h> -#include <pango/pangocairo.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <unistd.h> -#include <errno.h> -#include <sys/mman.h> -#include "client/window.h" -#include "client/buffer.h" -#include "list.h" -#include "log.h" - -static void pointer_handle_enter(void *data, struct wl_pointer *pointer, - uint32_t serial, struct wl_surface *surface, wl_fixed_t sx_w, wl_fixed_t sy_w) { - struct window *window = data; - if (window->registry->pointer) { - struct wl_cursor_image *image = window->cursor.cursor->images[0]; - wl_pointer_set_cursor(pointer, serial, window->cursor.surface, image->hotspot_x, image->hotspot_y); - } -} - -static void pointer_handle_leave(void *data, struct wl_pointer *pointer, - uint32_t serial, struct wl_surface *surface) { -} - -static void pointer_handle_motion(void *data, struct wl_pointer *pointer, - uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w) { - struct window *window = data; - - window->pointer_input.last_x = wl_fixed_to_int(sx_w); - window->pointer_input.last_y = wl_fixed_to_int(sy_w); -} - -static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial, - uint32_t time, uint32_t button, uint32_t state_w) { - struct window *window = data; - struct pointer_input *input = &window->pointer_input; - - if (window->pointer_input.notify_button) { - window->pointer_input.notify_button(window, input->last_x, input->last_y, button, state_w); - } -} - -static void pointer_handle_axis(void *data, struct wl_pointer *pointer, - uint32_t time, uint32_t axis, wl_fixed_t value) { - struct window *window = data; - enum scroll_direction direction; - - switch (axis) { - case 0: - direction = wl_fixed_to_double(value) < 0 ? SCROLL_UP : SCROLL_DOWN; - break; - case 1: - direction = wl_fixed_to_double(value) < 0 ? SCROLL_LEFT : SCROLL_RIGHT; - break; - default: - sway_log(L_DEBUG, "Unexpected axis value on mouse scroll"); - return; - } - - if (window->pointer_input.notify_scroll) { - window->pointer_input.notify_scroll(window, direction); - } -} - -static const struct wl_pointer_listener pointer_listener = { - .enter = pointer_handle_enter, - .leave = pointer_handle_leave, - .motion = pointer_handle_motion, - .button = pointer_handle_button, - .axis = pointer_handle_axis -}; - -void shell_surface_configure(void *data, struct wl_shell_surface *wl_shell_surface, - uint32_t edges, int32_t width, int32_t height) { - struct window *window = data; - window->width = width; - window->height = height; -} - -static const struct wl_shell_surface_listener surface_listener = { - .configure = shell_surface_configure -}; - -void window_make_shell(struct window *window) { - window->shell_surface = wl_shell_get_shell_surface(window->registry->shell, window->surface); - wl_shell_surface_add_listener(window->shell_surface, &surface_listener, window); - wl_shell_surface_set_toplevel(window->shell_surface); -} - -struct window *window_setup(struct registry *registry, uint32_t width, uint32_t height, - int32_t scale, bool shell_surface) { - struct window *window = malloc(sizeof(struct window)); - memset(window, 0, sizeof(struct window)); - window->width = width; - window->height = height; - window->scale = scale; - window->registry = registry; - window->font = "monospace 10"; - - window->surface = wl_compositor_create_surface(registry->compositor); - if (shell_surface) { - window_make_shell(window); - } - if (registry->pointer) { - wl_pointer_add_listener(registry->pointer, &pointer_listener, window); - } - - get_next_buffer(window); - - if (registry->pointer) { - char *cursor_theme = getenv("SWAY_CURSOR_THEME"); - if (!cursor_theme) { - cursor_theme = "default"; - } - char *cursor_size = getenv("SWAY_CURSOR_SIZE"); - if (!cursor_size) { - cursor_size = "16"; - } - - sway_log(L_DEBUG, "Cursor scale: %d", scale); - window->cursor.cursor_theme = wl_cursor_theme_load(cursor_theme, - atoi(cursor_size) * scale, registry->shm); - window->cursor.cursor = wl_cursor_theme_get_cursor(window->cursor.cursor_theme, "left_ptr"); - window->cursor.surface = wl_compositor_create_surface(registry->compositor); - - struct wl_cursor_image *image = window->cursor.cursor->images[0]; - struct wl_buffer *cursor_buf = wl_cursor_image_get_buffer(image); - wl_surface_attach(window->cursor.surface, cursor_buf, 0, 0); - wl_surface_set_buffer_scale(window->cursor.surface, scale); - wl_surface_damage(window->cursor.surface, 0, 0, - image->width, image->height); - wl_surface_commit(window->cursor.surface); - } - - return window; -} - -static void frame_callback(void *data, struct wl_callback *callback, uint32_t time) { - struct window *window = data; - wl_callback_destroy(callback); - window->frame_cb = NULL; -} - -static const struct wl_callback_listener listener = { - frame_callback -}; - -int window_prerender(struct window *window) { - if (window->frame_cb) { - return 0; - } - - get_next_buffer(window); - return 1; -} - -int window_render(struct window *window) { - window->frame_cb = wl_surface_frame(window->surface); - wl_callback_add_listener(window->frame_cb, &listener, window); - - wl_surface_attach(window->surface, window->buffer->buffer, 0, 0); - wl_surface_set_buffer_scale(window->surface, window->scale); - wl_surface_damage(window->surface, 0, 0, window->width, window->height); - wl_surface_commit(window->surface); - - return 1; -} - -void window_teardown(struct window *window) { - // TODO -} |