From 60970b821421c974632b8351c7fbf73ba1bc1912 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 29 Jun 2025 18:22:46 +0200 Subject: [PATCH 01/12] Handle mouse integer scrolling locally Do not rely on SDL to expose integer scroll values, as they are not available in all versions. It was reimplemented in SDL 3.2.12 by this commit: Refs #6156 --- app/src/hid/hid_mouse.c | 35 ++++++++++++++++++++++++++++------- app/src/hid/hid_mouse.h | 10 +++++++++- app/src/input_events.h | 2 -- app/src/input_manager.c | 2 -- app/src/uhid/mouse_uhid.c | 5 ++++- app/src/uhid/mouse_uhid.h | 2 ++ app/src/usb/mouse_aoa.c | 5 ++++- app/src/usb/mouse_aoa.h | 2 ++ app/src/usb/screen_otg.c | 2 -- 9 files changed, 49 insertions(+), 16 deletions(-) diff --git a/app/src/hid/hid_mouse.c b/app/src/hid/hid_mouse.c index 33f0807e44..54105a176a 100644 --- a/app/src/hid/hid_mouse.c +++ b/app/src/hid/hid_mouse.c @@ -166,6 +166,12 @@ sc_hid_buttons_from_buttons_state(uint8_t buttons_state) { return c; } +void +sc_hid_mouse_init(struct sc_hid_mouse *hid) { + hid->residual_hscroll = 0; + hid->residual_vscroll = 0; +} + void sc_hid_mouse_generate_input_from_motion(struct sc_hid_input *hid_input, const struct sc_mouse_motion_event *event) { @@ -192,22 +198,37 @@ sc_hid_mouse_generate_input_from_click(struct sc_hid_input *hid_input, data[4] = 0; // no horizontal scrolling } +static int8_t +consume_scroll_integer(float *scroll) { + float value = CLAMP(*scroll, -127, 127); + int8_t consume = value; // truncate towards 0 + float residual = value - consume; + *scroll = residual; + return consume; +} + bool -sc_hid_mouse_generate_input_from_scroll(struct sc_hid_input *hid_input, +sc_hid_mouse_generate_input_from_scroll(struct sc_hid_mouse *hid, + struct sc_hid_input *hid_input, const struct sc_mouse_scroll_event *event) { - if (!event->vscroll_int && !event->hscroll_int) { - // Need a full integral value for HID + sc_hid_mouse_input_init(hid_input); + + hid->residual_hscroll += event->hscroll; + hid->residual_vscroll += event->vscroll; + int8_t hscroll = consume_scroll_integer(&hid->residual_hscroll); + int8_t vscroll = consume_scroll_integer(&hid->residual_vscroll); + + if (!hscroll && !vscroll) { + // Not enough scrolling to inject a scroll event return false; } - sc_hid_mouse_input_init(hid_input); - uint8_t *data = hid_input->data; data[0] = 0; // buttons state irrelevant (and unknown) data[1] = 0; // no x motion data[2] = 0; // no y motion - data[3] = CLAMP(event->vscroll_int, -127, 127); - data[4] = CLAMP(event->hscroll_int, -127, 127); + data[3] = vscroll; + data[4] = hscroll; return true; } diff --git a/app/src/hid/hid_mouse.h b/app/src/hid/hid_mouse.h index 4ae4bfd467..6d590267d8 100644 --- a/app/src/hid/hid_mouse.h +++ b/app/src/hid/hid_mouse.h @@ -8,6 +8,13 @@ #define SC_HID_ID_MOUSE 2 +struct sc_hid_mouse { + float residual_hscroll; + float residual_vscroll; +}; + +void sc_hid_mouse_init(struct sc_hid_mouse *hid); + void sc_hid_mouse_generate_open(struct sc_hid_open *hid_open); @@ -23,7 +30,8 @@ sc_hid_mouse_generate_input_from_click(struct sc_hid_input *hid_input, const struct sc_mouse_click_event *event); bool -sc_hid_mouse_generate_input_from_scroll(struct sc_hid_input *hid_input, +sc_hid_mouse_generate_input_from_scroll(struct sc_hid_mouse *hid, + struct sc_hid_input *hid_input, const struct sc_mouse_scroll_event *event); #endif diff --git a/app/src/input_events.h b/app/src/input_events.h index 1e34b50eda..0c022acc03 100644 --- a/app/src/input_events.h +++ b/app/src/input_events.h @@ -393,8 +393,6 @@ struct sc_mouse_scroll_event { struct sc_position position; float hscroll; float vscroll; - int32_t hscroll_int; - int32_t vscroll_int; uint8_t buttons_state; // bitwise-OR of sc_mouse_button values }; diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 3e4dd0f32f..f7a787d1e2 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -903,8 +903,6 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im, .hscroll = event->x, .vscroll = event->y, #endif - .hscroll_int = event->x, - .vscroll_int = event->y, .buttons_state = im->mouse_buttons_state, }; diff --git a/app/src/uhid/mouse_uhid.c b/app/src/uhid/mouse_uhid.c index 869e48a468..1277ed849b 100644 --- a/app/src/uhid/mouse_uhid.c +++ b/app/src/uhid/mouse_uhid.c @@ -55,7 +55,8 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp, struct sc_mouse_uhid *mouse = DOWNCAST(mp); struct sc_hid_input hid_input; - if (!sc_hid_mouse_generate_input_from_scroll(&hid_input, event)) { + if (!sc_hid_mouse_generate_input_from_scroll(&mouse->hid, &hid_input, + event)) { return; } @@ -65,6 +66,8 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp, bool sc_mouse_uhid_init(struct sc_mouse_uhid *mouse, struct sc_controller *controller) { + sc_hid_mouse_init(&mouse->hid); + mouse->controller = controller; static const struct sc_mouse_processor_ops ops = { diff --git a/app/src/uhid/mouse_uhid.h b/app/src/uhid/mouse_uhid.h index f117ba973b..998bf21a2f 100644 --- a/app/src/uhid/mouse_uhid.h +++ b/app/src/uhid/mouse_uhid.h @@ -4,11 +4,13 @@ #include #include "controller.h" +#include "hid/hid_mouse.h" #include "trait/mouse_processor.h" struct sc_mouse_uhid { struct sc_mouse_processor mouse_processor; // mouse processor trait + struct sc_hid_mouse hid; struct sc_controller *controller; }; diff --git a/app/src/usb/mouse_aoa.c b/app/src/usb/mouse_aoa.c index fd5fa5e07f..4bc76c54bf 100644 --- a/app/src/usb/mouse_aoa.c +++ b/app/src/usb/mouse_aoa.c @@ -42,7 +42,8 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp, struct sc_mouse_aoa *mouse = DOWNCAST(mp); struct sc_hid_input hid_input; - if (!sc_hid_mouse_generate_input_from_scroll(&hid_input, event)) { + if (!sc_hid_mouse_generate_input_from_scroll(&mouse->hid, &hid_input, + event)) { return; } @@ -64,6 +65,8 @@ sc_mouse_aoa_init(struct sc_mouse_aoa *mouse, struct sc_aoa *aoa) { return false; } + sc_hid_mouse_init(&mouse->hid); + static const struct sc_mouse_processor_ops ops = { .process_mouse_motion = sc_mouse_processor_process_mouse_motion, .process_mouse_click = sc_mouse_processor_process_mouse_click, diff --git a/app/src/usb/mouse_aoa.h b/app/src/usb/mouse_aoa.h index 506286ba94..1a25d986e8 100644 --- a/app/src/usb/mouse_aoa.h +++ b/app/src/usb/mouse_aoa.h @@ -6,11 +6,13 @@ #include #include "usb/aoa_hid.h" +#include "hid/hid_mouse.h" #include "trait/mouse_processor.h" struct sc_mouse_aoa { struct sc_mouse_processor mouse_processor; // mouse processor trait + struct sc_hid_mouse hid; struct sc_aoa *aoa; }; diff --git a/app/src/usb/screen_otg.c b/app/src/usb/screen_otg.c index 5c580df994..bed48eb686 100644 --- a/app/src/usb/screen_otg.c +++ b/app/src/usb/screen_otg.c @@ -171,8 +171,6 @@ sc_screen_otg_process_mouse_wheel(struct sc_screen_otg *screen, .hscroll = event->x, .vscroll = event->y, #endif - .hscroll_int = event->x, - .vscroll_int = event->y, .buttons_state = sc_mouse_buttons_state_from_sdl(sdl_buttons_state), }; From 1680673d0493e0d8c728a99743027e2460a3b8e4 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 3 Jul 2025 18:36:17 +0200 Subject: [PATCH 02/12] Remove prepare_for_frame() Inline the function body in its only caller. This will simplify further refactors. --- app/src/screen.c | 53 +++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/app/src/screen.c b/app/src/screen.c index da17df0ed2..67177efa77 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -619,28 +619,6 @@ sc_screen_init_size(struct sc_screen *screen) { return res != SC_DISPLAY_RESULT_ERROR; } -// recreate the texture and resize the window if the frame size has changed -static enum sc_display_result -prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) { - assert(screen->video); - - if (screen->frame_size.width == new_frame_size.width - && screen->frame_size.height == new_frame_size.height) { - return SC_DISPLAY_RESULT_OK; - } - - // frame dimension changed - screen->frame_size = new_frame_size; - - struct sc_size new_content_size = - get_oriented_size(new_frame_size, screen->orientation); - set_content_size(screen, new_content_size); - - sc_screen_update_content_rect(screen); - - return sc_display_set_texture_size(&screen->display, screen->frame_size); -} - static bool sc_screen_apply_frame(struct sc_screen *screen) { assert(screen->video); @@ -649,16 +627,31 @@ sc_screen_apply_frame(struct sc_screen *screen) { AVFrame *frame = screen->frame; struct sc_size new_frame_size = {frame->width, frame->height}; - enum sc_display_result res = prepare_for_frame(screen, new_frame_size); - if (res == SC_DISPLAY_RESULT_ERROR) { - return false; - } - if (res == SC_DISPLAY_RESULT_PENDING) { - // Not an error, but do not continue - return true; + + if (screen->frame_size.width != new_frame_size.width + || screen->frame_size.height != new_frame_size.height) { + // frame dimension changed + screen->frame_size = new_frame_size; + + struct sc_size new_content_size = + get_oriented_size(new_frame_size, screen->orientation); + set_content_size(screen, new_content_size); + + sc_screen_update_content_rect(screen); + + enum sc_display_result res = + sc_display_set_texture_size(&screen->display, screen->frame_size); + if (res == SC_DISPLAY_RESULT_ERROR) { + return false; + } + if (res == SC_DISPLAY_RESULT_PENDING) { + // Not an error, but do not continue + return true; + } } - res = sc_display_update_texture(&screen->display, frame); + enum sc_display_result res = + sc_display_update_texture(&screen->display, frame); if (res == SC_DISPLAY_RESULT_ERROR) { return false; } From b266119fdff91934ebf216e2ae1aee9b83381311 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 3 Jul 2025 18:49:35 +0200 Subject: [PATCH 03/12] Create texture on first frame The texture was created as soon as the initial video size was known, even before the first frame arrived. However, texture creation will require other data, such as the color range, which is only available once the first frame is received. Therefore, delay texture creation until the first frame. --- app/src/events.h | 1 - app/src/screen.c | 66 ++++++++++++++---------------------------------- app/src/screen.h | 1 + 3 files changed, 20 insertions(+), 48 deletions(-) diff --git a/app/src/events.h b/app/src/events.h index 2fe4d3a7bc..2e7318e5b4 100644 --- a/app/src/events.h +++ b/app/src/events.h @@ -16,7 +16,6 @@ enum { SC_EVENT_USB_DEVICE_DISCONNECTED, SC_EVENT_DEMUXER_ERROR, SC_EVENT_RECORDER_ERROR, - SC_EVENT_SCREEN_INIT_SIZE, SC_EVENT_TIME_LIMIT_REACHED, SC_EVENT_CONTROLLER_ERROR, SC_EVENT_AOA_OPEN_ERROR, diff --git a/app/src/screen.c b/app/src/screen.c index 67177efa77..a2c22b2551 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -208,6 +208,7 @@ sc_screen_update_content_rect(struct sc_screen *screen) { static void sc_screen_render(struct sc_screen *screen, bool update_content_rect) { assert(screen->video); + assert(screen->has_video_window); if (update_content_rect) { sc_screen_update_content_rect(screen); @@ -264,19 +265,6 @@ sc_screen_frame_sink_open(struct sc_frame_sink *sink, return false; } - assert(ctx->width > 0 && ctx->width <= 0xFFFF); - assert(ctx->height > 0 && ctx->height <= 0xFFFF); - // screen->frame_size is never used before the event is pushed, and the - // event acts as a memory barrier so it is safe without mutex - screen->frame_size.width = ctx->width; - screen->frame_size.height = ctx->height; - - // Post the event on the UI thread (the texture must be created from there) - bool ok = sc_push_event(SC_EVENT_SCREEN_INIT_SIZE); - if (!ok) { - return false; - } - #ifndef NDEBUG screen->open = true; #endif @@ -327,6 +315,7 @@ sc_screen_init(struct sc_screen *screen, const struct sc_screen_params *params) { screen->resize_pending = false; screen->has_frame = false; + screen->has_video_window = false; screen->fullscreen = false; screen->maximized = false; screen->minimized = false; @@ -603,22 +592,6 @@ sc_screen_set_orientation(struct sc_screen *screen, sc_screen_render(screen, true); } -static bool -sc_screen_init_size(struct sc_screen *screen) { - // Before first frame - assert(!screen->has_frame); - - // The requested size is passed via screen->frame_size - - struct sc_size content_size = - get_oriented_size(screen->frame_size, screen->orientation); - screen->content_size = content_size; - - enum sc_display_result res = - sc_display_set_texture_size(&screen->display, screen->frame_size); - return res != SC_DISPLAY_RESULT_ERROR; -} - static bool sc_screen_apply_frame(struct sc_screen *screen) { assert(screen->video); @@ -628,16 +601,22 @@ sc_screen_apply_frame(struct sc_screen *screen) { AVFrame *frame = screen->frame; struct sc_size new_frame_size = {frame->width, frame->height}; - if (screen->frame_size.width != new_frame_size.width + if (!screen->has_frame + || screen->frame_size.width != new_frame_size.width || screen->frame_size.height != new_frame_size.height) { // frame dimension changed screen->frame_size = new_frame_size; struct sc_size new_content_size = get_oriented_size(new_frame_size, screen->orientation); - set_content_size(screen, new_content_size); - - sc_screen_update_content_rect(screen); + if (screen->has_frame) { + set_content_size(screen, new_content_size); + sc_screen_update_content_rect(screen); + } else { + // This is the first frame + screen->has_frame = true; + screen->content_size = new_content_size; + } enum sc_display_result res = sc_display_set_texture_size(&screen->display, screen->frame_size); @@ -660,8 +639,9 @@ sc_screen_apply_frame(struct sc_screen *screen) { return true; } - if (!screen->has_frame) { - screen->has_frame = true; + assert(screen->has_frame); + if (!screen->has_video_window) { + screen->has_video_window = true; // this is the very first frame, show the window sc_screen_show_initial_window(screen); @@ -794,15 +774,6 @@ sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) { bool sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) { switch (event->type) { - case SC_EVENT_SCREEN_INIT_SIZE: { - // The initial size is passed via screen->frame_size - bool ok = sc_screen_init_size(screen); - if (!ok) { - LOGE("Could not initialize screen size"); - return false; - } - return true; - } case SC_EVENT_NEW_FRAME: { bool ok = sc_screen_update_frame(screen); if (!ok) { @@ -815,11 +786,12 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) { if (!screen->video && event->window.event == SDL_WINDOWEVENT_EXPOSED) { sc_screen_render_novideo(screen); + return true; } - // !video implies !has_frame - assert(screen->video || !screen->has_frame); - if (!screen->has_frame) { + // !video implies !has_video_window + assert(screen->video || !screen->has_video_window); + if (!screen->has_video_window) { // Do nothing return true; } diff --git a/app/src/screen.h b/app/src/screen.h index 6621b2d2d1..89a25cda46 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -61,6 +61,7 @@ struct sc_screen { // rectangle of the content (excluding black borders) struct SDL_Rect rect; bool has_frame; + bool has_video_window; bool fullscreen; bool maximized; bool minimized; From e66d012ab727ef594d275e8d5ae31fb7bad2073a Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 3 Jul 2025 19:04:09 +0200 Subject: [PATCH 04/12] Set color range during texture creation This prepares for the migration to SDL3, where the color range can only be specified at the time of texture creation. --- app/src/display.c | 61 ++++++++++++++++++++++++----------------------- app/src/display.h | 12 ++++++---- app/src/screen.c | 3 ++- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/app/src/display.c b/app/src/display.c index 15f9a1f19e..c84971a014 100644 --- a/app/src/display.c +++ b/app/src/display.c @@ -94,7 +94,6 @@ sc_display_init(struct sc_display *display, SDL_Window *window, display->texture = NULL; display->pending.flags = 0; display->pending.frame = NULL; - display->has_frame = false; if (icon_novideo) { // Without video, set a static scrcpy icon as window content @@ -125,9 +124,15 @@ sc_display_destroy(struct sc_display *display) { SDL_DestroyRenderer(display->renderer); } +static SDL_YUV_CONVERSION_MODE +sc_display_to_sdl_color_range(enum AVColorRange color_range) { + return color_range == AVCOL_RANGE_JPEG ? SDL_YUV_CONVERSION_JPEG + : SDL_YUV_CONVERSION_AUTOMATIC; +} + static SDL_Texture * sc_display_create_texture(struct sc_display *display, - struct sc_size size) { + struct sc_size size, enum AVColorRange color_range) { SDL_Renderer *renderer = display->renderer; SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, @@ -150,14 +155,22 @@ sc_display_create_texture(struct sc_display *display, SDL_GL_UnbindTexture(texture); } + // Configure YUV color range conversion + SDL_YUV_CONVERSION_MODE sdl_color_range = + sc_display_to_sdl_color_range(color_range); + SDL_SetYUVConversionMode(sdl_color_range); + return texture; } static inline void -sc_display_set_pending_size(struct sc_display *display, struct sc_size size) { +sc_display_set_pending_texture(struct sc_display *display, + struct sc_size size, + enum AVColorRange color_range) { assert(!display->texture); - display->pending.size = size; - display->pending.flags |= SC_DISPLAY_PENDING_FLAG_SIZE; + display->pending.texture.size = size; + display->pending.texture.color_range = color_range; + display->pending.flags |= SC_DISPLAY_PENDING_FLAG_TEXTURE; } static bool @@ -189,15 +202,17 @@ sc_display_update_texture_internal(struct sc_display *display, static bool sc_display_apply_pending(struct sc_display *display) { - if (display->pending.flags & SC_DISPLAY_PENDING_FLAG_SIZE) { + if (display->pending.flags & SC_DISPLAY_PENDING_FLAG_TEXTURE) { assert(!display->texture); display->texture = - sc_display_create_texture(display, display->pending.size); + sc_display_create_texture(display, + display->pending.texture.size, + display->pending.texture.color_range); if (!display->texture) { return false; } - display->pending.flags &= ~SC_DISPLAY_PENDING_FLAG_SIZE; + display->pending.flags &= ~SC_DISPLAY_PENDING_FLAG_TEXTURE; } if (display->pending.flags & SC_DISPLAY_PENDING_FLAG_FRAME) { @@ -216,15 +231,16 @@ sc_display_apply_pending(struct sc_display *display) { } static bool -sc_display_set_texture_size_internal(struct sc_display *display, - struct sc_size size) { +sc_display_prepare_texture_internal(struct sc_display *display, + struct sc_size size, + enum AVColorRange color_range) { assert(size.width && size.height); if (display->texture) { SDL_DestroyTexture(display->texture); } - display->texture = sc_display_create_texture(display, size); + display->texture = sc_display_create_texture(display, size, color_range); if (!display->texture) { return false; } @@ -234,10 +250,11 @@ sc_display_set_texture_size_internal(struct sc_display *display, } enum sc_display_result -sc_display_set_texture_size(struct sc_display *display, struct sc_size size) { - bool ok = sc_display_set_texture_size_internal(display, size); +sc_display_prepare_texture(struct sc_display *display, struct sc_size size, + enum AVColorRange color_range) { + bool ok = sc_display_prepare_texture_internal(display, size, color_range); if (!ok) { - sc_display_set_pending_size(display, size); + sc_display_set_pending_texture(display, size, color_range); return SC_DISPLAY_RESULT_PENDING; } @@ -245,25 +262,9 @@ sc_display_set_texture_size(struct sc_display *display, struct sc_size size) { return SC_DISPLAY_RESULT_OK; } -static SDL_YUV_CONVERSION_MODE -sc_display_to_sdl_color_range(enum AVColorRange color_range) { - return color_range == AVCOL_RANGE_JPEG ? SDL_YUV_CONVERSION_JPEG - : SDL_YUV_CONVERSION_AUTOMATIC; -} - static bool sc_display_update_texture_internal(struct sc_display *display, const AVFrame *frame) { - if (!display->has_frame) { - // First frame - display->has_frame = true; - - // Configure YUV color range conversion - SDL_YUV_CONVERSION_MODE sdl_color_range = - sc_display_to_sdl_color_range(frame->color_range); - SDL_SetYUVConversionMode(sdl_color_range); - } - int ret = SDL_UpdateYUVTexture(display->texture, NULL, frame->data[0], frame->linesize[0], frame->data[1], frame->linesize[1], diff --git a/app/src/display.h b/app/src/display.h index 49110994f9..d302a34e26 100644 --- a/app/src/display.h +++ b/app/src/display.h @@ -28,14 +28,15 @@ struct sc_display { bool mipmaps; struct { -#define SC_DISPLAY_PENDING_FLAG_SIZE 1 +#define SC_DISPLAY_PENDING_FLAG_TEXTURE 1 #define SC_DISPLAY_PENDING_FLAG_FRAME 2 int8_t flags; - struct sc_size size; + struct { + struct sc_size size; + enum AVColorRange color_range; + } texture; AVFrame *frame; } pending; - - bool has_frame; }; enum sc_display_result { @@ -52,7 +53,8 @@ void sc_display_destroy(struct sc_display *display); enum sc_display_result -sc_display_set_texture_size(struct sc_display *display, struct sc_size size); +sc_display_prepare_texture(struct sc_display *display, struct sc_size size, + enum AVColorRange color_range); enum sc_display_result sc_display_update_texture(struct sc_display *display, const AVFrame *frame); diff --git a/app/src/screen.c b/app/src/screen.c index a2c22b2551..0d74cac710 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -619,7 +619,8 @@ sc_screen_apply_frame(struct sc_screen *screen) { } enum sc_display_result res = - sc_display_set_texture_size(&screen->display, screen->frame_size); + sc_display_prepare_texture(&screen->display, screen->frame_size, + frame->color_range); if (res == SC_DISPLAY_RESULT_ERROR) { return false; } From 808900468b2a75c920b2250f31f2dbf3b1bb09cc Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 10 Jul 2025 23:07:45 +0200 Subject: [PATCH 05/12] Improve color space conversion Use both the color space and color range from FFmpeg to determine the appropriate SDL YUV conversion mode. --- app/src/display.c | 35 ++++++++++++++++++++++++++--------- app/src/display.h | 2 ++ app/src/screen.c | 2 +- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/app/src/display.c b/app/src/display.c index c84971a014..5c71134540 100644 --- a/app/src/display.c +++ b/app/src/display.c @@ -125,14 +125,26 @@ sc_display_destroy(struct sc_display *display) { } static SDL_YUV_CONVERSION_MODE -sc_display_to_sdl_color_range(enum AVColorRange color_range) { - return color_range == AVCOL_RANGE_JPEG ? SDL_YUV_CONVERSION_JPEG - : SDL_YUV_CONVERSION_AUTOMATIC; +sc_display_to_sdl_color_mode(enum AVColorSpace color_space, + enum AVColorRange color_range) { + switch (color_space) { + case AVCOL_SPC_BT709: + return SDL_YUV_CONVERSION_BT709; + case AVCOL_SPC_BT470BG: + case AVCOL_SPC_SMPTE170M: + return SDL_YUV_CONVERSION_BT601; + default: + if (color_range == AVCOL_RANGE_JPEG) { + return SDL_YUV_CONVERSION_JPEG; + } + return SDL_YUV_CONVERSION_AUTOMATIC; + } } static SDL_Texture * sc_display_create_texture(struct sc_display *display, - struct sc_size size, enum AVColorRange color_range) { + struct sc_size size, enum AVColorSpace color_space, + enum AVColorRange color_range) { SDL_Renderer *renderer = display->renderer; SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, @@ -156,9 +168,9 @@ sc_display_create_texture(struct sc_display *display, } // Configure YUV color range conversion - SDL_YUV_CONVERSION_MODE sdl_color_range = - sc_display_to_sdl_color_range(color_range); - SDL_SetYUVConversionMode(sdl_color_range); + SDL_YUV_CONVERSION_MODE sdl_color_mode = + sc_display_to_sdl_color_mode(color_space, color_range); + SDL_SetYUVConversionMode(sdl_color_mode); return texture; } @@ -207,6 +219,7 @@ sc_display_apply_pending(struct sc_display *display) { display->texture = sc_display_create_texture(display, display->pending.texture.size, + display->pending.texture.color_space, display->pending.texture.color_range); if (!display->texture) { return false; @@ -233,6 +246,7 @@ sc_display_apply_pending(struct sc_display *display) { static bool sc_display_prepare_texture_internal(struct sc_display *display, struct sc_size size, + enum AVColorSpace color_space, enum AVColorRange color_range) { assert(size.width && size.height); @@ -240,7 +254,8 @@ sc_display_prepare_texture_internal(struct sc_display *display, SDL_DestroyTexture(display->texture); } - display->texture = sc_display_create_texture(display, size, color_range); + display->texture = + sc_display_create_texture(display, size, color_space, color_range); if (!display->texture) { return false; } @@ -251,8 +266,10 @@ sc_display_prepare_texture_internal(struct sc_display *display, enum sc_display_result sc_display_prepare_texture(struct sc_display *display, struct sc_size size, + enum AVColorSpace color_space, enum AVColorRange color_range) { - bool ok = sc_display_prepare_texture_internal(display, size, color_range); + bool ok = sc_display_prepare_texture_internal(display, size, color_space, + color_range); if (!ok) { sc_display_set_pending_texture(display, size, color_range); return SC_DISPLAY_RESULT_PENDING; diff --git a/app/src/display.h b/app/src/display.h index d302a34e26..c5f015ad6d 100644 --- a/app/src/display.h +++ b/app/src/display.h @@ -33,6 +33,7 @@ struct sc_display { int8_t flags; struct { struct sc_size size; + enum AVColorSpace color_space; enum AVColorRange color_range; } texture; AVFrame *frame; @@ -54,6 +55,7 @@ sc_display_destroy(struct sc_display *display); enum sc_display_result sc_display_prepare_texture(struct sc_display *display, struct sc_size size, + enum AVColorSpace color_space, enum AVColorRange color_range); enum sc_display_result diff --git a/app/src/screen.c b/app/src/screen.c index 0d74cac710..fc4a22ffd7 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -620,7 +620,7 @@ sc_screen_apply_frame(struct sc_screen *screen) { enum sc_display_result res = sc_display_prepare_texture(&screen->display, screen->frame_size, - frame->color_range); + frame->colorspace, frame->color_range); if (res == SC_DISPLAY_RESULT_ERROR) { return false; } From 61fa8cefd608faa9802911c4745b885797c9bf69 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 11 Jul 2025 09:42:03 +0200 Subject: [PATCH 06/12] Migrate from SDL2 to SDL3 Refs --- app/meson.build | 3 +- app/src/audio_player.c | 85 +++++++++++++++---- app/src/audio_player.h | 8 +- app/src/compat.h | 24 +----- app/src/display.c | 150 +++++++++++++++++++++----------- app/src/display.h | 3 +- app/src/events.c | 28 ++---- app/src/events.h | 4 +- app/src/icon.c | 66 ++++++++------- app/src/icon.h | 2 +- app/src/input_events.h | 160 +++++++++++++++++------------------ app/src/input_manager.c | 135 ++++++++++++++--------------- app/src/input_manager.h | 4 +- app/src/main.c | 5 +- app/src/mouse_capture.c | 41 ++++----- app/src/mouse_capture.h | 2 +- app/src/opengl.c | 18 ++-- app/src/opengl.h | 5 +- app/src/receiver.c | 11 ++- app/src/scrcpy.c | 58 ++++++------- app/src/screen.c | 111 +++++++++++++----------- app/src/screen.h | 2 +- app/src/shortcut_mod.h | 28 +++--- app/src/uhid/gamepad_uhid.c | 8 +- app/src/uhid/keyboard_uhid.c | 4 +- app/src/usb/scrcpy_otg.c | 12 +-- app/src/usb/screen_otg.c | 95 +++++++++++---------- app/src/usb/screen_otg.h | 2 +- app/src/util/log.c | 12 +-- app/src/util/log.h | 2 +- app/src/util/thread.c | 80 ++++-------------- app/src/util/thread.h | 8 +- app/src/util/window.c | 33 ++++++++ app/src/util/window.h | 13 +++ app/src/version.c | 13 +-- 35 files changed, 668 insertions(+), 567 deletions(-) create mode 100644 app/src/util/window.c create mode 100644 app/src/util/window.h diff --git a/app/meson.build b/app/meson.build index f7df69eb22..d7eb5384d3 100644 --- a/app/meson.build +++ b/app/meson.build @@ -63,6 +63,7 @@ src = [ 'src/util/thread.c', 'src/util/tick.c', 'src/util/timeout.c', + 'src/util/window.c', ] conf = configuration_data() @@ -117,7 +118,7 @@ dependencies = [ dependency('libavcodec', version: '>= 57.37', static: static), dependency('libavutil', static: static), dependency('libswresample', static: static), - dependency('sdl2', version: '>= 2.0.5', static: static), + dependency('sdl3', version: '>= 3.2.0', static: static), ] if v4l2_support diff --git a/app/src/audio_player.c b/app/src/audio_player.c index 9413c2ea8c..3e88ea453f 100644 --- a/app/src/audio_player.c +++ b/app/src/audio_player.c @@ -1,23 +1,40 @@ #include "audio_player.h" #include "util/log.h" +#include "SDL3/SDL_hints.h" /** Downcast frame_sink to sc_audio_player */ #define DOWNCAST(SINK) container_of(SINK, struct sc_audio_player, frame_sink) -#define SC_SDL_SAMPLE_FMT AUDIO_F32 +#define SC_SDL_SAMPLE_FMT SDL_AUDIO_F32LE static void SDLCALL -sc_audio_player_sdl_callback(void *userdata, uint8_t *stream, int len_int) { - struct sc_audio_player *ap = userdata; +sc_audio_player_stream_callback(void *userdata, SDL_AudioStream *stream, + int additional_amount, int total_amount) { + (void) total_amount; - assert(len_int > 0); - size_t len = len_int; + struct sc_audio_player *ap = userdata; + size_t len = additional_amount; assert(len % ap->audioreg.sample_size == 0); - uint32_t out_samples = len / ap->audioreg.sample_size; - sc_audio_regulator_pull(&ap->audioreg, stream, out_samples); + // The requested amount may exceed the internal aout_buffer size. + // In this (unlikely) case, send the data to the stream in multiple chunks. + while (len) { + size_t chunk_size = MIN(ap->aout_buffer_size, len); + uint32_t out_samples = chunk_size / ap->audioreg.sample_size; + sc_audio_regulator_pull(&ap->audioreg, ap->aout_buffer, + out_samples); + + assert(chunk_size <= len); + len -= chunk_size; + bool ok = + SDL_PutAudioStreamData(stream, ap->aout_buffer, chunk_size); + if (!ok) { + LOGW("Audio stream error: %s", SDL_GetError()); + return; + } + } } static bool @@ -61,23 +78,45 @@ sc_audio_player_frame_sink_open(struct sc_frame_sink *sink, / SC_TICK_FREQ; assert(aout_samples <= 0xFFFF); - SDL_AudioSpec desired = { + char str[5 + 1]; // max 65535 + int r = snprintf(str, sizeof(str), "%" PRIu16, (uint16_t) aout_samples); + assert(r >= 0 && (size_t) r < sizeof(str)); + (void) r; + if (!SDL_SetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES, str)) { + LOGE("Could not set audio output buffer"); + sc_audio_regulator_destroy(&ap->audioreg); + return false; + } + + // Make the buffer at least 1024 samples long (the hint is not always + // honored) + uint64_t aout_buffer_samples = MAX(1024, aout_samples); + ap->aout_buffer_size = aout_buffer_samples * sample_size; + ap->aout_buffer = malloc(ap->aout_buffer_size); + if (!ap->aout_buffer) { + sc_audio_regulator_destroy(&ap->audioreg); + return false; + } + + SDL_AudioSpec spec = { .freq = ctx->sample_rate, .format = SC_SDL_SAMPLE_FMT, .channels = nb_channels, - .samples = aout_samples, - .callback = sc_audio_player_sdl_callback, - .userdata = ap, }; - SDL_AudioSpec obtained; - ap->device = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, 0); - if (!ap->device) { + ap->stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, + &spec, + sc_audio_player_stream_callback, ap); + if (!ap->stream) { LOGE("Could not open audio device: %s", SDL_GetError()); + free(ap->aout_buffer); sc_audio_regulator_destroy(&ap->audioreg); return false; } + ap->device = SDL_GetAudioStreamDevice(ap->stream); + assert(ap->device); + // The thread calling open() is the thread calling push(), which fills the // audio buffer consumed by the SDL audio thread. ok = sc_thread_set_priority(SC_THREAD_PRIORITY_TIME_CRITICAL); @@ -86,7 +125,14 @@ sc_audio_player_frame_sink_open(struct sc_frame_sink *sink, (void) ok; // We don't care if it worked, at least we tried } - SDL_PauseAudioDevice(ap->device, 0); + ok = SDL_ResumeAudioDevice(ap->device); + if (!ok) { + LOGE("Could not resume audio device: %s", SDL_GetError()); + SDL_DestroyAudioStream(ap->stream); + free(ap->aout_buffer); + sc_audio_regulator_destroy(&ap->audioreg); + return false; + } return true; } @@ -95,11 +141,16 @@ static void sc_audio_player_frame_sink_close(struct sc_frame_sink *sink) { struct sc_audio_player *ap = DOWNCAST(sink); + assert(ap->stream); assert(ap->device); - SDL_PauseAudioDevice(ap->device, 1); - SDL_CloseAudioDevice(ap->device); + SDL_PauseAudioDevice(ap->device); + + // ap->device is owned by ap->stream + SDL_DestroyAudioStream(ap->stream); sc_audio_regulator_destroy(&ap->audioreg); + + free(ap->aout_buffer); } void diff --git a/app/src/audio_player.h b/app/src/audio_player.h index 5a66d43b1b..786200bbe8 100644 --- a/app/src/audio_player.h +++ b/app/src/audio_player.h @@ -3,7 +3,7 @@ #include "common.h" -#include +#include #include "audio_regulator.h" #include "trait/frame_sink.h" @@ -22,7 +22,11 @@ struct sc_audio_player { // SDL audio output buffer size sc_tick output_buffer_duration; - SDL_AudioDeviceID device; + uint8_t *aout_buffer; + size_t aout_buffer_size; + + SDL_AudioStream *stream; + SDL_AudioDeviceID device; // owned by the audio stream struct sc_audio_regulator audioreg; }; diff --git a/app/src/compat.h b/app/src/compat.h index 296d1a9ffa..a7ba289bc1 100644 --- a/app/src/compat.h +++ b/app/src/compat.h @@ -6,7 +6,7 @@ #include #include #include -#include +#include #ifndef _WIN32 # define PRIu64_ PRIu64 @@ -61,28 +61,6 @@ # define SCRCPY_LAVC_HAS_CODECPAR_CODEC_SIDEDATA #endif -#if SDL_VERSION_ATLEAST(2, 0, 6) -// -# define SCRCPY_SDL_HAS_HINT_TOUCH_MOUSE_EVENTS -#endif - -#if SDL_VERSION_ATLEAST(2, 0, 8) -// -# define SCRCPY_SDL_HAS_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR -#endif - -#if SDL_VERSION_ATLEAST(2, 0, 16) -# define SCRCPY_SDL_HAS_THREAD_PRIORITY_TIME_CRITICAL -#endif - -#if SDL_VERSION_ATLEAST(2, 0, 18) -# define SCRCPY_SDL_HAS_HINT_APP_NAME -#endif - -#if SDL_VERSION_ATLEAST(2, 0, 14) -# define SCRCPY_SDL_HAS_HINT_AUDIO_DEVICE_APP_NAME -#endif - #ifndef HAVE_STRDUP char *strdup(const char *s); #endif diff --git a/app/src/display.c b/app/src/display.c index 5c71134540..560d5614f1 100644 --- a/app/src/display.c +++ b/app/src/display.c @@ -12,8 +12,11 @@ sc_display_init_novideo_icon(struct sc_display *display, SDL_Surface *icon_novideo) { assert(icon_novideo); - if (SDL_RenderSetLogicalSize(display->renderer, - icon_novideo->w, icon_novideo->h)) { + bool ok = SDL_SetRenderLogicalPresentation(display->renderer, + icon_novideo->w, + icon_novideo->h, + SDL_LOGICAL_PRESENTATION_LETTERBOX); + if (!ok) { LOGW("Could not set renderer logical size: %s", SDL_GetError()); // don't fail } @@ -31,16 +34,13 @@ sc_display_init_novideo_icon(struct sc_display *display, bool sc_display_init(struct sc_display *display, SDL_Window *window, SDL_Surface *icon_novideo, bool mipmaps) { - display->renderer = - SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + display->renderer = SDL_CreateRenderer(window, NULL); if (!display->renderer) { LOGE("Could not create renderer: %s", SDL_GetError()); return false; } - SDL_RendererInfo renderer_info; - int r = SDL_GetRendererInfo(display->renderer, &renderer_info); - const char *renderer_name = r ? NULL : renderer_info.name; + const char *renderer_name = SDL_GetRendererName(display->renderer); LOGI("Renderer: %s", renderer_name ? renderer_name : "(unknown)"); display->mipmaps = false; @@ -56,8 +56,11 @@ sc_display_init(struct sc_display *display, SDL_Window *window, #ifdef SC_DISPLAY_FORCE_OPENGL_CORE_PROFILE // Persuade macOS to give us something better than OpenGL 2.1. // If we create a Core Profile context, we get the best OpenGL version. - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, - SDL_GL_CONTEXT_PROFILE_CORE); + bool ok = SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, + SDL_GL_CONTEXT_PROFILE_CORE); + if (!ok) { + LOGW("Could not set a GL Core Profile Context"); + } LOGD("Creating OpenGL Core Profile context"); display->gl_context = SDL_GL_CreateContext(window); @@ -100,7 +103,7 @@ sc_display_init(struct sc_display *display, SDL_Window *window, bool ok = sc_display_init_novideo_icon(display, icon_novideo); if (!ok) { #ifdef SC_DISPLAY_FORCE_OPENGL_CORE_PROFILE - SDL_GL_DeleteContext(display->gl_context); + SDL_GL_DestroyContext(display->gl_context); #endif SDL_DestroyRenderer(display->renderer); return false; @@ -116,7 +119,7 @@ sc_display_destroy(struct sc_display *display) { av_frame_free(&display->pending.frame); } #ifdef SC_DISPLAY_FORCE_OPENGL_CORE_PROFILE - SDL_GL_DeleteContext(display->gl_context); + SDL_GL_DestroyContext(display->gl_context); #endif if (display->texture) { SDL_DestroyTexture(display->texture); @@ -124,20 +127,25 @@ sc_display_destroy(struct sc_display *display) { SDL_DestroyRenderer(display->renderer); } -static SDL_YUV_CONVERSION_MODE -sc_display_to_sdl_color_mode(enum AVColorSpace color_space, - enum AVColorRange color_range) { +static enum SDL_Colorspace +sc_display_to_sdl_color_space(enum AVColorSpace color_space, + enum AVColorRange color_range) { + bool full_range = color_range == AVCOL_RANGE_JPEG; + switch (color_space) { case AVCOL_SPC_BT709: - return SDL_YUV_CONVERSION_BT709; + return full_range ? SDL_COLORSPACE_BT709_FULL + : SDL_COLORSPACE_BT709_LIMITED; case AVCOL_SPC_BT470BG: case AVCOL_SPC_SMPTE170M: - return SDL_YUV_CONVERSION_BT601; + return full_range ? SDL_COLORSPACE_BT601_FULL + : SDL_COLORSPACE_BT601_LIMITED; + case AVCOL_SPC_BT2020_NCL: + case AVCOL_SPC_BT2020_CL: + return full_range ? SDL_COLORSPACE_BT2020_FULL + : SDL_COLORSPACE_BT2020_LIMITED; default: - if (color_range == AVCOL_RANGE_JPEG) { - return SDL_YUV_CONVERSION_JPEG; - } - return SDL_YUV_CONVERSION_AUTOMATIC; + return SDL_COLORSPACE_JPEG; } } @@ -145,10 +153,36 @@ static SDL_Texture * sc_display_create_texture(struct sc_display *display, struct sc_size size, enum AVColorSpace color_space, enum AVColorRange color_range) { + SDL_PropertiesID props = SDL_CreateProperties(); + if (!props) { + return NULL; + } + + enum SDL_Colorspace sdl_color_space = + sc_display_to_sdl_color_space(color_space, color_range); + + bool ok = + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, + SDL_PIXELFORMAT_YV12); + ok &= SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, + SDL_TEXTUREACCESS_STREAMING); + ok &= SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, + size.width); + ok &= SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, + size.height); + ok &= SDL_SetNumberProperty(props, + SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, + sdl_color_space); + + if (!ok) { + LOGE("Could not set texture properties"); + SDL_DestroyProperties(props); + return NULL; + } + SDL_Renderer *renderer = display->renderer; - SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, - SDL_TEXTUREACCESS_STREAMING, - size.width, size.height); + SDL_Texture *texture = SDL_CreateTextureWithProperties(renderer, props); + SDL_DestroyProperties(props); if (!texture) { LOGD("Could not create texture: %s", SDL_GetError()); return NULL; @@ -157,21 +191,38 @@ sc_display_create_texture(struct sc_display *display, if (display->mipmaps) { struct sc_opengl *gl = &display->gl; - SDL_GL_BindTexture(texture, NULL, NULL); + SDL_PropertiesID props = SDL_GetTextureProperties(texture); + if (!props) { + LOGE("Could not get texture properties: %s", SDL_GetError()); + SDL_DestroyTexture(texture); + return NULL; + } + + const char *renderer_name = SDL_GetRendererName(display->renderer); + const char *key = !renderer_name || !strcmp(renderer_name, "opengl") + ? SDL_PROP_TEXTURE_OPENGL_TEXTURE_NUMBER + : SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_NUMBER; + + int64_t texture_id = SDL_GetNumberProperty(props, key, 0); + SDL_DestroyProperties(props); + if (!texture_id) { + LOGE("Could not get texture id: %s", SDL_GetError()); + SDL_DestroyTexture(texture); + return NULL; + } + + assert(!(texture_id & ~0xFFFFFFFF)); // fits in uint32_t + display->texture_id = texture_id; + gl->BindTexture(GL_TEXTURE_2D, display->texture_id); // Enable trilinear filtering for downscaling gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); gl->TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -1.f); - SDL_GL_UnbindTexture(texture); + gl->BindTexture(GL_TEXTURE_2D, 0); } - // Configure YUV color range conversion - SDL_YUV_CONVERSION_MODE sdl_color_mode = - sc_display_to_sdl_color_mode(color_space, color_range); - SDL_SetYUVConversionMode(sdl_color_mode); - return texture; } @@ -282,19 +333,22 @@ sc_display_prepare_texture(struct sc_display *display, struct sc_size size, static bool sc_display_update_texture_internal(struct sc_display *display, const AVFrame *frame) { - int ret = SDL_UpdateYUVTexture(display->texture, NULL, + bool ok = SDL_UpdateYUVTexture(display->texture, NULL, frame->data[0], frame->linesize[0], frame->data[1], frame->linesize[1], frame->data[2], frame->linesize[2]); - if (ret) { + if (!ok) { LOGD("Could not update texture: %s", SDL_GetError()); return false; } if (display->mipmaps) { - SDL_GL_BindTexture(display->texture, NULL, NULL); + assert(display->texture_id); + struct sc_opengl *gl = &display->gl; + + gl->BindTexture(GL_TEXTURE_2D, display->texture_id); display->gl.GenerateMipmap(GL_TEXTURE_2D); - SDL_GL_UnbindTexture(display->texture); + gl->BindTexture(GL_TEXTURE_2D, 0); } return true; @@ -332,8 +386,10 @@ sc_display_render(struct sc_display *display, const SDL_Rect *geometry, SDL_Texture *texture = display->texture; if (orientation == SC_ORIENTATION_0) { - int ret = SDL_RenderCopy(renderer, texture, NULL, geometry); - if (ret) { + SDL_FRect frect; + SDL_RectToFRect(geometry, &frect); + bool ok = SDL_RenderTexture(renderer, texture, NULL, &frect); + if (!ok) { LOGE("Could not render texture: %s", SDL_GetError()); return SC_DISPLAY_RESULT_ERROR; } @@ -341,24 +397,22 @@ sc_display_render(struct sc_display *display, const SDL_Rect *geometry, unsigned cw_rotation = sc_orientation_get_rotation(orientation); double angle = 90 * cw_rotation; - const SDL_Rect *dstrect = NULL; - SDL_Rect rect; + SDL_FRect frect; if (sc_orientation_is_swap(orientation)) { - rect.x = geometry->x + (geometry->w - geometry->h) / 2; - rect.y = geometry->y + (geometry->h - geometry->w) / 2; - rect.w = geometry->h; - rect.h = geometry->w; - dstrect = ▭ + frect.x = geometry->x + (geometry->w - geometry->h) / 2.f; + frect.y = geometry->y + (geometry->h - geometry->w) / 2.f; + frect.w = geometry->h; + frect.h = geometry->w; } else { - dstrect = geometry; + SDL_RectToFRect(geometry, &frect); } - SDL_RendererFlip flip = sc_orientation_is_mirror(orientation) + SDL_FlipMode flip = sc_orientation_is_mirror(orientation) ? SDL_FLIP_HORIZONTAL : 0; - int ret = SDL_RenderCopyEx(renderer, texture, NULL, dstrect, angle, - NULL, flip); - if (ret) { + bool ok = SDL_RenderTextureRotated(renderer, texture, NULL, &frect, + angle, NULL, flip); + if (!ok) { LOGE("Could not render texture: %s", SDL_GetError()); return SC_DISPLAY_RESULT_ERROR; } diff --git a/app/src/display.h b/app/src/display.h index c5f015ad6d..599eeb063c 100644 --- a/app/src/display.h +++ b/app/src/display.h @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include "coords.h" #include "opengl.h" @@ -26,6 +26,7 @@ struct sc_display { #endif bool mipmaps; + uint32_t texture_id; // only set if mipmaps is enabled struct { #define SC_DISPLAY_PENDING_FLAG_TEXTURE 1 diff --git a/app/src/events.c b/app/src/events.c index b4322d1b19..ca7e4008a6 100644 --- a/app/src/events.c +++ b/app/src/events.c @@ -9,11 +9,8 @@ bool sc_push_event_impl(uint32_t type, const char *name) { SDL_Event event; event.type = type; - int ret = SDL_PushEvent(&event); - // ret < 0: error (queue full) - // ret == 0: event was filtered - // ret == 1: success - if (ret != 1) { + bool ok = SDL_PushEvent(&event); + if (!ok) { LOGE("Could not post %s event: %s", name, SDL_GetError()); return false; } @@ -30,34 +27,25 @@ sc_post_to_main_thread(sc_runnable_fn run, void *userdata) { .data2 = userdata, }, }; - int ret = SDL_PushEvent(&event); - // ret < 0: error (queue full) - // ret == 0: event was filtered - // ret == 1: success - if (ret != 1) { - if (ret == 0) { - // if ret == 0, this is expected on exit, log in debug mode - LOGD("Could not post runnable to main thread (filtered)"); - } else { - assert(ret < 0); - LOGW("Could not post runnable to main thread: %s", SDL_GetError()); - } + bool ok = SDL_PushEvent(&event); + if (!ok) { + LOGW("Could not post runnable to main thread: %s", SDL_GetError()); return false; } return true; } -static int SDLCALL +static bool SDLCALL task_event_filter(void *userdata, SDL_Event *event) { (void) userdata; if (event->type == SC_EVENT_RUN_ON_MAIN_THREAD) { // Reject this event type from now on - return 0; + return false; } - return 1; + return true; } void diff --git a/app/src/events.h b/app/src/events.h index 2e7318e5b4..8667bc5796 100644 --- a/app/src/events.h +++ b/app/src/events.h @@ -5,10 +5,10 @@ #include #include -#include +#include enum { - SC_EVENT_NEW_FRAME = SDL_USEREVENT, + SC_EVENT_NEW_FRAME = SDL_EVENT_USER, SC_EVENT_RUN_ON_MAIN_THREAD, SC_EVENT_DEVICE_DISCONNECTED, SC_EVENT_SERVER_CONNECTION_FAILED, diff --git a/app/src/icon.c b/app/src/icon.c index 797afc75b3..ac80ecb3c4 100644 --- a/app/src/icon.c +++ b/app/src/icon.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include "config.h" #include "util/env.h" @@ -156,13 +156,7 @@ decode_image(const char *path) { return result; } -#if !SDL_VERSION_ATLEAST(2, 0, 10) -// SDL_PixelFormatEnum has been introduced in SDL 2.0.10. Use int for older SDL -// versions. -typedef int SDL_PixelFormatEnum; -#endif - -static SDL_PixelFormatEnum +static SDL_PixelFormat to_sdl_pixel_format(enum AVPixelFormat fmt) { switch (fmt) { case AV_PIX_FMT_RGB24: return SDL_PIXELFORMAT_RGB24; @@ -172,13 +166,11 @@ to_sdl_pixel_format(enum AVPixelFormat fmt) { case AV_PIX_FMT_ABGR: return SDL_PIXELFORMAT_ABGR32; case AV_PIX_FMT_BGRA: return SDL_PIXELFORMAT_BGRA32; case AV_PIX_FMT_RGB565BE: return SDL_PIXELFORMAT_RGB565; - case AV_PIX_FMT_RGB555BE: return SDL_PIXELFORMAT_RGB555; + case AV_PIX_FMT_RGB555BE: return SDL_PIXELFORMAT_XRGB1555; case AV_PIX_FMT_BGR565BE: return SDL_PIXELFORMAT_BGR565; - case AV_PIX_FMT_BGR555BE: return SDL_PIXELFORMAT_BGR555; - case AV_PIX_FMT_RGB444BE: return SDL_PIXELFORMAT_RGB444; -#if SDL_VERSION_ATLEAST(2, 0, 12) - case AV_PIX_FMT_BGR444BE: return SDL_PIXELFORMAT_BGR444; -#endif + case AV_PIX_FMT_BGR555BE: return SDL_PIXELFORMAT_XBGR1555; + case AV_PIX_FMT_RGB444BE: return SDL_PIXELFORMAT_XRGB4444; + case AV_PIX_FMT_BGR444BE: return SDL_PIXELFORMAT_XBGR4444; case AV_PIX_FMT_PAL8: return SDL_PIXELFORMAT_INDEX8; default: return SDL_PIXELFORMAT_UNKNOWN; } @@ -203,20 +195,16 @@ load_from_path(const char *path) { goto error; } - SDL_PixelFormatEnum format = to_sdl_pixel_format(frame->format); + SDL_PixelFormat format = to_sdl_pixel_format(frame->format); if (format == SDL_PIXELFORMAT_UNKNOWN) { LOGE("Unsupported icon pixel format: %s (%d)", desc->name, frame->format); goto error; } - int bits_per_pixel = av_get_bits_per_pixel(desc); SDL_Surface *surface = - SDL_CreateRGBSurfaceWithFormatFrom(frame->data[0], - frame->width, frame->height, - bits_per_pixel, - frame->linesize[0], - format); + SDL_CreateSurfaceFrom(frame->width, frame->height, format, + frame->data[0], frame->linesize[0]); if (!surface) { LOGE("Could not create icon surface"); @@ -248,17 +236,35 @@ load_from_path(const char *path) { #endif } - SDL_Palette *palette = surface->format->palette; - assert(palette); - int ret = SDL_SetPaletteColors(palette, colors, 0, 256); - if (ret) { + SDL_Palette *palette = SDL_CreateSurfacePalette(surface); + if (!palette) { + LOGE("Could not create palette"); + SDL_DestroySurface(surface); + goto error; + } + + bool ok = SDL_SetPaletteColors(palette, colors, 0, 256); + if (!ok) { LOGE("Could not set palette colors"); - SDL_FreeSurface(surface); + SDL_DestroySurface(surface); goto error; } } - surface->userdata = frame; // frame owns the data + SDL_PropertiesID props = SDL_GetSurfaceProperties(surface); + if (!props) { + LOGE("Could not get surface properties: %s", SDL_GetError()); + SDL_DestroySurface(surface); + goto error; + } + + // frame owns the data + bool ok = SDL_SetPointerProperty(props, "sc_frame", frame); + if (!ok) { + LOGE("Could not get surface properties: %s", SDL_GetError()); + SDL_DestroySurface(surface); + goto error; + } return surface; @@ -281,8 +287,10 @@ scrcpy_icon_load(void) { void scrcpy_icon_destroy(SDL_Surface *icon) { - AVFrame *frame = icon->userdata; + SDL_PropertiesID props = SDL_GetSurfaceProperties(icon); + assert(props); + AVFrame *frame = SDL_GetPointerProperty(props, "sc_frame", NULL); assert(frame); av_frame_free(&frame); - SDL_FreeSurface(icon); + SDL_DestroySurface(icon); } diff --git a/app/src/icon.h b/app/src/icon.h index 6bcf46d291..21fbc5488c 100644 --- a/app/src/icon.h +++ b/app/src/icon.h @@ -3,7 +3,7 @@ #include "common.h" -#include +#include SDL_Surface * scrcpy_icon_load(void); diff --git a/app/src/input_events.h b/app/src/input_events.h index 0c022acc03..87ea66349d 100644 --- a/app/src/input_events.h +++ b/app/src/input_events.h @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include "coords.h" @@ -43,17 +43,17 @@ */ enum sc_mod { - SC_MOD_LSHIFT = KMOD_LSHIFT, - SC_MOD_RSHIFT = KMOD_RSHIFT, - SC_MOD_LCTRL = KMOD_LCTRL, - SC_MOD_RCTRL = KMOD_RCTRL, - SC_MOD_LALT = KMOD_LALT, - SC_MOD_RALT = KMOD_RALT, - SC_MOD_LGUI = KMOD_LGUI, - SC_MOD_RGUI = KMOD_RGUI, - - SC_MOD_NUM = KMOD_NUM, - SC_MOD_CAPS = KMOD_CAPS, + SC_MOD_LSHIFT = SDL_KMOD_LSHIFT, + SC_MOD_RSHIFT = SDL_KMOD_RSHIFT, + SC_MOD_LCTRL = SDL_KMOD_LCTRL, + SC_MOD_RCTRL = SDL_KMOD_RCTRL, + SC_MOD_LALT = SDL_KMOD_LALT, + SC_MOD_RALT = SDL_KMOD_RALT, + SC_MOD_LGUI = SDL_KMOD_LGUI, + SC_MOD_RGUI = SDL_KMOD_RGUI, + + SC_MOD_NUM = SDL_KMOD_NUM, + SC_MOD_CAPS = SDL_KMOD_CAPS, }; enum sc_action { @@ -70,12 +70,12 @@ enum sc_keycode { SC_KEYCODE_TAB = SDLK_TAB, SC_KEYCODE_SPACE = SDLK_SPACE, SC_KEYCODE_EXCLAIM = SDLK_EXCLAIM, - SC_KEYCODE_QUOTEDBL = SDLK_QUOTEDBL, + SC_KEYCODE_QUOTEDBL = SDLK_DBLAPOSTROPHE, SC_KEYCODE_HASH = SDLK_HASH, SC_KEYCODE_PERCENT = SDLK_PERCENT, SC_KEYCODE_DOLLAR = SDLK_DOLLAR, SC_KEYCODE_AMPERSAND = SDLK_AMPERSAND, - SC_KEYCODE_QUOTE = SDLK_QUOTE, + SC_KEYCODE_QUOTE = SDLK_APOSTROPHE, SC_KEYCODE_LEFTPAREN = SDLK_LEFTPAREN, SC_KEYCODE_RIGHTPAREN = SDLK_RIGHTPAREN, SC_KEYCODE_ASTERISK = SDLK_ASTERISK, @@ -107,33 +107,33 @@ enum sc_keycode { SC_KEYCODE_RIGHTBRACKET = SDLK_RIGHTBRACKET, SC_KEYCODE_CARET = SDLK_CARET, SC_KEYCODE_UNDERSCORE = SDLK_UNDERSCORE, - SC_KEYCODE_BACKQUOTE = SDLK_BACKQUOTE, - SC_KEYCODE_a = SDLK_a, - SC_KEYCODE_b = SDLK_b, - SC_KEYCODE_c = SDLK_c, - SC_KEYCODE_d = SDLK_d, - SC_KEYCODE_e = SDLK_e, - SC_KEYCODE_f = SDLK_f, - SC_KEYCODE_g = SDLK_g, - SC_KEYCODE_h = SDLK_h, - SC_KEYCODE_i = SDLK_i, - SC_KEYCODE_j = SDLK_j, - SC_KEYCODE_k = SDLK_k, - SC_KEYCODE_l = SDLK_l, - SC_KEYCODE_m = SDLK_m, - SC_KEYCODE_n = SDLK_n, - SC_KEYCODE_o = SDLK_o, - SC_KEYCODE_p = SDLK_p, - SC_KEYCODE_q = SDLK_q, - SC_KEYCODE_r = SDLK_r, - SC_KEYCODE_s = SDLK_s, - SC_KEYCODE_t = SDLK_t, - SC_KEYCODE_u = SDLK_u, - SC_KEYCODE_v = SDLK_v, - SC_KEYCODE_w = SDLK_w, - SC_KEYCODE_x = SDLK_x, - SC_KEYCODE_y = SDLK_y, - SC_KEYCODE_z = SDLK_z, + SC_KEYCODE_BACKQUOTE = SDLK_GRAVE, + SC_KEYCODE_a = SDLK_A, + SC_KEYCODE_b = SDLK_B, + SC_KEYCODE_c = SDLK_C, + SC_KEYCODE_d = SDLK_D, + SC_KEYCODE_e = SDLK_E, + SC_KEYCODE_f = SDLK_F, + SC_KEYCODE_g = SDLK_G, + SC_KEYCODE_h = SDLK_H, + SC_KEYCODE_i = SDLK_I, + SC_KEYCODE_j = SDLK_J, + SC_KEYCODE_k = SDLK_K, + SC_KEYCODE_l = SDLK_L, + SC_KEYCODE_m = SDLK_M, + SC_KEYCODE_n = SDLK_N, + SC_KEYCODE_o = SDLK_O, + SC_KEYCODE_p = SDLK_P, + SC_KEYCODE_q = SDLK_Q, + SC_KEYCODE_r = SDLK_R, + SC_KEYCODE_s = SDLK_S, + SC_KEYCODE_t = SDLK_T, + SC_KEYCODE_u = SDLK_U, + SC_KEYCODE_v = SDLK_V, + SC_KEYCODE_w = SDLK_W, + SC_KEYCODE_x = SDLK_X, + SC_KEYCODE_y = SDLK_Y, + SC_KEYCODE_z = SDLK_Z, SC_KEYCODE_CAPSLOCK = SDLK_CAPSLOCK, @@ -315,11 +315,11 @@ enum sc_scancode { // to avoid unnecessary conversions (and confusion). enum sc_mouse_button { SC_MOUSE_BUTTON_UNKNOWN = 0, - SC_MOUSE_BUTTON_LEFT = SDL_BUTTON(SDL_BUTTON_LEFT), - SC_MOUSE_BUTTON_RIGHT = SDL_BUTTON(SDL_BUTTON_RIGHT), - SC_MOUSE_BUTTON_MIDDLE = SDL_BUTTON(SDL_BUTTON_MIDDLE), - SC_MOUSE_BUTTON_X1 = SDL_BUTTON(SDL_BUTTON_X1), - SC_MOUSE_BUTTON_X2 = SDL_BUTTON(SDL_BUTTON_X2), + SC_MOUSE_BUTTON_LEFT = SDL_BUTTON_MASK(SDL_BUTTON_LEFT), + SC_MOUSE_BUTTON_RIGHT = SDL_BUTTON_MASK(SDL_BUTTON_RIGHT), + SC_MOUSE_BUTTON_MIDDLE = SDL_BUTTON_MASK(SDL_BUTTON_MIDDLE), + SC_MOUSE_BUTTON_X1 = SDL_BUTTON_MASK(SDL_BUTTON_X1), + SC_MOUSE_BUTTON_X2 = SDL_BUTTON_MASK(SDL_BUTTON_X2), }; // Use the naming from SDL3 for gamepad axis and buttons: @@ -327,31 +327,31 @@ enum sc_mouse_button { enum sc_gamepad_axis { SC_GAMEPAD_AXIS_UNKNOWN = -1, - SC_GAMEPAD_AXIS_LEFTX = SDL_CONTROLLER_AXIS_LEFTX, - SC_GAMEPAD_AXIS_LEFTY = SDL_CONTROLLER_AXIS_LEFTY, - SC_GAMEPAD_AXIS_RIGHTX = SDL_CONTROLLER_AXIS_RIGHTX, - SC_GAMEPAD_AXIS_RIGHTY = SDL_CONTROLLER_AXIS_RIGHTY, - SC_GAMEPAD_AXIS_LEFT_TRIGGER = SDL_CONTROLLER_AXIS_TRIGGERLEFT, - SC_GAMEPAD_AXIS_RIGHT_TRIGGER = SDL_CONTROLLER_AXIS_TRIGGERRIGHT, + SC_GAMEPAD_AXIS_LEFTX = SDL_GAMEPAD_AXIS_LEFTX, + SC_GAMEPAD_AXIS_LEFTY = SDL_GAMEPAD_AXIS_LEFTY, + SC_GAMEPAD_AXIS_RIGHTX = SDL_GAMEPAD_AXIS_RIGHTX, + SC_GAMEPAD_AXIS_RIGHTY = SDL_GAMEPAD_AXIS_RIGHTY, + SC_GAMEPAD_AXIS_LEFT_TRIGGER = SDL_GAMEPAD_AXIS_LEFT_TRIGGER, + SC_GAMEPAD_AXIS_RIGHT_TRIGGER = SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, }; enum sc_gamepad_button { SC_GAMEPAD_BUTTON_UNKNOWN = -1, - SC_GAMEPAD_BUTTON_SOUTH = SDL_CONTROLLER_BUTTON_A, - SC_GAMEPAD_BUTTON_EAST = SDL_CONTROLLER_BUTTON_B, - SC_GAMEPAD_BUTTON_WEST = SDL_CONTROLLER_BUTTON_X, - SC_GAMEPAD_BUTTON_NORTH = SDL_CONTROLLER_BUTTON_Y, - SC_GAMEPAD_BUTTON_BACK = SDL_CONTROLLER_BUTTON_BACK, - SC_GAMEPAD_BUTTON_GUIDE = SDL_CONTROLLER_BUTTON_GUIDE, - SC_GAMEPAD_BUTTON_START = SDL_CONTROLLER_BUTTON_START, - SC_GAMEPAD_BUTTON_LEFT_STICK = SDL_CONTROLLER_BUTTON_LEFTSTICK, - SC_GAMEPAD_BUTTON_RIGHT_STICK = SDL_CONTROLLER_BUTTON_RIGHTSTICK, - SC_GAMEPAD_BUTTON_LEFT_SHOULDER = SDL_CONTROLLER_BUTTON_LEFTSHOULDER, - SC_GAMEPAD_BUTTON_RIGHT_SHOULDER = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, - SC_GAMEPAD_BUTTON_DPAD_UP = SDL_CONTROLLER_BUTTON_DPAD_UP, - SC_GAMEPAD_BUTTON_DPAD_DOWN = SDL_CONTROLLER_BUTTON_DPAD_DOWN, - SC_GAMEPAD_BUTTON_DPAD_LEFT = SDL_CONTROLLER_BUTTON_DPAD_LEFT, - SC_GAMEPAD_BUTTON_DPAD_RIGHT = SDL_CONTROLLER_BUTTON_DPAD_RIGHT, + SC_GAMEPAD_BUTTON_SOUTH = SDL_GAMEPAD_BUTTON_SOUTH, + SC_GAMEPAD_BUTTON_EAST = SDL_GAMEPAD_BUTTON_EAST, + SC_GAMEPAD_BUTTON_WEST = SDL_GAMEPAD_BUTTON_WEST, + SC_GAMEPAD_BUTTON_NORTH = SDL_GAMEPAD_BUTTON_NORTH, + SC_GAMEPAD_BUTTON_BACK = SDL_GAMEPAD_BUTTON_BACK, + SC_GAMEPAD_BUTTON_GUIDE = SDL_GAMEPAD_BUTTON_GUIDE, + SC_GAMEPAD_BUTTON_START = SDL_GAMEPAD_BUTTON_START, + SC_GAMEPAD_BUTTON_LEFT_STICK = SDL_GAMEPAD_BUTTON_LEFT_STICK, + SC_GAMEPAD_BUTTON_RIGHT_STICK = SDL_GAMEPAD_BUTTON_RIGHT_STICK, + SC_GAMEPAD_BUTTON_LEFT_SHOULDER = SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, + SC_GAMEPAD_BUTTON_RIGHT_SHOULDER = SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, + SC_GAMEPAD_BUTTON_DPAD_UP = SDL_GAMEPAD_BUTTON_DPAD_UP, + SC_GAMEPAD_BUTTON_DPAD_DOWN = SDL_GAMEPAD_BUTTON_DPAD_DOWN, + SC_GAMEPAD_BUTTON_DPAD_LEFT = SDL_GAMEPAD_BUTTON_DPAD_LEFT, + SC_GAMEPAD_BUTTON_DPAD_RIGHT = SDL_GAMEPAD_BUTTON_DPAD_RIGHT, }; static_assert(sizeof(enum sc_mod) >= sizeof(SDL_Keymod), @@ -449,8 +449,8 @@ sc_scancode_from_sdl(SDL_Scancode scancode) { static inline enum sc_action sc_action_from_sdl_keyboard_type(uint32_t type) { - assert(type == SDL_KEYDOWN || type == SDL_KEYUP); - if (type == SDL_KEYDOWN) { + assert(type == SDL_EVENT_KEY_DOWN || type == SDL_EVENT_KEY_UP); + if (type == SDL_EVENT_KEY_DOWN) { return SC_ACTION_DOWN; } return SC_ACTION_UP; @@ -458,8 +458,8 @@ sc_action_from_sdl_keyboard_type(uint32_t type) { static inline enum sc_action sc_action_from_sdl_mousebutton_type(uint32_t type) { - assert(type == SDL_MOUSEBUTTONDOWN || type == SDL_MOUSEBUTTONUP); - if (type == SDL_MOUSEBUTTONDOWN) { + assert(type == SDL_EVENT_MOUSE_BUTTON_DOWN || type == SDL_EVENT_MOUSE_BUTTON_UP); + if (type == SDL_EVENT_MOUSE_BUTTON_DOWN) { return SC_ACTION_DOWN; } return SC_ACTION_UP; @@ -467,12 +467,12 @@ sc_action_from_sdl_mousebutton_type(uint32_t type) { static inline enum sc_touch_action sc_touch_action_from_sdl(uint32_t type) { - assert(type == SDL_FINGERMOTION || type == SDL_FINGERDOWN || - type == SDL_FINGERUP); - if (type == SDL_FINGERMOTION) { + assert(type == SDL_EVENT_FINGER_MOTION || type == SDL_EVENT_FINGER_DOWN || + type == SDL_EVENT_FINGER_UP); + if (type == SDL_EVENT_FINGER_MOTION) { return SC_TOUCH_ACTION_MOVE; } - if (type == SDL_FINGERDOWN) { + if (type == SDL_EVENT_FINGER_DOWN) { return SC_TOUCH_ACTION_DOWN; } return SC_TOUCH_ACTION_UP; @@ -482,7 +482,7 @@ static inline enum sc_mouse_button sc_mouse_button_from_sdl(uint8_t button) { if (button >= SDL_BUTTON_LEFT && button <= SDL_BUTTON_X2) { // SC_MOUSE_BUTTON_* constants are initialized from SDL_BUTTON(index) - return SDL_BUTTON(button); + return SDL_BUTTON_MASK(button); } return SC_MOUSE_BUTTON_UNKNOWN; @@ -498,7 +498,7 @@ sc_mouse_buttons_state_from_sdl(uint32_t buttons_state) { static inline enum sc_gamepad_axis sc_gamepad_axis_from_sdl(uint8_t axis) { - if (axis <= SDL_CONTROLLER_AXIS_TRIGGERRIGHT) { + if (axis <= SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) { // SC_GAMEPAD_AXIS_* constants are initialized from // SDL_CONTROLLER_AXIS_* return axis; @@ -508,7 +508,7 @@ sc_gamepad_axis_from_sdl(uint8_t axis) { static inline enum sc_gamepad_button sc_gamepad_button_from_sdl(uint8_t button) { - if (button <= SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { + if (button <= SDL_GAMEPAD_BUTTON_DPAD_RIGHT) { // SC_GAMEPAD_BUTTON_* constants are initialized from // SDL_CONTROLLER_BUTTON_* return button; @@ -518,8 +518,8 @@ sc_gamepad_button_from_sdl(uint8_t button) { static inline enum sc_action sc_action_from_sdl_controllerbutton_type(uint32_t type) { - assert(type == SDL_CONTROLLERBUTTONDOWN || type == SDL_CONTROLLERBUTTONUP); - if (type == SDL_CONTROLLERBUTTONDOWN) { + assert(type == SDL_EVENT_GAMEPAD_BUTTON_DOWN || type == SDL_EVENT_GAMEPAD_BUTTON_UP); + if (type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) { return SC_ACTION_DOWN; } return SC_ACTION_UP; diff --git a/app/src/input_manager.c b/app/src/input_manager.c index f7a787d1e2..183ace64a2 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include "android/input.h" #include "android/keycodes.h" @@ -374,11 +374,11 @@ sc_input_manager_process_key(struct sc_input_manager *im, bool paused = im->screen->paused; bool video = im->screen->video; - SDL_Keycode sdl_keycode = event->keysym.sym; - uint16_t mod = event->keysym.mod; - bool down = event->type == SDL_KEYDOWN; - bool ctrl = event->keysym.mod & KMOD_CTRL; - bool shift = event->keysym.mod & KMOD_SHIFT; + SDL_Keycode sdl_keycode = event->key; + uint16_t mod = event->mod; + bool down = event->type == SDL_EVENT_KEY_DOWN; + bool ctrl = event->mod & SDL_KMOD_CTRL; + bool shift = event->mod & SDL_KMOD_SHIFT; bool repeat = event->repeat; // Either the modifier includes a shortcut modifier, or the key @@ -402,39 +402,39 @@ sc_input_manager_process_key(struct sc_input_manager *im, if (is_shortcut) { enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP; switch (sdl_keycode) { - case SDLK_h: + case SDLK_H: if (im->kp && !shift && !repeat && !paused) { action_home(im, action); } return; - case SDLK_b: // fall-through + case SDLK_B: // fall-through case SDLK_BACKSPACE: if (im->kp && !shift && !repeat && !paused) { action_back(im, action); } return; - case SDLK_s: + case SDLK_S: if (im->kp && !shift && !repeat && !paused) { action_app_switch(im, action); } return; - case SDLK_m: + case SDLK_M: if (im->kp && !shift && !repeat && !paused) { action_menu(im, action); } return; - case SDLK_p: + case SDLK_P: if (im->kp && !shift && !repeat && !paused) { action_power(im, action); } return; - case SDLK_o: + case SDLK_O: if (control && !repeat && down && !paused) { bool on = shift; set_display_power(im, on); } return; - case SDLK_z: + case SDLK_Z: if (video && down && !repeat) { sc_screen_set_paused(im->screen, !shift); } @@ -483,17 +483,17 @@ sc_input_manager_process_key(struct sc_input_manager *im, } } return; - case SDLK_c: + case SDLK_C: if (im->kp && !shift && !repeat && down && !paused) { get_device_clipboard(im, SC_COPY_KEY_COPY); } return; - case SDLK_x: + case SDLK_X: if (im->kp && !shift && !repeat && down && !paused) { get_device_clipboard(im, SC_COPY_KEY_CUT); } return; - case SDLK_v: + case SDLK_V: if (im->kp && !repeat && down && !paused) { if (shift || im->legacy_paste) { // inject the text as input events @@ -505,27 +505,27 @@ sc_input_manager_process_key(struct sc_input_manager *im, } } return; - case SDLK_f: + case SDLK_F: if (video && !shift && !repeat && down) { sc_screen_toggle_fullscreen(im->screen); } return; - case SDLK_w: + case SDLK_W: if (video && !shift && !repeat && down) { sc_screen_resize_to_fit(im->screen); } return; - case SDLK_g: + case SDLK_G: if (video && !shift && !repeat && down) { sc_screen_resize_to_pixel_perfect(im->screen); } return; - case SDLK_i: + case SDLK_I: if (video && !shift && !repeat && down) { switch_fps_counter_state(im); } return; - case SDLK_n: + case SDLK_N: if (control && !repeat && down && !paused) { if (shift) { collapse_panels(im); @@ -536,7 +536,7 @@ sc_input_manager_process_key(struct sc_input_manager *im, } } return; - case SDLK_r: + case SDLK_R: if (control && !repeat && down && !paused) { if (shift) { reset_video(im); @@ -545,7 +545,7 @@ sc_input_manager_process_key(struct sc_input_manager *im, } } return; - case SDLK_k: + case SDLK_K: if (control && !shift && !repeat && down && !paused && im->kp && im->kp->hid) { // Only if the current keyboard is hid @@ -562,7 +562,7 @@ sc_input_manager_process_key(struct sc_input_manager *im, } uint64_t ack_to_wait = SC_SEQUENCE_INVALID; - bool is_ctrl_v = ctrl && !shift && sdl_keycode == SDLK_v && down && !repeat; + bool is_ctrl_v = ctrl && !shift && sdl_keycode == SDLK_V && down && !repeat; if (im->clipboard_autosync && is_ctrl_v) { if (im->legacy_paste) { // inject the text as input events @@ -595,7 +595,7 @@ sc_input_manager_process_key(struct sc_input_manager *im, return; } - enum sc_scancode scancode = sc_scancode_from_sdl(event->keysym.scancode); + enum sc_scancode scancode = sc_scancode_from_sdl(event->scancode); if (scancode == SC_SCANCODE_UNKNOWN) { return; } @@ -605,7 +605,7 @@ sc_input_manager_process_key(struct sc_input_manager *im, .keycode = keycode, .scancode = scancode, .repeat = event->repeat, - .mods_state = sc_mods_state_from_sdl(event->keysym.mod), + .mods_state = sc_mods_state_from_sdl(event->mod), }; assert(im->kp->ops->process_key); @@ -674,7 +674,7 @@ sc_input_manager_process_touch(struct sc_input_manager *im, int dw; int dh; - SDL_GL_GetDrawableSize(im->screen->window, &dw, &dh); + SDL_GetWindowSizeInPixels(im->screen->window, &dw, &dh); // SDL touch event coordinates are normalized in the range [0; 1] int32_t x = event->x * dw; @@ -687,7 +687,7 @@ sc_input_manager_process_touch(struct sc_input_manager *im, sc_screen_convert_drawable_to_frame_coords(im->screen, x, y), }, .action = sc_touch_action_from_sdl(event->type), - .pointer_id = event->fingerId, + .pointer_id = event->fingerID, .pressure = event->pressure, }; @@ -723,7 +723,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im, bool control = im->controller; bool paused = im->screen->paused; - bool down = event->type == SDL_MOUSEBUTTONDOWN; + bool down = event->type == SDL_EVENT_MOUSE_BUTTON_DOWN; enum sc_mouse_button button = sc_mouse_button_from_sdl(event->button); if (button == SC_MOUSE_BUTTON_UNKNOWN) { @@ -736,8 +736,8 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im, } SDL_Keymod keymod = SDL_GetModState(); - bool ctrl_pressed = keymod & KMOD_CTRL; - bool shift_pressed = keymod & KMOD_SHIFT; + bool ctrl_pressed = keymod & SDL_KMOD_CTRL; + bool shift_pressed = keymod & SDL_KMOD_SHIFT; if (control && !paused) { enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP; @@ -889,20 +889,15 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im, } // mouse_x and mouse_y are expressed in pixels relative to the window - int mouse_x; - int mouse_y; + float mouse_x; + float mouse_y; uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y); (void) buttons; // Actual buttons are tracked manually to ignore shortcuts struct sc_mouse_scroll_event evt = { .position = sc_input_manager_get_position(im, mouse_x, mouse_y), -#if SDL_VERSION_ATLEAST(2, 0, 18) - .hscroll = event->preciseX, - .vscroll = event->preciseY, -#else .hscroll = event->x, .vscroll = event->y, -#endif .buttons_state = im->mouse_buttons_state, }; @@ -911,31 +906,31 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im, static void sc_input_manager_process_gamepad_device(struct sc_input_manager *im, - const SDL_ControllerDeviceEvent *event) { - if (event->type == SDL_CONTROLLERDEVICEADDED) { - SDL_GameController *gc = SDL_GameControllerOpen(event->which); + const SDL_GamepadDeviceEvent *event) { + if (event->type == SDL_EVENT_GAMEPAD_ADDED) { + SDL_Gamepad *gc = SDL_OpenGamepad(event->which); if (!gc) { LOGW("Could not open game controller"); return; } - SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gc); + SDL_Joystick *joystick = SDL_GetGamepadJoystick(gc); if (!joystick) { LOGW("Could not get controller joystick"); - SDL_GameControllerClose(gc); + SDL_CloseGamepad(gc); return; } struct sc_gamepad_device_event evt = { - .gamepad_id = SDL_JoystickInstanceID(joystick), + .gamepad_id = SDL_GetJoystickID(joystick), }; im->gp->ops->process_gamepad_added(im->gp, &evt); - } else if (event->type == SDL_CONTROLLERDEVICEREMOVED) { + } else if (event->type == SDL_EVENT_GAMEPAD_REMOVED) { SDL_JoystickID id = event->which; - SDL_GameController *gc = SDL_GameControllerFromInstanceID(id); + SDL_Gamepad *gc = SDL_GetGamepadFromID(id); if (gc) { - SDL_GameControllerClose(gc); + SDL_CloseGamepad(gc); } else { LOGW("Unknown gamepad device removed"); } @@ -952,7 +947,7 @@ sc_input_manager_process_gamepad_device(struct sc_input_manager *im, static void sc_input_manager_process_gamepad_axis(struct sc_input_manager *im, - const SDL_ControllerAxisEvent *event) { + const SDL_GamepadAxisEvent *event) { enum sc_gamepad_axis axis = sc_gamepad_axis_from_sdl(event->axis); if (axis == SC_GAMEPAD_AXIS_UNKNOWN) { return; @@ -968,7 +963,7 @@ sc_input_manager_process_gamepad_axis(struct sc_input_manager *im, static void sc_input_manager_process_gamepad_button(struct sc_input_manager *im, - const SDL_ControllerButtonEvent *event) { + const SDL_GamepadButtonEvent *event) { enum sc_gamepad_button button = sc_gamepad_button_from_sdl(event->button); if (button == SC_GAMEPAD_BUTTON_UNKNOWN) { return; @@ -991,8 +986,8 @@ is_apk(const char *file) { static void sc_input_manager_process_file(struct sc_input_manager *im, const SDL_DropEvent *event) { - char *file = strdup(event->file); - SDL_free(event->file); + assert(event->type == SDL_EVENT_DROP_FILE); + char *file = strdup(event->data); if (!file) { LOG_OOM(); return; @@ -1016,66 +1011,66 @@ sc_input_manager_handle_event(struct sc_input_manager *im, bool control = im->controller; bool paused = im->screen->paused; switch (event->type) { - case SDL_TEXTINPUT: + case SDL_EVENT_TEXT_INPUT: if (!im->kp || paused) { break; } sc_input_manager_process_text_input(im, &event->text); break; - case SDL_KEYDOWN: - case SDL_KEYUP: + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: // some key events do not interact with the device, so process the // event even if control is disabled sc_input_manager_process_key(im, &event->key); break; - case SDL_MOUSEMOTION: + case SDL_EVENT_MOUSE_MOTION: if (!im->mp || paused) { break; } sc_input_manager_process_mouse_motion(im, &event->motion); break; - case SDL_MOUSEWHEEL: + case SDL_EVENT_MOUSE_WHEEL: if (!im->mp || paused) { break; } sc_input_manager_process_mouse_wheel(im, &event->wheel); break; - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: // some mouse events do not interact with the device, so process // the event even if control is disabled sc_input_manager_process_mouse_button(im, &event->button); break; - case SDL_FINGERMOTION: - case SDL_FINGERDOWN: - case SDL_FINGERUP: + case SDL_EVENT_FINGER_MOTION: + case SDL_EVENT_FINGER_DOWN: + case SDL_EVENT_FINGER_UP: if (!im->mp || paused) { break; } sc_input_manager_process_touch(im, &event->tfinger); break; - case SDL_CONTROLLERDEVICEADDED: - case SDL_CONTROLLERDEVICEREMOVED: + case SDL_EVENT_GAMEPAD_ADDED: + case SDL_EVENT_GAMEPAD_REMOVED: // Handle device added or removed even if paused if (!im->gp) { break; } - sc_input_manager_process_gamepad_device(im, &event->cdevice); + sc_input_manager_process_gamepad_device(im, &event->gdevice); break; - case SDL_CONTROLLERAXISMOTION: + case SDL_EVENT_GAMEPAD_AXIS_MOTION: if (!im->gp || paused) { break; } - sc_input_manager_process_gamepad_axis(im, &event->caxis); + sc_input_manager_process_gamepad_axis(im, &event->gaxis); break; - case SDL_CONTROLLERBUTTONDOWN: - case SDL_CONTROLLERBUTTONUP: + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + case SDL_EVENT_GAMEPAD_BUTTON_UP: if (!im->gp || paused) { break; } - sc_input_manager_process_gamepad_button(im, &event->cbutton); + sc_input_manager_process_gamepad_button(im, &event->gbutton); break; - case SDL_DROPFILE: { + case SDL_EVENT_DROP_FILE: { if (!control) { break; } diff --git a/app/src/input_manager.h b/app/src/input_manager.h index af4cbc6973..6f4af8f776 100644 --- a/app/src/input_manager.h +++ b/app/src/input_manager.h @@ -5,8 +5,8 @@ #include #include -#include -#include +#include +#include #include "controller.h" #include "file_pusher.h" diff --git a/app/src/main.c b/app/src/main.c index 968b193484..a3d323d24b 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -5,8 +5,9 @@ #ifdef HAVE_V4L2 # include #endif -#define SDL_MAIN_HANDLED // avoid link error on Linux Windows Subsystem -#include +#define SDL_FUNCTION_POINTER_IS_VOID_POINTER +#include +#include #include "cli.h" #include "options.h" diff --git a/app/src/mouse_capture.c b/app/src/mouse_capture.c index 25345faadd..86aca21543 100644 --- a/app/src/mouse_capture.c +++ b/app/src/mouse_capture.c @@ -20,14 +20,11 @@ bool sc_mouse_capture_handle_event(struct sc_mouse_capture *mc, const SDL_Event *event) { switch (event->type) { - case SDL_WINDOWEVENT: - if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST) { - sc_mouse_capture_set_active(mc, false); - return true; - } - break; - case SDL_KEYDOWN: { - SDL_Keycode key = event->key.keysym.sym; + case SDL_EVENT_WINDOW_FOCUS_LOST: + sc_mouse_capture_set_active(mc, false); + return true; + case SDL_EVENT_KEY_DOWN: { + SDL_Keycode key = event->key.key; if (sc_mouse_capture_is_capture_key(mc, key)) { if (!mc->mouse_capture_key_pressed) { mc->mouse_capture_key_pressed = key; @@ -41,8 +38,8 @@ sc_mouse_capture_handle_event(struct sc_mouse_capture *mc, } break; } - case SDL_KEYUP: { - SDL_Keycode key = event->key.keysym.sym; + case SDL_EVENT_KEY_UP: { + SDL_Keycode key = event->key.key; SDL_Keycode cap = mc->mouse_capture_key_pressed; mc->mouse_capture_key_pressed = 0; if (sc_mouse_capture_is_capture_key(mc, key)) { @@ -56,24 +53,24 @@ sc_mouse_capture_handle_event(struct sc_mouse_capture *mc, } break; } - case SDL_MOUSEWHEEL: - case SDL_MOUSEMOTION: - case SDL_MOUSEBUTTONDOWN: + case SDL_EVENT_MOUSE_WHEEL: + case SDL_EVENT_MOUSE_MOTION: + case SDL_EVENT_MOUSE_BUTTON_DOWN: if (!sc_mouse_capture_is_active(mc)) { // The mouse will be captured on SDL_MOUSEBUTTONUP, so consume // the event return true; } break; - case SDL_MOUSEBUTTONUP: + case SDL_EVENT_MOUSE_BUTTON_UP: if (!sc_mouse_capture_is_active(mc)) { sc_mouse_capture_set_active(mc, true); return true; } break; - case SDL_FINGERMOTION: - case SDL_FINGERDOWN: - case SDL_FINGERUP: + case SDL_EVENT_FINGER_MOTION: + case SDL_EVENT_FINGER_DOWN: + case SDL_EVENT_FINGER_UP: // Touch events are not compatible with relative mode // (coordinates are not relative), so consume the event return true; @@ -88,7 +85,7 @@ sc_mouse_capture_set_active(struct sc_mouse_capture *mc, bool capture) { // Workaround for SDL bug on macOS: // if (capture) { - int mouse_x, mouse_y; + float mouse_x, mouse_y; SDL_GetGlobalMouseState(&mouse_x, &mouse_y); int x, y, w, h; @@ -101,10 +98,9 @@ sc_mouse_capture_set_active(struct sc_mouse_capture *mc, bool capture) { SDL_WarpMouseInWindow(mc->window, w / 2, h / 2); } } -#else - (void) mc; #endif - if (SDL_SetRelativeMouseMode(capture)) { + bool ok = SDL_SetWindowRelativeMouseMode(mc->window, capture); + if (!ok) { LOGE("Could not set relative mouse mode to %s: %s", capture ? "true" : "false", SDL_GetError()); } @@ -112,8 +108,7 @@ sc_mouse_capture_set_active(struct sc_mouse_capture *mc, bool capture) { bool sc_mouse_capture_is_active(struct sc_mouse_capture *mc) { - (void) mc; - return SDL_GetRelativeMouseMode(); + return SDL_GetWindowRelativeMouseMode(mc->window); } void diff --git a/app/src/mouse_capture.h b/app/src/mouse_capture.h index f352cc133f..65770f8a43 100644 --- a/app/src/mouse_capture.h +++ b/app/src/mouse_capture.h @@ -5,7 +5,7 @@ #include -#include +#include struct sc_mouse_capture { SDL_Window *window; diff --git a/app/src/opengl.c b/app/src/opengl.c index 0cb83ed7c9..91e8d01d8c 100644 --- a/app/src/opengl.c +++ b/app/src/opengl.c @@ -3,21 +3,29 @@ #include #include #include -#include +#include void sc_opengl_init(struct sc_opengl *gl) { - gl->GetString = SDL_GL_GetProcAddress("glGetString"); + gl->GetString = (const GLubyte *(*)(GLenum)) + SDL_GL_GetProcAddress("glGetString"); assert(gl->GetString); - gl->TexParameterf = SDL_GL_GetProcAddress("glTexParameterf"); + gl->BindTexture = (void (*)(GLenum, GLuint)) + SDL_GL_GetProcAddress("glBindTexture"); + assert(gl->BindTexture); + + gl->TexParameterf = (void (*)(GLenum, GLenum, GLfloat)) + SDL_GL_GetProcAddress("glTexParameterf"); assert(gl->TexParameterf); - gl->TexParameteri = SDL_GL_GetProcAddress("glTexParameteri"); + gl->TexParameteri = (void (*)(GLenum, GLenum, GLint)) + SDL_GL_GetProcAddress("glTexParameteri"); assert(gl->TexParameteri); // optional - gl->GenerateMipmap = SDL_GL_GetProcAddress("glGenerateMipmap"); + gl->GenerateMipmap = (void (*)(GLenum)) + SDL_GL_GetProcAddress("glGenerateMipmap"); const char *version = (const char *) gl->GetString(GL_VERSION); assert(version); diff --git a/app/src/opengl.h b/app/src/opengl.h index 81163704d5..8db4e5d42d 100644 --- a/app/src/opengl.h +++ b/app/src/opengl.h @@ -4,7 +4,7 @@ #include "common.h" #include -#include +#include struct sc_opengl { const char *version; @@ -15,6 +15,9 @@ struct sc_opengl { const GLubyte * (*GetString)(GLenum name); + void + (*BindTexture)(GLenum target, GLuint texture); + void (*TexParameterf)(GLenum target, GLenum pname, GLfloat param); diff --git a/app/src/receiver.c b/app/src/receiver.c index 2ccb8a8b4b..659f443ff9 100644 --- a/app/src/receiver.c +++ b/app/src/receiver.c @@ -2,7 +2,8 @@ #include #include -#include +#include +#include #include "device_msg.h" #include "events.h" @@ -53,8 +54,12 @@ task_set_clipboard(void *userdata) { if (same) { LOGD("Computer clipboard unchanged"); } else { - LOGI("Device clipboard copied"); - SDL_SetClipboardText(text); + bool ok = SDL_SetClipboardText(text); + if (ok) { + LOGI("Device clipboard copied"); + } else { + LOGE("Could not set clipboard: %s", SDL_GetError()); + } } free(text); diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index aedfdf9cf8..97ba861749 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #ifdef _WIN32 // not needed here, but winsock2.h must never be included AFTER windows.h @@ -94,7 +94,7 @@ struct scrcpy { #ifdef _WIN32 static BOOL WINAPI windows_ctrl_handler(DWORD ctrl_type) { if (ctrl_type == CTRL_C_EVENT || ctrl_type == CTRL_BREAK_EVENT) { - sc_push_event(SDL_QUIT); + sc_push_event(SDL_EVENT_QUIT); return TRUE; } return FALSE; @@ -108,41 +108,26 @@ sdl_set_hints(const char *render_driver) { } // App name used in various contexts (such as PulseAudio) -#if defined(SCRCPY_SDL_HAS_HINT_APP_NAME) if (!SDL_SetHint(SDL_HINT_APP_NAME, "scrcpy")) { LOGW("Could not set app name"); } -#elif defined(SCRCPY_SDL_HAS_HINT_AUDIO_DEVICE_APP_NAME) - if (!SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "scrcpy")) { - LOGW("Could not set audio device app name"); - } -#endif - - // Linear filtering - if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) { - LOGW("Could not enable linear filtering"); - } // Handle a click to gain focus as any other click if (!SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1")) { LOGW("Could not enable mouse focus clickthrough"); } -#ifdef SCRCPY_SDL_HAS_HINT_TOUCH_MOUSE_EVENTS // Disable synthetic mouse events from touch events // Touch events with id SDL_TOUCH_MOUSEID are ignored anyway, but it is // better not to generate them in the first place. if (!SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0")) { LOGW("Could not disable synthetic mouse events"); } -#endif -#ifdef SCRCPY_SDL_HAS_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR // Disable compositor bypassing on X11 if (!SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0")) { LOGW("Could not disable X11 compositor bypass"); } -#endif // Do not minimize on focus loss if (!SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0")) { @@ -169,9 +154,15 @@ sdl_configure(bool video_playback, bool disable_screensaver) { } if (disable_screensaver) { - SDL_DisableScreenSaver(); + bool ok = SDL_DisableScreenSaver(); + if (!ok) { + LOGW("Could not disable screen saver"); + } } else { - SDL_EnableScreenSaver(); + bool ok = SDL_EnableScreenSaver(); + if (!ok) { + LOGW("Could not enable screen saver"); + } } } @@ -198,7 +189,7 @@ event_loop(struct scrcpy *s, bool has_screen) { case SC_EVENT_TIME_LIMIT_REACHED: LOGI("Time limit reached"); return SCRCPY_EXIT_SUCCESS; - case SDL_QUIT: + case SDL_EVENT_QUIT: LOGD("User requested to quit"); return SCRCPY_EXIT_SUCCESS; case SC_EVENT_RUN_ON_MAIN_THREAD: { @@ -238,7 +229,7 @@ await_for_server(bool *connected) { SDL_Event event; while (SDL_WaitEvent(&event)) { switch (event.type) { - case SDL_QUIT: + case SDL_EVENT_QUIT: if (connected) { *connected = false; } @@ -366,12 +357,19 @@ static void init_sdl_gamepads(void) { // Trigger a SDL_CONTROLLERDEVICEADDED event for all gamepads already // connected - int num_joysticks = SDL_NumJoysticks(); - for (int i = 0; i < num_joysticks; ++i) { - if (SDL_IsGameController(i)) { + int count; + SDL_JoystickID *joysticks = SDL_GetJoysticks(&count); + if (!joysticks) { + LOGE("Could not list joysticks: %s", SDL_GetError()); + return; + } + + for (int i = 0; i < count; ++i) { + SDL_JoystickID joystick = joysticks[i]; + if (SDL_IsGamepad(joystick)) { SDL_Event event; - event.cdevice.type = SDL_CONTROLLERDEVICEADDED; - event.cdevice.which = i; + event.gdevice.type = SDL_EVENT_GAMEPAD_ADDED; + event.gdevice.which = i; SDL_PushEvent(&event); } } @@ -387,7 +385,7 @@ scrcpy(struct scrcpy_options *options) { struct scrcpy *s = &scrcpy; // Minimal SDL initialization - if (SDL_Init(SDL_INIT_EVENTS)) { + if (!SDL_Init(SDL_INIT_EVENTS)) { LOGE("Could not initialize SDL: %s", SDL_GetError()); return SCRCPY_EXIT_FAILURE; } @@ -513,7 +511,7 @@ scrcpy(struct scrcpy_options *options) { // --no-video-playback is passed so that clipboard synchronization // still works. // - if (SDL_Init(SDL_INIT_VIDEO)) { + if (!SDL_Init(SDL_INIT_VIDEO)) { // If it fails, it is an error only if video playback is enabled if (options->video_playback) { LOGE("Could not initialize SDL video: %s", SDL_GetError()); @@ -525,14 +523,14 @@ scrcpy(struct scrcpy_options *options) { } if (options->audio_playback) { - if (SDL_Init(SDL_INIT_AUDIO)) { + if (!SDL_Init(SDL_INIT_AUDIO)) { LOGE("Could not initialize SDL audio: %s", SDL_GetError()); goto end; } } if (options->gamepad_input_mode != SC_GAMEPAD_INPUT_MODE_DISABLED) { - if (SDL_Init(SDL_INIT_GAMECONTROLLER)) { + if (!SDL_Init(SDL_INIT_GAMEPAD)) { LOGE("Could not initialize SDL gamepad: %s", SDL_GetError()); goto end; } diff --git a/app/src/screen.c b/app/src/screen.c index fc4a22ffd7..0ed5c4d19b 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -2,12 +2,13 @@ #include #include -#include +#include #include "events.h" #include "icon.h" #include "options.h" #include "util/log.h" +#include "util/window.h" #define DISPLAY_MARGINS 96 @@ -64,7 +65,9 @@ set_window_size(struct sc_screen *screen, struct sc_size new_size) { static bool get_preferred_display_bounds(struct sc_size *bounds) { SDL_Rect rect; - if (SDL_GetDisplayUsableBounds(0, &rect)) { + SDL_DisplayID display = SDL_GetPrimaryDisplay(); + bool ok = SDL_GetDisplayUsableBounds(display, &rect); + if (!ok) { LOGW("Could not get display usable bounds: %s", SDL_GetError()); return false; } @@ -168,7 +171,7 @@ sc_screen_update_content_rect(struct sc_screen *screen) { int dw; int dh; - SDL_GL_GetDrawableSize(screen->window, &dw, &dh); + SDL_GetWindowSizeInPixels(screen->window, &dw, &dh); struct sc_size content_size = screen->content_size; // The drawable size is the window size * the HiDPI scale @@ -236,18 +239,18 @@ sc_screen_render_novideo(struct sc_screen *screen) { // // // -static int +static bool event_watcher(void *data, SDL_Event *event) { struct sc_screen *screen = data; assert(screen->video); - if (event->type == SDL_WINDOWEVENT - && event->window.event == SDL_WINDOWEVENT_RESIZED) { + if (event->type == SDL_EVENT_WINDOW_EXPOSED) { // In practice, it seems to always be called from the same thread in // that specific case. Anyway, it's just a workaround. sc_screen_render(screen, true); } - return 0; + + return true; } #endif @@ -258,6 +261,7 @@ sc_screen_frame_sink_open(struct sc_frame_sink *sink, (void) ctx; struct sc_screen *screen = DOWNCAST(sink); + (void) screen; if (ctx->width <= 0 || ctx->width > 0xFFFF || ctx->height <= 0 || ctx->height > 0xFFFF) { @@ -349,7 +353,7 @@ sc_screen_init(struct sc_screen *screen, } } - uint32_t window_flags = SDL_WINDOW_ALLOW_HIGHDPI; + uint32_t window_flags = SDL_WINDOW_HIGH_PIXEL_DENSITY; if (params->always_on_top) { window_flags |= SDL_WINDOW_ALWAYS_ON_TOP; } @@ -383,15 +387,24 @@ sc_screen_init(struct sc_screen *screen, } // The window will be positioned and sized on first video frame - screen->window = SDL_CreateWindow(title, x, y, width, height, window_flags); + screen->window = + sc_create_sdl_window(title, x, y, width, height, window_flags); if (!screen->window) { LOGE("Could not create window: %s", SDL_GetError()); goto error_destroy_fps_counter; } + ok = SDL_StartTextInput(screen->window); + if (!ok) { + LOGE("Could not enable text input: %s", SDL_GetError()); + goto error_destroy_window; + } + SDL_Surface *icon = scrcpy_icon_load(); if (icon) { - SDL_SetWindowIcon(screen->window, icon); + if (!SDL_SetWindowIcon(screen->window, icon)) { + LOGW("Could not set window icon: %s", SDL_GetError()); + } } else if (params->video) { // just a warning LOGW("Could not load icon"); @@ -438,7 +451,11 @@ sc_screen_init(struct sc_screen *screen, #ifdef CONTINUOUS_RESIZING_WORKAROUND if (screen->video) { - SDL_AddEventWatch(event_watcher, screen); + ok = SDL_AddEventWatch(event_watcher, screen); + if (!ok) { + LOGW("Could not add event watcher for continuous resizing: %s", + SDL_GetError()); + } } #endif @@ -712,8 +729,8 @@ void sc_screen_toggle_fullscreen(struct sc_screen *screen) { assert(screen->video); - uint32_t new_mode = screen->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP; - if (SDL_SetWindowFullscreen(screen->window, new_mode)) { + bool ok = SDL_SetWindowFullscreen(screen->window, !screen->fullscreen); + if (!ok) { LOGW("Could not switch fullscreen mode: %s", SDL_GetError()); return; } @@ -774,6 +791,8 @@ sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) { bool sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) { + // !video implies !has_video_window + assert(screen->video || !screen->has_video_window); switch (event->type) { case SC_EVENT_NEW_FRAME: { bool ok = sc_screen_update_frame(screen); @@ -783,46 +802,38 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) { } return true; } - case SDL_WINDOWEVENT: - if (!screen->video - && event->window.event == SDL_WINDOWEVENT_EXPOSED) { + case SDL_EVENT_WINDOW_EXPOSED: + if (!screen->video) { sc_screen_render_novideo(screen); - return true; + } else if (screen->has_video_window) { + sc_screen_render(screen, true); } - - // !video implies !has_video_window - assert(screen->video || !screen->has_video_window); - if (!screen->has_video_window) { - // Do nothing + return true; + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: + if (screen->has_video_window) { + sc_screen_render(screen, true); + } + return true; + case SDL_EVENT_WINDOW_MAXIMIZED: + screen->maximized = true; + return true; + case SDL_EVENT_WINDOW_MINIMIZED: + screen->minimized = true; + return true; + case SDL_EVENT_WINDOW_RESTORED: + if (screen->fullscreen) { + // On Windows, in maximized+fullscreen, disabling + // fullscreen mode unexpectedly triggers the "restored" + // then "maximized" events, leaving the window in a + // weird state (maximized according to the events, but + // not maximized visually). return true; } - switch (event->window.event) { - case SDL_WINDOWEVENT_EXPOSED: - sc_screen_render(screen, true); - break; - case SDL_WINDOWEVENT_SIZE_CHANGED: - sc_screen_render(screen, true); - break; - case SDL_WINDOWEVENT_MAXIMIZED: - screen->maximized = true; - break; - case SDL_WINDOWEVENT_MINIMIZED: - screen->minimized = true; - break; - case SDL_WINDOWEVENT_RESTORED: - if (screen->fullscreen) { - // On Windows, in maximized+fullscreen, disabling - // fullscreen mode unexpectedly triggers the "restored" - // then "maximized" events, leaving the window in a - // weird state (maximized according to the events, but - // not maximized visually). - break; - } - screen->maximized = false; - screen->minimized = false; - apply_pending_resize(screen); - sc_screen_render(screen, true); - break; + screen->maximized = false; + screen->minimized = false; + if (screen->has_video_window) { + apply_pending_resize(screen); + sc_screen_render(screen, true); } return true; } @@ -905,7 +916,7 @@ sc_screen_hidpi_scale_coords(struct sc_screen *screen, int32_t *x, int32_t *y) { // take the HiDPI scaling (dw/ww and dh/wh) into account int ww, wh, dw, dh; SDL_GetWindowSize(screen->window, &ww, &wh); - SDL_GL_GetDrawableSize(screen->window, &dw, &dh); + SDL_GetWindowSizeInPixels(screen->window, &dw, &dh); // scale for HiDPI (64 bits for intermediate multiplications) *x = (int64_t) *x * dw / ww; diff --git a/app/src/screen.h b/app/src/screen.h index 89a25cda46..0b992292ea 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include #include diff --git a/app/src/shortcut_mod.h b/app/src/shortcut_mod.h index f6c13f0355..c53b0ff9f7 100644 --- a/app/src/shortcut_mod.h +++ b/app/src/shortcut_mod.h @@ -6,11 +6,11 @@ #include #include #include -#include +#include #include "options.h" -#define SC_SDL_SHORTCUT_MODS_MASK (KMOD_CTRL | KMOD_ALT | KMOD_GUI) +#define SC_SDL_SHORTCUT_MODS_MASK (SDL_KMOD_CTRL | SDL_KMOD_ALT | SDL_KMOD_GUI) // input: OR of enum sc_shortcut_mod // output: OR of SDL_Keymod @@ -18,22 +18,22 @@ static inline uint16_t sc_shortcut_mods_to_sdl(uint8_t shortcut_mods) { uint16_t sdl_mod = 0; if (shortcut_mods & SC_SHORTCUT_MOD_LCTRL) { - sdl_mod |= KMOD_LCTRL; + sdl_mod |= SDL_KMOD_LCTRL; } if (shortcut_mods & SC_SHORTCUT_MOD_RCTRL) { - sdl_mod |= KMOD_RCTRL; + sdl_mod |= SDL_KMOD_RCTRL; } if (shortcut_mods & SC_SHORTCUT_MOD_LALT) { - sdl_mod |= KMOD_LALT; + sdl_mod |= SDL_KMOD_LALT; } if (shortcut_mods & SC_SHORTCUT_MOD_RALT) { - sdl_mod |= KMOD_RALT; + sdl_mod |= SDL_KMOD_RALT; } if (shortcut_mods & SC_SHORTCUT_MOD_LSUPER) { - sdl_mod |= KMOD_LGUI; + sdl_mod |= SDL_KMOD_LGUI; } if (shortcut_mods & SC_SHORTCUT_MOD_RSUPER) { - sdl_mod |= KMOD_RGUI; + sdl_mod |= SDL_KMOD_RGUI; } return sdl_mod; } @@ -50,12 +50,12 @@ sc_shortcut_mods_is_shortcut_mod(uint16_t sdl_shortcut_mods, uint16_t sdl_mod) { static inline bool sc_shortcut_mods_is_shortcut_key(uint16_t sdl_shortcut_mods, SDL_Keycode keycode) { - return (sdl_shortcut_mods & KMOD_LCTRL && keycode == SDLK_LCTRL) - || (sdl_shortcut_mods & KMOD_RCTRL && keycode == SDLK_RCTRL) - || (sdl_shortcut_mods & KMOD_LALT && keycode == SDLK_LALT) - || (sdl_shortcut_mods & KMOD_RALT && keycode == SDLK_RALT) - || (sdl_shortcut_mods & KMOD_LGUI && keycode == SDLK_LGUI) - || (sdl_shortcut_mods & KMOD_RGUI && keycode == SDLK_RGUI); + return (sdl_shortcut_mods & SDL_KMOD_LCTRL && keycode == SDLK_LCTRL) + || (sdl_shortcut_mods & SDL_KMOD_RCTRL && keycode == SDLK_RCTRL) + || (sdl_shortcut_mods & SDL_KMOD_LALT && keycode == SDLK_LALT) + || (sdl_shortcut_mods & SDL_KMOD_RALT && keycode == SDLK_RALT) + || (sdl_shortcut_mods & SDL_KMOD_LGUI && keycode == SDLK_LGUI) + || (sdl_shortcut_mods & SDL_KMOD_RGUI && keycode == SDLK_RGUI); } #endif diff --git a/app/src/uhid/gamepad_uhid.c b/app/src/uhid/gamepad_uhid.c index c64feb1851..b4fef59e39 100644 --- a/app/src/uhid/gamepad_uhid.c +++ b/app/src/uhid/gamepad_uhid.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include "hid/hid_gamepad.h" #include "input_events.h" @@ -74,10 +74,10 @@ sc_gamepad_processor_process_gamepad_added(struct sc_gamepad_processor *gp, return; } - SDL_GameController* game_controller = - SDL_GameControllerFromInstanceID(event->gamepad_id); + SDL_Gamepad * game_controller = + SDL_GetGamepadFromID(event->gamepad_id); assert(game_controller); - const char *name = SDL_GameControllerName(game_controller); + const char *name = SDL_GetGamepadName(game_controller); LOGI("Gamepad added: [%" PRIu32 "] %s", event->gamepad_id, name); sc_gamepad_uhid_send_open(gamepad, &hid_open); diff --git a/app/src/uhid/keyboard_uhid.c b/app/src/uhid/keyboard_uhid.c index 7008299054..d8337df08f 100644 --- a/app/src/uhid/keyboard_uhid.c +++ b/app/src/uhid/keyboard_uhid.c @@ -2,8 +2,8 @@ #include #include -#include -#include +#include +#include #include "util/log.h" #include "util/thread.h" diff --git a/app/src/usb/scrcpy_otg.c b/app/src/usb/scrcpy_otg.c index 1a9cc46ee1..ef0c1c5c69 100644 --- a/app/src/usb/scrcpy_otg.c +++ b/app/src/usb/scrcpy_otg.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include #ifdef _WIN32 # include "adb/adb.h" @@ -45,7 +45,7 @@ event_loop(struct scrcpy_otg *s) { case SC_EVENT_AOA_OPEN_ERROR: LOGE("AOA open error"); return SCRCPY_EXIT_FAILURE; - case SDL_QUIT: + case SDL_EVENT_QUIT: LOGD("User requested to quit"); return SCRCPY_EXIT_SUCCESS; default: @@ -63,22 +63,18 @@ scrcpy_otg(struct scrcpy_options *options) { const char *serial = options->serial; - if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) { - LOGW("Could not enable linear filtering"); - } - if (!SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1")) { LOGW("Could not allow joystick background events"); } // Minimal SDL initialization - if (SDL_Init(SDL_INIT_EVENTS)) { + if (!SDL_Init(SDL_INIT_EVENTS)) { LOGE("Could not initialize SDL: %s", SDL_GetError()); return SCRCPY_EXIT_FAILURE; } if (options->gamepad_input_mode != SC_GAMEPAD_INPUT_MODE_DISABLED) { - if (SDL_Init(SDL_INIT_GAMECONTROLLER)) { + if (!SDL_Init(SDL_INIT_GAMEPAD)) { LOGE("Could not initialize SDL controller: %s", SDL_GetError()); // Not fatal, keyboard/mouse should still work } diff --git a/app/src/usb/screen_otg.c b/app/src/usb/screen_otg.c index bed48eb686..ebdc42a266 100644 --- a/app/src/usb/screen_otg.c +++ b/app/src/usb/screen_otg.c @@ -7,12 +7,17 @@ #include "options.h" #include "util/acksync.h" #include "util/log.h" +#include "util/window.h" static void sc_screen_otg_render(struct sc_screen_otg *screen) { SDL_RenderClear(screen->renderer); if (screen->texture) { - SDL_RenderCopy(screen->renderer, screen->texture, NULL, NULL); + bool ok = + SDL_RenderTexture(screen->renderer, screen->texture, NULL, NULL); + if (!ok) { + LOGW("Could not render texture: %s", SDL_GetError()); + } } SDL_RenderPresent(screen->renderer); } @@ -34,7 +39,7 @@ sc_screen_otg_init(struct sc_screen_otg *screen, int width = params->window_width ? params->window_width : 256; int height = params->window_height ? params->window_height : 256; - uint32_t window_flags = SDL_WINDOW_ALLOW_HIGHDPI; + uint32_t window_flags = SDL_WINDOW_HIGH_PIXEL_DENSITY; if (params->always_on_top) { window_flags |= SDL_WINDOW_ALWAYS_ON_TOP; } @@ -42,13 +47,14 @@ sc_screen_otg_init(struct sc_screen_otg *screen, window_flags |= SDL_WINDOW_BORDERLESS; } - screen->window = SDL_CreateWindow(title, x, y, width, height, window_flags); + screen->window = + sc_create_sdl_window(title, x, y, width, height, window_flags); if (!screen->window) { LOGE("Could not create window: %s", SDL_GetError()); return false; } - screen->renderer = SDL_CreateRenderer(screen->window, -1, 0); + screen->renderer = SDL_CreateRenderer(screen->window, NULL); if (!screen->renderer) { LOGE("Could not create renderer: %s", SDL_GetError()); goto error_destroy_window; @@ -57,9 +63,15 @@ sc_screen_otg_init(struct sc_screen_otg *screen, SDL_Surface *icon = scrcpy_icon_load(); if (icon) { - SDL_SetWindowIcon(screen->window, icon); + bool ok = SDL_SetWindowIcon(screen->window, icon); + if (!ok) { + LOGW("Could not set window icon: %s", SDL_GetError()); + } - if (SDL_RenderSetLogicalSize(screen->renderer, icon->w, icon->h)) { + ok = SDL_SetRenderLogicalPresentation(screen->renderer, icon->w, + icon->h, + SDL_LOGICAL_PRESENTATION_LETTERBOX); + if (!ok) { LOGW("Could not set renderer logical size: %s", SDL_GetError()); // don't fail } @@ -108,10 +120,10 @@ sc_screen_otg_process_key(struct sc_screen_otg *screen, struct sc_key_event evt = { .action = sc_action_from_sdl_keyboard_type(event->type), - .keycode = sc_keycode_from_sdl(event->keysym.sym), - .scancode = sc_scancode_from_sdl(event->keysym.scancode), + .keycode = sc_keycode_from_sdl(event->key), + .scancode = sc_scancode_from_sdl(event->scancode), .repeat = event->repeat, - .mods_state = sc_mods_state_from_sdl(event->keysym.mod), + .mods_state = sc_mods_state_from_sdl(event->mod), }; assert(kp->ops->process_key); @@ -164,13 +176,8 @@ sc_screen_otg_process_mouse_wheel(struct sc_screen_otg *screen, struct sc_mouse_scroll_event evt = { // .position not used for HID events -#if SDL_VERSION_ATLEAST(2, 0, 18) - .hscroll = event->preciseX, - .vscroll = event->preciseY, -#else .hscroll = event->x, .vscroll = event->y, -#endif .buttons_state = sc_mouse_buttons_state_from_sdl(sdl_buttons_state), }; @@ -180,34 +187,34 @@ sc_screen_otg_process_mouse_wheel(struct sc_screen_otg *screen, static void sc_screen_otg_process_gamepad_device(struct sc_screen_otg *screen, - const SDL_ControllerDeviceEvent *event) { + const SDL_GamepadDeviceEvent *event) { assert(screen->gamepad); struct sc_gamepad_processor *gp = &screen->gamepad->gamepad_processor; - if (event->type == SDL_CONTROLLERDEVICEADDED) { - SDL_GameController *gc = SDL_GameControllerOpen(event->which); + if (event->type == SDL_EVENT_GAMEPAD_ADDED) { + SDL_Gamepad *gc = SDL_OpenGamepad(event->which); if (!gc) { LOGW("Could not open game controller"); return; } - SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gc); + SDL_Joystick *joystick = SDL_GetGamepadJoystick(gc); if (!joystick) { LOGW("Could not get controller joystick"); - SDL_GameControllerClose(gc); + SDL_CloseGamepad(gc); return; } struct sc_gamepad_device_event evt = { - .gamepad_id = SDL_JoystickInstanceID(joystick), + .gamepad_id = SDL_GetJoystickID(joystick), }; gp->ops->process_gamepad_added(gp, &evt); - } else if (event->type == SDL_CONTROLLERDEVICEREMOVED) { + } else if (event->type == SDL_EVENT_GAMEPAD_REMOVED) { SDL_JoystickID id = event->which; - SDL_GameController *gc = SDL_GameControllerFromInstanceID(id); + SDL_Gamepad *gc = SDL_GetGamepadFromID(id); if (gc) { - SDL_GameControllerClose(gc); + SDL_CloseGamepad(gc); } else { LOGW("Unknown gamepad device removed"); } @@ -221,7 +228,7 @@ sc_screen_otg_process_gamepad_device(struct sc_screen_otg *screen, static void sc_screen_otg_process_gamepad_axis(struct sc_screen_otg *screen, - const SDL_ControllerAxisEvent *event) { + const SDL_GamepadAxisEvent *event) { assert(screen->gamepad); struct sc_gamepad_processor *gp = &screen->gamepad->gamepad_processor; @@ -240,7 +247,7 @@ sc_screen_otg_process_gamepad_axis(struct sc_screen_otg *screen, static void sc_screen_otg_process_gamepad_button(struct sc_screen_otg *screen, - const SDL_ControllerButtonEvent *event) { + const SDL_GamepadButtonEvent *event) { assert(screen->gamepad); struct sc_gamepad_processor *gp = &screen->gamepad->gamepad_processor; @@ -265,59 +272,55 @@ sc_screen_otg_handle_event(struct sc_screen_otg *screen, SDL_Event *event) { } switch (event->type) { - case SDL_WINDOWEVENT: - switch (event->window.event) { - case SDL_WINDOWEVENT_EXPOSED: - sc_screen_otg_render(screen); - break; - } - return; - case SDL_KEYDOWN: + case SDL_EVENT_WINDOW_EXPOSED: + sc_screen_otg_render(screen); + break; + case SDL_EVENT_KEY_DOWN: if (screen->keyboard) { sc_screen_otg_process_key(screen, &event->key); } break; - case SDL_KEYUP: + case SDL_EVENT_KEY_UP: if (screen->keyboard) { sc_screen_otg_process_key(screen, &event->key); } break; - case SDL_MOUSEMOTION: + case SDL_EVENT_MOUSE_MOTION: if (screen->mouse) { sc_screen_otg_process_mouse_motion(screen, &event->motion); } break; - case SDL_MOUSEBUTTONDOWN: + case SDL_EVENT_MOUSE_BUTTON_DOWN: if (screen->mouse) { sc_screen_otg_process_mouse_button(screen, &event->button); } break; - case SDL_MOUSEBUTTONUP: + case SDL_EVENT_MOUSE_BUTTON_UP: if (screen->mouse) { sc_screen_otg_process_mouse_button(screen, &event->button); } break; - case SDL_MOUSEWHEEL: + case SDL_EVENT_MOUSE_WHEEL: if (screen->mouse) { sc_screen_otg_process_mouse_wheel(screen, &event->wheel); } break; - case SDL_CONTROLLERDEVICEADDED: - case SDL_CONTROLLERDEVICEREMOVED: + case SDL_EVENT_GAMEPAD_ADDED: + case SDL_EVENT_GAMEPAD_REMOVED: // Handle device added or removed even if paused if (screen->gamepad) { - sc_screen_otg_process_gamepad_device(screen, &event->cdevice); + sc_screen_otg_process_gamepad_device(screen, &event->gdevice); } break; - case SDL_CONTROLLERAXISMOTION: + case SDL_EVENT_GAMEPAD_AXIS_MOTION: if (screen->gamepad) { - sc_screen_otg_process_gamepad_axis(screen, &event->caxis); + sc_screen_otg_process_gamepad_axis(screen, &event->gaxis); } break; - case SDL_CONTROLLERBUTTONDOWN: - case SDL_CONTROLLERBUTTONUP: + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + case SDL_EVENT_GAMEPAD_BUTTON_UP: if (screen->gamepad) { - sc_screen_otg_process_gamepad_button(screen, &event->cbutton); + sc_screen_otg_process_gamepad_button(screen, &event->gbutton); } break; } diff --git a/app/src/usb/screen_otg.h b/app/src/usb/screen_otg.h index 08b76ae761..32d9da961d 100644 --- a/app/src/usb/screen_otg.h +++ b/app/src/usb/screen_otg.h @@ -5,7 +5,7 @@ #include #include -#include +#include #include "mouse_capture.h" #include "usb/gamepad_aoa.h" diff --git a/app/src/util/log.c b/app/src/util/log.c index 9114a25863..c583b8fb48 100644 --- a/app/src/util/log.c +++ b/app/src/util/log.c @@ -50,13 +50,13 @@ log_level_sdl_to_sc(SDL_LogPriority priority) { void sc_set_log_level(enum sc_log_level level) { SDL_LogPriority sdl_log = log_level_sc_to_sdl(level); - SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, sdl_log); - SDL_LogSetPriority(SDL_LOG_CATEGORY_CUSTOM, sdl_log); + SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, sdl_log); + SDL_SetLogPriority(SDL_LOG_CATEGORY_CUSTOM, sdl_log); } enum sc_log_level sc_get_log_level(void) { - SDL_LogPriority sdl_log = SDL_LogGetPriority(SDL_LOG_CATEGORY_APPLICATION); + SDL_LogPriority sdl_log = SDL_GetLogPriority(SDL_LOG_CATEGORY_APPLICATION); return log_level_sdl_to_sc(sdl_log); } @@ -128,7 +128,7 @@ sc_av_log_callback(void *avcl, int level, const char *fmt, va_list vl) { free(local_fmt); } -static const char *const sc_sdl_log_priority_names[SDL_NUM_LOG_PRIORITIES] = { +static const char *const sc_sdl_log_priority_names[SDL_LOG_PRIORITY_COUNT] = { [SDL_LOG_PRIORITY_VERBOSE] = "VERBOSE", [SDL_LOG_PRIORITY_DEBUG] = "DEBUG", [SDL_LOG_PRIORITY_INFO] = "INFO", @@ -144,14 +144,14 @@ sc_sdl_log_print(void *userdata, int category, SDL_LogPriority priority, (void) category; FILE *out = priority < SDL_LOG_PRIORITY_WARN ? stdout : stderr; - assert(priority < SDL_NUM_LOG_PRIORITIES); + assert(priority < SDL_LOG_PRIORITY_COUNT); const char *prio_name = sc_sdl_log_priority_names[priority]; fprintf(out, "%s: %s\n", prio_name, message); } void sc_log_configure(void) { - SDL_LogSetOutputFunction(sc_sdl_log_print, NULL); + SDL_SetLogOutputFunction(sc_sdl_log_print, NULL); // Redirect FFmpeg logs to SDL logs av_log_set_callback(sc_av_log_callback); } diff --git a/app/src/util/log.h b/app/src/util/log.h index 0d79c9a41a..02606cb464 100644 --- a/app/src/util/log.h +++ b/app/src/util/log.h @@ -3,7 +3,7 @@ #include "common.h" -#include +#include #include "options.h" diff --git a/app/src/util/thread.c b/app/src/util/thread.c index 2a5253f723..dce189bd1b 100644 --- a/app/src/util/thread.c +++ b/app/src/util/thread.c @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include "util/log.h" @@ -31,11 +31,7 @@ static SDL_ThreadPriority to_sdl_thread_priority(enum sc_thread_priority priority) { switch (priority) { case SC_THREAD_PRIORITY_TIME_CRITICAL: -#ifdef SCRCPY_SDL_HAS_THREAD_PRIORITY_TIME_CRITICAL return SDL_THREAD_PRIORITY_TIME_CRITICAL; -#else - // fall through -#endif case SC_THREAD_PRIORITY_HIGH: return SDL_THREAD_PRIORITY_HIGH; case SC_THREAD_PRIORITY_NORMAL: @@ -51,8 +47,8 @@ to_sdl_thread_priority(enum sc_thread_priority priority) { bool sc_thread_set_priority(enum sc_thread_priority priority) { SDL_ThreadPriority sdl_priority = to_sdl_thread_priority(priority); - int r = SDL_SetThreadPriority(sdl_priority); - if (r) { + bool ok = SDL_SetCurrentThreadPriority(sdl_priority); + if (!ok) { LOGD("Could not set thread priority: %s", SDL_GetError()); return false; } @@ -67,7 +63,7 @@ sc_thread_join(sc_thread *thread, int *status) { bool sc_mutex_init(sc_mutex *mutex) { - SDL_mutex *sdl_mutex = SDL_CreateMutex(); + SDL_Mutex *sdl_mutex = SDL_CreateMutex(); if (!sdl_mutex) { LOG_OOM(); return false; @@ -89,40 +85,25 @@ void sc_mutex_lock(sc_mutex *mutex) { // SDL mutexes are recursive, but we don't want to use recursive mutexes assert(!sc_mutex_held(mutex)); - int r = SDL_LockMutex(mutex->mutex); + SDL_LockMutex(mutex->mutex); #ifndef NDEBUG - if (r) { - LOGE("Could not lock mutex: %s", SDL_GetError()); - abort(); - } - atomic_store_explicit(&mutex->locker, sc_thread_get_id(), memory_order_relaxed); -#else - (void) r; #endif } void sc_mutex_unlock(sc_mutex *mutex) { -#ifndef NDEBUG assert(sc_mutex_held(mutex)); - atomic_store_explicit(&mutex->locker, 0, memory_order_relaxed); -#endif - int r = SDL_UnlockMutex(mutex->mutex); #ifndef NDEBUG - if (r) { - LOGE("Could not lock mutex: %s", SDL_GetError()); - abort(); - } -#else - (void) r; + atomic_store_explicit(&mutex->locker, 0, memory_order_relaxed); #endif + SDL_UnlockMutex(mutex->mutex); } sc_thread_id sc_thread_get_id(void) { - return SDL_ThreadID(); + return SDL_GetCurrentThreadID(); } #ifndef NDEBUG @@ -136,7 +117,7 @@ sc_mutex_held(struct sc_mutex *mutex) { bool sc_cond_init(sc_cond *cond) { - SDL_cond *sdl_cond = SDL_CreateCond(); + SDL_Condition *sdl_cond = SDL_CreateCondition(); if (!sdl_cond) { LOG_OOM(); return false; @@ -148,22 +129,15 @@ sc_cond_init(sc_cond *cond) { void sc_cond_destroy(sc_cond *cond) { - SDL_DestroyCond(cond->cond); + SDL_DestroyCondition(cond->cond); } void sc_cond_wait(sc_cond *cond, sc_mutex *mutex) { - int r = SDL_CondWait(cond->cond, mutex->mutex); + SDL_WaitCondition(cond->cond, mutex->mutex); #ifndef NDEBUG - if (r) { - LOGE("Could not wait on condition: %s", SDL_GetError()); - abort(); - } - atomic_store_explicit(&mutex->locker, sc_thread_get_id(), memory_order_relaxed); -#else - (void) r; #endif } @@ -177,44 +151,22 @@ sc_cond_timedwait(sc_cond *cond, sc_mutex *mutex, sc_tick deadline) { // Round up to the next millisecond to guarantee that the deadline is // reached when returning due to timeout uint32_t ms = SC_TICK_TO_MS(deadline - now + SC_TICK_FROM_MS(1) - 1); - int r = SDL_CondWaitTimeout(cond->cond, mutex->mutex, ms); + bool signaled = SDL_WaitConditionTimeout(cond->cond, mutex->mutex, ms); #ifndef NDEBUG - if (r < 0) { - LOGE("Could not wait on condition with timeout: %s", SDL_GetError()); - abort(); - } - atomic_store_explicit(&mutex->locker, sc_thread_get_id(), memory_order_relaxed); #endif - assert(r == 0 || r == SDL_MUTEX_TIMEDOUT); // The deadline is reached on timeout - assert(r != SDL_MUTEX_TIMEDOUT || sc_tick_now() >= deadline); - return r == 0; + assert(signaled || sc_tick_now() >= deadline); + return signaled; } void sc_cond_signal(sc_cond *cond) { - int r = SDL_CondSignal(cond->cond); -#ifndef NDEBUG - if (r) { - LOGE("Could not signal a condition: %s", SDL_GetError()); - abort(); - } -#else - (void) r; -#endif + SDL_SignalCondition(cond->cond); } void sc_cond_broadcast(sc_cond *cond) { - int r = SDL_CondBroadcast(cond->cond); -#ifndef NDEBUG - if (r) { - LOGE("Could not broadcast a condition: %s", SDL_GetError()); - abort(); - } -#else - (void) r; -#endif + SDL_BroadcastCondition(cond->cond); } diff --git a/app/src/util/thread.h b/app/src/util/thread.h index 3d54404674..13734bdcca 100644 --- a/app/src/util/thread.h +++ b/app/src/util/thread.h @@ -10,8 +10,8 @@ /* Forward declarations */ typedef struct SDL_Thread SDL_Thread; -typedef struct SDL_mutex SDL_mutex; -typedef struct SDL_cond SDL_cond; +typedef struct SDL_Mutex SDL_Mutex; +typedef struct SDL_Condition SDL_Condition; typedef int sc_thread_fn(void *); typedef unsigned sc_thread_id; @@ -29,14 +29,14 @@ enum sc_thread_priority { }; typedef struct sc_mutex { - SDL_mutex *mutex; + SDL_Mutex *mutex; #ifndef NDEBUG sc_atomic_thread_id locker; #endif } sc_mutex; typedef struct sc_cond { - SDL_cond *cond; + SDL_Condition *cond; } sc_cond; extern sc_thread_id SC_MAIN_THREAD_ID; diff --git a/app/src/util/window.c b/app/src/util/window.c new file mode 100644 index 0000000000..ae1c0ac1c7 --- /dev/null +++ b/app/src/util/window.c @@ -0,0 +1,33 @@ +#include "window.h" + +SDL_Window * +sc_create_sdl_window(const char *title, int64_t x, int64_t y, int64_t width, + int64_t height, int64_t flags) { + SDL_Window *window = NULL; + + SDL_PropertiesID props = SDL_CreateProperties(); + if (!props) { + return NULL; + } + + bool ok = + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, + title); + ok &= SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, x); + ok &= SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, y); + ok &= SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, + width); + ok &= SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, + height); + ok &= SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, + flags); + + if (!ok) { + SDL_DestroyProperties(props); + return NULL; + } + + window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); + return window; +} diff --git a/app/src/util/window.h b/app/src/util/window.h new file mode 100644 index 0000000000..c6f349e504 --- /dev/null +++ b/app/src/util/window.h @@ -0,0 +1,13 @@ +#ifndef SC_WINDOW_H +#define SC_WINDOW_H + +#include "common.h" + +#include +#include + +SDL_Window * +sc_create_sdl_window(const char *title, int64_t x, int64_t y, int64_t width, + int64_t height, int64_t flags); + +#endif diff --git a/app/src/version.c b/app/src/version.c index f861071424..9a22a7e9b8 100644 --- a/app/src/version.c +++ b/app/src/version.c @@ -10,17 +10,20 @@ #ifdef HAVE_USB # include #endif -#include +#include void scrcpy_print_version(void) { printf("\nDependencies (compiled / linked):\n"); - SDL_version sdl; - SDL_GetVersion(&sdl); + int sdl = SDL_GetVersion(); printf(" - SDL: %u.%u.%u / %u.%u.%u\n", - SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL, - (unsigned) sdl.major, (unsigned) sdl.minor, (unsigned) sdl.patch); + SDL_MAJOR_VERSION, + SDL_MINOR_VERSION, + SDL_MICRO_VERSION, + SDL_VERSIONNUM_MAJOR(sdl), + SDL_VERSIONNUM_MINOR(sdl), + SDL_VERSIONNUM_MICRO(sdl)); unsigned avcodec = avcodec_version(); printf(" - libavcodec: %u.%u.%u / %u.%u.%u\n", From e59d40ebc7507a2eee75a1ea6c350768b4f713b5 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 14 Oct 2025 00:04:38 +0200 Subject: [PATCH 07/12] Remove workaround for macOS TODO TODO refs 3031 TODO refs SDL 5340 5976 --- app/src/mouse_capture.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/app/src/mouse_capture.c b/app/src/mouse_capture.c index 86aca21543..fe5022eb70 100644 --- a/app/src/mouse_capture.c +++ b/app/src/mouse_capture.c @@ -81,24 +81,6 @@ sc_mouse_capture_handle_event(struct sc_mouse_capture *mc, void sc_mouse_capture_set_active(struct sc_mouse_capture *mc, bool capture) { -#ifdef __APPLE__ - // Workaround for SDL bug on macOS: - // - if (capture) { - float mouse_x, mouse_y; - SDL_GetGlobalMouseState(&mouse_x, &mouse_y); - - int x, y, w, h; - SDL_GetWindowPosition(mc->window, &x, &y); - SDL_GetWindowSize(mc->window, &w, &h); - - bool outside_window = mouse_x < x || mouse_x >= x + w - || mouse_y < y || mouse_y >= y + h; - if (outside_window) { - SDL_WarpMouseInWindow(mc->window, w / 2, h / 2); - } - } -#endif bool ok = SDL_SetWindowRelativeMouseMode(mc->window, capture); if (!ok) { LOGE("Could not set relative mouse mode to %s: %s", From 1c8063e0abba5341c160f6c252f7320514b9e8f0 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 13 Oct 2025 23:36:39 +0200 Subject: [PATCH 08/12] Add a wrapper for some SDL functions This provides a more convenient API (e.g., using types sc_size and sc_point), and will hide API changes made in SDL3. --- app/src/display.c | 5 ++- app/src/input_manager.c | 10 ++--- app/src/screen.c | 89 +++++++++++++++++----------------------- app/src/usb/screen_otg.c | 6 +-- app/src/util/window.c | 76 +++++++++++++++++++++++++++++++++- app/src/util/window.h | 35 +++++++++++++++- 6 files changed, 158 insertions(+), 63 deletions(-) diff --git a/app/src/display.c b/app/src/display.c index 560d5614f1..06b1704c47 100644 --- a/app/src/display.c +++ b/app/src/display.c @@ -6,6 +6,7 @@ #include #include "util/log.h" +#include "util/window.h" static bool sc_display_init_novideo_icon(struct sc_display *display, @@ -373,7 +374,7 @@ sc_display_update_texture(struct sc_display *display, const AVFrame *frame) { enum sc_display_result sc_display_render(struct sc_display *display, const SDL_Rect *geometry, enum sc_orientation orientation) { - SDL_RenderClear(display->renderer); + sc_sdl_render_clear(display->renderer); if (display->pending.flags) { bool ok = sc_display_apply_pending(display); @@ -418,6 +419,6 @@ sc_display_render(struct sc_display *display, const SDL_Rect *geometry, } } - SDL_RenderPresent(display->renderer); + sc_sdl_render_present(display->renderer); return SC_DISPLAY_RESULT_OK; } diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 183ace64a2..a8c3e76c85 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -11,6 +11,7 @@ #include "screen.h" #include "shortcut_mod.h" #include "util/log.h" +#include "util/window.h" void sc_input_manager_init(struct sc_input_manager *im, @@ -672,13 +673,12 @@ sc_input_manager_process_touch(struct sc_input_manager *im, return; } - int dw; - int dh; - SDL_GetWindowSizeInPixels(im->screen->window, &dw, &dh); + struct sc_size drawable_size = + sc_sdl_get_window_size_in_pixels(im->screen->window); // SDL touch event coordinates are normalized in the range [0; 1] - int32_t x = event->x * dw; - int32_t y = event->y * dh; + int32_t x = event->x * (int32_t) drawable_size.width; + int32_t y = event->y * (int32_t) drawable_size.height; struct sc_touch_event evt = { .position = { diff --git a/app/src/screen.c b/app/src/screen.c index 0ed5c4d19b..00114081b7 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -9,6 +9,7 @@ #include "options.h" #include "util/log.h" #include "util/window.h" +#include "util/window.h" #define DISPLAY_MARGINS 96 @@ -27,38 +28,12 @@ get_oriented_size(struct sc_size size, enum sc_orientation orientation) { return oriented_size; } -// get the window size in a struct sc_size -static struct sc_size -get_window_size(const struct sc_screen *screen) { - int width; - int height; - SDL_GetWindowSize(screen->window, &width, &height); - - struct sc_size size; - size.width = width; - size.height = height; - return size; -} - -static struct sc_point -get_window_position(const struct sc_screen *screen) { - int x; - int y; - SDL_GetWindowPosition(screen->window, &x, &y); - - struct sc_point point; - point.x = x; - point.y = y; - return point; -} - // set the window size to be applied when fullscreen is disabled -static void -set_window_size(struct sc_screen *screen, struct sc_size new_size) { +static inline void +assert_not_fullscreen(struct sc_screen *screen) { assert(!screen->fullscreen); assert(!screen->maximized); assert(!screen->minimized); - SDL_SetWindowSize(screen->window, new_size.width, new_size.height); } // get the preferred display bounds (i.e. the screen bounds with some margins) @@ -169,13 +144,10 @@ static void sc_screen_update_content_rect(struct sc_screen *screen) { assert(screen->video); - int dw; - int dh; - SDL_GetWindowSizeInPixels(screen->window, &dw, &dh); - struct sc_size content_size = screen->content_size; // The drawable size is the window size * the HiDPI scale - struct sc_size drawable_size = {dw, dh}; + struct sc_size drawable_size = + sc_sdl_get_window_size_in_pixels(screen->window); SDL_Rect *rect = &screen->rect; @@ -388,7 +360,7 @@ sc_screen_init(struct sc_screen *screen, // The window will be positioned and sized on first video frame screen->window = - sc_create_sdl_window(title, x, y, width, height, window_flags); + sc_sdl_create_window(title, x, y, width, height, window_flags); if (!screen->window) { LOGE("Could not create window: %s", SDL_GetError()); goto error_destroy_fps_counter; @@ -496,13 +468,18 @@ sc_screen_show_initial_window(struct sc_screen *screen) { ? screen->req.x : (int) SDL_WINDOWPOS_CENTERED; int y = screen->req.y != SC_WINDOW_POSITION_UNDEFINED ? screen->req.y : (int) SDL_WINDOWPOS_CENTERED; + struct sc_point position = { + .x = x, + .y = y, + }; struct sc_size window_size = get_initial_optimal_size(screen->content_size, screen->req.width, screen->req.height); - set_window_size(screen, window_size); - SDL_SetWindowPosition(screen->window, x, y); + assert_not_fullscreen(screen); + sc_sdl_set_window_size(screen->window, window_size); + sc_sdl_set_window_position(screen->window, position); if (screen->req.fullscreen) { sc_screen_toggle_fullscreen(screen); @@ -512,13 +489,13 @@ sc_screen_show_initial_window(struct sc_screen *screen) { sc_fps_counter_start(&screen->fps_counter); } - SDL_ShowWindow(screen->window); + sc_sdl_show_window(screen->window); sc_screen_update_content_rect(screen); } void sc_screen_hide_window(struct sc_screen *screen) { - SDL_HideWindow(screen->window); + sc_sdl_hide_window(screen->window); } void @@ -548,7 +525,7 @@ resize_for_content(struct sc_screen *screen, struct sc_size old_content_size, struct sc_size new_content_size) { assert(screen->video); - struct sc_size window_size = get_window_size(screen); + struct sc_size window_size = sc_sdl_get_window_size(screen->window); struct sc_size target_size = { .width = (uint32_t) window_size.width * new_content_size.width / old_content_size.width, @@ -556,7 +533,8 @@ resize_for_content(struct sc_screen *screen, struct sc_size old_content_size, / old_content_size.height, }; target_size = get_optimal_size(target_size, new_content_size, true); - set_window_size(screen, target_size); + assert_not_fullscreen(screen); + sc_sdl_set_window_size(screen->window, target_size); } static void @@ -752,8 +730,8 @@ sc_screen_resize_to_fit(struct sc_screen *screen) { return; } - struct sc_point point = get_window_position(screen); - struct sc_size window_size = get_window_size(screen); + struct sc_point point = sc_sdl_get_window_position(screen->window); + struct sc_size window_size = sc_sdl_get_window_size(screen->window); struct sc_size optimal_size = get_optimal_size(window_size, screen->content_size, false); @@ -761,11 +739,14 @@ sc_screen_resize_to_fit(struct sc_screen *screen) { // Center the window related to the device screen assert(optimal_size.width <= window_size.width); assert(optimal_size.height <= window_size.height); - uint32_t new_x = point.x + (window_size.width - optimal_size.width) / 2; - uint32_t new_y = point.y + (window_size.height - optimal_size.height) / 2; - SDL_SetWindowSize(screen->window, optimal_size.width, optimal_size.height); - SDL_SetWindowPosition(screen->window, new_x, new_y); + struct sc_point new_position = { + .x = point.x + (window_size.width - optimal_size.width) / 2, + .y = point.y + (window_size.height - optimal_size.height) / 2, + }; + + sc_sdl_set_window_size(screen->window, optimal_size); + sc_sdl_set_window_position(screen->window, new_position); LOGD("Resized to optimal size: %ux%u", optimal_size.width, optimal_size.height); } @@ -779,12 +760,12 @@ sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) { } if (screen->maximized) { - SDL_RestoreWindow(screen->window); + sc_sdl_restore_window(screen->window); screen->maximized = false; } struct sc_size content_size = screen->content_size; - SDL_SetWindowSize(screen->window, content_size.width, content_size.height); + sc_sdl_set_window_size(screen->window, content_size); LOGD("Resized to pixel-perfect: %ux%u", content_size.width, content_size.height); } @@ -914,9 +895,15 @@ sc_screen_convert_window_to_frame_coords(struct sc_screen *screen, void sc_screen_hidpi_scale_coords(struct sc_screen *screen, int32_t *x, int32_t *y) { // take the HiDPI scaling (dw/ww and dh/wh) into account - int ww, wh, dw, dh; - SDL_GetWindowSize(screen->window, &ww, &wh); - SDL_GetWindowSizeInPixels(screen->window, &dw, &dh); + + struct sc_size window_size = sc_sdl_get_window_size(screen->window); + int64_t ww = window_size.width; + int64_t wh = window_size.height; + + struct sc_size drawable_size = + sc_sdl_get_window_size_in_pixels(screen->window); + int64_t dw = drawable_size.width; + int64_t dh = drawable_size.height; // scale for HiDPI (64 bits for intermediate multiplications) *x = (int64_t) *x * dw / ww; diff --git a/app/src/usb/screen_otg.c b/app/src/usb/screen_otg.c index ebdc42a266..a1f9465734 100644 --- a/app/src/usb/screen_otg.c +++ b/app/src/usb/screen_otg.c @@ -11,7 +11,7 @@ static void sc_screen_otg_render(struct sc_screen_otg *screen) { - SDL_RenderClear(screen->renderer); + sc_sdl_render_clear(screen->renderer); if (screen->texture) { bool ok = SDL_RenderTexture(screen->renderer, screen->texture, NULL, NULL); @@ -19,7 +19,7 @@ sc_screen_otg_render(struct sc_screen_otg *screen) { LOGW("Could not render texture: %s", SDL_GetError()); } } - SDL_RenderPresent(screen->renderer); + sc_sdl_render_present(screen->renderer); } bool @@ -48,7 +48,7 @@ sc_screen_otg_init(struct sc_screen_otg *screen, } screen->window = - sc_create_sdl_window(title, x, y, width, height, window_flags); + sc_sdl_create_window(title, x, y, width, height, window_flags); if (!screen->window) { LOGE("Could not create window: %s", SDL_GetError()); return false; diff --git a/app/src/util/window.c b/app/src/util/window.c index ae1c0ac1c7..483294c338 100644 --- a/app/src/util/window.c +++ b/app/src/util/window.c @@ -1,7 +1,7 @@ #include "window.h" SDL_Window * -sc_create_sdl_window(const char *title, int64_t x, int64_t y, int64_t width, +sc_sdl_create_window(const char *title, int64_t x, int64_t y, int64_t width, int64_t height, int64_t flags) { SDL_Window *window = NULL; @@ -31,3 +31,77 @@ sc_create_sdl_window(const char *title, int64_t x, int64_t y, int64_t width, SDL_DestroyProperties(props); return window; } + +struct sc_size +sc_sdl_get_window_size(SDL_Window *window) { + int width; + int height; + SDL_GetWindowSize(window, &width, &height); + + struct sc_size size = { + .width = width, + .height = height, + }; + return size; +} + +struct sc_size +sc_sdl_get_window_size_in_pixels(SDL_Window *window) { + int width; + int height; + SDL_GetWindowSizeInPixels(window, &width, &height); + + struct sc_size size = { + .width = width, + .height = height, + }; + return size; +} + +void +sc_sdl_set_window_size(SDL_Window *window, struct sc_size size) { + SDL_SetWindowSize(window, size.width, size.height); +} + +struct sc_point +sc_sdl_get_window_position(SDL_Window *window) { + int x; + int y; + SDL_GetWindowPosition(window, &x, &y); + + struct sc_point point = { + .x = x, + .y = y, + }; + return point; +} + +void +sc_sdl_set_window_position(SDL_Window *window, struct sc_point point) { + SDL_SetWindowPosition(window, point.x, point.y); +} + +void +sc_sdl_show_window(SDL_Window *window) { + SDL_ShowWindow(window); +} + +void +sc_sdl_hide_window(SDL_Window *window) { + SDL_HideWindow(window); +} + +void +sc_sdl_restore_window(SDL_Window *window) { + SDL_RestoreWindow(window); +} + +bool +sc_sdl_render_clear(SDL_Renderer *renderer) { + return SDL_RenderClear(renderer); +} + +void +sc_sdl_render_present(SDL_Renderer *renderer) { + SDL_RenderPresent(renderer); +} diff --git a/app/src/util/window.h b/app/src/util/window.h index c6f349e504..6e24d209aa 100644 --- a/app/src/util/window.h +++ b/app/src/util/window.h @@ -4,10 +4,43 @@ #include "common.h" #include +#include #include +#include "coords.h" + SDL_Window * -sc_create_sdl_window(const char *title, int64_t x, int64_t y, int64_t width, +sc_sdl_create_window(const char *title, int64_t x, int64_t y, int64_t width, int64_t height, int64_t flags); +struct sc_size +sc_sdl_get_window_size(SDL_Window *window); + +void +sc_sdl_set_window_size(SDL_Window *window, struct sc_size size); + +struct sc_size +sc_sdl_get_window_size_in_pixels(SDL_Window *window); + +struct sc_point +sc_sdl_get_window_position(SDL_Window *window); + +void +sc_sdl_set_window_position(SDL_Window *window, struct sc_point point); + +void +sc_sdl_show_window(SDL_Window *window); + +void +sc_sdl_hide_window(SDL_Window *window); + +void +sc_sdl_restore_window(SDL_Window *window); + +bool +sc_sdl_render_clear(SDL_Renderer *renderer); + +void +sc_sdl_render_present(SDL_Renderer *renderer); + #endif From a9a8fb190cb8dd9b2db1021c9bf0fbe3cf2a78f4 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 14 Oct 2025 00:25:16 +0200 Subject: [PATCH 09/12] Check programming errors reported by SDL3 TODO ref SDL issue 14223 --- app/src/util/window.c | 71 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/app/src/util/window.c b/app/src/util/window.c index 483294c338..3ca3cd5009 100644 --- a/app/src/util/window.c +++ b/app/src/util/window.c @@ -1,5 +1,10 @@ #include "window.h" +#include +#include + +#include "util/log.h" + SDL_Window * sc_sdl_create_window(const char *title, int64_t x, int64_t y, int64_t width, int64_t height, int64_t flags) { @@ -36,7 +41,13 @@ struct sc_size sc_sdl_get_window_size(SDL_Window *window) { int width; int height; - SDL_GetWindowSize(window, &width, &height); + bool ok = SDL_GetWindowSize(window, &width, &height); + if (!ok) { + LOGE("Could not get window size: %s", SDL_GetError()); + LOGE("Please report the error"); + // fatal error + abort(); + } struct sc_size size = { .width = width, @@ -49,7 +60,13 @@ struct sc_size sc_sdl_get_window_size_in_pixels(SDL_Window *window) { int width; int height; - SDL_GetWindowSizeInPixels(window, &width, &height); + bool ok = SDL_GetWindowSizeInPixels(window, &width, &height); + if (!ok) { + LOGE("Could not get window size: %s", SDL_GetError()); + LOGE("Please report the error"); + // fatal error + abort(); + } struct sc_size size = { .width = width, @@ -60,14 +77,24 @@ sc_sdl_get_window_size_in_pixels(SDL_Window *window) { void sc_sdl_set_window_size(SDL_Window *window, struct sc_size size) { - SDL_SetWindowSize(window, size.width, size.height); + bool ok = SDL_SetWindowSize(window, size.width, size.height); + if (!ok) { + LOGE("Could not set window size: %s", SDL_GetError()); + assert(!"unexpected"); + } } struct sc_point sc_sdl_get_window_position(SDL_Window *window) { int x; int y; - SDL_GetWindowPosition(window, &x, &y); + bool ok = SDL_GetWindowPosition(window, &x, &y); + if (!ok) { + LOGE("Could not get window position: %s", SDL_GetError()); + LOGE("Please report the error"); + // fatal error + abort(); + } struct sc_point point = { .x = x, @@ -78,30 +105,54 @@ sc_sdl_get_window_position(SDL_Window *window) { void sc_sdl_set_window_position(SDL_Window *window, struct sc_point point) { - SDL_SetWindowPosition(window, point.x, point.y); + bool ok = SDL_SetWindowPosition(window, point.x, point.y); + if (!ok) { + LOGE("Could not set window position: %s", SDL_GetError()); + assert(!"unexpected"); + } } void sc_sdl_show_window(SDL_Window *window) { - SDL_ShowWindow(window); + bool ok = SDL_ShowWindow(window); + if (!ok) { + LOGE("Could not show window: %s", SDL_GetError()); + assert(!"unexpected"); + } } void sc_sdl_hide_window(SDL_Window *window) { - SDL_HideWindow(window); + bool ok = SDL_HideWindow(window); + if (!ok) { + LOGE("Could not hide window: %s", SDL_GetError()); + assert(!"unexpected"); + } } void sc_sdl_restore_window(SDL_Window *window) { - SDL_RestoreWindow(window); + bool ok = SDL_RestoreWindow(window); + if (!ok) { + LOGE("Could not restore window: %s", SDL_GetError()); + assert(!"unexpected"); + } } bool sc_sdl_render_clear(SDL_Renderer *renderer) { - return SDL_RenderClear(renderer); + bool ok = SDL_RenderClear(renderer); + if (!ok) { + LOGW("Could not clear rendering: %s", SDL_GetError()); + } + return ok; } void sc_sdl_render_present(SDL_Renderer *renderer) { - SDL_RenderPresent(renderer); + bool ok = SDL_RenderPresent(renderer); + if (!ok) { + LOGE("Could not render: %s", SDL_GetError()); + assert(!"unexpected"); + } } From 2e9b8c00090351232501a55c70a1f99db34d2534 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 11 Jul 2025 09:42:20 +0200 Subject: [PATCH 10/12] Upgrade SDL build script for SDL3 --- app/deps/sdl.sh | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/app/deps/sdl.sh b/app/deps/sdl.sh index e04deb0d63..48f5c4ae8b 100755 --- a/app/deps/sdl.sh +++ b/app/deps/sdl.sh @@ -5,10 +5,10 @@ cd "$DEPS_DIR" . common process_args "$@" -VERSION=2.32.8 +VERSION=3.2.18 FILENAME=SDL-$VERSION.tar.gz PROJECT_DIR=SDL-release-$VERSION -SHA256SUM=dd35e05644ae527848d02433bec24dd0ea65db59faecf1a0e5d1880c533dac2c +SHA256SUM=51539fa13e546bc50c632beed3f34257de2baa38a4c642048de56377903b4265 cd "$SOURCES_DIR" @@ -35,45 +35,48 @@ else cd "$DIRNAME" conf=( - --prefix="$INSTALL_DIR/$DIRNAME" + -DCMAKE_INSTALL_PREFIX="$INSTALL_DIR/$DIRNAME" ) if [[ "$HOST" == linux ]] then conf+=( - --enable-video-wayland - --enable-video-x11 + -DSDL_WAYLAND=ON + -DSDL_X11=ON ) fi if [[ "$LINK_TYPE" == static ]] then conf+=( - --enable-static - --disable-shared + -DBUILD_SHARED_LIBS=OFF ) else conf+=( - --disable-static - --enable-shared + -DBUILD_SHARED_LIBS=ON ) fi if [[ "$BUILD_TYPE" == cross ]] then + if [[ "$HOST" = win32 ]] + then + TOOLCHAIN_FILENAME="cmake-toolchain-mingw64-i686.cmake" + elif [[ "$HOST" = win64 ]] + then + TOOLCHAIN_FILENAME="cmake-toolchain-mingw64-x86_64.cmake" + else + echo "Unsupported cross-build to host: $HOST" >&2 + exit 1 + fi + conf+=( - --host="$HOST_TRIPLET" + -DCMAKE_TOOLCHAIN_FILE="$SOURCES_DIR/$PROJECT_DIR/build-scripts/$TOOLCHAIN_FILENAME" ) fi - "$SOURCES_DIR/$PROJECT_DIR"/configure "${conf[@]}" + cmake "$SOURCES_DIR/$PROJECT_DIR" "${conf[@]}" fi -make -j -# There is no "make install-strip" -make install -# Strip manually -if [[ "$LINK_TYPE" == shared && "$HOST" == win* ]] -then - ${HOST_TRIPLET}-strip "$INSTALL_DIR/$DIRNAME/bin/SDL2.dll" -fi +cmake --build . +cmake --install . From 9ed71944ac495837695a054dab9a565c47a1efce Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 11 Jul 2025 19:33:26 +0200 Subject: [PATCH 11/12] Build SDL3 for test step on Github Actions The latest Ubuntu does not provide the SDL3 package yet. --- .github/workflows/release.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 27e3abf243..65950d2379 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -80,8 +80,16 @@ jobs: libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \ libv4l-dev + # SDL3 is not available in Ubuntu yet + - name: Install SDL3 + run: | + app/deps/sdl.sh linux native shared + - name: Test - run: release/test_client.sh + run: | + export PKG_CONFIG_PATH="$PWD"/app/deps/work/install/linux-native-shared/lib/pkgconfig + export LD_LIBRARY_PATH="$PWD"/app/deps/work/install/linux-native-shared/lib + release/test_client.sh build-linux-x86_64: runs-on: ubuntu-22.04 From b96b81e031d437960b48ae74308050e924aca01b Mon Sep 17 00:00:00 2001 From: dddddjent <1306043330a@gmail.com> Date: Mon, 15 Sep 2025 11:13:06 -0400 Subject: [PATCH 12/12] Fix SDL colorspace matching AVCOL_SPC_RGB Use BT.709 color space for AVCOL_SPC_RGB. Refs #1868 comment Signed-off-by: Romain Vimont --- app/src/display.c | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/display.c b/app/src/display.c index 06b1704c47..8c4bc8dd69 100644 --- a/app/src/display.c +++ b/app/src/display.c @@ -135,6 +135,7 @@ sc_display_to_sdl_color_space(enum AVColorSpace color_space, switch (color_space) { case AVCOL_SPC_BT709: + case AVCOL_SPC_RGB: return full_range ? SDL_COLORSPACE_BT709_FULL : SDL_COLORSPACE_BT709_LIMITED; case AVCOL_SPC_BT470BG: