Compare commits

..

29 Commits

Author SHA1 Message Date
Romain Vimont
9ac57d6e71 hid_mouse_initialization 2022-01-01 20:05:17 +01:00
Romain Vimont
82ac944d23 refactor_hid_keyboard_init 2022-01-01 20:00:33 +01:00
Romain Vimont
b33e9ca3a7 hidmousecli 2022-01-01 19:48:44 +01:00
Romain Vimont
d140537e93 hid_mouse 2022-01-01 19:48:44 +01:00
Romain Vimont
9f80b91d72 Add CLAMP() macro 2022-01-01 19:48:44 +01:00
Romain Vimont
7dba4a9e98 relative-mouse-mode 2022-01-01 19:48:44 +01:00
Romain Vimont
9ec4017910 relative_mode 2022-01-01 19:48:44 +01:00
Romain Vimont
a97151707e Add relative mouse motion in event
This will allow the mouse processor to handle relative motion easily.
2022-01-01 19:48:44 +01:00
Romain Vimont
e84cdf963c Destroy acksync immediately on error
If AOA or HID keyboard may not be initialized for some reason, acksync
is useless.
2022-01-01 19:47:47 +01:00
Romain Vimont
4fcace8ab0 Remove duplicate boolean
The AOA initialization state is already tracked by aoa_hid_initialized.
2022-01-01 19:45:50 +01:00
Romain Vimont
e9bef7e880 Reorder controller and HID initialization
This allows to merge two "#ifdef HAVE_AOA_HID" blocks to simplify.
2022-01-01 19:38:27 +01:00
Romain Vimont
eb23a2afc8 Move input_manager into screen
The input_manager is strongly tied to the screen, it could not work
independently of the specific screen implementation.

To implement a user-friendly HID mouse behavior, some SDL events
will need to be handled both by the screen and by the input manager. For
example, a click must typically be handled by the input_manager so that
it is forwarded to the device, but in HID mouse mode, the first click
should be handled by the screen to capture the mouse (enable relative
mouse mode).

Make the input_manager a descendant of the screen, so that the screen
decides what to do on SDL events.

Concretely, replace this structure hierarchy:

     +- struct scrcpy
        +- struct input_manager
        +- struct screen

by this one:

     +- struct scrcpy
        +- struct screen
           +- struct input_manager
2022-01-01 19:01:27 +01:00
Romain Vimont
c7038da147 Use separate struct for input manager params
This avoids to directly pass the options instance (which contains more
data than strictly necessary), and limit the number of parameters for
the init function.
2022-01-01 19:01:27 +01:00
Romain Vimont
9e4773fd24 Pass buttons state in scroll events
A scroll event might be produced when a mouse button is pressed (for
example when scrolling while selecting a text). For consistency, pass
the actual buttons state (instead of 0).

In practice, it seems that this use case does not work properly with
Android event injection, but it will work with HID mouse.
2022-01-01 19:01:27 +01:00
Romain Vimont
dada6883d6 Make some mouse processors ops optional
Do not force all mouse processors implementations to implement scroll
events or touch events.
2022-01-01 19:01:27 +01:00
Romain Vimont
65087fcf57 Make process_text() optional
Not all key processors support text injection (HID keyboard does not
support it).

Instead of providing a dummy op function, set it to NULL and check on
the caller side before calling it.
2022-01-01 19:01:27 +01:00
Romain Vimont
71c2af1aed Apply buttons mask if not --forward-all-clicks
If --forward-all-clicks is not set, then only left clicks are forwarded.
For consistency, also mask the buttons state in other events.
2022-01-01 19:01:27 +01:00
Romain Vimont
4ab8775ed0 Reorder mouse processor ops
Group the mouse events callbacks before the touch event callback.
2022-01-01 19:01:27 +01:00
Romain Vimont
396bbc1a84 Simplify mouse injection implementation
The static functions are now so simple they become unnecessary: the
control message may be initialized directly instead.
2022-01-01 19:01:27 +01:00
Romain Vimont
f92e9edd1d Make some event conversions infallible
When the implementation handles all possible input values, it may never
fail.
2022-01-01 19:01:27 +01:00
Romain Vimont
07e46dabe4 Use scrcpy input events for mouse processors
Pass scrcpy input events instead of SDL input events to mouse
processors.

These events represent exactly what mouse processors need, abstracted
from any visual orientation and scaling applied on the SDL window.

This makes the mouse processors independent of the "screen" instance,
and the implementation source code independent of the SDL API.
2022-01-01 19:01:27 +01:00
Romain Vimont
abc5bcc6df Use scrcpy input events for key processors
Pass scrcpy input events instead of SDL input events to key processors.

