Compare commits

...

8 Commits
dev ... sdl3.41

Author SHA1 Message Date
dddddjent
a101c6dda6 Fix SDL colorspace matching AVCOL_SPC_RGB
Use BT.709 color space for AVCOL_SPC_RGB.

Refs #1868 comment <https://github.com/Genymobile/scrcpy/issues/1868#issuecomment-3293917796>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2026-01-06 16:22:50 +01:00
Romain Vimont
07a45c5e40 Upgrade SDL3 dependency list for Github Actions
Explicitly install required packages listed here:
<https://wiki.libsdl.org/SDL3/README-linux#build-dependencies>

Refs #6216 comment <https://github.com/Genymobile/scrcpy/pull/6216#issuecomment-3703680127>
PR #6216 <https://github.com/Genymobile/scrcpy/pull/6216>
2026-01-06 16:22:50 +01:00
Romain Vimont
f5ae59df82 Build SDL3 for test step on Github Actions
The Ubuntu version used to build scrcpy does not provide an SDL3
package.

PR #6216 <https://github.com/Genymobile/scrcpy/pull/6216>
2026-01-06 16:22:50 +01:00
Romain Vimont
2f20c793a2 Upgrade SDL build script for SDL3
PR #6216 <https://github.com/Genymobile/scrcpy/pull/6216>
2026-01-06 16:22:50 +01:00
Romain Vimont
ad9d0713c1 Remove obsolete workaround for macOS
Refs #3031 <https://github.com/Genymobile/scrcpy/pull/3031>
Refs SDL/#5340 <https://github.com/libsdl-org/SDL/issues/5340>
Refs SDL/#5976 <https://github.com/libsdl-org/SDL/issues/5976>
PR #6216 <https://github.com/Genymobile/scrcpy/pull/6216>
2026-01-06 16:22:50 +01:00
Romain Vimont
a66f3da2b4 Fix asynchronous SDL window state handling
The SDL3 migration guide [1] says:

> The following window operations are now considered to be asynchronous
requests and should not be assumed to succeed unless a corresponding
event has been received […]

Remove the boolean fields used to track the window state, as they were
fundamentally inconsistent with SDL's internal state. Use
SDL_GetWindowFlags() explicitly instead.

[1]: <https://wiki.libsdl.org/SDL3/README-migration>

Refs #6216 comment <https://github.com/Genymobile/scrcpy/pull/6216#discussion_r2651810005>
PR #6216 <https://github.com/Genymobile/scrcpy/pull/6216>
2026-01-06 16:22:50 +01:00
Romain Vimont
69f467e83c Disable "resize to pixel-perfect" when maximized
This improves consistency and will simplify further refactors.

PR #6216 <https://github.com/Genymobile/scrcpy/pull/6216>
2026-01-06 16:22:50 +01:00
Romain Vimont
dc86c55672 Check programming errors reported by SDL3
Refs #6216 comment <https://github.com/Genymobile/scrcpy/pull/6216#issuecomment-3399353701>
Refs SDL/#14223 <https://github.com/libsdl-org/SDL/issues/14223>>
PR #6216 <https://github.com/Genymobile/scrcpy/pull/6216>
2026-01-06 16:22:50 +01:00
8 changed files with 137 additions and 112 deletions

View File

@@ -72,16 +72,30 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
# Same as build-linux-x86_64
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \
libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \
libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \
libv4l-dev
sudo apt install -y meson ninja-build nasm ffmpeg libavcodec-dev \
libavdevice-dev libavformat-dev libavutil-dev libswresample-dev \
libusb-1.0-0 libusb-1.0-0-dev libv4l-dev \
libasound2-dev libpulse-dev \
libaudio-dev libfribidi-dev libjack-dev libsndio-dev libx11-dev libxext-dev \
libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev \
libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \
libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libthai-dev \
libpipewire-0.3-dev libwayland-dev libdecor-0-dev liburing-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
@@ -98,13 +112,19 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
# https://wiki.libsdl.org/SDL3/README-linux#build-dependencies
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \
libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \
libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \
libv4l-dev
sudo apt install -y meson ninja-build nasm ffmpeg libavcodec-dev \
libavdevice-dev libavformat-dev libavutil-dev libswresample-dev \
libusb-1.0-0 libusb-1.0-0-dev libv4l-dev \
libasound2-dev libpulse-dev \
libaudio-dev libfribidi-dev libjack-dev libsndio-dev libx11-dev libxext-dev \
libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev \
libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \
libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libthai-dev \
libpipewire-0.3-dev libwayland-dev libdecor-0-dev liburing-dev
- name: Build
run: release/build_linux.sh x86_64

View File

@@ -3,9 +3,9 @@ set -ex
. $(dirname ${BASH_SOURCE[0]})/_init
process_args "$@"
VERSION=2.32.8
VERSION=3.4.0
URL="https://github.com/libsdl-org/SDL/archive/refs/tags/release-$VERSION.tar.gz"
SHA256SUM=dd35e05644ae527848d02433bec24dd0ea65db59faecf1a0e5d1880c533dac2c
SHA256SUM=9614b9696abc4597ffce6b888829dc6537ae500423474c342ac4a67222c5654c
PROJECT_DIR="sdl-$VERSION"
FILENAME="$PROJECT_DIR.tar.gz"
@@ -36,45 +36,49 @@ else
cd "$DIRNAME"
conf=(
--prefix="$INSTALL_DIR/$DIRNAME"
-DCMAKE_INSTALL_PREFIX="$INSTALL_DIR/$DIRNAME"
-DSDL_TESTS=OFF
)
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 .

