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:

    scrcpy -KM --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 <Genymobile/scrcpy#4727>
Fixes #4793 <Genymobile/scrcpy#4793>
PR #4868 <Genymobile/scrcpy#4868>
  • Loading branch information
rom1v committed Apr 29, 2024
1 parent cca2c9f commit a26c4e7
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 64 deletions.
4 changes: 4 additions & 0 deletions app/scrcpy.1
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
36 changes: 35 additions & 1 deletion app/src/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,30 @@

#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
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 +90,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
Original file line number Diff line number Diff line change
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
56 changes: 30 additions & 26 deletions app/src/input_manager.c
Original file line number Diff line number Diff line change
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 Expand Up @@ -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);
}

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
sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
const SDL_MouseMotionEvent *event) {
Expand All @@ -634,12 +652,7 @@ sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
}

struct sc_mouse_motion_event evt = {
.position = {
.screen_size = im->screen->frame_size,
.point = sc_screen_convert_window_to_frame_coords(im->screen,
event->x,
event->y),
},
.position = sc_input_manager_get_position(im, event->x, event->y),
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
: POINTER_ID_GENERIC_FINGER,
.xrel = event->xrel,
Expand Down Expand Up @@ -759,12 +772,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL);

struct sc_mouse_click_event evt = {
.position = {
.screen_size = im->screen->frame_size,
.point = sc_screen_convert_window_to_frame_coords(im->screen,
event->x,
event->y),
},
.position = sc_input_manager_get_position(im, event->x, event->y),
.action = sc_action_from_sdl_mousebutton_type(event->type),
.button = sc_mouse_button_from_sdl(event->button),
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
Expand Down Expand Up @@ -839,11 +847,7 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y);

struct sc_mouse_scroll_event evt = {
.position = {
.screen_size = im->screen->frame_size,
.point = sc_screen_convert_window_to_frame_coords(im->screen,
mouse_x, mouse_y),
},
.position = sc_input_manager_get_position(im, mouse_x, mouse_y),
#if SDL_VERSION_ATLEAST(2, 0, 18)
.hscroll = CLAMP(event->preciseX, -1.0f, 1.0f),
.vscroll = CLAMP(event->preciseY, -1.0f, 1.0f),
Expand Down
1 change: 1 addition & 0 deletions app/src/options.c
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
26 changes: 16 additions & 10 deletions app/src/scrcpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ scrcpy(struct scrcpy_options *options) {
return SCRCPY_EXIT_FAILURE;
}

if (options->video_playback) {
if (options->window) {
// Set hints before starting the server thread to avoid race conditions
// in SDL
sdl_set_hints(options->render_driver);
Expand All @@ -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
Loading

0 comments on commit a26c4e7

Please sign in to comment.