mirror of
				https://github.com/swaywm/sway.git
				synced 2025-10-29 05:40:18 -04:00 
			
		
		
		
	Add solid-color rendering to swaybg
This commit is contained in:
		
							parent
							
								
									eccf0b2598
								
							
						
					
					
						commit
						632bb948b7
					
				
					 11 changed files with 550 additions and 39 deletions
				
			
		
							
								
								
									
										123
									
								
								client/buffer-pool.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								client/buffer-pool.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,123 @@ | ||||||
|  | #define _XOPEN_SOURCE 500 | ||||||
|  | #include <assert.h> | ||||||
|  | #include <cairo/cairo.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <sys/mman.h> | ||||||
|  | #include <pango/pangocairo.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <wayland-client.h> | ||||||
|  | #include "buffer_pool.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) { | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	int ts = (path[strlen(path) - 1] == '/'); | ||||||
|  | 
 | ||||||
|  | 	*name = malloc( | ||||||
|  | 		strlen(template) + | ||||||
|  | 		strlen(path) + | ||||||
|  | 		(ts ? 0 : 1) + 1); | ||||||
|  | 	sprintf(*name, "%s%s%s", path, ts ? "" : "/", template); | ||||||
|  | 
 | ||||||
|  | 	int fd = mkstemp(*name); | ||||||
|  | 
 | ||||||
