Skip to content

Commit

Permalink
Add scrcpy window without video playback
Browse files Browse the repository at this point in the history
Add the possibility to only control the device with any keyboard and
mouse mode without screen mirroring.

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 <#4727>
Fixes #4793 <#4793>
PR #4868 <#4868>
  • Loading branch information
rom1v committed Apr 23, 2024
1 parent cca2c9f commit 3ea1444
Show file tree
Hide file tree
Showing 10 changed files with 195 additions and 41 deletions.
4 changes: 4 additions & 0 deletions app/scrcpy.1
Expand Up @@ -316,6 +316,10 @@ Disable video forwarding.
.B \-\-no\-video\-playback
Disable video playback on the computer.

.TP
.B \-\-no\-window
Disable scrcpy window. Implies --no-video-playback and --no-control.

.TP
.BI "\-\-orientation " value
Same as --display-orientation=value --record-orientation=value.
Expand Down
47 changes: 38 additions & 9 deletions app/src/cli.c
Expand Up @@ -97,6 +97,7 @@ enum {
OPT_MOUSE,
OPT_HID_KEYBOARD_DEPRECATED,
OPT_HID_MOUSE_DEPRECATED,
OPT_NO_WINDOW,
};

struct sc_option {
Expand Down Expand Up @@ -566,6 +567,12 @@ static const struct sc_option options[] = {
.longopt = "no-video-playback",
.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 = "orientation",
Expand Down Expand Up @@ -2486,6 +2493,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
case OPT_CAMERA_HIGH_SPEED:
opts->camera_high_speed = true;
break;
case OPT_NO_WINDOW:
opts->window = false;
break;
default:
// getopt prints the error message on stderr
return false;
Expand Down Expand Up @@ -2523,6 +2533,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
v4l2 = !!opts->v4l2_device;
#endif

if (!opts->window) {
// Without window, there cannot be any video playback or control
opts->video_playback = false;
opts->control = false;
}

if (!opts->video) {
opts->video_playback = false;
// Do not power on the device on start if video capture is disabled
Expand All @@ -2544,8 +2560,8 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
opts->audio = false;
}

if (!opts->video && !opts->audio && !otg) {
LOGE("No video, no audio, no OTG: nothing to do");
if (!opts->video && !opts->audio && !opts->control && !otg) {
LOGE("No video, no audio, no control, no OTG: nothing to do");
return false;
}

Expand Down Expand Up @@ -2588,13 +2604,26 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
}
#endif

if (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AUTO) {
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
: SC_MOUSE_INPUT_MODE_SDK;
if (opts->control) {
if (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AUTO) {
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) {
if (otg) {
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_AOA;
} else if (!opts->video_playback) {
LOGI("No video mirroring, SDK mouse disabled (you might want "
"--mouse=uhid)");
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_DISABLED;
} 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) {
Expand Down
42 changes: 41 additions & 1 deletion app/src/display.c
Expand Up @@ -5,8 +5,36 @@

#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;
}

SDL_RenderClear(display->renderer);
if (display->texture) {
SDL_RenderCopy(display->renderer, display->texture, NULL, NULL);
}
SDL_RenderPresent(display->renderer);

return true;
}

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 =
SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (!display->renderer) {
Expand Down Expand Up @@ -68,6 +96,18 @@ sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps) {
display->pending.frame = NULL;
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;
}

Expand Down
3 changes: 2 additions & 1 deletion app/src/display.h
Expand Up @@ -44,7 +44,8 @@ enum sc_display_result {
};

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
sc_display_destroy(struct sc_display *display);
Expand Down
19 changes: 10 additions & 9 deletions app/src/input_manager.c
Expand Up @@ -403,6 +403,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
// controller is NULL if --no-control is requested
bool control = im->controller;
bool paused = im->screen->paused;
bool video = im->screen->video;

SDL_Keycode keycode = event->keysym.sym;
uint16_t mod = event->keysym.mod;
Expand Down Expand Up @@ -462,13 +463,13 @@ sc_input_manager_process_key(struct sc_input_manager *im,
}
return;
case SDLK_z:
if (down && !repeat) {
if (video && down && !repeat) {
sc_screen_set_paused(im->screen, !shift);
}
return;
case SDLK_DOWN:
if (shift) {
if (!repeat && down) {
if (video && !repeat && down) {
apply_orientation_transform(im,
SC_ORIENTATION_FLIP_180);
}
Expand All @@ -479,7 +480,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
return;
case SDLK_UP:
if (shift) {
if (!repeat && down) {
if (video && !repeat && down) {
apply_orientation_transform(im,
SC_ORIENTATION_FLIP_180);
}
Expand All @@ -489,7 +490,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
}
return;
case SDLK_LEFT:
if (!repeat && down) {
if (video && !repeat && down) {
if (shift) {
apply_orientation_transform(im,
SC_ORIENTATION_FLIP_0);
Expand All @@ -500,7 +501,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
}
return;
case SDLK_RIGHT:
if (!repeat && down) {
if (video && !repeat && down) {
if (shift) {
apply_orientation_transform(im,
SC_ORIENTATION_FLIP_0);
Expand Down Expand Up @@ -533,22 +534,22 @@ sc_input_manager_process_key(struct sc_input_manager *im,
}
return;
case SDLK_f:
if (!shift && !repeat && down) {
if (video && !shift && !repeat && down) {
sc_screen_switch_fullscreen(im->screen);
}
return;
case SDLK_w:
if (!shift && !repeat && down) {
if (video && !shift && !repeat && down) {
sc_screen_resize_to_fit(im->screen);
}
return;
case SDLK_g:
if (!shift && !repeat && down) {
if (video && !shift && !repeat && down) {
sc_screen_resize_to_pixel_perfect(im->screen);
}
return;
case SDLK_i:
if (!shift && !repeat && down) {
if (video && !shift && !repeat && down) {
switch_fps_counter_state(im);
}
return;
Expand Down
1 change: 1 addition & 0 deletions app/src/options.c
Expand Up @@ -89,6 +89,7 @@ const struct scrcpy_options scrcpy_options_default = {
.kill_adb_on_close = false,
.camera_high_speed = false,
.list = 0,
.window = true,
};

enum sc_orientation
Expand Down
1 change: 1 addition & 0 deletions app/src/options.h
Expand Up @@ -279,6 +279,7 @@ struct scrcpy_options {
#define SC_OPTION_LIST_CAMERAS 0x4
#define SC_OPTION_LIST_CAMERA_SIZES 0x8
uint8_t list;
bool window;
};

extern const struct scrcpy_options scrcpy_options_default;
Expand Down
24 changes: 15 additions & 9 deletions app/src/scrcpy.c
Expand Up @@ -430,7 +430,7 @@ scrcpy(struct scrcpy_options *options) {
assert(!options->video_playback || options->video);
assert(!options->audio_playback || options->audio);

if (options->video_playback ||
if (options->window ||
(options->control && options->clipboard_autosync)) {
// Initialize the video subsystem even if --no-video or
// --no-video-playback is passed so that clipboard synchronization
Expand Down Expand Up @@ -684,11 +684,12 @@ scrcpy(struct scrcpy_options *options) {
// There is a controller if and only if control is enabled
assert(options->control == !!controller);

if (options->video_playback) {
if (options->window) {
const char *window_title =
options->window_title ? options->window_title : info->device_name;

struct sc_screen_params screen_params = {
.video = options->video_playback,
.controller = controller,
.fp = fp,
.kp = kp,
Expand All @@ -710,20 +711,25 @@ scrcpy(struct scrcpy_options *options) {
.start_fps_counter = options->start_fps_counter,
};

struct sc_frame_source *src = &s->video_decoder.frame_source;
if (options->display_buffer) {
sc_delay_buffer_init(&s->display_buffer, options->display_buffer,
true);
sc_frame_source_add_sink(src, &s->display_buffer.frame_sink);
src = &s->display_buffer.frame_source;
struct sc_frame_source *src;
if (options->video_playback) {
src = &s->video_decoder.frame_source;
if (options->display_buffer) {
sc_delay_buffer_init(&s->display_buffer,
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)) {
goto end;
}
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) {
Expand Down

0 comments on commit 3ea1444

Please sign in to comment.