This makes the source code of key processors independent of the SDL API.
2022-01-01 19:01:27 +01:00
Romain Vimont
2ba699902e Use common sc_action in input manager
Now that the scrcpy input events API exposes a sc_action enum, use the
same from the input manager.
2022-01-01 19:01:27 +01:00
Romain Vimont
191339987f Add intermediate input events layer
This aims to make the key/mouse processors independent of the "screen",
instead of processing SDL events themselves.

In particular, these scrcpy events are not impacted by any UI window
scaling or rotation (contrary to SDL events).
2022-01-01 19:01:27 +01:00
Romain Vimont
8cd245214d Rename SC_MOD_* to SC_SHORTCUT_MOD_*
This will avoid conflicts with new SC_MOD_* constants.
2022-01-01 19:01:27 +01:00
Romain Vimont
cc59906a38 Remove actions bitset
The input manager exposed functions taking an "actions" parameter,
containing a bitmask-OR of ACTION_UP and ACTION_DOWN.

But they are never called with both actions simultaneously anymore, so
simplify.

Refs 964b6d2243
Refs d0739911a3
2022-01-01 19:01:27 +01:00
Romain Vimont
7bd3da79b6 Expose V4L2 option on all platforms
This allows to report a meaningful error message if an unsupported
feature is used on another platform. This is consistent with the
behavior of -K/--hid-keyboard.
2022-01-01 19:01:27 +01:00
Romain Vimont
d76bf4c50c Fail on unsupported HID option
If the feature is not supported on the platform, fail during command
line parsing instead of using a fallback.
2022-01-01 19:01:26 +01:00
Romain Vimont
6b9f397733 Happy new year 2022! 2022-01-01 17:20:36 +01:00
16 changed files with 146 additions and 75 deletions

View File