|  | 	if (fd < 0) { | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (ftruncate(fd, size) < 0) { | ||||||
|  | 		close(fd); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return fd; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void buffer_release(void *data, struct wl_buffer *wl_buffer) { | ||||||
|  | 	struct pool_buffer *buffer = data; | ||||||
|  | 	buffer->busy = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct wl_buffer_listener buffer_listener = { | ||||||
|  | 	.release = buffer_release | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
|  | 	assert(fd); | ||||||
|  | 	void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | ||||||
|  | 	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); | ||||||
|  | 	close(fd); | ||||||
|  | 	unlink(name); | ||||||
|  | 	free(name); | ||||||
|  | 	fd = -1; | ||||||
|  | 
 | ||||||
|  | 	buf->width = width; | ||||||
|  | 	buf->height = height; | ||||||
|  | 	buf->surface = cairo_image_surface_create_for_data(data, | ||||||
|  | 			CAIRO_FORMAT_ARGB32, width, height, stride); | ||||||
|  | 	buf->cairo = cairo_create(buf->surface); | ||||||
|  | 	buf->pango = pango_cairo_create_context(buf->cairo); | ||||||
|  | 
 | ||||||
|  | 	wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); | ||||||
|  | 	return buf; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void destroy_buffer(struct pool_buffer *buffer) { | ||||||
|  | 	if (buffer->buffer) { | ||||||
|  | 		wl_buffer_destroy(buffer->buffer); | ||||||
|  | 	} | ||||||
|  | 	if (buffer->cairo) { | ||||||
|  | 		cairo_destroy(buffer->cairo); | ||||||
|  | 	} | ||||||
|  | 	if (buffer->surface) { | ||||||
|  | 		cairo_surface_destroy(buffer->surface); | ||||||
|  | 	} | ||||||
|  | 	if (buffer->pango) { | ||||||
|  | 		g_object_unref(buffer->pango); | ||||||
|  | 	} | ||||||
|  | 	memset(buffer, 0, sizeof(struct pool_buffer)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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; | ||||||
|  | 
 | ||||||
|  | 	for (size_t i = 0; i < 2; ++i) { | ||||||
|  | 		if (pool[i].busy) { | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 		buffer = &pool[i]; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!buffer) { | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (buffer->width != width || buffer->height != height) { | ||||||
|  | 		destroy_buffer(buffer); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!buffer->buffer) { | ||||||
|  | 		if (!create_buffer(shm, buffer, width, height, | ||||||
|  | 					WL_SHM_FORMAT_ARGB8888)) { | ||||||
|  | 			return NULL; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return buffer; | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								client/meson.build
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								client/meson.build
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  | deps = [ | ||||||
|  | 	cairo, | ||||||
|  | 	pango, | ||||||
|  | 	pangocairo, | ||||||
|  | 	wlroots, | ||||||
|  | 	wayland_client, | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | if gdk_pixbuf.found() | ||||||
|  | 	deps += [gdk_pixbuf] | ||||||
|  | endif | ||||||
|  | 
 | ||||||
|  | lib_sway_client = static_library( | ||||||
|  | 	'sway-client', | ||||||
|  | 	files( | ||||||
|  | 		'buffer-pool.c', | ||||||
|  | 	), | ||||||
|  | 	dependencies: deps, | ||||||
|  | 	link_with: [lib_sway_common], | ||||||
|  | 	include_directories: sway_inc | ||||||
|  | ) | ||||||
							
								
								
									
										127
									
								
								common/cairo.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								common/cairo.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,127 @@ | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <cairo/cairo.h> | ||||||
|  | #include "cairo.h" | ||||||
|  | #ifdef WITH_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, | ||||||
|  | 			(color >> (3*8) & 0xFF) / 255.0, | ||||||
|  | 			(color >> (2*8) & 0xFF) / 255.0, | ||||||
|  | 			(color >> (1*8) & 0xFF) / 255.0, | ||||||
|  | 			(color >> (0*8) & 0xFF) / 255.0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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_set_source_surface(cairo, image, 0, 0); | ||||||
|  | 
 | ||||||
|  | 	cairo_paint(cairo); | ||||||
|  | 	cairo_destroy(cairo); | ||||||
|  | 	return new; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #ifdef WITH_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; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	const guint8* gdkpix = gdk_pixbuf_read_pixels(gdkbuf); | ||||||
|  | 	if (!gdkpix) { | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 	gint w = gdk_pixbuf_get_width(gdkbuf); | ||||||
|  | 	gint h = gdk_pixbuf_get_height(gdkbuf); | ||||||
|  | 	int stride = gdk_pixbuf_get_rowstride(gdkbuf); | ||||||
|  | 
 | ||||||
|  | 	cairo_format_t fmt = (chan == 3) ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32; | ||||||
|  | 	cairo_surface_t * cs = cairo_image_surface_create (fmt, w, h); | ||||||
|  | 	cairo_surface_flush (cs); | ||||||
|  | 	if ( !cs || cairo_surface_status(cs) != CAIRO_STATUS_SUCCESS) { | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	int cstride = cairo_image_surface_get_stride(cs); | ||||||
|  | 	unsigned char * cpix = cairo_image_surface_get_data(cs); | ||||||
|  | 
 | ||||||
|  | 	if (chan == 3) { | ||||||
|  | 		int i; | ||||||
|  | 		for (i = h; i; --i) { | ||||||
|  | 			const guint8 *gp = gdkpix; | ||||||
|  | 			unsigned char *cp = cpix; | ||||||
|  | 			const guint8* end = gp + 3*w; | ||||||
|  | 			while (gp < end) { | ||||||
|  | #if G_BYTE_ORDER == G_LITTLE_ENDIAN | ||||||
|  | 				cp[0] = gp[2]; | ||||||
|  | 				cp[1] = gp[1]; | ||||||
|  | 				cp[2] = gp[0]; | ||||||
|  | #else | ||||||
|  | 				cp[1] = gp[0]; | ||||||
|  | 				cp[2] = gp[1]; | ||||||
|  | 				cp[3] = gp[2]; | ||||||
|  | #endif | ||||||
|  | 				gp += 3; | ||||||
|  | 				cp += 4; | ||||||
|  | 			} | ||||||
|  | 			gdkpix += stride; | ||||||
|  | 			cpix += cstride; | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		/* premul-color = alpha/255 * color/255 * 255 = (alpha*color)/255
 | ||||||
|  | 		 * (z/255) = z/256 * 256/255     = z/256 (1 + 1/255) | ||||||
|  | 		 *         = z/256 + (z/256)/255 = (z + z/255)/256 | ||||||
|  | 		 *         # recurse once | ||||||
|  | 		 *         = (z + (z + z/255)/256)/256 | ||||||
|  | 		 *         = (z + z/256 + z/256/255) / 256 | ||||||
|  | 		 *         # only use 16bit uint operations, loose some precision, | ||||||
|  | 		 *         # result is floored. | ||||||
|  | 		 *       ->  (z + z>>8)>>8 | ||||||
|  | 		 *         # add 0x80/255 = 0.5 to convert floor to round | ||||||
|  | 		 *       =>  (z+0x80 + (z+0x80)>>8 ) >> 8 | ||||||
|  | 		 * ------ | ||||||
|  | 		 * 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 | ||||||
|  | 		int i; | ||||||
|  | 		for (i = h; i; --i) { | ||||||
|  | 			const guint8 *gp = gdkpix; | ||||||
|  | 			unsigned char *cp = cpix; | ||||||
|  | 			const guint8* end = gp + 4*w; | ||||||
|  | 			guint z1, z2, z3; | ||||||
|  | 			while (gp < end) { | ||||||
|  | #if G_BYTE_ORDER == G_LITTLE_ENDIAN | ||||||
|  | 				PREMUL_ALPHA(cp[0], gp[2], gp[3], z1); | ||||||
|  | 				PREMUL_ALPHA(cp[1], gp[1], gp[3], z2); | ||||||
|  | 				PREMUL_ALPHA(cp[2], gp[0], gp[3], z3); | ||||||
|  | 				cp[3] = gp[3]; | ||||||
|  | #else | ||||||
|  | 				PREMUL_ALPHA(cp[1], gp[0], gp[3], z1); | ||||||
|  | 				PREMUL_ALPHA(cp[2], gp[1], gp[3], z2); | ||||||
|  | 				PREMUL_ALPHA(cp[3], gp[2], gp[3], z3); | ||||||
|  | 				cp[0] = gp[3]; | ||||||
|  | #endif | ||||||
|  | 				gp += 4; | ||||||
|  | 				cp += 4; | ||||||
|  | 			} | ||||||
|  | 			gdkpix += stride; | ||||||
|  | 			cpix += cstride; | ||||||
|  | 		} | ||||||
|  | #undef PREMUL_ALPHA | ||||||
|  | 	} | ||||||
|  | 	cairo_surface_mark_dirty(cs); | ||||||
|  | 	return cs; | ||||||
|  | } | ||||||
|  | #endif //WITH_GDK_PIXBUF
 | ||||||
|  | @ -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( | 	files( | ||||||
|  | 		'cairo.c', | ||||||
|  | 		'ipc-client.c', | ||||||
| 		'log.c', | 		'log.c', | ||||||
| 		'list.c', | 		'list.c', | ||||||
| 		'util.c', |  | ||||||
| 		'stringop.c', |  | ||||||
| 		'readline.c', | 		'readline.c', | ||||||
| 		'ipc-client.c' | 		'stringop.c', | ||||||
|  | 		'util.c' | ||||||
| 	), | 	), | ||||||
| 	dependencies: [ wlroots ], | 	dependencies: deps, | ||||||
| 	include_directories: sway_inc | 	include_directories: sway_inc | ||||||
| ) | ) | ||||||
|  |  | ||||||
							
								
								
									
										21
									
								
								include/buffer_pool.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								include/buffer_pool.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||||
							
								
								
									
										18
									
								
								include/cairo.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								include/cairo.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | #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); | ||||||
|  | 
 | ||||||
|  | #ifdef WITH_GDK_PIXBUF | ||||||
|  | #include <gdk-pixbuf/gdk-pixbuf.h> | ||||||
|  | 
 | ||||||
|  | cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( | ||||||
|  | 		const GdkPixbuf *gdkbuf); | ||||||
|  | #endif //WITH_GDK_PIXBUF
 | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | @ -27,7 +27,10 @@ wayland_client = dependency('wayland-client') | ||||||
| wayland_egl    = dependency('wayland-egl') | wayland_egl    = dependency('wayland-egl') | ||||||
| wayland_protos = dependency('wayland-protocols') | wayland_protos = dependency('wayland-protocols') | ||||||
| xkbcommon      = dependency('xkbcommon') | xkbcommon      = dependency('xkbcommon') | ||||||
|  | cairo          = dependency('cairo') | ||||||
| pango          = dependency('pango') | pango          = dependency('pango') | ||||||
|  | pangocairo     = dependency('pangocairo') | ||||||
|  | gdk_pixbuf     = dependency('gdk-pixbuf-2.0', required: false) | ||||||
| pixman         = dependency('pixman-1') | pixman         = dependency('pixman-1') | ||||||
| libcap         = dependency('libcap') | libcap         = dependency('libcap') | ||||||
| libinput       = dependency('libinput') | libinput       = dependency('libinput') | ||||||
|  | @ -35,6 +38,10 @@ math           = cc.find_library('m') | ||||||
| git = find_program('git', required: false) | git = find_program('git', required: false) | ||||||
| a2x = find_program('a2x', required: false) | a2x = find_program('a2x', required: false) | ||||||
| 
 | 
 | ||||||
|  | if gdk_pixbuf.found() | ||||||
|  | 	add_project_arguments('-DWITH_GDK_PIXBUF', language : 'c') | ||||||
|  | endif | ||||||
|  | 
 | ||||||
| if a2x.found() | if a2x.found() | ||||||
| 	mandir = get_option('mandir') | 	mandir = get_option('mandir') | ||||||
| 	man_files = [ | 	man_files = [ | ||||||
|  | @ -89,6 +96,7 @@ subdir('protocols') | ||||||
| subdir('common') | subdir('common') | ||||||
| subdir('sway') | subdir('sway') | ||||||
| subdir('swaymsg') | subdir('swaymsg') | ||||||
|  | subdir('client') | ||||||
| subdir('swaybg') | subdir('swaybg') | ||||||
| 
 | 
 | ||||||
| config = configuration_data() | config = configuration_data() | ||||||
|  |  | ||||||
|  | @ -2,12 +2,6 @@ wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') | ||||||
| 
 | 
 | ||||||
| wayland_scanner = find_program('wayland-scanner') | wayland_scanner = find_program('wayland-scanner') | ||||||
| 
 | 
 | ||||||
| wayland_scanner_server = generator( |  | ||||||
| 	wayland_scanner, |  | ||||||
| 	output: '@BASENAME@-protocol.h', |  | ||||||
| 	arguments: ['server-header', '@INPUT@', '@OUTPUT@'], |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| wayland_scanner_code = generator( | wayland_scanner_code = generator( | ||||||
| 	wayland_scanner, | 	wayland_scanner, | ||||||
| 	output: '@BASENAME@-protocol.c', | 	output: '@BASENAME@-protocol.c', | ||||||
|  | @ -20,10 +14,9 @@ wayland_scanner_client = generator( | ||||||
| 	arguments: ['client-header', '@INPUT@', '@OUTPUT@'], | 	arguments: ['client-header', '@INPUT@', '@OUTPUT@'], | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| protocols = [] | protocols = [ | ||||||
| 
 | 	[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], | ||||||
| client_protocols = [ | 	['wlr-layer-shell-unstable-v1.xml'] | ||||||
| 	'wlr-layer-shell-unstable-v1.xml', |  | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| wl_protos_src = [] | wl_protos_src = [] | ||||||
|  | @ -32,11 +25,6 @@ wl_protos_headers = [] | ||||||
| foreach p : protocols | foreach p : protocols | ||||||
| 	xml = join_paths(p) | 	xml = join_paths(p) | ||||||
| 	wl_protos_src += wayland_scanner_code.process(xml) | 	wl_protos_src += wayland_scanner_code.process(xml) | ||||||
| 	wl_protos_headers += wayland_scanner_server.process(xml) |  | ||||||
| endforeach |  | ||||||
| 
 |  | ||||||
| foreach p : client_protocols |  | ||||||
| 	xml = join_paths(p) |  | ||||||
| 	wl_protos_headers += wayland_scanner_client.process(xml) | 	wl_protos_headers += wayland_scanner_client.process(xml) | ||||||
| endforeach | endforeach | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										187
									
								
								swaybg/main.c
									
										
									
									
									
								
							
							
						
						
									
										187
									
								
								swaybg/main.c
									
										
									
									
									
								
							|  | @ -6,6 +6,10 @@ | ||||||
| #include <time.h> | #include <time.h> | ||||||
| #include <wayland-client.h> | #include <wayland-client.h> | ||||||
| #include <wlr/util/log.h> | #include <wlr/util/log.h> | ||||||
|  | #include "buffer_pool.h" | ||||||
|  | #include "cairo.h" | ||||||
|  | #include "util.h" | ||||||
|  | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | ||||||
| 
 | 
 | ||||||
| enum scaling_mode { | enum scaling_mode { | ||||||
| 	SCALING_MODE_STRETCH, | 	SCALING_MODE_STRETCH, | ||||||
|  | @ -13,6 +17,31 @@ enum scaling_mode { | ||||||
| 	SCALING_MODE_FIT, | 	SCALING_MODE_FIT, | ||||||
| 	SCALING_MODE_CENTER, | 	SCALING_MODE_CENTER, | ||||||
| 	SCALING_MODE_TILE, | 	SCALING_MODE_TILE, | ||||||
|  | 	SCALING_MODE_SOLID_COLOR, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct swaybg_args { | ||||||
|  | 	int output_idx; | ||||||
|  | 	const char *path; | ||||||
|  | 	enum scaling_mode mode; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct swaybg_state { | ||||||
|  | 	const struct swaybg_args *args; | ||||||
|  | 
 | ||||||
|  | 	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) { | bool is_valid_color(const char *color) { | ||||||
|  | @ -33,7 +62,165 @@ bool is_valid_color(const char *color) { | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void render_frame(struct swaybg_state *state) { | ||||||
|  | 	if (!state->run_display) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	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 SCALING_MODE_SOLID_COLOR: | ||||||
|  | 		cairo_set_source_u32(cairo, parse_color(state->args->path)); | ||||||
|  | 		cairo_paint(cairo); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		exit(1); | ||||||
|  | 		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); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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; | ||||||
|  | 	render_frame(state); | ||||||
|  | 	zwlr_layer_surface_v1_ack_configure(surface, serial); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| int main(int argc, const char **argv) { | 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); | 	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 = atoi(argv[3]); | ||||||
|  | 
 | ||||||
|  | 	args.mode = SCALING_MODE_STRETCH; | ||||||
|  | 	if (strcmp(argv[3], "stretch") == 0) { | ||||||
|  | 		args.mode = SCALING_MODE_STRETCH; | ||||||
|  | 	} else if (strcmp(argv[3], "fill") == 0) { | ||||||
|  | 		args.mode = SCALING_MODE_FILL; | ||||||
|  | 	} else if (strcmp(argv[3], "fit") == 0) { | ||||||
|  | 		args.mode = SCALING_MODE_FIT; | ||||||
|  | 	} else if (strcmp(argv[3], "center") == 0) { | ||||||
|  | 		args.mode = SCALING_MODE_CENTER; | ||||||
|  | 	} else if (strcmp(argv[3], "tile") == 0) { | ||||||
|  | 		args.mode = SCALING_MODE_TILE; | ||||||
|  | 	} else if (strcmp(argv[3], "solid_color") == 0) { | ||||||
|  | 		args.mode = SCALING_MODE_SOLID_COLOR; | ||||||
|  | 	} else { | ||||||
|  | 		wlr_log(L_ERROR, "Unsupported scaling mode: %s", argv[3]); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	state.display = wl_display_connect(NULL); | ||||||
|  | 	if (!state.display) { | ||||||
|  | 		wlr_log(L_ERROR, "Failed to create display\n"); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct wl_registry *registry = wl_display_get_registry(state.display); | ||||||
|  | 	wl_registry_add_listener(registry, ®istry_listener, &state); | ||||||
|  | 	wl_display_roundtrip(state.display); | ||||||
|  | 
 | ||||||
|  | 	if (!state.compositor) { | ||||||
|  | 		wlr_log(L_DEBUG, "wl-compositor not available"); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 	if (!state.layer_shell) { | ||||||
|  | 		wlr_log(L_ERROR, "layer-shell not available"); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	state.surface = wl_compositor_create_surface(state.compositor); | ||||||
|  | 	if (!state.surface) { | ||||||
|  | 		wlr_log(L_ERROR, "failed to create wl_surface"); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	state.layer_surface = zwlr_layer_shell_v1_get_layer_surface( | ||||||
|  | 			state.layer_shell, state.surface, state.output, | ||||||
|  | 			ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper"); | ||||||
|  | 	if (!state.layer_surface) { | ||||||
|  | 		wlr_log(L_ERROR, "failed to create zwlr_layer_surface"); | ||||||
|  | 		return 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); | ||||||
|  | 	wl_surface_commit(state.surface); | ||||||
|  | 	wl_display_roundtrip(state.display); | ||||||
|  | 
 | ||||||
|  | 	state.run_display = true; | ||||||
|  | 	render_frame(&state); | ||||||
|  | 	while (wl_display_dispatch(state.display) != -1 && state.run_display) { | ||||||
|  | 		// This space intentionally left blank
 | ||||||
|  | 	} | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,8 +1,22 @@ | ||||||
|  | deps = [ | ||||||
|  | 	cairo, | ||||||
|  | 	jsonc, | ||||||
|  | 	math, | ||||||
|  | 	pango, | ||||||
|  | 	pangocairo, | ||||||
|  | 	sway_protos, | ||||||
|  | 	wayland_client, | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | if gdk_pixbuf.found() | ||||||
|  | 	deps += [gdk_pixbuf] | ||||||
|  | endif | ||||||
|  | 
 | ||||||
| executable( | executable( | ||||||
|     'swaybg', |     'swaybg', | ||||||
|     'main.c', |     'main.c', | ||||||
|     include_directories: [sway_inc], |     include_directories: [sway_inc], | ||||||
|     dependencies: [wayland_client, sway_protos, jsonc, wlroots], |     dependencies: deps, | ||||||
|     link_with: [lib_sway_common], |     link_with: [lib_sway_common, lib_sway_client], | ||||||
|     install: true |     install: true | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | @ -50,11 +50,8 @@ static const struct wl_buffer_listener buffer_listener = { | ||||||
| 	.release = buffer_release | 	.release = buffer_release | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static struct buffer *create_buffer(struct window *window, struct buffer *buf, | static struct buffer *create_buffer(struct wl_shm *shm, struct buffer *buf, | ||||||
| 		int32_t width, int32_t height, int32_t scale, uint32_t format) { | 		int32_t width, int32_t height, uint32_t format) { | ||||||
| 
 |  | ||||||
| 	width *= scale; |  | ||||||
| 	height *= scale; |  | ||||||
| 	uint32_t stride = width * 4; | 	uint32_t stride = width * 4; | ||||||
| 	uint32_t size = stride * height; | 	uint32_t size = stride * height; | ||||||
| 
 | 
 | ||||||
|  | @ -65,7 +62,7 @@ static struct buffer *create_buffer(struct window *window, struct buffer *buf, | ||||||
| 		return NULL; // never reached
 | 		return NULL; // never reached
 | ||||||
| 	} | 	} | ||||||
| 	void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | 	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, | 	buf->buffer = wl_shm_pool_create_buffer(pool, 0, | ||||||
| 			width, height, stride, format); | 			width, height, stride, format); | ||||||
| 	wl_shm_pool_destroy(pool); | 	wl_shm_pool_destroy(pool); | ||||||
|  | @ -101,34 +98,30 @@ static void destroy_buffer(struct buffer *buffer) { | ||||||
| 	memset(buffer, 0, sizeof(struct buffer)); | 	memset(buffer, 0, sizeof(struct buffer)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct buffer *get_next_buffer(struct window *window) { | struct pool_buffer *get_next_buffer(struct wl_shm *shm, | ||||||
|  | 		struct pool_buffer pool[2], uint32_t width, uint32_t height) { | ||||||
| 	struct buffer *buffer = NULL; | 	struct buffer *buffer = NULL; | ||||||
| 
 | 
 | ||||||
| 	int i; | 	for (size_t i = 0; i < sizeof(pool) / sizeof(pool[0]); ++i) { | ||||||
| 	for (i = 0; i < 2; ++i) { | 		if (buffers[i].busy) { | ||||||
| 		if (window->buffers[i].busy) { |  | ||||||
| 			continue; | 			continue; | ||||||
| 		} | 		} | ||||||
| 		buffer = &window->buffers[i]; | 		buffer = &buffers[i]; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (!buffer) { | 	if (!buffer) { | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (buffer->width != window->width || buffer->height != window->height) { | 	if (buffer->width != width || buffer->height != height) { | ||||||
| 		destroy_buffer(buffer); | 		destroy_buffer(buffer); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (!buffer->buffer) { | 	if (!buffer->buffer) { | ||||||
| 		if (!create_buffer(window, buffer, | 		if (!create_buffer(shm, buffer, width, height, | ||||||
| 					window->width, window->height, window->scale, |  | ||||||
| 					WL_SHM_FORMAT_ARGB8888)) { | 					WL_SHM_FORMAT_ARGB8888)) { | ||||||
| 			return NULL; | 			return NULL; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	window->cairo = buffer->cairo; |  | ||||||
| 	window->buffer = buffer; |  | ||||||
| 	return buffer; | 	return buffer; | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Drew DeVault
						Drew DeVault