View File

@@ -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:

View File

@@ -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:
// <https://github.com/libsdl-org/SDL/issues/5340>
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",

View File

@@ -27,12 +27,11 @@ get_oriented_size(struct sc_size size, enum sc_orientation orientation) {
return oriented_size;
}
static inline void
assert_not_fullscreen(struct sc_screen *screen) {
(void) screen;
assert(!screen->fullscreen);
assert(!screen->maximized);
assert(!screen->minimized);
static inline bool
is_windowed(struct sc_screen *screen) {
return !(SDL_GetWindowFlags(screen->window) & (SDL_WINDOW_FULLSCREEN
| SDL_WINDOW_MINIMIZED
| SDL_WINDOW_MAXIMIZED));
}
// get the preferred display bounds (i.e. the screen bounds with some margins)
@@ -296,9 +295,6 @@ sc_screen_init(struct sc_screen *screen,
screen->resize_pending = false;
screen->has_frame = false;
screen->has_video_window = false;
screen->fullscreen = false;
screen->maximized = false;
screen->minimized = false;
screen->paused = false;
screen->resume_frame = NULL;
screen->orientation = SC_ORIENTATION_0;
@@ -481,7 +477,7 @@ sc_screen_show_initial_window(struct sc_screen *screen) {
get_initial_optimal_size(screen->content_size, screen->req.width,
screen->req.height);
assert_not_fullscreen(screen);
assert(is_windowed(screen));
sc_sdl_set_window_size(screen->window, window_size);
sc_sdl_set_window_position(screen->window, position);
@@ -537,7 +533,7 @@ 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);
assert_not_fullscreen(screen);
assert(is_windowed(screen));
sc_sdl_set_window_size(screen->window, target_size);
}
@@ -545,7 +541,7 @@ static void
set_content_size(struct sc_screen *screen, struct sc_size new_content_size) {
assert(screen->video);
if (!screen->fullscreen && !screen->maximized && !screen->minimized) {
if (is_windowed(screen)) {
resize_for_content(screen, screen->content_size, new_content_size);
} else if (!screen->resize_pending) {
// Store the windowed size to be able to compute the optimal size once
@@ -561,9 +557,7 @@ static void
apply_pending_resize(struct sc_screen *screen) {
assert(screen->video);
assert(!screen->fullscreen);
assert(!screen->maximized);
assert(!screen->minimized);
assert(is_windowed(screen));
if (screen->resize_pending) {
resize_for_content(screen, screen->windowed_content_size,
screen->content_size);
@@ -711,26 +705,23 @@ void
sc_screen_toggle_fullscreen(struct sc_screen *screen) {
assert(screen->video);
bool ok = SDL_SetWindowFullscreen(screen->window, !screen->fullscreen);
bool req_fullscreen =
!(SDL_GetWindowFlags(screen->window) & SDL_WINDOW_FULLSCREEN);
bool ok = SDL_SetWindowFullscreen(screen->window, req_fullscreen);
if (!ok) {
LOGW("Could not switch fullscreen mode: %s", SDL_GetError());
return;
}
screen->fullscreen = !screen->fullscreen;
if (!screen->fullscreen && !screen->maximized && !screen->minimized) {
apply_pending_resize(screen);
}
LOGD("Switched to %s mode", screen->fullscreen ? "fullscreen" : "windowed");
sc_screen_render(screen, true);
LOGD("Requested %s mode", req_fullscreen ? "fullscreen" : "windowed");
}
void
sc_screen_resize_to_fit(struct sc_screen *screen) {
assert(screen->video);
if (screen->fullscreen || screen->maximized || screen->minimized) {
if (!is_windowed(screen)) {
return;
}
@@ -759,15 +750,10 @@ void
sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) {
assert(screen->video);
if (screen->fullscreen || screen->minimized) {
if (!is_windowed(screen)) {
return;
}
if (screen->maximized) {
sc_sdl_restore_window(screen->window);
screen->maximized = false;
}
struct sc_size content_size = screen->content_size;
sc_sdl_set_window_size(screen->window, content_size);
LOGD("Resized to pixel-perfect: %ux%u", content_size.width,
@@ -799,24 +785,20 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
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;
if (screen->has_video_window && is_windowed(screen)) {
apply_pending_resize(screen);
sc_screen_render(screen, true);
}
screen->maximized = false;
screen->minimized = false;
if (screen->has_video_window) {
return true;
case SDL_EVENT_WINDOW_ENTER_FULLSCREEN:
LOGD("Switched to fullscreen mode");
assert(screen->has_video_window);
return true;
case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN:
LOGD("Switched to windowed mode");
assert(screen->has_video_window);
if (is_windowed(screen)) {
apply_pending_resize(screen);
sc_screen_render(screen, true);
}

View File

@@ -62,9 +62,6 @@ struct sc_screen {
struct SDL_Rect rect;
bool has_frame;
bool has_video_window;
bool fullscreen;
bool maximized;
bool minimized;
AVFrame *frame;

View File

@@ -1,5 +1,10 @@
#include "sdl.h"
#include <assert.h>
#include <stdlib.h>
#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,45 @@ 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);
}
void
sc_sdl_restore_window(SDL_Window *window) {
SDL_RestoreWindow(window);
bool ok = SDL_HideWindow(window);
if (!ok) {
LOGE("Could not hide 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");
}
}

View File

@@ -34,9 +34,6 @@ 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);