From c0c52a07376d22339a06d031892af649c2eb8acc Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Wed, 22 Jan 2025 17:27:34 +0100 Subject: Clipboard image and sound proc decls --- reduced_system_layer.c | 269 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 177 insertions(+), 92 deletions(-) (limited to 'reduced_system_layer.c') diff --git a/reduced_system_layer.c b/reduced_system_layer.c index 088a8a5..758b5d9 100644 --- a/reduced_system_layer.c +++ b/reduced_system_layer.c @@ -28,8 +28,12 @@ // To-Do list // // - Work in progress +// - Clipboard +// - Images +// - Sound // - Seldom allocator // - Logging +// - Graphics perf // - Examples // - Conway's Game of Life // - Julia Set @@ -54,8 +58,6 @@ // - Networking // - Windows sockets // - TCP -// - Clipboard -// - Images // - Switching canvases - Web // - Long term // - dlopen - load libraries conditionally @@ -104,7 +106,7 @@ // - Window - X11, Web // - Screenshot - X11, Wayland // - Clipboard -// - Text - X11 +// - Text - X11, Web // - Sound - ALSA, Web // - Networking // - Unix UDP sockets @@ -571,7 +573,10 @@ typedef struct { i32 real_width; i32 real_height; i64 input_size; - i64 clipboard_size; + i64 clipboard_text_len; + i64 clipboard_image_width; + i64 clipboard_image_height; + i64 num_clipboard_sound_samples; i32 cursor_x; i32 cursor_y; i32 cursor_dx; @@ -583,12 +588,14 @@ typedef struct { i64 num_drop_files; Drop_File *drop_files; - vec4_f32 pixels [MAX_NUM_PIXELS]; - vec4_f32 sketch [MAX_NUM_PIXELS]; - Input_Key input [MAX_INPUT_SIZE]; - c8 clipboard [MAX_CLIPBOARD_SIZE]; - b8 key_down [MAX_NUM_KEYS]; - b8 key_pressed [MAX_NUM_KEYS]; + vec4_f32 pixels [MAX_NUM_PIXELS]; + vec4_f32 sketch [MAX_NUM_PIXELS]; + Input_Key input [MAX_INPUT_SIZE]; + c8 clipboard_text [MAX_CLIPBOARD_SIZE]; + vec4_f32 clipboard_image [MAX_CLIPBOARD_SIZE]; + f32 clipboard_sound [MAX_CLIPBOARD_SIZE]; + b8 key_down [MAX_NUM_KEYS]; + b8 key_pressed [MAX_NUM_KEYS]; } Platform; // UTF-8 @@ -613,7 +620,9 @@ void p_render_frame(void); void p_event_loop(void); // Clipboard -void p_clipboard_write(i64 size, c8 *data); +void p_clipboard_write_text(i64 size, c8 *data); +void p_clipboard_write_image(i64 width, i64 heiht, vec4_f32 *pixels); +void p_clipboard_write_sound(i8 num_channels, i64 num_samples, f32 *frames); // Take a screenshot void p_screenshot(i64 max_num_pixels, i64 *width, i64 *height, vec4_f32 *pixels); @@ -1457,6 +1466,9 @@ void p_queue_sound(i64 delay_in_samples, i64 num_samples, f32 *frames) { // // Wayland // +// FIXME: Remove dynamic memory management. +// Use arena allocator for a static buffer. +// // ================================================================ #if defined(__linux__) && !NO_WAYLAND @@ -1578,18 +1590,15 @@ static void zxdg_output_v1_destroy(void *zxdg_output_v1) { } 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); + 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); } static void zwlr_screencopy_frame_v1_destroy(void *zwlr_screencopy_frame_v1) { - wl_proxy_marshal_flags((struct wl_proxy *) zwlr_screencopy_frame_v1, - 1, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_screencopy_frame_v1), WL_MARSHAL_FLAG_DESTROY); + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_screencopy_frame_v1, 1, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_screencopy_frame_v1), WL_MARSHAL_FLAG_DESTROY); } static void zwlr_screencopy_frame_v1_copy(void *zwlr_screencopy_frame_v1, struct wl_buffer *buffer) { - wl_proxy_marshal_flags((struct wl_proxy *) zwlr_screencopy_frame_v1, - 0, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_screencopy_frame_v1), 0, buffer); + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_screencopy_frame_v1, 0, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_screencopy_frame_v1), 0, buffer); } static i32 zwlr_screencopy_frame_v1_add_listener(void *zwlr_screencopy_frame_v1, struct zwlr_screencopy_frame_v1_listener *listener, void *data) { @@ -1756,18 +1765,18 @@ static void screencopy_frame_handle_flags( } static void screencopy_frame_handle_ready( - void * data, + void *data, void *frame, - u32 tv_sec_hi, - u32 tv_sec_lo, - u32 tv_nsec + u32 tv_sec_hi, + u32 tv_sec_lo, + u32 tv_nsec ) { WL_Output_ *output = data; ++output->state->n_done; } static void screencopy_frame_handle_failed( - void * data, + void *data, void *frame ) { WL_Output_ *output = data; @@ -1910,6 +1919,7 @@ b8 wayland_screenshot_(i64 max_num_pixels, i64 *width, i64 *height, vec4_f32 *pi while (!done && wl_display_dispatch(state.display) != -1) { if (!state.ok) goto finalize; + usleep(0); done = (state.n_done == n_pending); } @@ -1948,7 +1958,6 @@ b8 wayland_screenshot_(i64 max_num_pixels, i64 *width, i64 *height, vec4_f32 *pi 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) { @@ -2007,32 +2016,37 @@ finalize: #define FILE_PATH_PREFIX_ "file://" #define FILE_PATH_PREFIX_LEN_ ((sizeof FILE_PATH_PREFIX_) - 1) -static i64 _frame_time = 0; -static XImage _image = {0}; -static Display *_display = NULL; -static Window _root_window = 0; -static GC _gc = NULL; -static XIM _im = NULL; -static XIC _ic = NULL; -static Window _window = 0; -static Window _drop_source = 0; -static Atom _wm_delete_window = 0; -static Atom _clipboard = 0; -static Atom _targets = 0; -static Atom _utf8_string = 0; -static Atom _target = 0; -static Atom _dnd_aware = 0; -static Atom _dnd_enter = 0; -static Atom _dnd_position = 0; -static Atom _dnd_status = 0; -static Atom _dnd_leave = 0; -static Atom _dnd_drop = 0; -static Atom _dnd_finished = 0; -static Atom _dnd_action_copy = 0; -static Atom _dnd_selection = 0; -static Atom _text_uri_list = 0; -static Window _dnd_source = 0; -static b8 _mapped = 0; +static i64 _frame_time = 0; +static XImage _image = {0}; +static Display *_display = NULL; +static Window _root_window = 0; +static GC _gc = NULL; +static XIM _im = NULL; +static XIC _ic = NULL; +static Window _window = 0; +static Window _drop_source = 0; +static Atom _wm_delete_window = 0; +static Atom _clipboard = 0; +static Atom _targets = 0; +static Atom _text_plain = 0; +static Atom _utf8_string = 0; +static Atom _image_bmp = 0; +static Atom _image_ppm = 0; +static Atom _audio_wav = 0; +static Atom _dnd_aware = 0; +static Atom _dnd_enter = 0; +static Atom _dnd_position = 0; +static Atom _dnd_status = 0; +static Atom _dnd_leave = 0; +static Atom _dnd_drop = 0; +static Atom _dnd_finished = 0; +static Atom _dnd_action_copy = 0; +static Atom _dnd_selection = 0; +static Atom _text_uri_list = 0; +static Window _dnd_source = 0; +static b8 _mapped = 0; +static b8 _requested_clipboard = 0; + static i16 _key_table [MAX_NUM_KEYS] = {0}; static b8 _key_repeat [MAX_NUM_KEYS] = {0}; @@ -2244,9 +2258,11 @@ void p_init(void) { _wm_delete_window = XInternAtom(_display, "WM_DELETE_WINDOW", False); _clipboard = XInternAtom(_display, "CLIPBOARD", False); _targets = XInternAtom(_display, "TARGETS", False); + _text_plain = XInternAtom(_display, "text/plain", False); _utf8_string = XInternAtom(_display, "UTF8_STRING", False); - - _target = None; + _image_bmp = XInternAtom(_display, "image/bmp", False); + _image_ppm = XInternAtom(_display, "image/ppm", False); + _audio_wav = XInternAtom(_display, "audio/wav", False); XSetICFocus(_ic); XSetWMProtocols(_display, _window, &_wm_delete_window, 1); @@ -2310,8 +2326,6 @@ i32 p_handle_events(void) { XEvent ev; - b8 requested_clipboard = 0; - while (XEventsQueued(_display, QueuedAlready) > 0) { ++num_events; @@ -2352,9 +2366,9 @@ i32 p_handle_events(void) { case Button5 + 2: g_platform.wheel_dx += MOUSE_WHEEL_FACTOR; break; default:; } - if (!requested_clipboard) { + if (!_requested_clipboard) { XConvertSelection(_display, _clipboard, _targets, _clipboard, _window, CurrentTime); - requested_clipboard = 1; + _requested_clipboard = 1; } break; @@ -2417,9 +2431,9 @@ i32 p_handle_events(void) { } } - if (!requested_clipboard) { + if (!_requested_clipboard) { XConvertSelection(_display, _clipboard, _targets, _clipboard, _window, CurrentTime); - requested_clipboard = 1; + _requested_clipboard = 1; } } break; @@ -2446,18 +2460,37 @@ i32 p_handle_events(void) { ev.xselectionrequest.selection == _clipboard && XGetSelectionOwner(_display, _clipboard) == _window) { if (ev.xselectionrequest.property != None) { - if (ev.xselectionrequest.target == _targets) - XChangeProperty( - ev.xselectionrequest.display, - ev.xselectionrequest.requestor, - ev.xselectionrequest.property, - XA_ATOM, - 32, - PropModeReplace, - (u8 *) &_utf8_string, - 1 - ); - else if (ev.xselectionrequest.target == _utf8_string) + if (ev.xselectionrequest.target == _targets) { + b8 has_text = g_platform.clipboard_text_len > 0; + b8 has_image = g_platform.clipboard_image_width > 0 && g_platform.clipboard_image_height > 0; + b8 has_sound = g_platform.num_clipboard_sound_samples > 0; + + if (!has_text && !has_image && !has_sound) + XDeleteProperty( + ev.xselectionrequest.display, + ev.xselectionrequest.requestor, + ev.xselectionrequest.property + ); + else + XChangeProperty( + ev.xselectionrequest.display, + ev.xselectionrequest.requestor, + ev.xselectionrequest.property, + XA_ATOM, + 32, + PropModeReplace, + has_text && has_image && has_sound ? (u8 *) &(Atom[]) { XA_STRING, _text_plain, _utf8_string, _image_ppm, _image_bmp, _audio_wav, } + : has_text && has_image ? (u8 *) &(Atom[]) { XA_STRING, _text_plain, _utf8_string, _image_ppm, _image_bmp, } + : has_text && has_sound ? (u8 *) &(Atom[]) { XA_STRING, _text_plain, _utf8_string, _audio_wav, } + : has_image && has_sound ? (u8 *) &(Atom[]) { _image_ppm, _image_bmp, _audio_wav, } + : has_text ? (u8 *) &(Atom[]) { XA_STRING, _text_plain, _utf8_string, } + : has_image ? (u8 *) &(Atom[]) { _image_ppm, _image_bmp, } + : (u8 *) &(Atom[]) { _audio_wav, }, + has_text * 3 + has_image * 2 + has_sound * 1 + ); + } else if (ev.xselectionrequest.target == XA_STRING + || ev.xselectionrequest.target == _text_plain + || ev.xselectionrequest.target == _utf8_string) { XChangeProperty( ev.xselectionrequest.display, ev.xselectionrequest.requestor, @@ -2465,9 +2498,19 @@ i32 p_handle_events(void) { ev.xselectionrequest.target, 8, PropModeReplace, - (u8 *) g_platform.clipboard, - g_platform.clipboard_size + (u8 *) g_platform.clipboard_text, + g_platform.clipboard_text_len ); + } else if (ev.xselectionrequest.target == _image_bmp) { + // TODO + LOG_ERROR("Send BMP image - not implemented."); + } else if (ev.xselectionrequest.target == _image_ppm) { + // TODO + LOG_ERROR("Send PPM image - not implemented."); + } else if (ev.xselectionrequest.target == _audio_wav) { + // TODO + LOG_ERROR("Send WAV audio - not implemented."); + } } XSendEvent(_display, ev.xselectionrequest.requestor, 0, 0, (XEvent *) &(XSelectionEvent) { @@ -2560,26 +2603,67 @@ i32 p_handle_events(void) { ); if (ev.xselection.target == _targets) { - Atom *list = (Atom *) data; - _target = None; - len /= 4; + Atom *list = (Atom *) data; + Atom target_text = None; + Atom target_image = None; + Atom target_sound = None; + len /= 4; for (i64 i = 0; i < len; i++) if (list[i] == XA_STRING) - _target = XA_STRING; + target_text = XA_STRING; + else if (list[i] == _text_plain) + target_text = _text_plain; else if (list[i] == _utf8_string) { - _target = _utf8_string; + target_text = _utf8_string; + break; + } + + for (i64 i = 0; i < len; i++) + if (list[i] == _image_ppm) + target_image = _image_ppm; + else if (list[i] == _image_bmp) { + target_image = _image_bmp; break; } - if (_target != None) - XConvertSelection(_display, _clipboard, _target, _clipboard, _window, CurrentTime); - } else if (ev.xselection.target == _target) { - if (len > MAX_CLIPBOARD_SIZE) - len = MAX_CLIPBOARD_SIZE; - g_platform.clipboard_size = len; + for (i64 i = 0; i < len; i++) + if (list[i] == _audio_wav) { + target_sound = _audio_wav; + break; + } + + if (target_text != None) + XConvertSelection(_display, _clipboard, target_text, _clipboard, _window, CurrentTime); + + if (target_image != None) + XConvertSelection(_display, _clipboard, target_image, _clipboard, _window, CurrentTime); + + if (target_sound != None) + XConvertSelection(_display, _clipboard, target_sound, _clipboard, _window, CurrentTime); + + if (target_text == None && target_image == None && target_sound == None) + _requested_clipboard = 0; + } else if (ev.xselection.target == XA_STRING || ev.xselection.target == _text_plain || ev.xselection.target == _utf8_string) { + if (len >= MAX_CLIPBOARD_SIZE) + len = MAX_CLIPBOARD_SIZE - 1; + g_platform.clipboard_text_len = len; if (len > 0) - memcpy(g_platform.clipboard, data, len); + memcpy(g_platform.clipboard_text, data, len); + g_platform.clipboard_text[len] = '\0'; + _requested_clipboard = 0; + } else if (ev.xselection.target == _image_bmp) { + // TODO + LOG_ERROR("Receive BMP image - not implemented."); + _requested_clipboard = 0; + } else if (ev.xselection.target == _image_ppm) { + // TODO + LOG_ERROR("Receive PPM image - not implemented."); + _requested_clipboard = 0; + } else if (ev.xselection.target == _audio_wav) { + // TODO + LOG_ERROR("Receive WAV audio - not implemented."); + _requested_clipboard = 0; } if (data) @@ -2594,9 +2678,9 @@ i32 p_handle_events(void) { case FocusIn: g_platform.has_focus = 1; - if (!requested_clipboard) { + if (!_requested_clipboard) { XConvertSelection(_display, _clipboard, _targets, _clipboard, _window, CurrentTime); - requested_clipboard = 1; + _requested_clipboard = 1; } break; @@ -2790,7 +2874,7 @@ void p_render_frame(void) { pixel_size_calubrate_(p_time() - _frame_time); } -void p_clipboard_write(i64 size, c8 *data) { +void p_clipboard_write_text(i64 size, c8 *data) { if (size > MAX_CLIPBOARD_SIZE) { LOG_ERROR("Size is too big %lld.", size); return; @@ -2798,9 +2882,10 @@ void p_clipboard_write(i64 size, c8 *data) { XSetSelectionOwner(_display, _clipboard, _window, CurrentTime); - g_platform.clipboard_size = size < MAX_CLIPBOARD_SIZE ? size : MAX_CLIPBOARD_SIZE; - if (g_platform.clipboard_size > 0) - memcpy(g_platform.clipboard, data, g_platform.clipboard_size); + g_platform.clipboard_text_len = size < MAX_CLIPBOARD_SIZE ? size : MAX_CLIPBOARD_SIZE - 1; + if (g_platform.clipboard_text_len > 0) + memcpy(g_platform.clipboard_text, data, g_platform.clipboard_text_len); + g_platform.clipboard_text[g_platform.clipboard_text_len] = '\0'; } b8 x11_screenshot_(i64 max_num_pixels, i64 *width, i64 *height, vec4_f32 *pixels) { @@ -3117,9 +3202,9 @@ void p_render_frame(void) { } } -void p_clipboard_write_impl(i32 size, c8 *data); +void p_clipboard_write_text_impl(i32 size, c8 *data); -void p_clipboard_write(i64 size, c8 *data) { +void p_clipboard_write_text(i64 size, c8 *data) { if (size < 0 || data == NULL) return; if (size > MAX_CLIPBOARD_SIZE) @@ -3129,7 +3214,7 @@ void p_clipboard_write(i64 size, c8 *data) { for (i64 i = 0; i < size; ++i) g_platform.clipboard[i] = data[i]; - p_clipboard_write_impl((i32) size, data); + p_clipboard_write_text_impl((i32) size, data); } void p_screenshot(i64 max_num_pixels, i64 *width, i64 *height, vec4_f32 *pixels) { -- cgit v1.2.3