Add scrcpy window without video playback

Add the possibility to solely control the device without screen
mirroring:

    scrcpy --no-video --no-audio

This is different from OTG mode, which does not require USB debugging at
all. Here, the standard mode is used but with the possibility to disable
video playback.

By default, always open a window (even without video playback), and add
an option --no-window.

Fixes #4727 <https://github.com/Genymobile/scrcpy/issues/4727>
Fixes #4793 <https://github.com/Genymobile/scrcpy/issues/4793>
PR #4868 <https://github.com/Genymobile/scrcpy/pull/4868>
This commit is contained in:
Romain Vimont 2024-04-07 16:01:26 +02:00
parent cca2c9ffb7
commit 45fe6b602b
10 changed files with 243 additions and 65 deletions

View File

@ -316,6 +316,10 @@ Disable video forwarding.
.B \-\-no\-video\-playback .B \-\-no\-video\-playback
Disable video playback on the computer. Disable video playback on the computer.
.TP
.B \-\-no\-window
Disable scrcpy window. Implies --no-video-playback and --no-control.
.TP .TP
.BI "\-\-orientation " value .BI "\-\-orientation " value
Same as --display-orientation=value --record-orientation=value. Same as --display-orientation=value --record-orientation=value.

View File

@ -97,6 +97,7 @@ enum {
OPT_MOUSE, OPT_MOUSE,
OPT_HID_KEYBOARD_DEPRECATED, OPT_HID_KEYBOARD_DEPRECATED,
OPT_HID_MOUSE_DEPRECATED, OPT_HID_MOUSE_DEPRECATED,
OPT_NO_WINDOW,
}; };
struct sc_option { struct sc_option {
@ -566,6 +567,12 @@ static const struct sc_option options[] = {
.longopt = "no-video-playback", .longopt = "no-video-playback",
.text = "Disable video playback on the computer.", .text = "Disable video playback on the computer.",
}, },
{
.longopt_id = OPT_NO_WINDOW,
.longopt = "no-window",
.text = "Disable scrcpy window. Implies --no-video-playback and "
"--no-control.",
},
{ {
.longopt_id = OPT_ORIENTATION, .longopt_id = OPT_ORIENTATION,
.longopt = "orientation", .longopt = "orientation",
@ -2486,6 +2493,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
case OPT_CAMERA_HIGH_SPEED: case OPT_CAMERA_HIGH_SPEED:
opts->camera_high_speed = true; opts->camera_high_speed = true;
break; break;
case OPT_NO_WINDOW:
opts->window = false;
break;
default: default:
// getopt prints the error message on stderr // getopt prints the error message on stderr
return false; return false;
@ -2523,6 +2533,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
v4l2 = !!opts->v4l2_device; v4l2 = !!opts->v4l2_device;
#endif #endif
if (!opts->window) {
// Without window, there cannot be any video playback or control
opts->video_playback = false;
opts->control = false;
}
if (!opts->video) { if (!opts->video) {
opts->video_playback = false; opts->video_playback = false;
// Do not power on the device on start if video capture is disabled // Do not power on the device on start if video capture is disabled
@ -2544,8 +2560,8 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
opts->audio = false; opts->audio = false;
} }
if (!opts->video && !opts->audio && !otg) { if (!opts->video && !opts->audio && !opts->control && !otg) {
LOGE("No video, no audio, no OTG: nothing to do"); LOGE("No video, no audio, no control, no OTG: nothing to do");
return false; return false;
} }
@ -2569,6 +2585,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
#ifdef HAVE_V4L2 #ifdef HAVE_V4L2
if (v4l2) { if (v4l2) {
if (!opts->video) {
LOGE("V4L2 sink requires video capture, but --no-video was set.");
return false;
}
if (opts->lock_video_orientation == if (opts->lock_video_orientation ==
SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) { SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) {
LOGI("Video orientation is locked for v4l2 sink. " LOGI("Video orientation is locked for v4l2 sink. "
@ -2588,13 +2609,25 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
} }
#endif #endif
if (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AUTO) { if (opts->control) {
opts->keyboard_input_mode = otg ? SC_KEYBOARD_INPUT_MODE_AOA if (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AUTO) {
: SC_KEYBOARD_INPUT_MODE_SDK; opts->keyboard_input_mode = otg ? SC_KEYBOARD_INPUT_MODE_AOA
} : SC_KEYBOARD_INPUT_MODE_SDK;
if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_AUTO) { }
opts->mouse_input_mode = otg ? SC_MOUSE_INPUT_MODE_AOA if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_AUTO) {
: SC_MOUSE_INPUT_MODE_SDK; if (otg) {
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_AOA;
} else if (!opts->video_playback) {
LOGI("No video mirroring, mouse mode switched to UHID");
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_UHID;
} else {
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_SDK;
}
} else if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_SDK
&& !opts->video_playback) {
LOGE("SDK mouse mode requires video playback. Try --mouse=uhid.");
return false;
}
} }
if (otg) { if (otg) {

View File

@ -5,8 +5,30 @@
#include "util/log.h" #include "util/log.h"
static bool
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)) {
LOGW("Could not set renderer logical size: %s", SDL_GetError());
// don't fail
}
display->texture = SDL_CreateTextureFromSurface(display->renderer,
icon_novideo);
if (!display->texture) {
LOGE("Could not create texture: %s", SDL_GetError());
return false;
}
return true;
}
bool bool
sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps) { sc_display_init(struct sc_display *display, SDL_Window *window,
SDL_Surface *icon_novideo, bool mipmaps) {
display->renderer = display->renderer =
SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (!display->renderer) { if (!display->renderer) {
@ -68,6 +90,18 @@ sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps) {
display->pending.frame = NULL; display->pending.frame = NULL;
display->has_frame = false; display->has_frame = false;
if (icon_novideo) {
// Without video, set a static scrcpy icon as window content
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);
#endif
SDL_DestroyRenderer(display->renderer);
return false;
}
}
return true; return true;
} }

View File

@ -44,7 +44,8 @@ enum sc_display_result {
}; };
bool bool
sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps); sc_display_init(struct sc_display *display, SDL_Window *window,
SDL_Surface *icon_novideo, bool mipmaps);
void void
sc_display_destroy(struct sc_display *display); sc_display_destroy(struct sc_display *display);

