From c96f8fa375df937ffe9365deb1a70919c7f16742 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 18 Jun 2024 08:50:45 +0300 Subject: [PATCH] Support transforms and more formats --- main.c | 206 ++++++++++++++++++++++++++++++++++++++++------------ meson.build | 6 ++ 2 files changed, 165 insertions(+), 47 deletions(-) diff --git a/main.c b/main.c index 23b3fc9..1fed318 100644 --- a/main.c +++ b/main.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -41,7 +42,9 @@ struct dp_output { struct wp_viewport *ui_viewport; int width, height; - int width_px, height_px; + int px_width, px_height; + int frame_width, frame_height; + int32_t transform; struct zwlr_screencopy_frame_v1 *frame; bool frame_ready; @@ -99,7 +102,7 @@ static struct dp_output *pointer_output = NULL; static int pointer_x = 0; static int pointer_y = 0; -static bool save_on_select = false; +static bool quick = false; static void die(const char *fmt, ...) { va_list args; @@ -201,6 +204,12 @@ static void layer_surface_handle_configure(void *data, struct zwlr_layer_surface 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) { @@ -234,15 +243,15 @@ static void redraw(struct dp_output *output) { } wl_surface_attach(output->ui_surface, sc_buffer->buffer, 0, 0); - wl_surface_damage(output->ui_surface, 0, 0, output->width_px, output->height_px); + wl_surface_damage(output->ui_surface, 0, 0, 200000000, 200000000); wl_surface_commit(output->ui_surface); } static void select_whole(void) { selected_x = 0; selected_y = 0; - selected_width = selected_output->width_px; - selected_height = selected_output->height_px; + selected_width = selected_output->px_width; + selected_height = selected_output->px_height; } static void set_selected_output(struct dp_output *output) { @@ -266,14 +275,15 @@ static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { 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) { - // TODO: handle 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->width_px = width; - output->height_px = height; + output->frame_width = width; + output->frame_height = height; } static const struct wl_output_listener output_listener = { @@ -382,8 +392,8 @@ static const struct wl_keyboard_listener keyboard_listener = { 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->width_px / pointer_output->width; - *out_y = (int)wl_fixed_to_double(y) * pointer_output->height_px / pointer_output->height; + *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, @@ -433,7 +443,7 @@ static void pointer_handle_button(void *data, struct wl_pointer *pointer_, uint3 } if (state != WL_POINTER_BUTTON_STATE_PRESSED) { selecting_rect = false; - if (save_on_select) { + if (quick) { status = DP_SAVE; } return; @@ -454,6 +464,10 @@ static void pointer_handle_button(void *data, struct wl_pointer *pointer_, uint3 case BTN_RIGHT: set_selected_output(pointer_output); select_whole(); + if (quick) { + status = DP_SAVE; + return; + } redraw(selected_output); break; } @@ -473,11 +487,18 @@ static const struct wl_pointer_listener pointer_listener = { 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) { - // TODO + 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"); } } @@ -502,7 +523,9 @@ static const struct zwlr_screencopy_frame_v1_listener frame_listener = { 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) { - compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); + 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) { @@ -544,6 +567,57 @@ static const struct wl_registry_listener registry_listener = { .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) { @@ -599,27 +673,26 @@ int main(void) { 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 formats + // Get frames wl_display_roundtrip(display); - // TODO: actually check formats - wl_list_for_each (output, &outputs, link) { - output->frame_format = WL_SHM_FORMAT_XBGR8888; - output->frame_stride = output->width_px * 4; - } - - wl_list_for_each (output, &outputs, link) { - output->frame_buffer = create_buffer(output->width_px, output->height_px, - output->frame_stride, output->frame_format, &output->frame_data); - zwlr_screencopy_frame_v1_copy(output->frame, output->frame_buffer); - } - 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 } @@ -632,6 +705,11 @@ int main(void) { 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); + zwlr_layer_surface_v1_add_listener(output->layer_surface, &layer_surface_listener, output); zwlr_layer_surface_v1_set_anchor(output->layer_surface, @@ -652,11 +730,11 @@ int main(void) { for (size_t i = 0; i < SWAPCHAIN_LEN; i++) { struct dp_swapchain_buffer *sc_buffer = &output->swapchain[i]; - int stride = output->width_px * 4; - sc_buffer->buffer = create_buffer(output->width_px, output->height_px, stride, + 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->cairo_surface = cairo_image_surface_create_for_data(sc_buffer->data, - CAIRO_FORMAT_ARGB32, output->width_px, output->height_px, stride); + CAIRO_FORMAT_ARGB32, output->px_width, output->px_height, stride); sc_buffer->cairo = cairo_create(sc_buffer->cairo_surface); wl_buffer_add_listener(sc_buffer->buffer, &sc_buffer_listener, sc_buffer); @@ -665,21 +743,17 @@ int main(void) { wl_surface_commit(output->surface); } + wl_list_for_each (output, &outputs, link) { + while (!output->configured && wl_display_dispatch(display) != -1) { + // Wait + } + } + // 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) { - while (!output->configured && wl_display_dispatch(display) != -1) { - // Wait - } - - 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); - redraw(output); } @@ -692,21 +766,59 @@ int main(void) { die("selected region is empty"); } - // TODO: work with compositor-provided formats - // TODO: actually figure out the byte ordering - // TODO: a better file format + 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); - char *data = selected_output->frame_data; - int stride = selected_output->width_px * 4; for (int y = 0; y < selected_height; y++) { - int base = (selected_y + y) * stride; for (int x = 0; x < selected_width; x++) { - int i = base + (selected_x + x) * 4; - fwrite(&data[i], 1, 3, stdout); + 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); diff --git a/meson.build b/meson.build index 96341a5..dd69fee 100644 --- a/meson.build +++ b/meson.build @@ -18,6 +18,8 @@ add_project_arguments( cc = meson.get_compiler('c') +big_endian = target_machine.endian() == 'big' + add_project_arguments(cc.get_supported_arguments([ '-Wconversion', '-Wendif-labels', @@ -34,12 +36,15 @@ add_project_arguments(cc.get_supported_arguments([ '-Wundef', '-Wno-unused-parameter', + + '-DDP_BIG_ENDIAN=@0@'.format(big_endian.to_int()), ]), language: 'c') wayland_client = dependency('wayland-client') wayland_protos = dependency('wayland-protocols') cairo = dependency('cairo') +pixman = dependency('pixman-1') xkbcommon = dependency('xkbcommon') subdir('protocols') @@ -55,6 +60,7 @@ executable( client_protos, wayland_client, cairo, + pixman, xkbcommon, ], install: true,