@@ -188,7 +188,7 @@
identification within third-party archives.
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2021 Romain Vimont
Copyright (C) 2018-2022 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -672,7 +672,7 @@ Baca [halaman pengembang].
## Lisensi
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2021 Romain Vimont
Copyright (C) 2018-2022 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -790,7 +790,7 @@ Leggi la [pagina per sviluppatori].
## Licenza (in inglese)
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2021 Romain Vimont
Copyright (C) 2018-2022 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -776,7 +776,7 @@ _⁴Android 7以上のみ._
## ライセンス
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2021 Romain Vimont
Copyright (C) 2018-2022 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -475,7 +475,7 @@ _²화면이 꺼진 상태에서 우클릭 시 다시 켜지며, 그 외의 상
## 라이선스
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2021 Romain Vimont
Copyright (C) 2018-2022 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -1017,7 +1017,7 @@ Read the [developers page].
## Licence
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2021 Romain Vimont
Copyright (C) 2018-2022 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -857,7 +857,7 @@ Leia a [página dos desenvolvedores][developers page].
## Licença
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2021 Romain Vimont
Copyright (C) 2018-2022 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -720,7 +720,7 @@ Lea la [hoja de desarrolladores (en inglés)](DEVELOP.md).
## Licencia
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2021 Romain Vimont
Copyright (C) 2018-2022 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -801,7 +801,7 @@ Bakınız [FAQ](FAQ.md).
## Lisans
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2021 Romain Vimont
Copyright (C) 2018-2022 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -842,7 +842,7 @@ ADB=/path/to/adb scrcpy
## 许可协议
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2021 Romain Vimont
Copyright (C) 2018-2022 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -679,7 +679,7 @@ _³只支援 Android 7+。_
## Licence
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2021 Romain Vimont
Copyright (C) 2018-2022 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -446,7 +446,7 @@ Copyright \(co 2018 Genymobile
Genymobile
.UE
Copyright \(co 2018\-2021
Copyright \(co 2018\-2022
.MT rom@rom1v.com
Romain Vimont
.ME

View File

@@ -214,6 +214,17 @@ static const struct sc_option options[] = {
.text = "Limit the frame rate of screen capture (officially supported "
"since Android 10, but may work on earlier versions).",
},
{
.shortopt = 'M',
.longopt = "hid-mouse",
.text = "Simulate a physical mouse by using HID over AOAv2.\n"
"In this mode, the computer mouse is captured to control the "
"device directly (relative mouse mode).\n"
"LAlt, LSuper or RSuper toggle the capture mode, to give "
"control of the mouse back to the computer.\n"
"It may only work over USB, and is currently only supported "
"on Linux.\n",
},
{
.shortopt = 'm',
.longopt = "max-size",
@@ -240,11 +251,8 @@ static const struct sc_option options[] = {
{
.shortopt = 'N',
.longopt = "no-display",
.text = "Do not display device (only when screen recording "
#ifdef HAVE_V4L2
"or V4L2 sink "
#endif
"is enabled).",
.text = "Do not display device (only when screen recording or V4L2 "
"sink is enabled).",
},
{
.longopt_id = OPT_NO_KEY_REPEAT,
@@ -381,14 +389,14 @@ static const struct sc_option options[] = {
"Default is 0 (not forced): the local port used for "
"establishing the tunnel will be used.",
},
#ifdef HAVE_V4L2
{
.longopt_id = OPT_V4L2_SINK,
.longopt = "v4l2-sink",
.argdesc = "/dev/videoN",
.text = "Output to v4l2loopback device.\n"
"It requires to lock the video orientation (see "
"--lock-video-orientation).",
"--lock-video-orientation).\n"
"This feature is only available on Linux.",
},
{
.longopt_id = OPT_V4L2_BUFFER,
@@ -398,9 +406,9 @@ static const struct sc_option options[] = {
"frames. This increases latency to compensate for jitter.\n"
"This option is similar to --display-buffer, but specific to "
"V4L2 sink.\n"
"This feature is only available on Linux.\n"
"Default is 0 (no buffering).",
},
#endif
{
.shortopt = 'V',
.longopt = "verbosity",
@@ -1300,7 +1308,13 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
args->help = true;
break;
case 'K':
#ifdef HAVE_AOA_HID
opts->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_HID;
#else
LOGE("HID over AOA (-K/--hid-keyboard) is not supported on "
"this platform. It is only available on Linux.");
return false;
#endif
break;
case OPT_MAX_FPS:
if (!parse_max_fps(optarg, &opts->max_fps)) {
@@ -1312,6 +1326,15 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false;
}
break;
case 'M':
#ifdef HAVE_AOA_HID
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_HID;
#else
LOGE("HID over AOA (-M/--hid-mouse) is not supported on this"
"platform. It is only available on Linux.");
return false;
#endif
break;
case OPT_LOCK_VIDEO_ORIENTATION:
if (!parse_lock_video_orientation(optarg,
&opts->lock_video_orientation)) {
@@ -1464,16 +1487,24 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
opts->tcpip = true;
opts->tcpip_dst = optarg;
break;
#ifdef HAVE_V4L2
case OPT_V4L2_SINK:
#ifdef HAVE_V4L2
opts->v4l2_device = optarg;
#else
LOGE("V4L2 (--v4l2-sink) is only available on Linux.");
return false;
#endif
break;
case OPT_V4L2_BUFFER:
#ifdef HAVE_V4L2
if (!parse_buffering_time(optarg, &opts->v4l2_buffer)) {
return false;
}
break;
#else
LOGE("V4L2 (--v4l2-buffer) is only available on Linux.");
return false;
#endif
break;
default:
// getopt prints the error message on stderr
return false;

View File

@@ -38,6 +38,11 @@ enum sc_keyboard_input_mode {
SC_KEYBOARD_INPUT_MODE_HID,
};
enum sc_mouse_input_mode {
SC_MOUSE_INPUT_MODE_INJECT,
SC_MOUSE_INPUT_MODE_HID,
};
enum sc_key_inject_mode {
// Inject special keys, letters and space as key events.
// Inject numbers and punctuation as text events.
@@ -90,6 +95,7 @@ struct scrcpy_options {
enum sc_log_level log_level;
enum sc_record_format record_format;
enum sc_keyboard_input_mode keyboard_input_mode;
enum sc_mouse_input_mode mouse_input_mode;
struct sc_port_range port_range;
uint32_t tunnel_host;
uint16_t tunnel_port;

View File

@@ -335,6 +335,8 @@ scrcpy(struct scrcpy_options *options) {
bool stream_started = false;
#ifdef HAVE_AOA_HID
bool aoa_hid_initialized = false;
bool hid_keyboard_initialized = false;
bool hid_mouse_initialized = false;
#endif
bool controller_initialized = false;
bool controller_started = false;
@@ -454,15 +456,95 @@ scrcpy(struct scrcpy_options *options) {
if (options->control) {
#ifdef HAVE_AOA_HID
if (options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_HID) {
bool use_hid_keyboard =
options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_HID;
bool use_hid_mouse =
options->mouse_input_mode == SC_MOUSE_INPUT_MODE_HID;
if (use_hid_keyboard || use_hid_mouse) {
bool ok = sc_acksync_init(&s->acksync);
if (!ok) {
goto end;
}
ok = sc_aoa_init(&s->aoa, serial, &s->acksync);
if (!ok) {
LOGE("Failed to enable HID over AOA");
sc_acksync_destroy(&s->acksync);
goto aoa_hid_end;
}
if (use_hid_keyboard) {
if (sc_hid_keyboard_init(&s->keyboard_hid, &s->aoa)) {
hid_keyboard_initialized = true;
kp = &s->keyboard_hid.key_processor;
} else {
LOGE("Could not initialize HID keyboard");
}
}
if (use_hid_mouse) {
if (sc_hid_mouse_init(&s->mouse_hid, &s->aoa)) {
hid_mouse_initialized = true;
mp = &s->mouse_hid.mouse_processor;
} else {
LOGE("Could not initialized HID mouse");
}
}
bool need_aoa = hid_keyboard_initialized || hid_mouse_initialized;
if (!need_aoa || !sc_aoa_start(&s->aoa)) {
sc_acksync_destroy(&s->acksync);
sc_aoa_destroy(&s->aoa);
goto aoa_hid_end;
}
acksync = &s->acksync;
aoa_hid_initialized = true;
aoa_hid_end:
if (!aoa_hid_initialized) {
if (hid_keyboard_initialized) {
sc_hid_keyboard_destroy(&s->keyboard_hid);
hid_keyboard_initialized = false;
}
if (hid_mouse_initialized) {
sc_hid_mouse_destroy(&s->mouse_hid);
hid_mouse_initialized = false;
}
}
if (use_hid_keyboard && !hid_keyboard_initialized) {
LOGE("Fallback to default keyboard injection method "
"(-K/--hid-keyboard ignored)");
options->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_INJECT;
}
if (use_hid_mouse && !hid_mouse_initialized) {
LOGE("Fallback to default mouse injection method "
"(-M/--hid-mouse ignored)");
options->mouse_input_mode = SC_MOUSE_INPUT_MODE_INJECT;
}
}
#else
assert(options->keyboard_input_mode != SC_KEYBOARD_INPUT_MODE_HID);
assert(options->mouse_input_mode != SC_MOUSE_INPUT_MODE_HID);
#endif
// keyboard_input_mode may have been reset if HID mode failed
if (options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_INJECT) {
sc_keyboard_inject_init(&s->keyboard_inject, &s->controller,
options);
kp = &s->keyboard_inject.key_processor;
}
// mouse_input_mode may have been reset if HID mode failed
if (options->mouse_input_mode == SC_MOUSE_INPUT_MODE_INJECT) {
sc_mouse_inject_init(&s->mouse_inject, &s->controller);
mp = &s->mouse_inject.mouse_processor;
}
if (!controller_init(&s->controller, s->server.control_socket,
acksync)) {
goto end;
@@ -484,56 +566,6 @@ scrcpy(struct scrcpy_options *options) {
}
}
if (options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_HID) {
#ifdef HAVE_AOA_HID
bool aoa_hid_ok = false;
bool ok = sc_aoa_init(&s->aoa, serial, acksync);
if (!ok) {
goto aoa_hid_end;
}
if (!sc_hid_keyboard_init(&s->keyboard_hid, &s->aoa)) {
sc_aoa_destroy(&s->aoa);
goto aoa_hid_end;
}
if (!sc_aoa_start(&s->aoa)) {
sc_hid_keyboard_destroy(&s->keyboard_hid);
sc_aoa_destroy(&s->aoa);
goto aoa_hid_end;
}
aoa_hid_ok = true;
kp = &s->keyboard_hid.key_processor;
aoa_hid_initialized = true;
aoa_hid_end:
if (!aoa_hid_ok) {
LOGE("Failed to enable HID over AOA, "
"fallback to default keyboard injection method "
"(-K/--hid-keyboard ignored)");
options->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_INJECT;
}
#else
LOGE("HID over AOA is not supported on this platform, "
"fallback to default keyboard injection method "
"(-K/--hid-keyboard ignored)");
options->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_INJECT;
#endif
}
// keyboard_input_mode may have been reset if HID mode failed
if (options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_INJECT) {
sc_keyboard_inject_init(&s->keyboard_inject, &s->controller,
options);
kp = &s->keyboard_inject.key_processor;
}
//sc_mouse_inject_init(&s->mouse_inject, &s->controller);
sc_hid_mouse_init(&s->mouse_hid, &s->aoa);
mp = &s->mouse_hid.mouse_processor;
}
if (options->display) {
@@ -603,7 +635,9 @@ end:
// end-of-stream
#ifdef HAVE_AOA_HID
if (aoa_hid_initialized) {
sc_hid_keyboard_destroy(&s->keyboard_hid);
if (hid_keyboard_initialized) {
sc_hid_keyboard_destroy(&s->keyboard_hid);
}
sc_aoa_stop(&s->aoa);
}
if (acksync) {

View File

@@ -861,8 +861,8 @@ screen_handle_event(struct screen *screen, SDL_Event *event) {
case SDL_MOUSEBUTTONUP:
if (screen->im.mp->relative_mode && !screen->mouse_captured) {
screen_capture_mouse(screen, true);
return true;
}
return true;
}
return input_manager_handle_event(&screen->im, event);