1#include "snap.h"
2#include "compositor.h"
3#include "internal.h"
4#include "pointer.h"
5#include "screen.h"
6#include "seat.h"
7#include "shm.h"
8#include "util.h"
9
10#include "swc_snap-server-protocol.h"
11#include <stdint.h>
12#include <string.h>
13#include <time.h>
14#include <wayland-server.h>
15#include <wld/wld.h>
16
17/* get cursor */
18static void
19cursor(uint8_t *dst, uint32_t dst_width, uint32_t dst_height,
20 uint32_t dst_pitch, struct screen *screen)
21{
22 struct pointer *pointer = swc.seat ? swc.seat->pointer : NULL;
23 struct wld_buffer *cursor_buf;
24 const uint8_t *src;
25 int32_t dst_x, dst_y;
26 int32_t src_x = 0, src_y = 0;
27 uint32_t copy_w, copy_h;
28
29 if (!pointer || !pointer->cursor.buffer || !pointer->cursor.view.buffer) {
30 return;
31 }
32
33 if (!(pointer->cursor.view.screens & screen_mask(screen))) {
34 return;
35 }
36
37 cursor_buf = pointer->cursor.buffer;
38 if (!wld_map(cursor_buf) || !cursor_buf->map) {
39 return;
40 }
41
42 dst_x = pointer->cursor.view.geometry.x - screen->base.geometry.x;
43 dst_y = pointer->cursor.view.geometry.y - screen->base.geometry.y;
44
45 if (dst_x >= (int32_t)dst_width || dst_y >= (int32_t)dst_height ||
46 dst_x + (int32_t)cursor_buf->width <= 0 ||
47 dst_y + (int32_t)cursor_buf->height <= 0) {
48 wld_unmap(cursor_buf);
49 return;
50 }
51
52 if (dst_x < 0) {
53 src_x = -dst_x;
54 dst_x = 0;
55 }
56 if (dst_y < 0) {
57 src_y = -dst_y;
58 dst_y = 0;
59 }
60
61 copy_w = cursor_buf->width - (uint32_t)src_x;
62 if (copy_w > dst_width - (uint32_t)dst_x) {
63 copy_w = dst_width - (uint32_t)dst_x;
64 }
65 copy_h = cursor_buf->height - (uint32_t)src_y;
66 if (copy_h > dst_height - (uint32_t)dst_y) {
67 copy_h = dst_height - (uint32_t)dst_y;
68 }
69
70 src = cursor_buf->map;
71
72 for (uint32_t y = 0; y < copy_h; y++) {
73 const uint32_t *src_row =
74 (const uint32_t *)(src + ((size_t)(src_y + (int32_t)y) *
75 cursor_buf->pitch)) +
76 src_x;
77 uint32_t *dst_row =
78 (uint32_t *)(dst + ((size_t)(dst_y + (int32_t)y) * dst_pitch)) +
79 dst_x;
80
81 for (uint32_t x = 0; x < copy_w; x++) {
82 uint32_t src_px = src_row[x];
83 uint32_t a = src_px >> 24;
84
85 if (a == 0) {
86 continue;
87 }
88 if (a == 255) {
89 dst_row[x] = 0xFF000000 | (src_px & 0x00FFFFFF);
90 continue;
91 }
92
93 uint32_t dst_px = dst_row[x];
94 uint32_t inv = 255 - a;
95 uint32_t r = ((src_px >> 16) & 0xFF) +
96 ((((dst_px >> 16) & 0xFF) * inv + 127) / 255);
97 uint32_t g = ((src_px >> 8) & 0xFF) +
98 ((((dst_px >> 8) & 0xFF) * inv + 127) / 255);
99 uint32_t b =
100 (src_px & 0xFF) + (((dst_px & 0xFF) * inv + 127) / 255);
101
102 dst_row[x] = 0xFF000000 | (r << 16) | (g << 8) | b;
103 }
104 }
105
106 wld_unmap(cursor_buf);
107}
108
109static void
110send_buffer_metadata(struct wl_resource *resource, uint32_t width,
111 uint32_t height)
112{
113 swc_snap_send_buffer(resource, width, height,
114 SWC_SNAP_PIXEL_FORMAT_ARGB8888);
115}
116
117static bool
118get_screen(struct screen **screen, uint32_t *width, uint32_t *height)
119{
120 struct screen *s;
121
122 if (wl_list_empty(&swc.screens)) {
123 return false;
124 }
125
126 s = wl_container_of(swc.screens.next, s, link);
127 *screen = s;
128 *width = s->base.geometry.width;
129 *height = s->base.geometry.height;
130 return true;
131}
132
133static void
134capture(struct wl_client *client, struct wl_resource *resource,
135 struct wl_resource *buffer_resource, uint32_t flags)
136{
137 struct screen *screen;
138 struct swc_shm_buffer_info target;
139 struct wld_buffer *shm_buffer;
140 uint8_t *src_pixels;
141 uint8_t *dst_pixels;
142 uint32_t width, height;
143 uint32_t target_width, target_height, target_stride, target_format;
144 uint32_t row_bytes;
145 struct timespec ts;
146 (void)client;
147
148 if (!get_screen(&screen, &width, &height)) {
149 swc_snap_send_failed(resource, SWC_SNAP_FAILURE_REASON_NO_SCREEN);
150 return;
151 }
152
153 send_buffer_metadata(resource, width, height);
154
155 if (!shm_buffer_get_info(buffer_resource, &target)) {
156 swc_snap_send_failed(resource,
157 SWC_SNAP_FAILURE_REASON_UNSUPPORTED_BUFFER);
158 return;
159 }
160
161 target_format = target.format;
162 if (target_format != WL_SHM_FORMAT_XRGB8888 &&
163 target_format != WL_SHM_FORMAT_ARGB8888) {
164 swc_snap_send_failed(resource,
165 SWC_SNAP_FAILURE_REASON_UNSUPPORTED_BUFFER);
166 return;
167 }
168
169 target_width = (uint32_t)target.width;
170 target_height = (uint32_t)target.height;
171 target_stride = (uint32_t)target.stride;
172 row_bytes = width * 4;
173
174 if (target_width < width || target_height < height ||
175 target_stride < row_bytes) {
176 swc_snap_send_failed(resource, SWC_SNAP_FAILURE_REASON_SIZE_MISMATCH);
177 return;
178 }
179 if (!target.writable) {
180 swc_snap_send_failed(resource, SWC_SNAP_FAILURE_REASON_UNSUPPORTED_BUFFER);
181 return;
182 }
183
184 shm_buffer = compositor_render_to_shm(screen);
185 if (!shm_buffer) {
186 swc_snap_send_failed(resource, SWC_SNAP_FAILURE_REASON_INTERNAL);
187 return;
188 }
189
190 if (!wld_map(shm_buffer) || !shm_buffer->map) {
191 wld_buffer_unreference(shm_buffer);
192 swc_snap_send_failed(resource, SWC_SNAP_FAILURE_REASON_INTERNAL);
193 return;
194 }
195
196 src_pixels = shm_buffer->map;
197
198 if (flags & SWC_SNAP_FLAGS_OVERLAY_CURSOR) {
199 cursor(src_pixels, width, height, shm_buffer->pitch, screen);
200 }
201
202 dst_pixels = target.data;
203
204 for (uint32_t y = 0; y < height; y++) {
205 memcpy(dst_pixels + (size_t)y * target_stride,
206 src_pixels + (size_t)y * shm_buffer->pitch, row_bytes);
207 }
208
209 wld_unmap(shm_buffer);
210 wld_buffer_unreference(shm_buffer);
211
212 clock_gettime(CLOCK_MONOTONIC, &ts);
213 swc_snap_send_ready(resource, (uint32_t)ts.tv_sec, (uint32_t)ts.tv_nsec);
214}
215
216static const struct swc_snap_interface snap_impl = {
217 .destroy = destroy_resource,
218 .capture = capture,
219};
220
221static void
222bind_snap(struct wl_client *client, void *data, uint32_t version, uint32_t id)
223{
224 struct wl_resource *resource;
225
226 resource = wl_resource_create(client, &swc_snap_interface, version, id);
227 if (!resource) {
228 wl_client_post_no_memory(client);
229 return;
230 }
231 wl_resource_set_implementation(resource, &snap_impl, NULL, NULL);
232
233 if (!wl_list_empty(&swc.screens)) {
234 struct screen *screen;
235
236 screen = wl_container_of(swc.screens.next, screen, link);
237 send_buffer_metadata(resource, screen->base.geometry.width,
238 screen->base.geometry.height);
239 }
240}
241
242struct wl_global *
243snap_manager_create(struct wl_display *display)
244{
245 return wl_global_create(display, &swc_snap_interface, 2, NULL, &bind_snap);
246}