You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

138 lines
4.5 KiB

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <wayland-client-core.h>
#include <wayland-client-protocol.h>
#include <wayland-client.h>
#include "wlr-screencopy-client-protocol.h"
#include "rif.h"
#define ASSERT(cond, msg)\
if(!(cond)) {\
fprintf(stderr, "Runtime assertion %s at %s:%d failed: %s\n", #cond, __FILE__, __LINE__, msg);\
exit(-1);\
}
// Not included in any standard header
// Thus, declaring manually.
// See memfd_create(2).
int memfd_create(const char* name, unsigned int flags);
struct wl_output* wl_output = NULL;
struct wl_shm* wl_shm = NULL;
struct zwlr_screencopy_manager_v1* zwlr_manager = NULL;
int8_t ready = 0;
uint32_t buf_size = 0;
void* shm_data = NULL;
uint32_t image_width = 0;
uint32_t image_height = 0;
uint32_t image_format = 0;
void handle_global(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
if(strcmp(interface, wl_shm_interface.name) == 0) {
wl_shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
} else if(strcmp(interface, wl_output_interface.name) == 0) {
wl_output = wl_registry_bind(registry, name, &wl_output_interface, 3);
} else if(strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) {
zwlr_manager = wl_registry_bind(registry, name, &zwlr_screencopy_manager_v1_interface, 1);
}
}
void handle_global_remove(void* data, struct wl_registry* registry, uint32_t name) {
// Who cares
}
const struct wl_registry_listener reg_callbacks = {
.global = handle_global,
.global_remove = handle_global_remove,
};
void frame_handle_buffer(void* data, struct zwlr_screencopy_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
printf("Handling buffer.\n");
image_width = width;
image_height = height;
image_format = format;
buf_size = height * stride;
printf("Format: %010p\n", format);
ASSERT(format == WL_SHM_FORMAT_XBGR8888 || format == WL_SHM_FORMAT_ABGR8888, "Unsupported buffer format copied.");
int shm_fd = memfd_create("wl_shm data", 0);
ASSERT(shm_fd != -1, "Failed to create memfd.");
ASSERT(ftruncate(shm_fd, buf_size) != -1, "Failed to ftruncate the memfd.");
shm_data = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
ASSERT(shm_data != MAP_FAILED, "Failed to mmap the memfd.");
struct wl_shm_pool* pool = wl_shm_create_pool(wl_shm, shm_fd, buf_size);
ASSERT(pool != NULL, "Failed to create the wl_shm_pool.");
struct wl_buffer* buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, format);
ASSERT(buffer != NULL, "Failed to create the wl_buffer.");
zwlr_screencopy_frame_v1_copy(frame, buffer);
}
void frame_handle_flags(void* data, struct zwlr_screencopy_frame_v1* frame, uint32_t flags) {
// Ignored for now
}
void frame_handle_ready(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
printf("Ready.\n");
ready = 1;
}
void frame_handle_failed(void* data, struct zwlr_screencopy_frame_v1* frame) {
fprintf(stderr, "Failed to copy output\n");
exit(EXIT_FAILURE);
}
static const struct zwlr_screencopy_frame_v1_listener frame_listener = {
.buffer = frame_handle_buffer,
.flags = frame_handle_flags,
.ready = frame_handle_ready,
.failed = frame_handle_failed,
};
int main() {
struct wl_display* dpy = wl_display_connect(NULL);
ASSERT(dpy != NULL, "Unable to connect to Wayland");
struct wl_registry* registry = wl_display_get_registry(dpy);
wl_registry_add_listener(registry, &reg_callbacks, NULL);
wl_display_roundtrip(dpy);
ASSERT(wl_shm != NULL, "core.wl_shm not supported.");
ASSERT(zwlr_manager != NULL, "wlr-screencopy-unstable-v1.zwlr_screencopy_manager_v1 not supported.");
ASSERT(wl_output != NULL, "no core.wl_output found.");
int32_t with_cursor = 1;
struct zwlr_screencopy_frame_v1* frame = zwlr_screencopy_manager_v1_capture_output(zwlr_manager, with_cursor, wl_output);
ASSERT(frame != NULL, "wlr-screencopy-unstable-v1.zwlr_screencopy_manager_v1::capture_output has failed");
zwlr_screencopy_frame_v1_add_listener(frame, &frame_listener, wl_output);
wl_display_roundtrip(dpy);
while(ready == 0 && wl_display_dispatch(dpy) != -1) {
// Empty
}
printf("Saving screenshot.\n");
// void write_rif_little(char* path, uint32_t width, uint32_t size, uint8_t format, void* data) {
// Thanks to endianess shenanigans, ABGR/XBGR is RGBA.
write_rif_little("out.rif", image_width, buf_size, RIF_FORMAT_R8G8B8A8, shm_data);
printf("Saved screenshot to ./out.rif.\n");
}