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 solely 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 <#4727>
Fixes #4793 <#4793>
PR #4868 <#4868>
  • Loading branch information
rom1v committed May 11, 2024
1 parent cca2c9f commit 65b9f04
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 65 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
51 changes: 42 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 All @@ -2569,6 +2585,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],

#ifdef HAVE_V4L2
if (v4l2) {
if (!opts->video) {
LOGE("V4L2 sink requires video capture, but --no-video was set.");
return false;
}

if (opts->lock_video_orientation ==
SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) {
LOGI("Video orientation is locked for v4l2 sink. "
Expand All @@ -2588,13 +2609,25 @@ 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, 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) {
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
59 changes: 32 additions & 27 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 @@ -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
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 y = event->y;
sc_screen_hidpi_scale_coords(im->screen, &x, &y);
Expand All @@ -759,12 +773,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 +848,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
Loading

0 comments on commit 65b9f04

Please sign in to comment.