Compare commits

...

7 Commits

Author SHA1 Message Date
chengjiansu
bad2183087 Fix orientation error message
When running `scrcpy --capture-orientation=@100`, the error shown is:

> Unsupported orientation: @100 (expected 0, 90, 180, 270, flip0, flip90, flip180 or flip270)

We should tell the user that the error is 100, not @100.

Moreover, the function parse_orientation() should not access the
`optarg` global variable.

PR #6695 <https://github.com/Genymobile/scrcpy/pull/6695>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2026-03-10 20:14:23 +01:00
Romain Vimont
e17a885f32 Draw a dark square when no texture is rendered
Draw a dark 10x10 square in the top-right corner to distinguish a black
frame from the absence of a frame (useful for debugging).

PR #6694 <https://github.com/Genymobile/scrcpy/pull/6694>
2026-03-10 20:11:04 +01:00
Romain Vimont
691a63761c Open the scrcpy window earlier
Previously, the window was opened only once the first frame was
available. Instead, open it as soon as the frame dimensions are known.

This is especially useful when the encoder does not produce an initial
frame while the phone is turned off (e.g., with --no-power-on).

Fixes #6546 <https://github.com/Genymobile/scrcpy/issues/6546>
PR #6694 <https://github.com/Genymobile/scrcpy/pull/6694>
2026-03-10 20:11:04 +01:00
Romain Vimont
57af6b9209 Track window shown state
Use a flag to indicate whether the window is currently shown.

This replaces the old has_video_window flag, which was true only when
the window was shown and video was enabled.

This will simplify performing actions only when the window is currently
shown.

PR #6694 <https://github.com/Genymobile/scrcpy/pull/6694>
2026-03-10 20:11:04 +01:00
Romain Vimont
0597c5eb8d Remove unnecessary void cast
The ctx variable is used.

PR #6694 <https://github.com/Genymobile/scrcpy/pull/6694>
2026-03-10 20:11:04 +01:00
Romain Vimont
10e0c3226c Fix typo in documentation
Refs #6667 comment <https://github.com/Genymobile/scrcpy/pull/6667#pullrequestreview-3848495119>

Suggested-by: anotheruserofgithub
2026-02-24 16:01:00 +01:00
Romain Vimont
323549e82f Prevent build from falling back to system libs
Ensure that if a file or function is not found, the build does not
attempt to use system libraries. Falling back could result in using
libraries that are incompatible due to wrong versions or features.

PR #6671 <https://github.com/Genymobile/scrcpy/pull/6671>
2026-02-21 12:24:41 +01:00
8 changed files with 66 additions and 45 deletions

View File

@@ -1729,7 +1729,7 @@ parse_orientation(const char *s, enum sc_orientation *orientation) {
return true;
}
LOGE("Unsupported orientation: %s (expected 0, 90, 180, 270, flip0, "
"flip90, flip180 or flip270)", optarg);
"flip90, flip180 or flip270)", s);
return false;
}

View File

