Skip to content

Commit 65b9f04

Browse files
committed
Add scrcpy window without video playback
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>
1 parent cca2c9f commit 65b9f04

File tree

10 files changed

+243
-65
lines changed

10 files changed

+243
-65
lines changed

app/scrcpy.1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,10 @@ Disable video forwarding.
316316
.B \-\-no\-video\-playback
317317
Disable video playback on the computer.
318318

319+
.TP
320+
.B \-\-no\-window
321+
Disable scrcpy window. Implies --no-video-playback and --no-control.
322+
319323
.TP
320324
.BI "\-\-orientation " value
321325
Same as --display-orientation=value --record-orientation=value.

app/src/cli.c

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ enum {
9797
OPT_MOUSE,
9898
OPT_HID_KEYBOARD_DEPRECATED,
9999
OPT_HID_MOUSE_DEPRECATED,
100+
OPT_NO_WINDOW,
100101
};
101102

102103
struct sc_option {
@@ -566,6 +567,12 @@ static const struct sc_option options[] = {
566567
.longopt = "no-video-playback",
567568
.text = "Disable video playback on the computer.",
568569
},
570+
{
571+
.longopt_id = OPT_NO_WINDOW,
572+
.longopt = "no-window",
573+
.text = "Disable scrcpy window. Implies --no-video-playback and "
574+
"--no-control.",
575+
},
569576
{
570577
.longopt_id = OPT_ORIENTATION,
571578
.longopt = "orientation",
@@ -2486,6 +2493,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
24862493
case OPT_CAMERA_HIGH_SPEED:
24872494
opts->camera_high_speed = true;
24882495
break;
2496+
case OPT_NO_WINDOW:
2497+
opts->window = false;
2498+
break;
24892499
default:
24902500
// getopt prints the error message on stderr
24912501
return false;
@@ -2523,6 +2533,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
25232533
v4l2 = !!opts->v4l2_device;
25242534
#endif
25252535

2536+
if (!opts->window) {
2537+
// Without window, there cannot be any video playback or control
2538+
opts->video_playback = false;
2539+
opts->control = false;
2540+
}
2541+
25262542
if (!opts->video) {
25272543
opts->video_playback = false;
25282544
// 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[],
25442560
opts->audio = false;
25452561
}
25462562

2547-
if (!opts->video && !opts->audio && !otg) {
2548-
LOGE("No video, no audio, no OTG: nothing to do");
2563+
if (!opts->video && !opts->audio && !opts->control && !otg) {
2564+
LOGE("No video, no audio, no control, no OTG: nothing to do");
25492565
return false;
25502566
}
25512567

@@ -2569,6 +2585,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
25692585

25702586
#ifdef HAVE_V4L2
25712587
if (v4l2) {
2588+
if (!opts->video) {
2589+
LOGE("V4L2 sink requires video capture, but --no-video was set.");
2590+
return false;
2591+
}
2592+
25722593
if (opts->lock_video_orientation ==
25732594
SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) {
25742595
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[],
25882609
}
25892610
#endif
25902611

2591-
if (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AUTO) {
2592-
opts->keyboard_input_mode = otg ? SC_KEYBOARD_INPUT_MODE_AOA
2593-
: SC_KEYBOARD_INPUT_MODE_SDK;
2594-
}
2595-
if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_AUTO) {
2596-
opts->mouse_input_mode = otg ? SC_MOUSE_INPUT_MODE_AOA
2597-
: SC_MOUSE_INPUT_MODE_SDK;
2612+
if (opts->control) {
2613+
if (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AUTO) {
2614+
opts->keyboard_input_mode = otg ? SC_KEYBOARD_INPUT_MODE_AOA
2615+
: SC_KEYBOARD_INPUT_MODE_SDK;
2616+
}
2617+
if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_AUTO) {
2618+
if (otg) {
2619+
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_AOA;
2620+
} else if (!opts->video_playback) {
2621+
LOGI("No video mirroring, mouse mode switched to UHID");
2622+
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_UHID;
2623+
} else {
2624+
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_SDK;
2625+
}
2626+
} else if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_SDK
2627+
&& !opts->video_playback) {
2628+
LOGE("SDK mouse mode requires video playback. Try --mouse=uhid.");
2629+
return false;
2630+
}
25982631
}
25992632

26002633
if (otg) {

app/src/display.c

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,30 @@
55

66
#include "util/log.h"
77

8+
static bool
9+
sc_display_init_novideo_icon(struct sc_display *display,
10+
SDL_Surface *icon_novideo) {
11+
assert(icon_novideo);
12+
13+
if (SDL_RenderSetLogicalSize(display->renderer,
14+
icon_novideo->w, icon_novideo->h)) {
15+
LOGW("Could not set renderer logical size: %s", SDL_GetError());
16+
// don't fail
17+
}
18+
19+
display->texture = SDL_CreateTextureFromSurface(display->renderer,
20+
icon_novideo);
21+
if (!display->texture) {
22+
LOGE("Could not create texture: %s", SDL_GetError());
23+
return false;
24+
}
25+
26+
return true;
27+
}
28+
829
bool
9-
sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps) {
30+
sc_display_init(struct sc_display *display, SDL_Window *window,
31+
SDL_Surface *icon_novideo, bool mipmaps) {
1032
display->renderer =
1133
SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
1234
if (!display->renderer) {
@@ -68,6 +90,18 @@ sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps) {
6890
display->pending.frame = NULL;
6991
display->has_frame = false;
7092

93+
if (icon_novideo) {
94+
// Without video, set a static scrcpy icon as window content
95+
bool ok = sc_display_init_novideo_icon(display, icon_novideo);
96+
if (!ok) {
97+
#ifdef SC_DISPLAY_FORCE_OPENGL_CORE_PROFILE
98+
SDL_GL_DeleteContext(display->gl_context);
99+
#endif
100+
SDL_DestroyRenderer(display->renderer);
101+
return false;
102+
}
103+
}
104+
71105
return true;
72106
}
73107

app/src/display.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ enum sc_display_result {
4444
};
4545

4646
bool
47-
sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps);
47+
sc_display_init(struct sc_display *display, SDL_Window *window,
48+
SDL_Surface *icon_novideo, bool mipmaps);
4849

4950
void
5051
sc_display_destroy(struct sc_display *display);

app/src/input_manager.c

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
403403
// controller is NULL if --no-control is requested
404404
bool control = im->controller;
405405
bool paused = im->screen->paused;
406+
bool video = im->screen->video;
406407

407408
SDL_Keycode keycode = event->keysym.sym;
408409
uint16_t mod = event->keysym.mod;
@@ -462,13 +463,13 @@ sc_input_manager_process_key(struct sc_input_manager *im,
462463
}
463464
return;
464465
case SDLK_z:
465-
if (down && !repeat) {
466+
if (video && down && !repeat) {
466467
sc_screen_set_paused(im->screen, !shift);
467468
}
468469
return;
469470
case SDLK_DOWN:
470471
if (shift) {
471-
if (!repeat && down) {
472+
if (video && !repeat && down) {
472473
apply_orientation_transform(im,
473474
SC_ORIENTATION_FLIP_180);
474475
}
@@ -479,7 +480,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
479480
return;
480481
case SDLK_UP:
481482
if (shift) {
482-
if (!repeat && down) {
483+
if (video && !repeat && down) {
483484
apply_orientation_transform(im,
484485
SC_ORIENTATION_FLIP_180);
485486
}
@@ -489,7 +490,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
489490
}
490491
return;
491492
case SDLK_LEFT:
492-
if (!repeat && down) {
493+
if (video && !repeat && down) {
493494
if (shift) {
494495
apply_orientation_transform(im,
495496
SC_ORIENTATION_FLIP_0);
@@ -500,7 +501,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
500501
}
501502
return;
502503
case SDLK_RIGHT:
503-
if (!repeat && down) {
504+
if (video && !repeat && down) {
504505
if (shift) {
505506
apply_orientation_transform(im,
506507
SC_ORIENTATION_FLIP_0);
@@ -533,22 +534,22 @@ sc_input_manager_process_key(struct sc_input_manager *im,
533534
}
534535
return;
535536
case SDLK_f:
536-
if (!shift && !repeat && down) {
537+
if (video && !shift && !repeat && down) {
537538
sc_screen_switch_fullscreen(im->screen);
538539
}
539540
return;
540541
case SDLK_w:
541-
if (!shift && !repeat && down) {
542+
if (video && !shift && !repeat && down) {
542543
sc_screen_resize_to_fit(im->screen);
543544
}
544545
return;
545546
case SDLK_g:
546-
if (!shift && !repeat && down) {
547+
if (video && !shift && !repeat && down) {
547548
sc_screen_resize_to_pixel_perfect(im->screen);
548549
}
549550
return;
550551
case SDLK_i:
551-
if (!shift && !repeat && down) {
552+
if (video && !shift && !repeat && down) {
552553
switch_fps_counter_state(im);
553554
}
554555
return;
@@ -625,6 +626,23 @@ sc_input_manager_process_key(struct sc_input_manager *im,
625626
im->kp->ops->process_key(im->kp, &evt, ack_to_wait);
626627
}
627628

629+
static struct sc_position
630+
sc_input_manager_get_position(struct sc_input_manager *im, int32_t x,
631+
int32_t y) {
632+
if (im->mp->relative_mode) {
633+
// No absolute position
634+
return (struct sc_position) {
635+
.screen_size = {0, 0},
636+
.point = {0, 0},
637+
};
638+
}
639+
640+
return (struct sc_position) {
641+
.screen_size = im->screen->frame_size,
642+
.point = sc_screen_convert_window_to_frame_coords(im->screen, x, y),
643+
};
644+
}
645+
628646
static void
629647
sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
630648
const SDL_MouseMotionEvent *event) {
@@ -634,12 +652,7 @@ sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
634652
}
635653

636654
struct sc_mouse_motion_event evt = {
637-
.position = {
638-
.screen_size = im->screen->frame_size,
639-
.point = sc_screen_convert_window_to_frame_coords(im->screen,
640-
event->x,
641-
event->y),
642-
},
655+
.position = sc_input_manager_get_position(im, event->x, event->y),
643656
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
644657
: POINTER_ID_GENERIC_FINGER,
645658
.xrel = event->xrel,
@@ -735,7 +748,8 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
735748
}
736749

737750
// double-click on black borders resize to fit the device screen
738-
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
751+
bool video = im->screen->video;
752+
if (video && event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
739753
int32_t x = event->x;
740754
int32_t y = event->y;
741755
sc_screen_hidpi_scale_coords(im->screen, &x, &y);
@@ -759,12 +773,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
759773
uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL);
760774

761775
struct sc_mouse_click_event evt = {
762-
.position = {
763-
.screen_size = im->screen->frame_size,
764-
.point = sc_screen_convert_window_to_frame_coords(im->screen,
765-
event->x,
766-
event->y),
767-
},
776+
.position = sc_input_manager_get_position(im, event->x, event->y),
768777
.action = sc_action_from_sdl_mousebutton_type(event->type),
769778
.button = sc_mouse_button_from_sdl(event->button),
770779
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
@@ -839,11 +848,7 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
839848
uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y);
840849

841850
struct sc_mouse_scroll_event evt = {
842-
.position = {
843-
.screen_size = im->screen->frame_size,
844-
.point = sc_screen_convert_window_to_frame_coords(im->screen,
845-
mouse_x, mouse_y),
846-
},
851+
.position = sc_input_manager_get_position(im, mouse_x, mouse_y),
847852
#if SDL_VERSION_ATLEAST(2, 0, 18)
848853
.hscroll = CLAMP(event->preciseX, -1.0f, 1.0f),
849854
.vscroll = CLAMP(event->preciseY, -1.0f, 1.0f),

app/src/options.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ const struct scrcpy_options scrcpy_options_default = {
8989
.kill_adb_on_close = false,
9090
.camera_high_speed = false,
9191
.list = 0,
92+
.window = true,
9293
};
9394

9495
enum sc_orientation

app/src/options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ struct scrcpy_options {
279279
#define SC_OPTION_LIST_CAMERAS 0x4
280280
#define SC_OPTION_LIST_CAMERA_SIZES 0x8
281281
uint8_t list;
282+
bool window;
282283
};
283284

284285
extern const struct scrcpy_options scrcpy_options_default;

0 commit comments

Comments
 (0)