parent
							
								
									eccf0b2598
								
							
						
					
					
						commit
						632bb948b7
					
				| @ -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; | ||||
| } | ||||
| @ -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 | ||||
| ) | ||||
| @ -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( | ||||
| 		'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 | ||||
| ) | ||||
|  | ||||
| @ -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 | ||||
| @ -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 | ||||
| @ -1,8 +1,22 @@ | ||||
| deps = [ | ||||
| 	cairo, | ||||
| 	jsonc, | ||||
| 	math, | ||||
| 	pango, | ||||
| 	pangocairo, | ||||
| 	sway_protos, | ||||
| 	wayland_client, | ||||
| ] | ||||
| 
 | ||||
| if gdk_pixbuf.found() | ||||
| 	deps += [gdk_pixbuf] | ||||
| endif | ||||
| 
 | ||||
| executable( | ||||
|     'swaybg', | ||||
|     'main.c', | ||||
|     include_directories: [sway_inc], | ||||
|     dependencies: [wayland_client, sway_protos, jsonc, wlroots], | ||||
|     link_with: [lib_sway_common], | ||||
|     dependencies: deps, | ||||
|     link_with: [lib_sway_common, lib_sway_client], | ||||
|     install: true | ||||
| ) | ||||
|  | ||||
					Loading…
					
					
				
		Reference in new issue