@@ -9,6 +9,7 @@
enum {
SC_EVENT_NEW_FRAME = SDL_EVENT_USER,
SC_EVENT_OPEN_WINDOW,
SC_EVENT_RUN_ON_MAIN_THREAD,
SC_EVENT_DEVICE_DISCONNECTED,
SC_EVENT_SERVER_CONNECTION_FAILED,

View File

@@ -198,19 +198,30 @@ sc_screen_update_content_rect(struct sc_screen *screen) {
// changed, so that the content rectangle is recomputed
static void
sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
assert(!screen->video || screen->has_video_window);
assert(screen->window_shown);
if (update_content_rect) {
sc_screen_update_content_rect(screen);
}
SDL_Renderer *renderer = screen->renderer;
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
sc_sdl_render_clear(renderer);
bool ok = false;
SDL_Texture *texture = screen->tex.texture;
if (!texture) {
LOGW("No texture to render");
// Draw a dark 10x10 square in the top-right corner to distinguish a
// black frame from the absence of a frame
struct sc_size render_size = sc_sdl_get_render_output_size(renderer);
SDL_SetRenderDrawColor(renderer, 0, 0, 0x33, 0xff);
SDL_FRect rect = {
.x = render_size.width - 20,
.y = 10,
.w = 10,
.h = 10,
};
SDL_RenderFillRect(renderer, &rect);
goto end;
}
@@ -280,11 +291,8 @@ sc_screen_frame_sink_open(struct sc_frame_sink *sink,
const AVCodecContext *ctx,
const struct sc_stream_session *session) {
assert(ctx->pix_fmt == AV_PIX_FMT_YUV420P);
(void) ctx;
(void) session;
struct sc_screen *screen = DOWNCAST(sink);
(void) screen;
if (ctx->width <= 0 || ctx->width > 0xFFFF
|| ctx->height <= 0 || ctx->height > 0xFFFF) {
@@ -292,6 +300,19 @@ sc_screen_frame_sink_open(struct sc_frame_sink *sink,
return false;
}
// content_size can be written from this thread, because it is never read
// from the main thread before handling SC_EVENT_OPEN_WINDOW (which acts as
// a synchronization point) when video is enabled
screen->frame_size.width = session->video.width;
screen->frame_size.height = session->video.height;
screen->content_size = get_oriented_size(screen->frame_size,
screen->orientation);
bool ok = sc_push_event(SC_EVENT_OPEN_WINDOW);
if (!ok) {
return false;
}
#ifndef NDEBUG
screen->open = true;
#endif
@@ -341,8 +362,7 @@ bool
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->window_shown = false;
screen->paused = false;
screen->resume_frame = NULL;
screen->orientation = SC_ORIENTATION_0;
@@ -533,6 +553,7 @@ sc_screen_init(struct sc_screen *screen,
if (!screen->video) {
// Show the window immediately
screen->window_shown = true;
sc_sdl_show_window(screen->window);
if (sc_screen_is_relative_mode(screen)) {
@@ -589,6 +610,7 @@ sc_screen_show_initial_window(struct sc_screen *screen) {
sc_fps_counter_start(&screen->fps_counter);
}
screen->window_shown = true;
sc_sdl_show_window(screen->window);
sc_screen_update_content_rect(screen);
}
@@ -596,6 +618,7 @@ sc_screen_show_initial_window(struct sc_screen *screen) {
void
sc_screen_hide_window(struct sc_screen *screen) {
sc_sdl_hide_window(screen->window);
screen->window_shown = false;
}
void
@@ -692,14 +715,14 @@ sc_screen_set_orientation(struct sc_screen *screen,
static bool
sc_screen_apply_frame(struct sc_screen *screen) {
assert(screen->video);
assert(screen->window_shown);
sc_fps_counter_add_rendered_frame(&screen->fps_counter);
AVFrame *frame = screen->frame;
struct sc_size new_frame_size = {frame->width, frame->height};
if (!screen->has_frame
|| screen->frame_size.width != new_frame_size.width
if (screen->frame_size.width != new_frame_size.width
|| screen->frame_size.height != new_frame_size.height) {
// frame dimension changed
@@ -707,14 +730,8 @@ sc_screen_apply_frame(struct sc_screen *screen) {
struct sc_size new_content_size =
get_oriented_size(new_frame_size, screen->orientation);
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;
}
set_content_size(screen, new_content_size);
sc_screen_update_content_rect(screen);
}
bool ok = sc_texture_set_from_frame(&screen->tex, frame);
@@ -722,18 +739,6 @@ sc_screen_apply_frame(struct sc_screen *screen) {
return false;
}
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);
if (sc_screen_is_relative_mode(screen)) {
// Capture mouse on start
sc_mouse_capture_set_active(&screen->mc, true);
}
}
sc_screen_render(screen, false);
return true;
}
@@ -854,9 +859,17 @@ sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) {
void
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_OPEN_WINDOW:
sc_screen_show_initial_window(screen);
if (sc_screen_is_relative_mode(screen)) {
// Capture mouse on start
sc_mouse_capture_set_active(&screen->mc, true);
}
sc_screen_render(screen, false);
return;
case SC_EVENT_NEW_FRAME: {
bool ok = sc_screen_update_frame(screen);
if (!ok) {
@@ -865,28 +878,27 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
return;
}
case SDL_EVENT_WINDOW_EXPOSED:
if (!screen->video || screen->has_video_window) {
sc_screen_render(screen, true);
}
sc_screen_render(screen, true);
return;
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
if (screen->has_video_window) {
// This event can be triggered before the window is shown
if (screen->window_shown) {
sc_screen_render(screen, true);
}
return;
case SDL_EVENT_WINDOW_RESTORED:
if (screen->has_video_window && is_windowed(screen)) {
if (screen->video && is_windowed(screen)) {
apply_pending_resize(screen);
sc_screen_render(screen, true);
}
return;
case SDL_EVENT_WINDOW_ENTER_FULLSCREEN:
LOGD("Switched to fullscreen mode");
assert(screen->has_video_window);
assert(screen->video);
return;
case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN:
LOGD("Switched to windowed mode");
assert(screen->has_video_window);
assert(screen->video);
if (is_windowed(screen)) {
apply_pending_resize(screen);
sc_screen_render(screen, true);

View File

@@ -70,8 +70,7 @@ struct sc_screen {
enum sc_orientation orientation;
// rectangle of the content (excluding black borders)
struct SDL_FRect rect;
bool has_frame;
bool has_video_window;
bool window_shown;
AVFrame *frame;

View File

@@ -93,4 +93,4 @@ Then just double-click on that file to run it.
To start scrcpy without opening a terminal, double-click `scrcpy-noconsole.vbs`
(note that errors won't be shown). To pass arguments, edit (a copy of)
`scrcpy-noconsole.vbs` add and the desired arguments.
`scrcpy-noconsole.vbs` and add the desired arguments.

View File

@@ -22,9 +22,12 @@ app/deps/libusb.sh linux native static
DEPS_INSTALL_DIR="$PWD/app/deps/work/install/linux-native-static"
ADB_INSTALL_DIR="$PWD/app/deps/work/install/adb-linux"
# Never fall back to system libs
unset PKG_CONFIG_PATH
export PKG_CONFIG_LIBDIR="$DEPS_INSTALL_DIR/lib/pkgconfig"
rm -rf "$LINUX_BUILD_DIR"
meson setup "$LINUX_BUILD_DIR" \
--pkg-config-path="$DEPS_INSTALL_DIR/lib/pkgconfig" \
-Dc_args="-I$DEPS_INSTALL_DIR/include" \
-Dc_link_args="-L$DEPS_INSTALL_DIR/lib" \
--buildtype=release \

View File

@@ -22,9 +22,12 @@ app/deps/libusb.sh macos native static
DEPS_INSTALL_DIR="$PWD/app/deps/work/install/macos-native-static"
ADB_INSTALL_DIR="$PWD/app/deps/work/install/adb-macos"
# Never fall back to system libs
unset PKG_CONFIG_PATH
export PKG_CONFIG_LIBDIR="$DEPS_INSTALL_DIR/lib/pkgconfig"
rm -rf "$MACOS_BUILD_DIR"
meson setup "$MACOS_BUILD_DIR" \
--pkg-config-path="$DEPS_INSTALL_DIR/lib/pkgconfig" \
-Dc_args="-I$DEPS_INSTALL_DIR/include" \
-Dc_link_args="-L$DEPS_INSTALL_DIR/lib" \
--buildtype=release \

View File

@@ -29,9 +29,12 @@ app/deps/libusb.sh $WINXX cross shared
DEPS_INSTALL_DIR="$PWD/app/deps/work/install/$WINXX-cross-shared"
ADB_INSTALL_DIR="$PWD/app/deps/work/install/adb-windows"
# Never fall back to system libs
unset PKG_CONFIG_PATH
export PKG_CONFIG_LIBDIR="$DEPS_INSTALL_DIR/lib/pkgconfig"
rm -rf "$WINXX_BUILD_DIR"
meson setup "$WINXX_BUILD_DIR" \
--pkg-config-path="$DEPS_INSTALL_DIR/lib/pkgconfig" \
-Dc_args="-I$DEPS_INSTALL_DIR/include" \
-Dc_link_args="-L$DEPS_INSTALL_DIR/lib" \
--cross-file=cross_$WINXX.txt \