1
0
mirror of https://codeberg.org/vyivel/dulcepan/ synced 2025-03-12 18:59:15 +02:00
dulcepan/main.c
Kirill Primak cc13becc46 Drop cairo
2024-06-18 19:01:51 +03:00

856 lines
26 KiB
C

#include <assert.h>
#include <fcntl.h>
#include <linux/input-event-codes.h>
#include <pixman.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <wayland-client-core.h>
#include <wayland-client-protocol.h>
#include <xkbcommon/xkbcommon.h>
#include "viewporter-protocol.h"
#include "wlr-layer-shell-unstable-v1-protocol.h"
#include "wlr-screencopy-unstable-v1-protocol.h"
#define SWAPCHAIN_LEN 4
struct dp_swapchain_buffer {
struct wl_buffer *buffer;
void *data;
pixman_image_t *image;
bool used;
};
struct dp_output {
uint32_t name;
struct wl_output *global;
struct wl_surface *surface;
struct zwlr_layer_surface_v1 *layer_surface;
struct wp_viewport *viewport;
struct wl_surface *ui_surface;
struct wl_subsurface *ui_subsurface;
struct wp_viewport *ui_viewport;
int width, height;
int px_width, px_height;
int frame_width, frame_height;
int32_t transform;
struct zwlr_screencopy_frame_v1 *frame;
bool frame_ready;
struct wl_buffer *frame_buffer;
int32_t frame_stride;
uint32_t frame_format;
void *frame_data;
bool configured;
struct dp_swapchain_buffer swapchain[SWAPCHAIN_LEN];
struct wl_list link;
};
enum dp_status {
DP_RUNNING,
DP_SAVE,
DP_QUIT,
};
static enum dp_status status = DP_RUNNING;
static struct wl_compositor *compositor = NULL;
static struct wl_subcompositor *subcompositor = NULL;
static struct wp_viewporter *viewporter = NULL;
static struct wl_shm *shm = NULL;
static struct zwlr_layer_shell_v1 *layer_shell = NULL;
static struct zwlr_screencopy_manager_v1 *screencopy_manager = NULL;
static struct wl_seat *seat = NULL;
static struct wl_keyboard *keyboard = NULL;
static struct wl_pointer *pointer = NULL;
static struct wl_list outputs;
static uint32_t seat_caps = false;
static struct xkb_context *xkb_context = NULL;
static struct xkb_keymap *xkb_keymap = NULL;
static struct xkb_state *xkb_state = NULL;
static struct dp_output *selected_output = NULL;
static int selected_x = 0;
static int selected_y = 0;
static int selected_width = 0;
static int selected_height = 0;
static bool selecting_rect = false;
static int select_start_x = 0;
static int select_start_y = 0;
static struct dp_output *pointer_output = NULL;
static int pointer_x = 0;
static int pointer_y = 0;
static bool quick = false;
static pixman_image_t *unselected_fill_image = NULL;
static pixman_image_t *selected_fill_image = NULL;
static pixman_image_t *border_fill_image = NULL;
static void die(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
fprintf(stderr, "Fatal: ");
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
va_end(args);
exit(1);
}
static void *xzalloc(size_t size) {
void *ptr = calloc(1, size);
if (ptr == NULL) {
die("allocation error");
}
return ptr;
}
static void sc_buffer_handle_release(void *data, struct wl_buffer *wl_buffer) {
struct dp_swapchain_buffer *sc_buffer = data;
sc_buffer->used = false;
}
static const struct wl_buffer_listener sc_buffer_listener = {
.release = sc_buffer_handle_release,
};
static void fnv_1a_cont(uint64_t *h, const void *data, size_t len) {
const uint8_t *bytes = data;
for (size_t i = 0; i < len; i++) {
*h = (*h * 0x00000100000001B3) ^ bytes[i];
}
}
static void generate_name(char buffer[static 16]) {
uint64_t h = 0xcbf29ce484222325;
int pid = getpid();
fnv_1a_cont(&h, &pid, sizeof(pid));
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
fnv_1a_cont(&h, &ts.tv_sec, sizeof(&ts.tv_sec));
fnv_1a_cont(&h, &ts.tv_nsec, sizeof(&ts.tv_nsec));
for (size_t i = 16; i-- > 0;) {
buffer[i] = "0123456789abcdef"[h & 0xF];
h >>= 4;
}
}
static struct wl_buffer *create_buffer(
int32_t width, int32_t height, int32_t stride, uint32_t format, void **data) {
int32_t size = stride * height;
static const char template[] = "randfall-";
char name[sizeof(template) + 16] = {0};
memcpy(name, template, sizeof(template) - 1);
generate_name(&name[sizeof(template) - 1]);
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd < 0) {
die("shm_open() failed");
}
shm_unlink(name);
if (ftruncate(fd, (off_t)size) < 0) {
die("ftruncate() failed");
}
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, (int32_t)size);
*data = mmap(NULL, (size_t)size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
die("mmap() failed");
}
struct wl_buffer *buffer =
wl_shm_pool_create_buffer(pool, 0, width, height, (int32_t)stride, format);
wl_shm_pool_destroy(pool);
close(fd);
return buffer;
}
static void layer_surface_handle_configure(void *data, struct zwlr_layer_surface_v1 *layer_surface,
uint32_t serial, uint32_t width, uint32_t height) {
struct dp_output *output = data;
if (output->configured && (output->width != (int)width || output->height != (int)height)) {
die("got unexpected layer surface configure size");
}
zwlr_layer_surface_v1_ack_configure(layer_surface, serial);
if (output->configured) {
wl_surface_commit(output->surface);
return;
}
output->width = (int)width;
output->height = (int)height;
output->configured = true;
wp_viewport_set_destination(output->viewport, output->width, output->height);
wp_viewport_set_destination(output->ui_viewport, output->width, output->height);
wl_surface_attach(output->surface, output->frame_buffer, 0, 0);
wl_surface_commit(output->surface);
}
static void redraw(struct dp_output *output) {
struct dp_swapchain_buffer *sc_buffer = NULL;
for (size_t i = 0; i < SWAPCHAIN_LEN; i++) {
struct dp_swapchain_buffer *iter = &output->swapchain[i];
if (!iter->used) {
sc_buffer = iter;
break;
}
}
if (sc_buffer == NULL) {
fprintf(stderr, "no free buffers in a swapchain\n");
return;
}
sc_buffer->used = true;
pixman_image_composite32(PIXMAN_OP_SRC, unselected_fill_image, NULL, sc_buffer->image, 0, 0, 0,
0, 0, 0, output->px_width, output->px_height);
if (output == selected_output) {
int border_size = 2;
pixman_image_composite32(PIXMAN_OP_SRC, border_fill_image, NULL, sc_buffer->image, 0, 0, 0,
0, selected_x - border_size, selected_y - border_size,
selected_width + border_size * 2, border_size);
pixman_image_composite32(PIXMAN_OP_SRC, border_fill_image, NULL, sc_buffer->image, 0, 0, 0,
0, selected_x - border_size, selected_y + selected_height,
selected_width + border_size * 2, border_size);
pixman_image_composite32(PIXMAN_OP_SRC, border_fill_image, NULL, sc_buffer->image, 0, 0, 0,
0, selected_x - border_size, selected_y, border_size, selected_height);
pixman_image_composite32(PIXMAN_OP_SRC, border_fill_image, NULL, sc_buffer->image, 0, 0, 0,
0, selected_x + selected_width, selected_y, border_size, selected_height);
pixman_image_composite32(PIXMAN_OP_SRC, selected_fill_image, NULL, sc_buffer->image, 0, 0,
0, 0, selected_x, selected_y, selected_width, selected_height);
}
wl_surface_attach(output->ui_surface, sc_buffer->buffer, 0, 0);
wl_surface_damage(output->ui_surface, 0, 0, output->px_width, output->px_height);
wl_surface_commit(output->ui_surface);
}
static void select_whole(void) {
selected_x = 0;
selected_y = 0;
selected_width = selected_output->px_width;
selected_height = selected_output->px_height;
}
static void set_selected_output(struct dp_output *output) {
if (output == selected_output) {
return;
}
struct dp_output *prev_selected_output = selected_output;
selected_output = output;
redraw(prev_selected_output);
}
static void layer_surface_handle_closed(void *data, struct zwlr_layer_surface_v1 *layer_surface) {
die("a layer surface was closed");
}
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.configure = layer_surface_handle_configure,
.closed = layer_surface_handle_closed,
};
static void output_handle_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y,
int32_t phys_width, int32_t phys_height, int32_t subpixel, const char *make,
const char *model, int32_t transform) {
struct dp_output *output = data;
output->transform = transform;
}
static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags,
int32_t width, int32_t height, int32_t refresh) {
struct dp_output *output = data;
output->frame_width = width;
output->frame_height = height;
}
static const struct wl_output_listener output_listener = {
.geometry = output_handle_geometry,
.mode = output_handle_mode,
};
static void seat_handle_capabilities(void *data, struct wl_seat *seat_, uint32_t caps) {
seat_caps = caps;
}
static void seat_handle_name(void *data, struct wl_seat *seat_, const char *name) {
// Ignored
}
static const struct wl_seat_listener seat_listener = {
.capabilities = seat_handle_capabilities,
.name = seat_handle_name,
};
static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard_,
enum wl_keyboard_keymap_format format, int32_t fd, uint32_t size) {
void *keymap_buffer;
size_t keymap_len;
xkb_keymap_unref(xkb_keymap);
xkb_state_unref(xkb_state);
switch (format) {
case WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP:
xkb_keymap = xkb_keymap_new_from_names(xkb_context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS);
break;
case WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1:
keymap_len = size - 1;
keymap_buffer = mmap(NULL, keymap_len, PROT_READ, MAP_PRIVATE, fd, 0);
if (keymap_buffer == MAP_FAILED) {
die("mmap() for a keymap failed");
}
xkb_keymap = xkb_keymap_new_from_buffer(xkb_context, keymap_buffer, keymap_len,
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
munmap(keymap_buffer, keymap_len);
close(fd);
break;
}
xkb_state = xkb_state_new(xkb_keymap);
}
static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard_, uint32_t serial,
struct wl_surface *surface, struct wl_array *mods) {
// Ignored
}
static void keyboard_handle_leave(
void *data, struct wl_keyboard *keyboard_, uint32_t serial, struct wl_surface *surface) {
// Ignored
}
static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard_, uint32_t serial,
uint32_t time_msec, uint32_t key, enum wl_keyboard_key_state state) {
if (xkb_state == NULL) {
return; // Shouldn't happen
}
if (state != WL_KEYBOARD_KEY_STATE_PRESSED) {
return;
}
xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, key + 8);
switch (keysym) {
case XKB_KEY_q:
status = DP_QUIT;
break;
case XKB_KEY_s:
status = DP_SAVE;
break;
case XKB_KEY_w:
select_whole();
redraw(selected_output);
break;
case XKB_KEY_Tab:
set_selected_output(wl_container_of(
(selected_output->link.next != &outputs ? &selected_output->link : &outputs)->next,
selected_output, link));
select_whole();
redraw(selected_output);
break;
}
}
static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard_, uint32_t serial,
uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) {
if (xkb_state == NULL) {
return; // Shouldn't happen
}
xkb_state_update_mask(xkb_state, depressed, latched, locked, 0, 0, group);
}
static const struct wl_keyboard_listener keyboard_listener = {
.keymap = keyboard_handle_keymap,
.enter = keyboard_handle_enter,
.leave = keyboard_handle_leave,
.key = keyboard_handle_key,
.modifiers = keyboard_handle_modifiers,
};
static void convert_pos(wl_fixed_t x, wl_fixed_t y, int *out_x, int *out_y) {
assert(pointer_output != NULL);
*out_x = (int)wl_fixed_to_double(x) * pointer_output->px_width / pointer_output->width;
*out_y = (int)wl_fixed_to_double(y) * pointer_output->px_height / pointer_output->height;
}
static void pointer_handle_enter(void *data, struct wl_pointer *pointer_, uint32_t serial,
struct wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy) {
pointer_output = wl_surface_get_user_data(surface);
assert(pointer_output != NULL);
convert_pos(sx, sy, &pointer_x, &pointer_y);
}
static void pointer_handle_leave(
void *data, struct wl_pointer *pointer_, uint32_t serial, struct wl_surface *surface) {
pointer_output = NULL;
selecting_rect = false;
}
static void pointer_handle_motion(
void *data, struct wl_pointer *pointer_, uint32_t serial, wl_fixed_t sx, wl_fixed_t sy) {
if (pointer_output == NULL) {
return; // Shouldn't happen
}
convert_pos(sx, sy, &pointer_x, &pointer_y);
if (!selecting_rect) {
return;
}
if (pointer_x < select_start_x) {
selected_x = pointer_x;
selected_width = select_start_x - pointer_x;
} else {
selected_x = select_start_x;
selected_width = pointer_x - select_start_x;
}
if (pointer_y < select_start_y) {
selected_y = pointer_y;
selected_height = select_start_y - pointer_y;
} else {
selected_y = select_start_y;
selected_height = pointer_y - select_start_y;
}
redraw(selected_output);
}
static void pointer_handle_button(void *data, struct wl_pointer *pointer_, uint32_t serial,
uint32_t time_msec, uint32_t button, enum wl_pointer_button_state state) {
if (pointer_output == NULL) {
return; // Shouldn't happen
}
if (state != WL_POINTER_BUTTON_STATE_PRESSED) {
selecting_rect = false;
if (quick) {
status = DP_SAVE;
}
return;
}
switch (button) {
case BTN_LEFT:
selecting_rect = true;
selected_x = pointer_x;
selected_y = pointer_y;
selected_width = 0;
selected_height = 0;
select_start_x = selected_x;
select_start_y = selected_y;
set_selected_output(pointer_output);
redraw(selected_output);
break;
case BTN_RIGHT:
set_selected_output(pointer_output);
select_whole();
if (quick) {
status = DP_SAVE;
return;
}
redraw(selected_output);
break;
}
}
static void pointer_handle_axis(void *data, struct wl_pointer *pointer_, uint32_t time_msec,
enum wl_pointer_axis axis, wl_fixed_t value) {
}
static const struct wl_pointer_listener pointer_listener = {
.enter = pointer_handle_enter,
.leave = pointer_handle_leave,
.motion = pointer_handle_motion,
.button = pointer_handle_button,
.axis = pointer_handle_axis,
};
static void frame_handle_buffer(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t format,
uint32_t width, uint32_t height, uint32_t stride) {
struct dp_output *output = data;
assert(output->frame_buffer == NULL);
output->frame_format = format;
output->frame_stride = (int)stride;
output->frame_buffer = create_buffer(output->frame_width, output->frame_height,
output->frame_stride, output->frame_format, &output->frame_data);
zwlr_screencopy_frame_v1_copy(output->frame, output->frame_buffer);
}
static void frame_handle_flags(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t flags) {
if (flags != 0) {
// TODO
die("who the fuck uses Y_INVERT");
}
}
static 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) {
struct dp_output *output = data;
output->frame_ready = true;
}
static void frame_handle_failed(void *data, struct zwlr_screencopy_frame_v1 *frame) {
die("frame copying has failed");
}
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,
};
static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t name,
const char *interface, uint32_t version) {
if (strcmp(interface, wl_compositor_interface.name) == 0) {
if (version >= 2) {
compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 2);
}
} else if (strcmp(interface, wl_subcompositor_interface.name) == 0) {
subcompositor = wl_registry_bind(registry, name, &wl_subcompositor_interface, 1);
} else if (strcmp(interface, wp_viewporter_interface.name) == 0) {
viewporter = wl_registry_bind(registry, name, &wp_viewporter_interface, 1);
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1);
} else if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) {
screencopy_manager =
wl_registry_bind(registry, name, &zwlr_screencopy_manager_v1_interface, 1);
} else if (strcmp(interface, wl_output_interface.name) == 0) {
struct dp_output *output = xzalloc(sizeof(*output));
output->name = name;
output->global = wl_registry_bind(registry, name, &wl_output_interface, 1);
wl_output_add_listener(output->global, &output_listener, output);
wl_list_insert(&outputs, &output->link);
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
// No multiseat
if (seat == NULL) {
seat = wl_registry_bind(registry, name, &wl_seat_interface, 1);
wl_seat_add_listener(seat, &seat_listener, NULL);
}
}
}
static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) {
struct dp_output *output;
wl_list_for_each (output, &outputs, link) {
if (output->name == name) {
die("an output was removed");
}
}
}
static const struct wl_registry_listener registry_listener = {
.global = registry_handle_global,
.global_remove = registry_handle_global_remove,
};
static pixman_format_code_t shm_to_pixman(enum wl_shm_format shm_format) {
struct {
enum wl_shm_format shm;
pixman_format_code_t pixman;
} formats[] = {
#if DP_BIG_ENDIAN
{WL_SHM_FORMAT_ARGB8888, PIXMAN_b8g8r8a8},
{WL_SHM_FORMAT_XRGB8888, PIXMAN_b8g8r8x8},
{WL_SHM_FORMAT_ABGR8888, PIXMAN_r8g8b8a8},
{WL_SHM_FORMAT_XBGR8888, PIXMAN_r8g8b8x8},
{WL_SHM_FORMAT_BGRA8888, PIXMAN_a8r8g8b8},
{WL_SHM_FORMAT_BGRX8888, PIXMAN_x8r8g8b8},
{WL_SHM_FORMAT_RGBA8888, PIXMAN_a8b8g8r8},
{WL_SHM_FORMAT_RGBX8888, PIXMAN_x8b8g8r8},
#else
{WL_SHM_FORMAT_RGB332, PIXMAN_r3g3b2},
{WL_SHM_FORMAT_BGR233, PIXMAN_b2g3r3},
{WL_SHM_FORMAT_ARGB4444, PIXMAN_a4r4g4b4},
{WL_SHM_FORMAT_XRGB4444, PIXMAN_x4r4g4b4},
{WL_SHM_FORMAT_ABGR4444, PIXMAN_a4b4g4r4},
{WL_SHM_FORMAT_XBGR4444, PIXMAN_x4b4g4r4},
{WL_SHM_FORMAT_ARGB1555, PIXMAN_a1r5g5b5},
{WL_SHM_FORMAT_XRGB1555, PIXMAN_x1r5g5b5},
{WL_SHM_FORMAT_ABGR1555, PIXMAN_a1b5g5r5},
{WL_SHM_FORMAT_XBGR1555, PIXMAN_x1b5g5r5},
{WL_SHM_FORMAT_RGB565, PIXMAN_r5g6b5},
{WL_SHM_FORMAT_BGR565, PIXMAN_b5g6r5},
{WL_SHM_FORMAT_RGB888, PIXMAN_r8g8b8},
{WL_SHM_FORMAT_BGR888, PIXMAN_b8g8r8},
{WL_SHM_FORMAT_ARGB8888, PIXMAN_a8r8g8b8},
{WL_SHM_FORMAT_XRGB8888, PIXMAN_x8r8g8b8},
{WL_SHM_FORMAT_ABGR8888, PIXMAN_a8b8g8r8},
{WL_SHM_FORMAT_XBGR8888, PIXMAN_x8b8g8r8},
{WL_SHM_FORMAT_BGRA8888, PIXMAN_b8g8r8a8},
{WL_SHM_FORMAT_BGRX8888, PIXMAN_b8g8r8x8},
{WL_SHM_FORMAT_RGBA8888, PIXMAN_r8g8b8a8},
{WL_SHM_FORMAT_RGBX8888, PIXMAN_r8g8b8x8},
{WL_SHM_FORMAT_ARGB2101010, PIXMAN_a2r10g10b10},
{WL_SHM_FORMAT_ABGR2101010, PIXMAN_a2b10g10r10},
{WL_SHM_FORMAT_XRGB2101010, PIXMAN_x2r10g10b10},
{WL_SHM_FORMAT_XBGR2101010, PIXMAN_x2b10g10r10},
#endif
};
for (size_t i = 0; i < sizeof(formats) / sizeof(*formats); i++) {
if (formats[i].shm == shm_format) {
return formats[i].pixman;
}
}
return 0;
}
int main(void) {
struct wl_display *display = wl_display_connect(NULL);
if (display == NULL) {
die("failed to connect to a Wayland compositor");
}
xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
wl_list_init(&outputs);
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
// Collect globals
wl_display_roundtrip(display);
if (compositor == NULL) {
die("the compositor has no wl_compositor");
} else if (subcompositor == NULL) {
die("the compositor has no wl_subcompositor");
} else if (viewporter == NULL) {
die("the compositor has no wp_viewporter");
} else if (shm == NULL) {
die("the compositor has no wl_shm");
} else if (layer_shell == NULL) {
die("the compositor has no zwlr_layer_shell_v1");
} else if (screencopy_manager == NULL) {
die("the compositor has no zwlr_screencopy_manager_v1");
} else if (seat == NULL) {
die("the compositor has no wl_seat");
}
if (wl_list_empty(&outputs)) {
die("no outputs found");
}
struct dp_output *output;
// Get output/seat info
wl_display_roundtrip(display);
if ((seat_caps & WL_SEAT_CAPABILITY_KEYBOARD) == 0) {
die("the seat has no keyboard capabilities");
}
if ((seat_caps & WL_SEAT_CAPABILITY_POINTER) == 0) {
die("the seat has no pointer capabilities");
}
keyboard = wl_seat_get_keyboard(seat);
wl_keyboard_add_listener(keyboard, &keyboard_listener, NULL);
pointer = wl_seat_get_pointer(seat);
wl_pointer_add_listener(pointer, &pointer_listener, NULL);
wl_list_for_each (output, &outputs, link) {
if ((output->transform & WL_OUTPUT_TRANSFORM_90) != 0) {
output->px_width = output->frame_height;
output->px_height = output->frame_width;
} else {
output->px_width = output->frame_width;
output->px_height = output->frame_height;
}
output->frame = zwlr_screencopy_manager_v1_capture_output(
screencopy_manager, false, output->global);
zwlr_screencopy_frame_v1_add_listener(output->frame, &frame_listener, output);
}
// Get frames
wl_display_roundtrip(display);
wl_list_for_each (output, &outputs, link) {
if (output->frame_buffer == NULL) {
die("failed to create a buffer for an output");
}
while (!output->frame_ready && wl_display_dispatch(display) != -1) {
// Wait
}
}
// Init surfaces
wl_list_for_each (output, &outputs, link) {
output->surface = wl_compositor_create_surface(compositor);
output->layer_surface = zwlr_layer_shell_v1_get_layer_surface(layer_shell, output->surface,
output->global, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "dulcepan");
output->viewport = wp_viewporter_get_viewport(viewporter, output->surface);
wl_surface_set_buffer_transform(output->surface, output->transform);
struct wl_region *empty_region = wl_compositor_create_region(compositor);
wl_surface_set_input_region(output->surface, empty_region);
wl_region_destroy(empty_region);
zwlr_layer_surface_v1_add_listener(output->layer_surface, &layer_surface_listener, output);
zwlr_layer_surface_v1_set_anchor(output->layer_surface,
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
zwlr_layer_surface_v1_set_keyboard_interactivity(
output->layer_surface, ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE);
zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, -1);
output->ui_surface = wl_compositor_create_surface(compositor);
output->ui_subsurface =
wl_subcompositor_get_subsurface(subcompositor, output->ui_surface, output->surface);
output->ui_viewport = wp_viewporter_get_viewport(viewporter, output->ui_surface);
wl_subsurface_set_desync(output->ui_subsurface);
wl_surface_set_user_data(output->surface, output);
wl_surface_set_user_data(output->ui_surface, output);
for (size_t i = 0; i < SWAPCHAIN_LEN; i++) {
struct dp_swapchain_buffer *sc_buffer = &output->swapchain[i];
int stride = output->frame_width * 4;
sc_buffer->buffer = create_buffer(output->px_width, output->px_height, stride,
WL_SHM_FORMAT_ARGB8888, &sc_buffer->data);
sc_buffer->image = pixman_image_create_bits(
PIXMAN_a8r8g8b8, output->px_width, output->px_height, sc_buffer->data, stride);
wl_buffer_add_listener(sc_buffer->buffer, &sc_buffer_listener, sc_buffer);
}
wl_surface_commit(output->surface);
}
wl_list_for_each (output, &outputs, link) {
while (!output->configured && wl_display_dispatch(display) != -1) {
// Wait
}
}
unselected_fill_image = pixman_image_create_solid_fill(&(pixman_color_t){
.red = UINT16_MAX / 2,
.green = UINT16_MAX / 2,
.blue = UINT16_MAX / 2,
.alpha = UINT16_MAX / 2,
});
selected_fill_image = pixman_image_create_solid_fill(&(pixman_color_t){
.red = 0,
.green = 0,
.blue = 0,
.alpha = 0,
});
border_fill_image = pixman_image_create_solid_fill(&(pixman_color_t){
.red = UINT16_MAX,
.green = UINT16_MAX,
.blue = UINT16_MAX,
.alpha = UINT16_MAX,
});
// Select the "first" output, whatever that is
selected_output = wl_container_of(outputs.next, selected_output, link);
select_whole();
wl_list_for_each (output, &outputs, link) {
redraw(output);
}
while (wl_display_dispatch(display) != -1 && status == DP_RUNNING) {
// Wait
}
if (status == DP_SAVE) {
if (selected_width == 0 || selected_height == 0) {
die("selected region is empty");
}
pixman_format_code_t pixman_format = shm_to_pixman(selected_output->frame_format);
if (pixman_format == 0) {
die("failed to get a matching Pixman format for wl_shm format");
}
pixman_image_t *frame = pixman_image_create_bits(pixman_format,
selected_output->frame_width, selected_output->frame_height,
selected_output->frame_data, selected_output->frame_stride);
static const int sines[] = {0, -1, 0, 1, 0, 1, 0, -1};
static const int cosines[] = {1, 0, -1, 0, 1, 0, -1, 0};
static const int flips[] = {1, 1, 1, 1, -1, -1, -1, -1};
struct pixman_transform frame_transform;
pixman_transform_init_identity(&frame_transform);
pixman_transform_translate(&frame_transform, NULL,
pixman_int_to_fixed(-selected_output->px_width / 2),
pixman_int_to_fixed(-selected_output->px_height / 2));
pixman_transform_rotate(&frame_transform, NULL,
pixman_int_to_fixed(cosines[selected_output->transform]),
pixman_int_to_fixed(sines[selected_output->transform]));
pixman_transform_scale(&frame_transform, NULL,
pixman_int_to_fixed(flips[selected_output->transform]), pixman_fixed_1);
pixman_transform_translate(&frame_transform, NULL,
pixman_int_to_fixed(selected_output->frame_width / 2),
pixman_int_to_fixed(selected_output->frame_height / 2));
pixman_image_set_transform(frame, &frame_transform);
pixman_image_t *result =
pixman_image_create_bits(PIXMAN_a8r8g8b8, selected_width, selected_height, NULL, 0);
pixman_image_composite32(PIXMAN_OP_SRC, frame, NULL, result, selected_x, selected_y, 0, 0,
0, 0, selected_width, selected_height);
pixman_image_unref(frame);
// TODO: a better format
uint32_t *data = pixman_image_get_data(result);
fprintf(stdout, "P6\n%d %d\n255\n", selected_width, selected_height);
for (int y = 0; y < selected_height; y++) {
for (int x = 0; x < selected_width; x++) {
uint32_t pixel = *(data++);
char bytes[3] = {
(char)((pixel >> 16) & 0xff),
(char)((pixel >> 8) & 0xff),
(char)((pixel >> 0) & 0xff),
};
fwrite(bytes, 1, sizeof(bytes), stdout);
}
}
fflush(stdout);
pixman_image_unref(result);
}
wl_registry_destroy(registry);
wl_display_disconnect(display);
return 0;
}