main neuswc / libswc / snap.c
  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}