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
M
Makefile
+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(®ion, 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, ®ion);
46+ else
47+ wld_fill_region(swc.shm->renderer, bgcolor, ®ion);
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(®ion);
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>