commit 10374b5

shrub  ·  2025-12-29 20:58:33 +0000 UTC
parent 2f6ca91
screenshots
initial implementation of swc_snap screenshot protocol
8 files changed,  +366, -2
+5, -0
 1@@ -81,6 +81,7 @@ CPPFLAGS+= -DENABLE_XWAYLAND
 2 PROTO_EXTENSIONS= \
 3 	protocol/server-decoration.xml \
 4 	protocol/swc.xml \
 5+	protocol/swc_snap.xml \
 6 	protocol/wayland-drm.xml \
 7 	${WAYLAND_PROTOCOLS_DATADIR}/stable/xdg-shell/xdg-shell.xml \
 8 	${WAYLAND_PROTOCOLS_DATADIR}/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml \
 9@@ -101,6 +102,7 @@ PROTO_GEN_C= \
10 	protocol/linux-dmabuf-unstable-v1-protocol.c \
11 	protocol/server-decoration-protocol.c \
12 	protocol/swc-protocol.c \
13+	protocol/swc_snap-protocol.c \
14 	protocol/wayland-drm-protocol.c \
15 	protocol/xdg-decoration-unstable-v1-protocol.c \
16 	protocol/xdg-shell-protocol.c
17@@ -109,6 +111,7 @@ PROTO_GEN_H= \
18 	protocol/linux-dmabuf-unstable-v1-server-protocol.h \
19 	protocol/server-decoration-server-protocol.h \
20 	protocol/swc-server-protocol.h \
21+	protocol/swc_snap-server-protocol.h \
22 	protocol/wayland-drm-server-protocol.h \
23 	protocol/xdg-decoration-unstable-v1-server-protocol.h \
24 	protocol/xdg-shell-server-protocol.h
25@@ -164,6 +167,7 @@ SWC_SOURCES= \
26 	libswc/shell.c \
27 	libswc/shell_surface.c \
28 	libswc/shm.c \
29+	libswc/snap.c \
30 	libswc/subcompositor.c \
31 	libswc/subsurface.c \
32 	libswc/surface.c \
33@@ -178,6 +182,7 @@ SWC_SOURCES= \
34 	protocol/linux-dmabuf-unstable-v1-protocol.c \
35 	protocol/server-decoration-protocol.c \
36 	protocol/swc-protocol.c \
37+	protocol/swc_snap-protocol.c \
38 	protocol/wayland-drm-protocol.c \
39 	protocol/xdg-decoration-unstable-v1-protocol.c \
40 	protocol/xdg-shell-protocol.c
+116, -0
  1@@ -1189,3 +1189,119 @@ compositor_finalize(void)
  2 	pixman_region32_fini(&compositor.opaque);
  3 	wl_global_destroy(compositor.global);
  4 }
  5+
  6+struct wld_buffer *
  7+compositor_get_buffer(struct screen *screen)
  8+{
  9+	struct target *target = target_get(screen);
 10+	if (!target)
 11+		return NULL;
 12+	return target->current_buffer;
 13+}
 14+
 15+struct wld_buffer *
 16+compositor_render_to_shm(struct screen *screen)
 17+{
 18+	uint32_t width = screen->base.geometry.width;
 19+	uint32_t height = screen->base.geometry.height;
 20+	struct wld_buffer *buffer;
 21+	struct compositor_view *view;
 22+	pixman_region32_t region;
 23+	pixman_region32_t damage;
 24+	uint32_t caps;
 25+
 26+	/* create shm buf */
 27+	buffer = wld_create_buffer(swc.shm->context, width, height,
 28+	                           WLD_FORMAT_ARGB8888, WLD_FLAG_MAP);
 29+	if (!buffer)
 30+		return NULL;
 31+
 32+	caps = wld_capabilities(swc.shm->renderer, buffer);
 33+	if (!(caps & WLD_CAPABILITY_WRITE) ||
 34+	    !wld_set_target_buffer(swc.shm->renderer, buffer)) {
 35+		wld_buffer_unreference(buffer);
 36+		return NULL;
 37+	}
 38+
 39+	/* set reigon */
 40+	pixman_region32_init_rect(&region, 0, 0, width, height);
 41+	pixman_region32_init_rect(&damage, screen->base.geometry.x, screen->base.geometry.y, width, height);
 42+
 43+	/* background */
 44+	if (wallbuf)
 45+		wld_copy_region(swc.shm->renderer, wallbuf, 0, 0, &region);
 46+	else
 47+		wld_fill_region(swc.shm->renderer, bgcolor, &region);
 48+
 49+	wl_list_for_each_reverse(view, &compositor.views, link) {
 50+		struct wld_buffer *src = view->buffer;
 51+
 52+		if (!view->visible)
 53+			continue;
 54+
 55+		if (src && !(wld_capabilities(swc.shm->renderer, src) & WLD_CAPABILITY_READ))
 56+			src = view->base.buffer;
 57+
 58+		if (src && (wld_capabilities(swc.shm->renderer, src) & WLD_CAPABILITY_READ)) {
 59+			int32_t x = view->base.geometry.x - screen->base.geometry.x;
 60+			int32_t y = view->base.geometry.y - screen->base.geometry.y;
 61+
 62+			wld_copy_rectangle(swc.shm->renderer, src,
 63+			                   x, y, 0, 0,
 64+			                   view->base.geometry.width, view->base.geometry.height);
 65+		}
 66+
 67+		if ((view->border.outwidth > 0 || view->border.inwidth > 0) && view->base.buffer) {
 68+			pixman_region32_t view_region, view_damage, border_damage;
 69+			const struct swc_rectangle *geom = &view->base.geometry;
 70+			const struct swc_rectangle *target_geom = &screen->base.geometry;
 71+
 72+			pixman_region32_init_rect(&view_region, geom->x, geom->y, geom->width, geom->height);
 73+			pixman_region32_init_with_extents(&view_damage, &view->extents);
 74+			pixman_region32_init(&border_damage);
 75+
 76+			pixman_region32_intersect(&view_damage, &view_damage, &damage);
 77+			pixman_region32_subtract(&view_damage, &view_damage, &view->clip);
 78+			pixman_region32_subtract(&border_damage, &view_damage, &view_region);
 79+
 80+			pixman_region32_t in_rect;
 81+			pixman_region32_init_rect(&in_rect,
 82+			                          geom->x - view->border.inwidth,
 83+			                          geom->y - view->border.inwidth,
 84+			                          geom->width + (2 * view->border.inwidth),
 85+			                          geom->height + (2 * view->border.inwidth));
 86+
 87+			pixman_region32_t out_border;
 88+			pixman_region32_init(&out_border);
 89+			pixman_region32_subtract(&out_border, &border_damage, &in_rect);
 90+
 91+			pixman_region32_t in_border;
 92+			pixman_region32_init(&in_border);
 93+			pixman_region32_subtract(&in_border, &in_rect, &view_region);
 94+			pixman_region32_intersect(&in_border, &in_border, &border_damage);
 95+
 96+			if (view->border.outwidth > 0 && pixman_region32_not_empty(&out_border)) {
 97+				pixman_region32_translate(&out_border, -target_geom->x, -target_geom->y);
 98+				wld_fill_region(swc.shm->renderer, view->border.outcolor, &out_border);
 99+			}
100+
101+			if (view->border.inwidth > 0 && pixman_region32_not_empty(&in_border)) {
102+				pixman_region32_translate(&in_border, -target_geom->x, -target_geom->y);
103+				wld_fill_region(swc.shm->renderer, view->border.incolor, &in_border);
104+			}
105+
106+			pixman_region32_fini(&border_damage);
107+			pixman_region32_fini(&view_region);
108+			pixman_region32_fini(&view_damage);
109+			pixman_region32_fini(&in_rect);
110+			pixman_region32_fini(&out_border);
111+			pixman_region32_fini(&in_border);
112+		}
113+	}
114+
115+	wld_flush(swc.shm->renderer);
116+	pixman_region32_fini(&region);
117+	pixman_region32_fini(&damage);
118+
119+	return buffer;
120+}
+15, -0
 1@@ -30,6 +30,9 @@
 2 #include <pixman.h>
 3 #include <wayland-server.h>
 4 
 5+struct screen;
 6+struct wld_buffer;
 7+
 8 struct swc_compositor {
 9 	struct pointer_handler *const pointer_handler;
10 	struct {
11@@ -97,4 +100,16 @@ void compositor_view_hide(struct compositor_view *view);
12 void compositor_view_set_border_color(struct compositor_view *view, uint32_t outcolor, uint32_t incolor);
13 void compositor_view_set_border_width(struct compositor_view *view, uint32_t outwidth, uint32_t inwidth);
14 
15+/**
16+ * get the current composited buffer for a screen for screenshotss.
17+ * returns null if no buffer
18+ */
19+struct wld_buffer *compositor_get_buffer(struct screen *screen);
20+
21+/**
22+ * render the compositor scene into a shm buffer 
23+ * caller must free with wld_buffer_unreference()
24+ */
25+struct wld_buffer *compositor_render_to_shm(struct screen *screen);
26+
27 #endif
+1, -0
1@@ -49,6 +49,7 @@ struct swc {
2 	struct wl_global *kde_decoration_manager;
3 	struct wl_global *panel_manager;
4 	struct wl_global *shell;
5+	struct wl_global *snap_manager;
6 	struct wl_global *subcompositor;
7 	struct wl_global *xdg_decoration_manager;
8 	struct wl_global *xdg_shell;
+193, -0
  1@@ -0,0 +1,193 @@
  2+#include "snap.h"
  3+#include "internal.h"
  4+#include "screen.h"
  5+#include "compositor.h"
  6+#include "shm.h"
  7+#include "seat.h"
  8+#include "pointer.h"
  9+
 10+#include <stdio.h>
 11+#include <stdlib.h>
 12+#include <stdint.h>
 13+#include <string.h>
 14+#include <unistd.h>
 15+#include <wayland-server.h>
 16+#include <wld/wld.h>
 17+#include "swc_snap-server-protocol.h"
 18+
 19+static void
 20+ppm(int fd, const uint8_t *pixels, uint32_t width, uint32_t height,
 21+    uint32_t pitch)
 22+{
 23+	FILE *f = fdopen(fd, "wb");
 24+	if (!f) {
 25+		close(fd);
 26+		return;
 27+	}
 28+
 29+	/* ppm  header */
 30+	fprintf(f, "P6\n%u %u\n255\n", width, height);
 31+
 32+	/* pixel data convert argb8888 to rgb) */
 33+	for (uint32_t y = 0; y < height; y++) {
 34+		const uint32_t *row = (const uint32_t *)(pixels + ((size_t)y * pitch));
 35+
 36+		for (uint32_t x = 0; x < width; x++) {
 37+			uint32_t pixel = row[x];
 38+			unsigned char rgb[3] = {
 39+				(pixel >> 16) & 0xFF,  
 40+				(pixel >> 8) & 0xFF,   
 41+				pixel & 0xFF           
 42+			};
 43+			fwrite(rgb, 1, 3, f);
 44+		}
 45+	}
 46+
 47+	fclose(f); 
 48+}
 49+
 50+
 51+/* get cursor */
 52+static void
 53+cursor(uint8_t *dst, uint32_t dst_width, uint32_t dst_height,
 54+       uint32_t dst_pitch, struct screen *screen)
 55+{
 56+	struct pointer *pointer = swc.seat ? swc.seat->pointer : NULL;
 57+	struct wld_buffer *cursor_buf;
 58+	const uint8_t *src;
 59+	int32_t dst_x, dst_y;
 60+	int32_t src_x = 0, src_y = 0;
 61+	uint32_t copy_w, copy_h;
 62+
 63+	if (!pointer || !pointer->cursor.buffer || !pointer->cursor.view.buffer)
 64+		return;
 65+
 66+	if (!(pointer->cursor.view.screens & screen_mask(screen)))
 67+		return;
 68+
 69+	cursor_buf = pointer->cursor.buffer;
 70+	if (!wld_map(cursor_buf) || !cursor_buf->map)
 71+		return;
 72+
 73+	dst_x = pointer->cursor.view.geometry.x - screen->base.geometry.x;
 74+	dst_y = pointer->cursor.view.geometry.y - screen->base.geometry.y;
 75+
 76+	if (dst_x >= (int32_t)dst_width || dst_y >= (int32_t)dst_height ||
 77+	    dst_x + (int32_t)cursor_buf->width <= 0 ||
 78+	    dst_y + (int32_t)cursor_buf->height <= 0) {
 79+		wld_unmap(cursor_buf);
 80+		return;
 81+	}
 82+
 83+	if (dst_x < 0) {
 84+		src_x = -dst_x;
 85+		dst_x = 0;
 86+	}
 87+	if (dst_y < 0) {
 88+		src_y = -dst_y;
 89+		dst_y = 0;
 90+	}
 91+
 92+	copy_w = cursor_buf->width - (uint32_t)src_x;
 93+	if (copy_w > dst_width - (uint32_t)dst_x)
 94+		copy_w = dst_width - (uint32_t)dst_x;
 95+	copy_h = cursor_buf->height - (uint32_t)src_y;
 96+	if (copy_h > dst_height - (uint32_t)dst_y)
 97+		copy_h = dst_height - (uint32_t)dst_y;
 98+
 99+	src = cursor_buf->map;
100+
101+	for (uint32_t y = 0; y < copy_h; y++) {
102+		const uint32_t *src_row = (const uint32_t *)(src + ((size_t)(src_y + (int32_t)y) * cursor_buf->pitch)) + src_x;
103+		uint32_t *dst_row = (uint32_t *)(dst + ((size_t)(dst_y + (int32_t)y) * dst_pitch)) + dst_x;
104+
105+		for (uint32_t x = 0; x < copy_w; x++) {
106+			uint32_t src_px = src_row[x];
107+			uint32_t a = src_px >> 24;
108+
109+			if (a == 0)
110+				continue;
111+			if (a == 255) {
112+				dst_row[x] = 0xFF000000 | (src_px & 0x00FFFFFF);
113+				continue;
114+			}
115+
116+			uint32_t dst_px = dst_row[x];
117+			uint32_t inv = 255 - a;
118+			uint32_t r = ((src_px >> 16) & 0xFF) + ((((dst_px >> 16) & 0xFF) * inv + 127) / 255);
119+			uint32_t g = ((src_px >> 8) & 0xFF) + ((((dst_px >> 8) & 0xFF) * inv + 127) / 255);
120+			uint32_t b = (src_px & 0xFF) + (((dst_px & 0xFF) * inv + 127) / 255);
121+
122+			dst_row[x] = 0xFF000000 | (r << 16) | (g << 8) | b;
123+		}
124+	}
125+
126+	wld_unmap(cursor_buf);
127+}
128+
129+static void
130+capture(struct wl_client *client, struct wl_resource *resource, int32_t fd)
131+{
132+	struct screen *screen;
133+	struct wld_buffer *shm_buffer;
134+	uint8_t *pixels;
135+	uint32_t width, height;
136+
137+	if (wl_list_empty(&swc.screens)) {
138+		fprintf(stderr, "snap: no screens available\n");
139+		close(fd);
140+		return;
141+	}
142+
143+	screen = wl_container_of(swc.screens.next, screen, link);
144+	width = screen->base.geometry.width;
145+	height = screen->base.geometry.height;
146+
147+	/* put compositor in shm*/
148+	shm_buffer = compositor_render_to_shm(screen);
149+	if (!shm_buffer) {
150+		fprintf(stderr, "snap: failed to render to SHM\n");
151+		close(fd);
152+		return;
153+	}
154+
155+	/* get pixel data from shm */
156+	if (!wld_map(shm_buffer) || !shm_buffer->map) {
157+		fprintf(stderr, "snap: failed to map buffer data\n");
158+		wld_buffer_unreference(shm_buffer);
159+		close(fd);
160+		return;
161+	}
162+
163+	pixels = shm_buffer->map;
164+
165+	cursor(pixels, width, height, shm_buffer->pitch, screen);
166+
167+	ppm(fd, pixels, width, height, shm_buffer->pitch);
168+
169+	wld_unmap(shm_buffer);
170+	wld_buffer_unreference(shm_buffer);
171+}
172+
173+static const struct swc_snap_interface snap_impl = {
174+	.capture = capture,
175+};
176+
177+static void
178+bind_snap(struct wl_client *client, void *data, uint32_t version, uint32_t id)
179+{
180+	struct wl_resource *resource;
181+
182+	resource = wl_resource_create(client, &swc_snap_interface, version, id);
183+	if (!resource) {
184+		wl_client_post_no_memory(client);
185+		return;
186+	}
187+	wl_resource_set_implementation(resource, &snap_impl, NULL, NULL);
188+}
189+
190+struct wl_global *
191+snap_manager_create(struct wl_display *display)
192+{
193+	return wl_global_create(display, &swc_snap_interface, 1, NULL, &bind_snap);
194+}
+9, -0
 1@@ -0,0 +1,9 @@
 2+#ifndef SWC_SNAP_H
 3+#define SWC_SNAP_H
 4+
 5+struct wl_display;
 6+struct wl_global;
 7+
 8+struct wl_global *snap_manager_create(struct wl_display *display);
 9+
10+#endif
+12, -2
 1@@ -37,6 +37,7 @@
 2 #include "seat.h"
 3 #include "shell.h"
 4 #include "shm.h"
 5+#include "snap.h"
 6 #include "subcompositor.h"
 7 #include "util.h"
 8 #include "window.h"
 9@@ -215,10 +216,16 @@ swc_initialize(struct wl_display *display, struct wl_event_loop *event_loop, con
10 		goto error13;
11 	}
12 
13+	swc.snap_manager = snap_manager_create(display);
14+	if (!swc.snap_manager) {
15+		ERROR("Could not initialize snap manager\n");
16+		goto error14;
17+	}
18+
19 #ifdef ENABLE_XWAYLAND
20 	if (!xserver_initialize()) {
21 		ERROR("Could not initialize xwayland\n");
22-		goto error14;
23+		goto error15;
24 	}
25 #endif
26 
27@@ -227,9 +234,11 @@ swc_initialize(struct wl_display *display, struct wl_event_loop *event_loop, con
28 	return true;
29 
30 #ifdef ENABLE_XWAYLAND
31+error15:
32+	wl_global_destroy(swc.snap_manager);
33+#endif
34 error14:
35 	wl_global_destroy(swc.panel_manager);
36-#endif
37 error13:
38 	wl_global_destroy(swc.kde_decoration_manager);
39 error12:
40@@ -266,6 +275,7 @@ swc_finalize(void)
41 #ifdef ENABLE_XWAYLAND
42 	xserver_finalize();
43 #endif
44+	wl_global_destroy(swc.snap_manager);
45 	wl_global_destroy(swc.panel_manager);
46 	wl_global_destroy(swc.xdg_decoration_manager);
47 	wl_global_destroy(swc.xdg_shell);
+15, -0
 1@@ -0,0 +1,15 @@
 2+<?xml version="1.0" encoding="UTF-8"?>
 3+<protocol name="swc_snap">
 4+    <interface name="swc_snap" version="1">
 5+        <description summary="simple screenshot to file descriptor">
 6+            provides a minimal screenshot capability.
 7+        </description>
 8+
 9+        <request name="capture">
10+            <description summary="capture screenshot to fd">
11+                captures the current screen and writes it as ppm formay
12+            </description>
13+            <arg name="fd" type="fd" summary="file descriptor to write ppm data"/>
14+        </request>
15+    </interface>
16+</protocol>