View File

@ -403,6 +403,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
// controller is NULL if --no-control is requested // controller is NULL if --no-control is requested
bool control = im->controller; bool control = im->controller;
bool paused = im->screen->paused; bool paused = im->screen->paused;
bool video = im->screen->video;
SDL_Keycode keycode = event->keysym.sym; SDL_Keycode keycode = event->keysym.sym;
uint16_t mod = event->keysym.mod; uint16_t mod = event->keysym.mod;
@ -462,13 +463,13 @@ sc_input_manager_process_key(struct sc_input_manager *im,
} }
return; return;
case SDLK_z: case SDLK_z:
if (down && !repeat) { if (video && down && !repeat) {
sc_screen_set_paused(im->screen, !shift); sc_screen_set_paused(im->screen, !shift);
} }
return; return;
case SDLK_DOWN: case SDLK_DOWN:
if (shift) { if (shift) {
if (!repeat && down) { if (video && !repeat && down) {
apply_orientation_transform(im, apply_orientation_transform(im,
SC_ORIENTATION_FLIP_180); SC_ORIENTATION_FLIP_180);
} }
@ -479,7 +480,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
return; return;
case SDLK_UP: case SDLK_UP:
if (shift) { if (shift) {
if (!repeat && down) { if (video && !repeat && down) {
apply_orientation_transform(im, apply_orientation_transform(im,
SC_ORIENTATION_FLIP_180); SC_ORIENTATION_FLIP_180);
} }
@ -489,7 +490,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
} }
return; return;
case SDLK_LEFT: case SDLK_LEFT:
if (!repeat && down) { if (video && !repeat && down) {
if (shift) { if (shift) {
apply_orientation_transform(im, apply_orientation_transform(im,
SC_ORIENTATION_FLIP_0); SC_ORIENTATION_FLIP_0);
@ -500,7 +501,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
} }
return; return;
case SDLK_RIGHT: case SDLK_RIGHT:
if (!repeat && down) { if (video && !repeat && down) {
if (shift) { if (shift) {
apply_orientation_transform(im, apply_orientation_transform(im,
SC_ORIENTATION_FLIP_0); SC_ORIENTATION_FLIP_0);
@ -533,22 +534,22 @@ sc_input_manager_process_key(struct sc_input_manager *im,
} }
return; return;
case SDLK_f: case SDLK_f:
if (!shift && !repeat && down) { if (video && !shift && !repeat && down) {
sc_screen_switch_fullscreen(im->screen); sc_screen_switch_fullscreen(im->screen);
} }
return; return;
case SDLK_w: case SDLK_w:
if (!shift && !repeat && down) { if (video && !shift && !repeat && down) {
sc_screen_resize_to_fit(im->screen); sc_screen_resize_to_fit(im->screen);
} }
return; return;
case SDLK_g: case SDLK_g:
if (!shift && !repeat && down) { if (video && !shift && !repeat && down) {
sc_screen_resize_to_pixel_perfect(im->screen); sc_screen_resize_to_pixel_perfect(im->screen);
} }
return; return;
case SDLK_i: case SDLK_i:
if (!shift && !repeat && down) { if (video && !shift && !repeat && down) {
switch_fps_counter_state(im); switch_fps_counter_state(im);
} }
return; return;
@ -625,6 +626,23 @@ sc_input_manager_process_key(struct sc_input_manager *im,
im->kp->ops->process_key(im->kp, &evt, ack_to_wait); im->kp->ops->process_key(im->kp, &evt, ack_to_wait);
} }
static struct sc_position
sc_input_manager_get_position(struct sc_input_manager *im, int32_t x,
int32_t y) {
if (im->mp->relative_mode) {
// No absolute position
return (struct sc_position) {
.screen_size = {0, 0},
.point = {0, 0},
};
}
return (struct sc_position) {
.screen_size = im->screen->frame_size,
.point = sc_screen_convert_window_to_frame_coords(im->screen, x, y),
};
}
static void static void
sc_input_manager_process_mouse_motion(struct sc_input_manager *im, sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
const SDL_MouseMotionEvent *event) { const SDL_MouseMotionEvent *event) {
@ -634,12 +652,7 @@ sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
} }
struct sc_mouse_motion_event evt = { struct sc_mouse_motion_event evt = {
.position = { .position = sc_input_manager_get_position(im, event->x, event->y),
.screen_size = im->screen->frame_size,
.point = sc_screen_convert_window_to_frame_coords(im->screen,
event->x,
event->y),
},
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE .pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
: POINTER_ID_GENERIC_FINGER, : POINTER_ID_GENERIC_FINGER,
.xrel = event->xrel, .xrel = event->xrel,
@ -735,7 +748,8 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
} }
// double-click on black borders resize to fit the device screen // double-click on black borders resize to fit the device screen
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) { bool video = im->screen->video;
if (video && event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
int32_t x = event->x; int32_t x = event->x;
int32_t y = event->y; int32_t y = event->y;
sc_screen_hidpi_scale_coords(im->screen, &x, &y); sc_screen_hidpi_scale_coords(im->screen, &x, &y);
@ -759,12 +773,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL); uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL);
struct sc_mouse_click_event evt = { struct sc_mouse_click_event evt = {
.position = { .position = sc_input_manager_get_position(im, event->x, event->y),
.screen_size = im->screen->frame_size,
.point = sc_screen_convert_window_to_frame_coords(im->screen,
event->x,
event->y),
},
.action = sc_action_from_sdl_mousebutton_type(event->type), .action = sc_action_from_sdl_mousebutton_type(event->type),
.button = sc_mouse_button_from_sdl(event->button), .button = sc_mouse_button_from_sdl(event->button),
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE .pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
@ -839,11 +848,7 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y); uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y);
struct sc_mouse_scroll_event evt = { struct sc_mouse_scroll_event evt = {
.position = { .position = sc_input_manager_get_position(im, mouse_x, mouse_y),
.screen_size = im->screen->frame_size,
.point = sc_screen_convert_window_to_frame_coords(im->screen,
mouse_x, mouse_y),
},
#if SDL_VERSION_ATLEAST(2, 0, 18) #if SDL_VERSION_ATLEAST(2, 0, 18)
.hscroll = CLAMP(event->preciseX, -1.0f, 1.0f), .hscroll = CLAMP(event->preciseX, -1.0f, 1.0f),
.vscroll = CLAMP(event->preciseY, -1.0f, 1.0f), .vscroll = CLAMP(event->preciseY, -1.0f, 1.0f),

View File

@ -89,6 +89,7 @@ const struct scrcpy_options scrcpy_options_default = {
.kill_adb_on_close = false, .kill_adb_on_close = false,
.camera_high_speed = false, .camera_high_speed = false,
.list = 0, .list = 0,
.window = true,
}; };
enum sc_orientation enum sc_orientation

View File

@ -279,6 +279,7 @@ struct scrcpy_options {
#define SC_OPTION_LIST_CAMERAS 0x4 #define SC_OPTION_LIST_CAMERAS 0x4
#define SC_OPTION_LIST_CAMERA_SIZES 0x8 #define SC_OPTION_LIST_CAMERA_SIZES 0x8
uint8_t list; uint8_t list;
bool window;
}; };
extern const struct scrcpy_options scrcpy_options_default; extern const struct scrcpy_options scrcpy_options_default;

View File

@ -408,7 +408,7 @@ scrcpy(struct scrcpy_options *options) {
return SCRCPY_EXIT_FAILURE; return SCRCPY_EXIT_FAILURE;
} }
if (options->video_playback) { if (options->window) {
// Set hints before starting the server thread to avoid race conditions // Set hints before starting the server thread to avoid race conditions
// in SDL // in SDL
sdl_set_hints(options->render_driver); sdl_set_hints(options->render_driver);
@ -430,7 +430,7 @@ scrcpy(struct scrcpy_options *options) {
assert(!options->video_playback || options->video); assert(!options->video_playback || options->video);
assert(!options->audio_playback || options->audio); assert(!options->audio_playback || options->audio);
if (options->video_playback || if (options->window ||
(options->control && options->clipboard_autosync)) { (options->control && options->clipboard_autosync)) {
// Initialize the video subsystem even if --no-video or // Initialize the video subsystem even if --no-video or
// --no-video-playback is passed so that clipboard synchronization // --no-video-playback is passed so that clipboard synchronization
@ -684,11 +684,12 @@ scrcpy(struct scrcpy_options *options) {
// There is a controller if and only if control is enabled // There is a controller if and only if control is enabled
assert(options->control == !!controller); assert(options->control == !!controller);
if (options->video_playback) { if (options->window) {
const char *window_title = const char *window_title =
options->window_title ? options->window_title : info->device_name; options->window_title ? options->window_title : info->device_name;
struct sc_screen_params screen_params = { struct sc_screen_params screen_params = {
.video = options->video_playback,
.controller = controller, .controller = controller,
.fp = fp, .fp = fp,
.kp = kp, .kp = kp,
@ -710,12 +711,15 @@ scrcpy(struct scrcpy_options *options) {
.start_fps_counter = options->start_fps_counter, .start_fps_counter = options->start_fps_counter,
}; };
struct sc_frame_source *src = &s->video_decoder.frame_source; struct sc_frame_source *src;
if (options->display_buffer) { if (options->video_playback) {
sc_delay_buffer_init(&s->display_buffer, options->display_buffer, src = &s->video_decoder.frame_source;
true); if (options->display_buffer) {
sc_frame_source_add_sink(src, &s->display_buffer.frame_sink); sc_delay_buffer_init(&s->display_buffer,
src = &s->display_buffer.frame_source; options->display_buffer, true);
sc_frame_source_add_sink(src, &s->display_buffer.frame_sink);
src = &s->display_buffer.frame_source;
}
} }
if (!sc_screen_init(&s->screen, &screen_params)) { if (!sc_screen_init(&s->screen, &screen_params)) {
@ -723,7 +727,9 @@ scrcpy(struct scrcpy_options *options) {
} }
screen_initialized = true; screen_initialized = true;
sc_frame_source_add_sink(src, &s->screen.frame_sink); if (options->video_playback) {
sc_frame_source_add_sink(src, &s->screen.frame_sink);
}
} }
if (options->audio_playback) { if (options->audio_playback) {

View File

@ -205,6 +205,8 @@ sc_screen_toggle_mouse_capture(struct sc_screen *screen) {
static void static void
sc_screen_update_content_rect(struct sc_screen *screen) { sc_screen_update_content_rect(struct sc_screen *screen) {
assert(screen->video);
int dw; int dw;
int dh; int dh;
SDL_GL_GetDrawableSize(screen->window, &dw, &dh); SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
@ -246,6 +248,8 @@ sc_screen_update_content_rect(struct sc_screen *screen) {
// changed, so that the content rectangle is recomputed // changed, so that the content rectangle is recomputed
static void static void
sc_screen_render(struct sc_screen *screen, bool update_content_rect) { sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
assert(screen->video);
if (update_content_rect) { if (update_content_rect) {
sc_screen_update_content_rect(screen); sc_screen_update_content_rect(screen);
} }
@ -255,6 +259,13 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
(void) res; // any error already logged (void) res; // any error already logged
} }
static void
sc_screen_render_novideo(struct sc_screen *screen) {
enum sc_display_result res =
sc_display_render(&screen->display, NULL, SC_ORIENTATION_0);
(void) res; // any error already logged
}
#if defined(__APPLE__) || defined(__WINDOWS__) #if defined(__APPLE__) || defined(__WINDOWS__)
# define CONTINUOUS_RESIZING_WORKAROUND # define CONTINUOUS_RESIZING_WORKAROUND
#endif #endif
@ -268,6 +279,8 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
static int static int
event_watcher(void *data, SDL_Event *event) { event_watcher(void *data, SDL_Event *event) {
struct sc_screen *screen = data; struct sc_screen *screen = data;
assert(screen->video);
if (event->type == SDL_WINDOWEVENT if (event->type == SDL_WINDOWEVENT
&& event->window.event == SDL_WINDOWEVENT_RESIZED) { && event->window.event == SDL_WINDOWEVENT_RESIZED) {
// In practice, it seems to always be called from the same thread in // In practice, it seems to always be called from the same thread in
@ -326,6 +339,7 @@ sc_screen_frame_sink_close(struct sc_frame_sink *sink) {
static bool static bool
sc_screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) { sc_screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) {
struct sc_screen *screen = DOWNCAST(sink); struct sc_screen *screen = DOWNCAST(sink);
assert(screen->video);
bool previous_skipped; bool previous_skipped;
bool ok = sc_frame_buffer_push(&screen->fb, frame, &previous_skipped); bool ok = sc_frame_buffer_push(&screen->fb, frame, &previous_skipped);
@ -364,6 +378,9 @@ sc_screen_init(struct sc_screen *screen,
screen->mouse_capture_key_pressed = 0; screen->mouse_capture_key_pressed = 0;
screen->paused = false; screen->paused = false;
screen->resume_frame = NULL; screen->resume_frame = NULL;
screen->orientation = SC_ORIENTATION_0;
screen->video = params->video;
screen->req.x = params->window_x; screen->req.x = params->window_x;
screen->req.y = params->window_y; screen->req.y = params->window_y;
@ -381,41 +398,75 @@ sc_screen_init(struct sc_screen *screen,
goto error_destroy_frame_buffer; goto error_destroy_frame_buffer;
} }
screen->orientation = params->orientation; if (screen->video) {
if (screen->orientation != SC_ORIENTATION_0) { screen->orientation = params->orientation;
LOGI("Initial display orientation set to %s", if (screen->orientation != SC_ORIENTATION_0) {
sc_orientation_get_name(screen->orientation)); LOGI("Initial display orientation set to %s",
sc_orientation_get_name(screen->orientation));
}
} }
uint32_t window_flags = SDL_WINDOW_HIDDEN uint32_t window_flags = SDL_WINDOW_ALLOW_HIGHDPI;
| SDL_WINDOW_RESIZABLE
| SDL_WINDOW_ALLOW_HIGHDPI;
if (params->always_on_top) { if (params->always_on_top) {
window_flags |= SDL_WINDOW_ALWAYS_ON_TOP; window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
} }
if (params->window_borderless) { if (params->window_borderless) {
window_flags |= SDL_WINDOW_BORDERLESS; window_flags |= SDL_WINDOW_BORDERLESS;
} }
if (params->video) {
// The window will be shown on first frame
window_flags |= SDL_WINDOW_HIDDEN
| SDL_WINDOW_RESIZABLE;
}
const char *title = params->window_title;
assert(title);
int x = SDL_WINDOWPOS_UNDEFINED;
int y = SDL_WINDOWPOS_UNDEFINED;
int width = 256;
int height = 256;
if (params->window_x != SC_WINDOW_POSITION_UNDEFINED) {
x = params->window_x;
}
if (params->window_y != SC_WINDOW_POSITION_UNDEFINED) {
y = params->window_y;
}
if (params->window_width) {
width = params->window_width;
}
if (params->window_height) {
height = params->window_height;
}
// The window will be positioned and sized on first video frame // The window will be positioned and sized on first video frame
screen->window = screen->window = SDL_CreateWindow(title, x, y, width, height, window_flags);
SDL_CreateWindow(params->window_title, 0, 0, 0, 0, window_flags);
if (!screen->window) { if (!screen->window) {
LOGE("Could not create window: %s", SDL_GetError()); LOGE("Could not create window: %s", SDL_GetError());
goto error_destroy_fps_counter; goto error_destroy_fps_counter;
} }
ok = sc_display_init(&screen->display, screen->window, params->mipmaps);
if (!ok) {
goto error_destroy_window;
}
SDL_Surface *icon = scrcpy_icon_load(); SDL_Surface *icon = scrcpy_icon_load();
if (icon) { if (icon) {
SDL_SetWindowIcon(screen->window, icon); SDL_SetWindowIcon(screen->window, icon);
scrcpy_icon_destroy(icon); } else if (params->video) {
} else { // just a warning
LOGW("Could not load icon"); LOGW("Could not load icon");
} else {
// without video, the icon is used as window content, it must be present
LOGE("Could not load icon");
goto error_destroy_fps_counter;
}
SDL_Surface *icon_novideo = params->video ? NULL : icon;
bool mipmaps = params->video && params->mipmaps;
ok = sc_display_init(&screen->display, screen->window, icon_novideo,
mipmaps);
if (icon) {
scrcpy_icon_destroy(icon);
}
if (!ok) {
goto error_destroy_window;
} }
screen->frame = av_frame_alloc(); screen->frame = av_frame_alloc();
@ -439,7 +490,9 @@ sc_screen_init(struct sc_screen *screen,
sc_input_manager_init(&screen->im, &im_params); sc_input_manager_init(&screen->im, &im_params);
#ifdef CONTINUOUS_RESIZING_WORKAROUND #ifdef CONTINUOUS_RESIZING_WORKAROUND
SDL_AddEventWatch(event_watcher, screen); if (screen->video) {
SDL_AddEventWatch(event_watcher, screen);
}
#endif #endif
static const struct sc_frame_sink_ops ops = { static const struct sc_frame_sink_ops ops = {
@ -454,6 +507,11 @@ sc_screen_init(struct sc_screen *screen,
screen->open = false; screen->open = false;
#endif #endif
if (!screen->video && sc_screen_is_relative_mode(screen)) {
// Capture mouse immediately if video mirroring is disabled
sc_screen_set_mouse_capture(screen, true);
}
return true; return true;
error_destroy_display: error_destroy_display:
@ -524,6 +582,8 @@ sc_screen_destroy(struct sc_screen *screen) {
static void static void
resize_for_content(struct sc_screen *screen, struct sc_size old_content_size, resize_for_content(struct sc_screen *screen, struct sc_size old_content_size,
struct sc_size new_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 = get_window_size(screen);
struct sc_size target_size = { struct sc_size target_size = {
.width = (uint32_t) window_size.width * new_content_size.width .width = (uint32_t) window_size.width * new_content_size.width
@ -537,6 +597,8 @@ resize_for_content(struct sc_screen *screen, struct sc_size old_content_size,
static void static void
set_content_size(struct sc_screen *screen, struct sc_size new_content_size) { set_content_size(struct sc_screen *screen, struct sc_size new_content_size) {
assert(screen->video);
if (!screen->fullscreen && !screen->maximized && !screen->minimized) { if (!screen->fullscreen && !screen->maximized && !screen->minimized) {
resize_for_content(screen, screen->content_size, new_content_size); resize_for_content(screen, screen->content_size, new_content_size);
} else if (!screen->resize_pending) { } else if (!screen->resize_pending) {
@ -551,6 +613,8 @@ set_content_size(struct sc_screen *screen, struct sc_size new_content_size) {
static void static void
apply_pending_resize(struct sc_screen *screen) { apply_pending_resize(struct sc_screen *screen) {
assert(screen->video);
assert(!screen->fullscreen); assert(!screen->fullscreen);
assert(!screen->maximized); assert(!screen->maximized);
assert(!screen->minimized); assert(!screen->minimized);
@ -564,6 +628,8 @@ apply_pending_resize(struct sc_screen *screen) {
void void
sc_screen_set_orientation(struct sc_screen *screen, sc_screen_set_orientation(struct sc_screen *screen,
enum sc_orientation orientation) { enum sc_orientation orientation) {
assert(screen->video);
if (orientation == screen->orientation) { if (orientation == screen->orientation) {
return; return;
} }
@ -598,6 +664,8 @@ sc_screen_init_size(struct sc_screen *screen) {
// recreate the texture and resize the window if the frame size has changed // recreate the texture and resize the window if the frame size has changed
static enum sc_display_result static enum sc_display_result
prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) { 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 if (screen->frame_size.width == new_frame_size.width
&& screen->frame_size.height == new_frame_size.height) { && screen->frame_size.height == new_frame_size.height) {
return SC_DISPLAY_RESULT_OK; return SC_DISPLAY_RESULT_OK;
@ -617,6 +685,8 @@ prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) {
static bool static bool
sc_screen_apply_frame(struct sc_screen *screen) { sc_screen_apply_frame(struct sc_screen *screen) {
assert(screen->video);
sc_fps_counter_add_rendered_frame(&screen->fps_counter); sc_fps_counter_add_rendered_frame(&screen->fps_counter);
AVFrame *frame = screen->frame; AVFrame *frame = screen->frame;
@ -656,6 +726,8 @@ sc_screen_apply_frame(struct sc_screen *screen) {
static bool static bool
sc_screen_update_frame(struct sc_screen *screen) { sc_screen_update_frame(struct sc_screen *screen) {
assert(screen->video);
if (screen->paused) { if (screen->paused) {
if (!screen->resume_frame) { if (!screen->resume_frame) {
screen->resume_frame = av_frame_alloc(); screen->resume_frame = av_frame_alloc();
@ -677,6 +749,8 @@ sc_screen_update_frame(struct sc_screen *screen) {
void void
sc_screen_set_paused(struct sc_screen *screen, bool paused) { sc_screen_set_paused(struct sc_screen *screen, bool paused) {
assert(screen->video);
if (!paused && !screen->paused) { if (!paused && !screen->paused) {
// nothing to do // nothing to do
return; return;
@ -704,6 +778,8 @@ sc_screen_set_paused(struct sc_screen *screen, bool paused) {
void void
sc_screen_switch_fullscreen(struct sc_screen *screen) { sc_screen_switch_fullscreen(struct sc_screen *screen) {
assert(screen->video);
uint32_t new_mode = screen->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP; uint32_t new_mode = screen->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP;
if (SDL_SetWindowFullscreen(screen->window, new_mode)) { if (SDL_SetWindowFullscreen(screen->window, new_mode)) {
LOGW("Could not switch fullscreen mode: %s", SDL_GetError()); LOGW("Could not switch fullscreen mode: %s", SDL_GetError());
@ -721,6 +797,8 @@ sc_screen_switch_fullscreen(struct sc_screen *screen) {
void void
sc_screen_resize_to_fit(struct sc_screen *screen) { sc_screen_resize_to_fit(struct sc_screen *screen) {
assert(screen->video);
if (screen->fullscreen || screen->maximized || screen->minimized) { if (screen->fullscreen || screen->maximized || screen->minimized) {
return; return;
} }
@ -745,6 +823,8 @@ sc_screen_resize_to_fit(struct sc_screen *screen) {
void void
sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) { sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) {
assert(screen->video);
if (screen->fullscreen || screen->minimized) { if (screen->fullscreen || screen->minimized) {
return; return;
} }
@ -788,6 +868,13 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
return true; return true;
} }
case SDL_WINDOWEVENT: case SDL_WINDOWEVENT:
if (!screen->video
&& event->window.event == SDL_WINDOWEVENT_EXPOSED) {
sc_screen_render_novideo(screen);
}
// !video implies !has_frame
assert(screen->video || !screen->has_frame);
if (!screen->has_frame) { if (!screen->has_frame) {
// Do nothing // Do nothing
return true; return true;
@ -891,6 +978,8 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
struct sc_point struct sc_point
sc_screen_convert_drawable_to_frame_coords(struct sc_screen *screen, sc_screen_convert_drawable_to_frame_coords(struct sc_screen *screen,
int32_t x, int32_t y) { int32_t x, int32_t y) {
assert(screen->video);
enum sc_orientation orientation = screen->orientation; enum sc_orientation orientation = screen->orientation;
int32_t w = screen->content_size.width; int32_t w = screen->content_size.width;

View File

@ -26,6 +26,8 @@ struct sc_screen {
bool open; // track the open/close state to assert correct behavior bool open; // track the open/close state to assert correct behavior
#endif #endif
bool video;
struct sc_display display; struct sc_display display;
struct sc_input_manager im; struct sc_input_manager im;
struct sc_frame_buffer fb; struct sc_frame_buffer fb;
@ -70,6 +72,8 @@ struct sc_screen {
}; };
struct sc_screen_params { struct sc_screen_params {
bool video;
struct sc_controller *controller; struct sc_controller *controller;
struct sc_file_pusher *fp; struct sc_file_pusher *fp;
struct sc_key_processor *kp; struct sc_key_processor *kp;