diff options
Diffstat (limited to 'reduced_system_layer.c')
-rw-r--r-- | reduced_system_layer.c | 468 |
1 files changed, 169 insertions, 299 deletions
diff --git a/reduced_system_layer.c b/reduced_system_layer.c index b1fb813..08e5fad 100644 --- a/reduced_system_layer.c +++ b/reduced_system_layer.c @@ -56,6 +56,10 @@ // - TCP // - Switching canvases - Web // - Long term +// - dlopen - load libraries conditionally +// - X11 +// - Wayland +// - ALSA // - Allocator // - Parsing // - Printing @@ -96,7 +100,7 @@ // - Anti-aliasing // - System // - Window - X11, Web -// - Screenshot +// - Screenshot - X11, Wayland // - Sound - ALSA, Web // - Networking // - Unix UDP sockets @@ -172,6 +176,14 @@ i32 main(i32 argc, c8 **argv); // // ================================================================ +#ifndef NO_WAYLAND +#define NO_WAYLAND 0 +#endif + +#ifndef NO_X11 +#define NO_X11 0 +#endif + #ifndef MAX_NUM_PIXELS #define MAX_NUM_PIXELS (10 * 1024 * 1024) #endif @@ -1451,7 +1463,7 @@ void p_queue_sound(i64 delay_in_samples, i64 num_samples, f32 *frames) { // // ================================================================ -#if defined(__linux__) && !defined(NO_WAYLAND) +#if defined(__linux__) && !NO_WAYLAND #include <sys/mman.h> #include <wayland-client.h> @@ -1569,17 +1581,6 @@ static void zxdg_output_v1_destroy(void *zxdg_output_v1) { wl_proxy_marshal_flags((struct wl_proxy *) zxdg_output_v1, 0, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_output_v1), WL_MARSHAL_FLAG_DESTROY); } -static i32 zxdg_output_v1_add_listener(void *zxdg_output_v1, struct zxdg_output_v1_listener *listener, void *data) { - return wl_proxy_add_listener((struct wl_proxy *) zxdg_output_v1, (void (**)(void)) listener, data); -} - -static void *zxdg_output_manager_v1_get_xdg_output(void *zxdg_output_manager_v1, struct wl_output *output) { - struct wl_proxy *id; - id = wl_proxy_marshal_flags((struct wl_proxy *) zxdg_output_manager_v1, - 1, &zxdg_output_v1_interface, wl_proxy_get_version((struct wl_proxy *) zxdg_output_manager_v1), 0, NULL, output); - return (void *) id; -} - static void zxdg_output_manager_v1_destroy(void *zxdg_output_manager_v1) { wl_proxy_marshal_flags((struct wl_proxy *) zxdg_output_manager_v1, 0, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_output_manager_v1), WL_MARSHAL_FLAG_DESTROY); @@ -1613,6 +1614,7 @@ typedef struct { void * screencopy_manager; struct wl_list outputs; u64 n_done; + b8 ok; } WL_State_; typedef struct { @@ -1631,100 +1633,37 @@ typedef struct { } WL_Box_; typedef struct { - WL_State_ * state; - struct wl_output * output; - void *xdg_output; - struct wl_list link; - WL_Box_ geometry; - i32 transform; - i32 scale; - WL_Box_ local_geometry; - WL_Box_ logical_geometry; - f64 logical_scale; - c8 * name; - WL_Buffer_ * buffer; - void * screencopy_frame; - u32 screencopy_frame_flags; + WL_State_ * state; + struct wl_output *output; + void * xdg_output; + struct wl_list link; + WL_Box_ geometry; + i32 transform; + i32 scale; + WL_Box_ local_geometry; + WL_Box_ logical_geometry; + f64 logical_scale; + c8 * name; + WL_Buffer_ * buffer; + void * screencopy_frame; + u32 screencopy_frame_flags; } WL_Output_; -// ================================================================ - -static void get_output_layout_extents(WL_State_ *state, WL_Box_ *box) { - i32 x1 = 0x10000000, y1 = 0x10000000; - i32 x2 = -0x10000000, y2 = -0x10000000; - - WL_Output_ *output; - wl_list_for_each(output, &state->outputs, link) { - if (output->logical_geometry.x < x1) - x1 = output->logical_geometry.x; - if (output->logical_geometry.y < y1) - y1 = output->logical_geometry.y; - if (output->logical_geometry.x + output->logical_geometry.width > x2) - x2 = output->logical_geometry.x + output->logical_geometry.width; - if (output->logical_geometry.y + output->logical_geometry.height > y2) - y2 = output->logical_geometry.y + output->logical_geometry.height; - } - - box->x = x1; - box->y = y1; - box->width = x2 - x1; - box->height = y2 - y1; -} - -static void apply_output_transform(enum wl_output_transform transform, i32 *width, i32 *height) { - if ((transform & WL_OUTPUT_TRANSFORM_90) != 0) { - i32 tmp = *width; - *width = *height; - *height = tmp; - } -} - -static f64 get_output_rotation(enum wl_output_transform transform) { - switch (transform & ~WL_OUTPUT_TRANSFORM_FLIPPED) { - case WL_OUTPUT_TRANSFORM_90: return M_PI / 2; - case WL_OUTPUT_TRANSFORM_180: return M_PI; - case WL_OUTPUT_TRANSFORM_270: return 3 * M_PI / 2; - } - return 0; -} - -static i32 get_output_flipped(enum wl_output_transform transform) { - return (transform & WL_OUTPUT_TRANSFORM_FLIPPED) != 0 ? -1 : 1; -} - -static void guess_output_logical_geometry(WL_Output_ *output) { - output->logical_geometry.x = output->geometry.x; - output->logical_geometry.y = output->geometry.y; - output->logical_geometry.width = output->geometry.width / output->scale; - output->logical_geometry.height = output->geometry.height / output->scale; - apply_output_transform( - output->transform, - &output->logical_geometry.width, - &output->logical_geometry.height - ); - - output->logical_scale = output->scale; -} - -static void randname(c8 *buf) { - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - long r = ts.tv_nsec; - for (int i = 0; i < 6; ++i) { - buf[i] = 'A'+(r&15)+(r&16)*2; - r >>= 5; - } -} - static i32 anonymous_shm_open(void) { - c8 name[] = "/tmp/scr_XXXXXX"; + c8 name[] = "scr_XXXXXX"; i32 retries = 100; do { - randname(name + sizeof name - 7); - --retries; + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + u64 r = ts.tv_nsec; + for (int i = 0; i < 6; ++i) { + name[sizeof name - 7 + i] = 'A' + (r&15) + (r&16) * 2; + r >>= 5; + } + i32 fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); if (fd >= 0) { shm_unlink(name); @@ -1781,13 +1720,15 @@ static WL_Buffer_ *create_buffer(struct wl_shm *shm, enum wl_shm_format format, return NULL; } - buffer->buffer = wl_buffer; - buffer->data = data; - buffer->width = width; - buffer->height = height; - buffer->stride = stride; - buffer->size = size; - buffer->format = format; + *buffer = (WL_Buffer_) { + .buffer = wl_buffer, + .data = data, + .width = width, + .height = height, + .stride = stride, + .size = size, + .format = format, + }; return buffer; } @@ -1801,28 +1742,29 @@ static void destroy_buffer(WL_Buffer_ *buffer) { } static void screencopy_frame_handle_buffer( - void * data, + void *data, void *frame, - u32 format, - u32 width, - u32 height, - u32 stride + u32 format, + u32 width, + u32 height, + u32 stride ) { WL_Output_ *output = data; output->buffer = create_buffer(output->state->shm, format, width, height, stride); - if (output->buffer == NULL) - // FAIL + if (output->buffer == NULL) { + output->state->ok = 0; return; + } zwlr_screencopy_frame_v1_copy(frame, output->buffer->buffer); } static void screencopy_frame_handle_flags( - void * data, + void *data, void *frame, - u32 flags + u32 flags ) { WL_Output_ *output = data; output->screencopy_frame_flags = flags; @@ -1844,7 +1786,7 @@ static void screencopy_frame_handle_failed( void *frame ) { WL_Output_ *output = data; - // FAIL + output->state->ok = 0; LOG_ERROR("Screenshot copy failed."); } @@ -1855,53 +1797,6 @@ static struct zwlr_screencopy_frame_v1_listener screencopy_frame_listener = { .failed = screencopy_frame_handle_failed, }; -static void xdg_output_handle_logical_position( - void * data, - void *xdg_output, - i32 x, - i32 y -) { - WL_Output_ *output = data; - - output->logical_geometry.x = x; - output->logical_geometry.y = y; -} - -static void xdg_output_handle_logical_size( - void * data, - void *xdg_output, - i32 width, - i32 height -) { - WL_Output_ *output = data; - - output->logical_geometry.width = width; - output->logical_geometry.height = height; -} - -static void xdg_output_handle_done( - void * data, - void *xdg_output -) { - WL_Output_ *output = data; - - i32 width = output->geometry.width; - i32 height = output->geometry.height; - apply_output_transform(output->transform, &width, &height); - output->logical_scale = (f64) width / output->logical_geometry.width; -} - -static void xdg_output_handle_name( - void * data, - void *xdg_output, - c8 * name -) { - WL_Output_ *output = data; - output->name = strdup(name); -} - -static void xdg_output_handle_description(void *data, void *xdg_output, c8 *name) { } - static void output_handle_geometry( void * data, struct wl_output *wl_output, @@ -1974,9 +1869,9 @@ static void handle_global( output->output = wl_registry_bind(registry, name, &wl_output_interface, 3); wl_output_add_listener(output->output, &(struct wl_output_listener) { .geometry = output_handle_geometry, - .mode = output_handle_mode, - .done = output_handle_done, - .scale = output_handle_scale, + .mode = output_handle_mode, + .done = output_handle_done, + .scale = output_handle_scale, }, output); wl_list_insert(&state->outputs, &output->link); } else if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) @@ -1995,45 +1890,33 @@ static struct wl_registry_listener _wayland_registry_listener = { .global_remove = handle_global_remove, }; -static b8 is_empty_box(WL_Box_ *box) { - return box->width <= 0 || box->height <= 0; -} - -static b8 intersect_box(WL_Box_ *a, WL_Box_ *b) { - if (is_empty_box(a) || is_empty_box(b)) { - return 0; +b8 check_format_(i32 format) { + switch (format) { + case WL_SHM_FORMAT_ARGB8888: + case WL_SHM_FORMAT_XRGB8888: + case WL_SHM_FORMAT_ABGR8888: + case WL_SHM_FORMAT_XBGR8888: + case WL_SHM_FORMAT_BGRA8888: + case WL_SHM_FORMAT_BGRX8888: + case WL_SHM_FORMAT_RGBA8888: + case WL_SHM_FORMAT_RGBX8888: + return 1; + default:; } - - i32 x1 = fmax(a->x, b->x); - i32 y1 = fmax(a->y, b->y); - i32 x2 = fmin(a->x + a->width, b->x + b->width); - i32 y2 = fmin(a->y + a->height, b->y + b->height); - - WL_Box_ box = { - .x = x1, - .y = y1, - .width = x2 - x1, - .height = y2 - y1, - }; - - return !is_empty_box(&box); + return 0; } -void wayland_screenshot_(i64 max_num_pixels, i64 *width, i64 *height, vec4_f32 *pixels) { - f32 scale = 1.0; - b8 use_greatest_scale = 1; +b8 wayland_screenshot_(i64 max_num_pixels, i64 *width, i64 *height, vec4_f32 *pixels) { + b8 ok = 0; - WL_Box_ *geometry = NULL; - c8 * geometry_output = NULL; - - WL_State_ state = {0}; + WL_State_ state = { .ok = 1, }; wl_list_init(&state.outputs); state.display = wl_display_connect(NULL); if (state.display == NULL) { LOG_ERROR("wl_display_connect failed."); - return; + goto finalize; } state.registry = wl_display_get_registry(state.display); @@ -2046,74 +1929,22 @@ void wayland_screenshot_(i64 max_num_pixels, i64 *width, i64 *height, vec4_f32 * if (state.shm == NULL) { LOG_ERROR("Compositor does not support wl_shm."); - return; + goto finalize; } if (wl_list_empty(&state.outputs)) { LOG_ERROR("No wl_output."); - return; - } - - if (state.xdg_output_manager != NULL) { - WL_Output_ *output; - wl_list_for_each(output, &state.outputs, link) { - output->xdg_output = zxdg_output_manager_v1_get_xdg_output( - state.xdg_output_manager, - output->output - ); - - zxdg_output_v1_add_listener( - output->xdg_output, - &(struct zxdg_output_v1_listener) { - .logical_position = xdg_output_handle_logical_position, - .logical_size = xdg_output_handle_logical_size, - .done = xdg_output_handle_done, - .name = xdg_output_handle_name, - .description = xdg_output_handle_description, - }, - output - ); - } - - wl_display_roundtrip(state.display); - } else { - WL_Output_ *output; - wl_list_for_each(output, &state.outputs, link) { - guess_output_logical_geometry(output); - } + goto finalize; } if (state.screencopy_manager == NULL) { LOG_ERROR("Compositor does not support wlr-screencopy-unstable-v1."); - return; - } - - if (geometry_output != NULL) { - WL_Output_ *output; - wl_list_for_each(output, &state.outputs, link) { - if (output->name != NULL && strcmp(output->name, geometry_output) == 0) { - geometry = calloc(1, sizeof(WL_Box_)); - if (geometry == NULL) { - LOG_ERROR("calloc failed.\n"); - } else - memcpy(geometry, &output->logical_geometry, sizeof(WL_Box_)); - } - } - - if (geometry == NULL) { - LOG_ERROR("Unknown output for: %s", geometry_output); - return; - } + goto finalize; } u64 n_pending = 0; WL_Output_ *output; wl_list_for_each(output, &state.outputs, link) { - if (geometry != NULL && !intersect_box(geometry, &output->logical_geometry)) - continue; - if (use_greatest_scale && output->logical_scale > scale) - scale = output->logical_scale; - output->screencopy_frame = zwlr_screencopy_manager_v1_capture_output( state.screencopy_manager, 1, @@ -2131,31 +1962,71 @@ void wayland_screenshot_(i64 max_num_pixels, i64 *width, i64 *height, vec4_f32 * if (n_pending == 0) { LOG_ERROR("Supplied geometry did not intersect with any outputs."); - return; + goto finalize; } b8 done = 0; + while (!done && wl_display_dispatch(state.display) != -1) { + if (!state.ok) + goto finalize; done = (state.n_done == n_pending); } + if (!done) { LOG_ERROR("Failed to screenshoot all outputs."); - return; + goto finalize; + } + + *width = 0; + *height = 0; + + wl_list_for_each(output, &state.outputs, link) { + WL_Buffer_ *buffer = output->buffer; + if (buffer == NULL) continue; + if (!check_format_(buffer->format)) continue; + + if (*width < buffer->width) + *width = buffer->width; + *height += buffer->height; } - if (geometry == NULL) { - geometry = calloc(1, sizeof(WL_Box_)); - get_output_layout_extents(&state, geometry); + if (pixels == NULL || *width * *height > max_num_pixels) { + ok = 1; + goto finalize; } - // - // TODO: - // SAVE - // - (void) get_output_flipped; - (void) get_output_rotation; + for (i32 i = 0; i < *width * *height; ++i) + pixels[i] = (vec4_f32) { 0.f, 0.f, 0.f, 1.f }; + + i32 y0 = 0; + + wl_list_for_each(output, &state.outputs, link) { + WL_Buffer_ *buffer = output->buffer; + if (buffer == NULL) continue; + if (!check_format_(buffer->format)) continue; + + for (i32 j = 0; j < buffer->height; ++j) + for (i32 i = 0; i < buffer->width; ++i) { + fflush(stdout); + vec3_f32 c = rgb_f32_from_u32_(((u32 *) buffer->data)[j * (buffer->stride / 4) + i]); + + pixels[(y0 + j) * *width + i] = (vec4_f32) { + .x = c.x, + .y = c.y, + .z = c.z, + .w = 1.f, + }; + } + + y0 += buffer->height; + } + + ok = 1; WL_Output_ *output_tmp; + +finalize: wl_list_for_each_safe(output, output_tmp, &state.outputs, link) { wl_list_remove(&output->link); free(output->name); @@ -2171,15 +2042,14 @@ void wayland_screenshot_(i64 max_num_pixels, i64 *width, i64 *height, vec4_f32 * if (state.xdg_output_manager != NULL) zxdg_output_manager_v1_destroy(state.xdg_output_manager); - wl_shm_destroy(state.shm); - wl_registry_destroy(state.registry); - wl_display_disconnect(state.display); + if (state.shm != NULL) wl_shm_destroy(state.shm); + if (state.registry != NULL) wl_registry_destroy(state.registry); + if (state.display != NULL) wl_display_disconnect(state.display); - free(geometry); - free(geometry_output); + return ok; } -#endif // defined(__linux__) && !defined(NO_WAYLAND) +#endif // defined(__linux__) && !NO_WAYLAND // ================================================================ // @@ -2187,7 +2057,7 @@ void wayland_screenshot_(i64 max_num_pixels, i64 *width, i64 *height, vec4_f32 * // // ================================================================ -#if defined(__linux__) && !defined(NO_X11) +#if defined(__linux__) && !NO_X11 #include <sys/stat.h> #include <X11/Xlib.h> @@ -2222,7 +2092,6 @@ static Atom _dnd_action_copy = 0; static Atom _dnd_selection = 0; static Atom _text_uri_list = 0; static Window _dnd_source = 0; -static b8 _ignore_errors = 0; static i16 _key_table [MAX_NUM_KEYS] = {0}; static b8 _key_repeat [MAX_NUM_KEYS] = {0}; @@ -2238,11 +2107,8 @@ static b8 sub_str_eq_(c8 *a, c8 *b, i64 len) { } i32 x11_error_handler_(Display *display, XErrorEvent *event) { - if (!_ignore_errors) { - XGetErrorText(display, event->error_code, _error_buffer, sizeof _error_buffer - 1); - LOG_ERROR("X11: %s", _error_buffer); - } - + XGetErrorText(display, event->error_code, _error_buffer, sizeof _error_buffer - 1); + LOG_ERROR("%s", _error_buffer); return 0; } @@ -2991,46 +2857,28 @@ void p_clipboard_write(i64 size, c8 *data) { memcpy(g_platform.clipboard, data, g_platform.clipboard_size); } -void x11_screenshot_(i64 max_num_pixels, i64 *width, i64 *height, vec4_f32 *pixels) { - if (width == NULL || height == NULL) { - LOG_ERROR("Invalid arguments."); - return; - } - - *width = 0; - *height = 0; - +b8 x11_screenshot_(i64 max_num_pixels, i64 *width, i64 *height, vec4_f32 *pixels) { if (_display == NULL) { LOG_ERROR("No display."); - return; + return 0; } i32 screen = DefaultScreen(_display); i32 display_width = DisplayWidth (_display, screen); i32 display_height = DisplayHeight(_display, screen); - _ignore_errors = 1; - XImage *image = XGetImage(_display, _root_window, 0, 0, display_width, display_height, AllPlanes, ZPixmap); - _ignore_errors = 0; - if (image == NULL) { - #if !defined(NO_WAYLAND) - // Fallback for Wayland. - wayland_screenshot_(max_num_pixels, width, height, pixels); - return; - #else LOG_ERROR("XGetImage failed."); - return; - #endif + return 0; } *width = image->width; *height = image->height; if (pixels == NULL || image->width * image->height > max_num_pixels) - return; + return 1; for (i64 j = 0; j < image->height; ++j) for (i64 i = 0; i < image->width; ++i) { @@ -3043,13 +2891,35 @@ void x11_screenshot_(i64 max_num_pixels, i64 *width, i64 *height, vec4_f32 *pixe .w = 1.f, }; } + + return 1; } +#endif // defined(__linux__) && !NO_X11 + +// ================================================================ + +#if defined(__linux__) && (!NO_X11 || !NO_WAYLAND) void p_screenshot(i64 max_num_pixels, i64 *width, i64 *height, vec4_f32 *pixels) { - x11_screenshot_(max_num_pixels, width, height, pixels); -} + if (width == NULL || height == NULL) { + LOG_ERROR("Invalid arguments."); + return; + } + + #if !NO_X11 + if (x11_screenshot_(max_num_pixels, width, height, pixels)) + return; + #endif -#endif // defined(__linux__) && !defined(NO_X11) + #if !NO_WAYLAND + if (wayland_screenshot_(max_num_pixels, width, height, pixels)) + return; + #endif + + *width = 0; + *height = 0; + } +#endif // defined(__linux__) && (!NO_X11 || !NO_WAYLAND) // ================================================================ // |