Compare commits
9 Commits
open_a_ter
...
input_even
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b6774174d | ||
|
|
4640b088ef | ||
|
|
bbe8a99a3e | ||
|
|
43416b6654 | ||
|
|
7f781f1085 | ||
|
|
c4648f1078 | ||
|
|
eec2d1d160 | ||
|
|
ccfb4d3cfd | ||
|
|
49a788841d |
14
FAQ.md
14
FAQ.md
@@ -219,9 +219,6 @@ scrcpy -m 1024
|
||||
scrcpy -m 800
|
||||
```
|
||||
|
||||
Since scrcpy v1.22, scrcpy automatically tries again with a lower definition
|
||||
before failing. This behavior can be disabled with `--no-downsize-on-error`.
|
||||
|
||||
You could also try another [encoder](README.md#encoder).
|
||||
|
||||
|
||||
@@ -248,15 +245,8 @@ Caused by: java.lang.IllegalArgumentException: displayToken must not be null
|
||||
|
||||
## Command line on Windows
|
||||
|
||||
Since v1.22, a "shortcut" has been added to directly open a terminal in the
|
||||
scrcpy directory. Double-click on `open_a_terminal_here.bat`, then type your
|
||||
command. For example:
|
||||
|
||||
```
|
||||
scrcpy --record file.mkv
|
||||
```
|
||||
|
||||
You could also open a terminal and go to the scrcpy folder manually:
|
||||
Some Windows users are not familiar with the command line. Here is how to open a
|
||||
terminal and run `scrcpy` with arguments:
|
||||
|
||||
1. Press <kbd>Windows</kbd>+<kbd>r</kbd>, this opens a dialog box.
|
||||
2. Type `cmd` and press <kbd>Enter</kbd>, this opens a terminal.
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -188,7 +188,7 @@
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright (C) 2018 Genymobile
|
||||
Copyright (C) 2018-2022 Romain Vimont
|
||||
Copyright (C) 2018-2021 Romain Vimont
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -672,7 +672,7 @@ Baca [halaman pengembang].
|
||||
## Lisensi
|
||||
|
||||
Copyright (C) 2018 Genymobile
|
||||
Copyright (C) 2018-2022 Romain Vimont
|
||||
Copyright (C) 2018-2021 Romain Vimont
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -790,7 +790,7 @@ Leggi la [pagina per sviluppatori].
|
||||
## Licenza (in inglese)
|
||||
|
||||
Copyright (C) 2018 Genymobile
|
||||
Copyright (C) 2018-2022 Romain Vimont
|
||||
Copyright (C) 2018-2021 Romain Vimont
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -776,7 +776,7 @@ _⁴Android 7以上のみ._
|
||||
## ライセンス
|
||||
|
||||
Copyright (C) 2018 Genymobile
|
||||
Copyright (C) 2018-2022 Romain Vimont
|
||||
Copyright (C) 2018-2021 Romain Vimont
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -475,7 +475,7 @@ _²화면이 꺼진 상태에서 우클릭 시 다시 켜지며, 그 외의 상
|
||||
## 라이선스
|
||||
|
||||
Copyright (C) 2018 Genymobile
|
||||
Copyright (C) 2018-2022 Romain Vimont
|
||||
Copyright (C) 2018-2021 Romain Vimont
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
33
README.md
33
README.md
@@ -31,8 +31,6 @@ Its features include:
|
||||
- device screen [as a webcam (V4L2)](#v4l2loopback) (Linux-only)
|
||||
- [physical keyboard simulation (HID)](#physical-keyboard-simulation-hid)
|
||||
(Linux-only)
|
||||
- [physical mouse simulation (HID)](#physical-mouse-simulation-hid)
|
||||
(Linux-only)
|
||||
- and more…
|
||||
|
||||
## Requirements
|
||||
@@ -817,35 +815,6 @@ a physical keyboard is connected).
|
||||
|
||||
[Physical keyboard]: https://github.com/Genymobile/scrcpy/pull/2632#issuecomment-923756915
|
||||
|
||||
#### Physical mouse simulation (HID)
|
||||
|
||||
Similarly to the physical keyboard simulation, it is possible to simulate a
|
||||
physical mouse. Likewise, it only works if the device is connected by USB, and
|
||||
is currently only supported on Linux.
|
||||
|
||||
By default, scrcpy uses Android mouse events injection, using absolute
|
||||
coordinates. By simulating a physical mouse, a mouse pointer appears on the
|
||||
Android device, and relative mouse motion, clicks and scrolls are injected.
|
||||
|
||||
To enable this mode:
|
||||
|
||||
```bash
|
||||
scrcpy --hid-mouse
|
||||
scrcpy -M # short version
|
||||
```
|
||||
|
||||
You could also add `--forward-all-clicks` to [forward all mouse
|
||||
buttons][forward_all_clicks].
|
||||
|
||||
[forward_all_clicks]: #right-click-and-middle-click
|
||||
|
||||
When this mode is enabled, the computer mouse is "captured" (the mouse pointer
|
||||
disappears from the computer and appears on the Android device instead).
|
||||
|
||||
Special capture keys, either <kbd>Alt</kbd> or <kbd>Super</kbd>, toggle
|
||||
(disable or enable) the mouse capture. Use one of them to give the control of
|
||||
the mouse back to the computer.
|
||||
|
||||
|
||||
#### Text injection preference
|
||||
|
||||
@@ -1048,7 +1017,7 @@ Read the [developers page].
|
||||
## Licence
|
||||
|
||||
Copyright (C) 2018 Genymobile
|
||||
Copyright (C) 2018-2022 Romain Vimont
|
||||
Copyright (C) 2018-2021 Romain Vimont
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -857,7 +857,7 @@ Leia a [página dos desenvolvedores][developers page].
|
||||
## Licença
|
||||
|
||||
Copyright (C) 2018 Genymobile
|
||||
Copyright (C) 2018-2022 Romain Vimont
|
||||
Copyright (C) 2018-2021 Romain Vimont
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -720,7 +720,7 @@ Lea la [hoja de desarrolladores (en inglés)](DEVELOP.md).
|
||||
## Licencia
|
||||
|
||||
Copyright (C) 2018 Genymobile
|
||||
Copyright (C) 2018-2022 Romain Vimont
|
||||
Copyright (C) 2018-2021 Romain Vimont
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -801,7 +801,7 @@ Bakınız [FAQ](FAQ.md).
|
||||
## Lisans
|
||||
|
||||
Copyright (C) 2018 Genymobile
|
||||
Copyright (C) 2018-2022 Romain Vimont
|
||||
Copyright (C) 2018-2021 Romain Vimont
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -842,7 +842,7 @@ ADB=/path/to/adb scrcpy
|
||||
## 许可协议
|
||||
|
||||
Copyright (C) 2018 Genymobile
|
||||
Copyright (C) 2018-2022 Romain Vimont
|
||||
Copyright (C) 2018-2021 Romain Vimont
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -679,7 +679,7 @@ _³只支援 Android 7+。_
|
||||
## Licence
|
||||
|
||||
Copyright (C) 2018 Genymobile
|
||||
Copyright (C) 2018-2022 Romain Vimont
|
||||
Copyright (C) 2018-2021 Romain Vimont
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -11,9 +11,10 @@ src = [
|
||||
'src/decoder.c',
|
||||
'src/device_msg.c',
|
||||
'src/icon.c',
|
||||
'src/file_pusher.c',
|
||||
'src/file_handler.c',
|
||||
'src/fps_counter.c',
|
||||
'src/frame_buffer.c',
|
||||
'src/input_events.c',
|
||||
'src/input_manager.c',
|
||||
'src/keyboard_inject.c',
|
||||
'src/mouse_inject.c',
|
||||
@@ -77,7 +78,6 @@ if aoa_hid_support
|
||||
src += [
|
||||
'src/aoa_hid.c',
|
||||
'src/hid_keyboard.c',
|
||||
'src/hid_mouse.c',
|
||||
]
|
||||
endif
|
||||
|
||||
@@ -118,20 +118,15 @@ else
|
||||
include_directories: include_directories(sdl2_include_dir)
|
||||
)
|
||||
|
||||
prebuilt_ffmpeg = meson.get_cross_property('prebuilt_ffmpeg')
|
||||
ffmpeg_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_ffmpeg + '/bin'
|
||||
ffmpeg_include_dir = '../prebuilt-deps/' + prebuilt_ffmpeg + '/include'
|
||||
|
||||
# ffmpeg versions are different for win32 and win64 builds
|
||||
ffmpeg_avcodec = meson.get_cross_property('ffmpeg_avcodec')
|
||||
ffmpeg_avformat = meson.get_cross_property('ffmpeg_avformat')
|
||||
ffmpeg_avutil = meson.get_cross_property('ffmpeg_avutil')
|
||||
|
||||
prebuilt_ffmpeg_shared = meson.get_cross_property('prebuilt_ffmpeg_shared')
|
||||
prebuilt_ffmpeg_dev = meson.get_cross_property('prebuilt_ffmpeg_dev')
|
||||
ffmpeg_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_ffmpeg_shared + '/bin'
|
||||
ffmpeg_include_dir = '../prebuilt-deps/' + prebuilt_ffmpeg_dev + '/include'
|
||||
ffmpeg = declare_dependency(
|
||||
dependencies: [
|
||||
cc.find_library(ffmpeg_avcodec, dirs: ffmpeg_bin_dir),
|
||||
cc.find_library(ffmpeg_avformat, dirs: ffmpeg_bin_dir),
|
||||
cc.find_library(ffmpeg_avutil, dirs: ffmpeg_bin_dir),
|
||||
cc.find_library('avcodec-58', dirs: ffmpeg_bin_dir),
|
||||
cc.find_library('avformat-58', dirs: ffmpeg_bin_dir),
|
||||
cc.find_library('avutil-56', dirs: ffmpeg_bin_dir),
|
||||
],
|
||||
include_directories: include_directories(ffmpeg_include_dir)
|
||||
)
|
||||
|
||||
22
app/scrcpy.1
22
app/scrcpy.1
@@ -96,8 +96,6 @@ The keyboard layout must be configured (once and for all) on the device, via Set
|
||||
|
||||
However, the option is only available when the HID keyboard is enabled (or a physical keyboard is connected).
|
||||
|
||||
Also see \fB\-\-hid\-mouse\fR.
|
||||
|
||||
.TP
|
||||
.B \-\-legacy\-paste
|
||||
Inject computer clipboard text as a sequence of key events on Ctrl+v (like MOD+Shift+v).
|
||||
@@ -122,30 +120,12 @@ Limit both the width and height of the video to \fIvalue\fR. The other dimension
|
||||
|
||||
Default is 0 (unlimited).
|
||||
|
||||
.TP
|
||||
.B \-M, \-\-hid\-mouse
|
||||
Simulate a physical mouse by using HID over AOAv2.
|
||||
|
||||
In this mode, the computer mouse is captured to control the device directly (relative mouse mode).
|
||||
|
||||
LAlt, LSuper or RSuper toggle the capture mode, to give control of the mouse back to the computer.
|
||||
|
||||
It may only work over USB, and is currently only supported on Linux.
|
||||
|
||||
Also see \fB\-\-hid\-keyboard\fR.
|
||||
|
||||
.TP
|
||||
.B \-\-no\-clipboard\-autosync
|
||||
By default, scrcpy automatically synchronizes the computer clipboard to the device clipboard before injecting Ctrl+v, and the device clipboard to the computer clipboard whenever it changes.
|
||||
|
||||
This option disables this automatic synchronization.
|
||||
|
||||
.TP
|
||||
.B \-\-no\-downsize\-on\-error
|
||||
By default, on MediaCodec error, scrcpy automatically tries again with a lower definition.
|
||||
|
||||
This option disables this behavior.
|
||||
|
||||
.TP
|
||||
.B \-n, \-\-no\-control
|
||||
Disable device control (mirror the device in read\-only).
|
||||
@@ -466,7 +446,7 @@ Copyright \(co 2018 Genymobile
|
||||
Genymobile
|
||||
.UE
|
||||
|
||||
Copyright \(co 2018\-2022
|
||||
Copyright \(co 2018\-2021
|
||||
.MT rom@rom1v.com
|
||||
Romain Vimont
|
||||
.ME
|
||||
|
||||
@@ -56,13 +56,14 @@ accept_device(libusb_device *device, const char *serial) {
|
||||
// devices available on the computer have permission restrictions
|
||||
|
||||
struct libusb_device_descriptor desc;
|
||||
int result = libusb_get_device_descriptor(device, &desc);
|
||||
if (result < 0 || !desc.iSerialNumber) {
|
||||
libusb_get_device_descriptor(device, &desc);
|
||||
|
||||
if (!desc.iSerialNumber) {
|
||||
return false;
|
||||
}
|
||||
|
||||
libusb_device_handle *handle;
|
||||
result = libusb_open(device, &handle);
|
||||
int result = libusb_open(device, &handle);
|
||||
if (result < 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -130,22 +131,31 @@ sc_aoa_init(struct sc_aoa *aoa, const char *serial,
|
||||
}
|
||||
|
||||
if (!sc_cond_init(&aoa->event_cond)) {
|
||||
goto error_destroy_mutex;
|
||||
sc_mutex_destroy(&aoa->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (libusb_init(&aoa->usb_context) != LIBUSB_SUCCESS) {
|
||||
goto error_destroy_cond;
|
||||
sc_cond_destroy(&aoa->event_cond);
|
||||
sc_mutex_destroy(&aoa->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
aoa->usb_device = sc_aoa_find_usb_device(serial);
|
||||
if (!aoa->usb_device) {
|
||||
LOGW("USB device of serial %s not found", serial);
|
||||
goto error_exit_libusb;
|
||||
libusb_exit(aoa->usb_context);
|
||||
sc_mutex_destroy(&aoa->mutex);
|
||||
sc_cond_destroy(&aoa->event_cond);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sc_aoa_open_usb_handle(aoa->usb_device, &aoa->usb_handle) < 0) {
|
||||
LOGW("Open USB handle failed");
|
||||
goto error_unref_device;
|
||||
libusb_unref_device(aoa->usb_device);
|
||||
libusb_exit(aoa->usb_context);
|
||||
sc_cond_destroy(&aoa->event_cond);
|
||||
sc_mutex_destroy(&aoa->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -153,16 +163,6 @@ sc_aoa_init(struct sc_aoa *aoa, const char *serial,
|
||||
aoa->acksync = acksync;
|
||||
|
||||
return true;
|
||||
|
||||
error_unref_device:
|
||||
libusb_unref_device(aoa->usb_device);
|
||||
error_exit_libusb:
|
||||
libusb_exit(aoa->usb_context);
|
||||
error_destroy_cond:
|
||||
sc_cond_destroy(&aoa->event_cond);
|
||||
error_destroy_mutex:
|
||||
sc_mutex_destroy(&aoa->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -343,8 +343,8 @@ run_aoa_thread(void *data) {
|
||||
|
||||
if (ack_to_wait != SC_SEQUENCE_INVALID) {
|
||||
LOGD("Waiting ack from server sequence=%" PRIu64_, ack_to_wait);
|
||||
// Do not block the loop indefinitely if the ack never comes (it
|
||||
// should never happen)
|
||||
// Do not block the loop indefinitely if the ack never comes (it should
|
||||
// never happen)
|
||||
sc_tick deadline = sc_tick_now() + SC_TICK_FROM_MS(500);
|
||||
enum sc_acksync_wait_result result =
|
||||
sc_acksync_wait(aoa->acksync, ack_to_wait, deadline);
|
||||
|
||||
108
app/src/cli.c
108
app/src/cli.c
@@ -52,7 +52,6 @@
|
||||
#define OPT_NO_CLIPBOARD_AUTOSYNC 1032
|
||||
#define OPT_TCPIP 1033
|
||||
#define OPT_RAW_KEY_EVENTS 1034
|
||||
#define OPT_NO_DOWNSIZE_ON_ERROR 1035
|
||||
|
||||
struct sc_option {
|
||||
char shortopt;
|
||||
@@ -179,8 +178,7 @@ static const struct sc_option options[] = {
|
||||
"directly: `adb shell am start -a "
|
||||
"android.settings.HARD_KEYBOARD_SETTINGS`.\n"
|
||||
"However, the option is only available when the HID keyboard "
|
||||
"is enabled (or a physical keyboard is connected).\n"
|
||||
"Also see --hid-mouse.",
|
||||
"is enabled (or a physical keyboard is connected).",
|
||||
},
|
||||
{
|
||||
.shortopt = 'h',
|
||||
@@ -216,18 +214,6 @@ 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"
|
||||
"Also see --hid-keyboard.",
|
||||
},
|
||||
{
|
||||
.shortopt = 'm',
|
||||
.longopt = "max-size",
|
||||
@@ -237,13 +223,6 @@ static const struct sc_option options[] = {
|
||||
"is preserved.\n"
|
||||
"Default is 0 (unlimited).",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_NO_DOWNSIZE_ON_ERROR,
|
||||
.longopt = "no-downsize-on-error",
|
||||
.text = "By default, on MediaCodec error, scrcpy automatically tries "
|
||||
"again with a lower definition.\n"
|
||||
"This option disables this behavior.",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_NO_CLIPBOARD_AUTOSYNC,
|
||||
.longopt = "no-clipboard-autosync",
|
||||
@@ -261,8 +240,11 @@ static const struct sc_option options[] = {
|
||||
{
|
||||
.shortopt = 'N',
|
||||
.longopt = "no-display",
|
||||
.text = "Do not display device (only when screen recording or V4L2 "
|
||||
"sink is enabled).",
|
||||
.text = "Do not display device (only when screen recording "
|
||||
#ifdef HAVE_V4L2
|
||||
"or V4L2 sink "
|
||||
#endif
|
||||
"is enabled).",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_NO_KEY_REPEAT,
|
||||
@@ -399,14 +381,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).\n"
|
||||
"This feature is only available on Linux.",
|
||||
"--lock-video-orientation).",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_V4L2_BUFFER,
|
||||
@@ -416,9 +398,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"
|
||||
"Default is 0 (no buffering).\n"
|
||||
"This option is only available on Linux.",
|
||||
"Default is 0 (no buffering).",
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.shortopt = 'V',
|
||||
.longopt = "verbosity",
|
||||
@@ -1318,14 +1300,8 @@ 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;
|
||||
break;
|
||||
#else
|
||||
LOGE("HID over AOA (-K/--hid-keyboard) is not supported on "
|
||||
"this platform. It is only available on Linux.");
|
||||
return false;
|
||||
#endif
|
||||
case OPT_MAX_FPS:
|
||||
if (!parse_max_fps(optarg, &opts->max_fps)) {
|
||||
return false;
|
||||
@@ -1336,15 +1312,6 @@ 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;
|
||||
break;
|
||||
#else
|
||||
LOGE("HID over AOA (-M/--hid-mouse) is not supported on this"
|
||||
"platform. It is only available on Linux.");
|
||||
return false;
|
||||
#endif
|
||||
case OPT_LOCK_VIDEO_ORIENTATION:
|
||||
if (!parse_lock_video_orientation(optarg,
|
||||
&opts->lock_video_orientation)) {
|
||||
@@ -1497,26 +1464,15 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
opts->tcpip = true;
|
||||
opts->tcpip_dst = optarg;
|
||||
break;
|
||||
case OPT_NO_DOWNSIZE_ON_ERROR:
|
||||
opts->downsize_on_error = false;
|
||||
break;
|
||||
case OPT_V4L2_SINK:
|
||||
#ifdef HAVE_V4L2
|
||||
case OPT_V4L2_SINK:
|
||||
opts->v4l2_device = optarg;
|
||||
break;
|
||||
#else
|
||||
LOGE("V4L2 (--v4l2-sink) is only available on Linux.");
|
||||
return false;
|
||||
#endif
|
||||
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
|
||||
default:
|
||||
// getopt prints the error message on stderr
|
||||
@@ -1545,18 +1501,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts->v4l2_device) {
|
||||
if (opts->lock_video_orientation ==
|
||||
SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) {
|
||||
LOGI("Video orientation is locked for v4l2 sink. "
|
||||
"See --lock-video-orientation.");
|
||||
opts->lock_video_orientation = SC_LOCK_VIDEO_ORIENTATION_INITIAL;
|
||||
}
|
||||
|
||||
// V4L2 could not handle size change.
|
||||
// Do not log because downsizing on error is the default behavior,
|
||||
// not an explicit request from the user.
|
||||
opts->downsize_on_error = false;
|
||||
if (opts->v4l2_device && opts->lock_video_orientation
|
||||
== SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) {
|
||||
LOGI("Video orientation is locked for v4l2 sink. "
|
||||
"See --lock-video-orientation.");
|
||||
opts->lock_video_orientation = SC_LOCK_VIDEO_ORIENTATION_INITIAL;
|
||||
}
|
||||
|
||||
if (opts->v4l2_buffer && !opts->v4l2_device) {
|
||||
@@ -1591,23 +1540,14 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
}
|
||||
}
|
||||
|
||||
if (!opts->control) {
|
||||
if (opts->turn_screen_off) {
|
||||
LOGE("Could not request to turn screen off if control is disabled");
|
||||
return false;
|
||||
}
|
||||
if (opts->stay_awake) {
|
||||
LOGE("Could not request to stay awake if control is disabled");
|
||||
return false;
|
||||
}
|
||||
if (opts->show_touches) {
|
||||
LOGE("Could not request to show touches if control is disabled");
|
||||
return false;
|
||||
}
|
||||
if (opts->power_off_on_close) {
|
||||
LOGE("Could not request power off on close if control is disabled");
|
||||
return false;
|
||||
}
|
||||
if (!opts->control && opts->turn_screen_off) {
|
||||
LOGE("Could not request to turn screen off if control is disabled");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!opts->control && opts->stay_awake) {
|
||||
LOGE("Could not request to stay awake if control is disabled");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
|
||||
#define MIN(X,Y) (X) < (Y) ? (X) : (Y)
|
||||
#define MAX(X,Y) (X) > (Y) ? (X) : (Y)
|
||||
#define CLAMP(V,X,Y) MIN( MAX((V),(X)), (Y) )
|
||||
|
||||
#define container_of(ptr, type, member) \
|
||||
((type *) (((char *) (ptr)) - offsetof(type, member)))
|
||||
|
||||
@@ -89,22 +89,22 @@ to_fixed_point_16(float f) {
|
||||
}
|
||||
|
||||
size_t
|
||||
sc_control_msg_serialize(const struct sc_control_msg *msg, unsigned char *buf) {
|
||||
control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
||||
buf[0] = msg->type;
|
||||
switch (msg->type) {
|
||||
case SC_CONTROL_MSG_TYPE_INJECT_KEYCODE:
|
||||
case CONTROL_MSG_TYPE_INJECT_KEYCODE:
|
||||
buf[1] = msg->inject_keycode.action;
|
||||
buffer_write32be(&buf[2], msg->inject_keycode.keycode);
|
||||
buffer_write32be(&buf[6], msg->inject_keycode.repeat);
|
||||
buffer_write32be(&buf[10], msg->inject_keycode.metastate);
|
||||
return 14;
|
||||
case SC_CONTROL_MSG_TYPE_INJECT_TEXT: {
|
||||
case CONTROL_MSG_TYPE_INJECT_TEXT: {
|
||||
size_t len =
|
||||
write_string(msg->inject_text.text,
|
||||
SC_CONTROL_MSG_INJECT_TEXT_MAX_LENGTH, &buf[1]);
|
||||
CONTROL_MSG_INJECT_TEXT_MAX_LENGTH, &buf[1]);
|
||||
return 1 + len;
|
||||
}
|
||||
case SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT:
|
||||
case CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT:
|
||||
buf[1] = msg->inject_touch_event.action;
|
||||
buffer_write64be(&buf[2], msg->inject_touch_event.pointer_id);
|
||||
write_position(&buf[10], &msg->inject_touch_event.position);
|
||||
@@ -113,34 +113,34 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, unsigned char *buf) {
|
||||
buffer_write16be(&buf[22], pressure);
|
||||
buffer_write32be(&buf[24], msg->inject_touch_event.buttons);
|
||||
return 28;
|
||||
case SC_CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
|
||||
case CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
|
||||
write_position(&buf[1], &msg->inject_scroll_event.position);
|
||||
buffer_write32be(&buf[13],
|
||||
(uint32_t) msg->inject_scroll_event.hscroll);
|
||||
buffer_write32be(&buf[17],
|
||||
(uint32_t) msg->inject_scroll_event.vscroll);
|
||||
buffer_write32be(&buf[21], msg->inject_scroll_event.buttons);
|
||||
return 25;
|
||||
case SC_CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
|
||||
return 21;
|
||||
case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
|
||||
buf[1] = msg->inject_keycode.action;
|
||||
return 2;
|
||||
case SC_CONTROL_MSG_TYPE_GET_CLIPBOARD:
|
||||
case CONTROL_MSG_TYPE_GET_CLIPBOARD:
|
||||
buf[1] = msg->get_clipboard.copy_key;
|
||||
return 2;
|
||||
case SC_CONTROL_MSG_TYPE_SET_CLIPBOARD:
|
||||
case CONTROL_MSG_TYPE_SET_CLIPBOARD: {
|
||||
buffer_write64be(&buf[1], msg->set_clipboard.sequence);
|
||||
buf[9] = !!msg->set_clipboard.paste;
|
||||
size_t len = write_string(msg->set_clipboard.text,
|
||||
SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH,
|
||||
CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH,
|
||||
&buf[10]);
|
||||
return 10 + len;
|
||||
case SC_CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE:
|
||||
}
|
||||
case CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE:
|
||||
buf[1] = msg->set_screen_power_mode.mode;
|
||||
return 2;
|
||||
case SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||
case SC_CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL:
|
||||
case SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS:
|
||||
case SC_CONTROL_MSG_TYPE_ROTATE_DEVICE:
|
||||
case CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||
case CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL:
|
||||
case CONTROL_MSG_TYPE_COLLAPSE_PANELS:
|
||||
case CONTROL_MSG_TYPE_ROTATE_DEVICE:
|
||||
// no additional data
|
||||
return 1;
|
||||
default:
|
||||
@@ -150,20 +150,20 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, unsigned char *buf) {
|
||||
}
|
||||
|
||||
void
|
||||
sc_control_msg_log(const struct sc_control_msg *msg) {
|
||||
control_msg_log(const struct control_msg *msg) {
|
||||
#define LOG_CMSG(fmt, ...) LOGV("input: " fmt, ## __VA_ARGS__)
|
||||
switch (msg->type) {
|
||||
case SC_CONTROL_MSG_TYPE_INJECT_KEYCODE:
|
||||
case CONTROL_MSG_TYPE_INJECT_KEYCODE:
|
||||
LOG_CMSG("key %-4s code=%d repeat=%" PRIu32 " meta=%06lx",
|
||||
KEYEVENT_ACTION_LABEL(msg->inject_keycode.action),
|
||||
(int) msg->inject_keycode.keycode,
|
||||
msg->inject_keycode.repeat,
|
||||
(long) msg->inject_keycode.metastate);
|
||||
break;
|
||||
case SC_CONTROL_MSG_TYPE_INJECT_TEXT:
|
||||
case CONTROL_MSG_TYPE_INJECT_TEXT:
|
||||
LOG_CMSG("text \"%s\"", msg->inject_text.text);
|
||||
break;
|
||||
case SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT: {
|
||||
case CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT: {
|
||||
int action = msg->inject_touch_event.action
|
||||
& AMOTION_EVENT_ACTION_MASK;
|
||||
uint64_t id = msg->inject_touch_event.pointer_id;
|
||||
@@ -190,43 +190,42 @@ sc_control_msg_log(const struct sc_control_msg *msg) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SC_CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
|
||||
case CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
|
||||
LOG_CMSG("scroll position=%" PRIi32 ",%" PRIi32 " hscroll=%" PRIi32
|
||||
" vscroll=%" PRIi32 " buttons=%06lx",
|
||||
" vscroll=%" PRIi32,
|
||||
msg->inject_scroll_event.position.point.x,
|
||||
msg->inject_scroll_event.position.point.y,
|
||||
msg->inject_scroll_event.hscroll,
|
||||
msg->inject_scroll_event.vscroll,
|
||||
(long) msg->inject_scroll_event.buttons);
|
||||
msg->inject_scroll_event.vscroll);
|
||||
break;
|
||||
case SC_CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
|
||||
case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
|
||||
LOG_CMSG("back-or-screen-on %s",
|
||||
KEYEVENT_ACTION_LABEL(msg->inject_keycode.action));
|
||||
break;
|
||||
case SC_CONTROL_MSG_TYPE_GET_CLIPBOARD:
|
||||
case CONTROL_MSG_TYPE_GET_CLIPBOARD:
|
||||
LOG_CMSG("get clipboard copy_key=%s",
|
||||
copy_key_labels[msg->get_clipboard.copy_key]);
|
||||
break;
|
||||
case SC_CONTROL_MSG_TYPE_SET_CLIPBOARD:
|
||||
case CONTROL_MSG_TYPE_SET_CLIPBOARD:
|
||||
LOG_CMSG("clipboard %" PRIu64_ " %s \"%s\"",
|
||||
msg->set_clipboard.sequence,
|
||||
msg->set_clipboard.paste ? "paste" : "nopaste",
|
||||
msg->set_clipboard.text);
|
||||
break;
|
||||
case SC_CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE:
|
||||
case CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE:
|
||||
LOG_CMSG("power mode %s",
|
||||
SCREEN_POWER_MODE_LABEL(msg->set_screen_power_mode.mode));
|
||||
break;
|
||||
case SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||
case CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||
LOG_CMSG("expand notification panel");
|
||||
break;
|
||||
case SC_CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL:
|
||||
case CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL:
|
||||
LOG_CMSG("expand settings panel");
|
||||
break;
|
||||
case SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS:
|
||||
case CONTROL_MSG_TYPE_COLLAPSE_PANELS:
|
||||
LOG_CMSG("collapse panels");
|
||||
break;
|
||||
case SC_CONTROL_MSG_TYPE_ROTATE_DEVICE:
|
||||
case CONTROL_MSG_TYPE_ROTATE_DEVICE:
|
||||
LOG_CMSG("rotate device");
|
||||
break;
|
||||
default:
|
||||
@@ -236,12 +235,12 @@ sc_control_msg_log(const struct sc_control_msg *msg) {
|
||||
}
|
||||
|
||||
void
|
||||
sc_control_msg_destroy(struct sc_control_msg *msg) {
|
||||
control_msg_destroy(struct control_msg *msg) {
|
||||
switch (msg->type) {
|
||||
case SC_CONTROL_MSG_TYPE_INJECT_TEXT:
|
||||
case CONTROL_MSG_TYPE_INJECT_TEXT:
|
||||
free(msg->inject_text.text);
|
||||
break;
|
||||
case SC_CONTROL_MSG_TYPE_SET_CLIPBOARD:
|
||||
case CONTROL_MSG_TYPE_SET_CLIPBOARD:
|
||||
free(msg->set_clipboard.text);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -11,44 +11,44 @@
|
||||
#include "android/keycodes.h"
|
||||
#include "coords.h"
|
||||
|
||||
#define SC_CONTROL_MSG_MAX_SIZE (1 << 18) // 256k
|
||||
#define CONTROL_MSG_MAX_SIZE (1 << 18) // 256k
|
||||
|
||||
#define SC_CONTROL_MSG_INJECT_TEXT_MAX_LENGTH 300
|
||||
#define CONTROL_MSG_INJECT_TEXT_MAX_LENGTH 300
|
||||
// type: 1 byte; sequence: 8 bytes; paste flag: 1 byte; length: 4 bytes
|
||||
#define SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH (SC_CONTROL_MSG_MAX_SIZE - 14)
|
||||
#define CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH (CONTROL_MSG_MAX_SIZE - 14)
|
||||
|
||||
#define POINTER_ID_MOUSE UINT64_C(-1)
|
||||
#define POINTER_ID_VIRTUAL_FINGER UINT64_C(-2)
|
||||
|
||||
enum sc_control_msg_type {
|
||||
SC_CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
||||
SC_CONTROL_MSG_TYPE_INJECT_TEXT,
|
||||
SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||
SC_CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
||||
SC_CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
|
||||
SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL,
|
||||
SC_CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL,
|
||||
SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS,
|
||||
SC_CONTROL_MSG_TYPE_GET_CLIPBOARD,
|
||||
SC_CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||
SC_CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
|
||||
SC_CONTROL_MSG_TYPE_ROTATE_DEVICE,
|
||||
enum control_msg_type {
|
||||
CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
||||
CONTROL_MSG_TYPE_INJECT_TEXT,
|
||||
CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||
CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
||||
CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
|
||||
CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL,
|
||||
CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL,
|
||||
CONTROL_MSG_TYPE_COLLAPSE_PANELS,
|
||||
CONTROL_MSG_TYPE_GET_CLIPBOARD,
|
||||
CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||
CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
|
||||
CONTROL_MSG_TYPE_ROTATE_DEVICE,
|
||||
};
|
||||
|
||||
enum sc_screen_power_mode {
|
||||
enum screen_power_mode {
|
||||
// see <https://android.googlesource.com/platform/frameworks/base.git/+/pie-release-2/core/java/android/view/SurfaceControl.java#305>
|
||||
SC_SCREEN_POWER_MODE_OFF = 0,
|
||||
SC_SCREEN_POWER_MODE_NORMAL = 2,
|
||||
SCREEN_POWER_MODE_OFF = 0,
|
||||
SCREEN_POWER_MODE_NORMAL = 2,
|
||||
};
|
||||
|
||||
enum sc_copy_key {
|
||||
SC_COPY_KEY_NONE,
|
||||
SC_COPY_KEY_COPY,
|
||||
SC_COPY_KEY_CUT,
|
||||
enum get_clipboard_copy_key {
|
||||
GET_CLIPBOARD_COPY_KEY_NONE,
|
||||
GET_CLIPBOARD_COPY_KEY_COPY,
|
||||
GET_CLIPBOARD_COPY_KEY_CUT,
|
||||
};
|
||||
|
||||
struct sc_control_msg {
|
||||
enum sc_control_msg_type type;
|
||||
struct control_msg {
|
||||
enum control_msg_type type;
|
||||
union {
|
||||
struct {
|
||||
enum android_keyevent_action action;
|
||||
@@ -70,14 +70,13 @@ struct sc_control_msg {
|
||||
struct sc_position position;
|
||||
int32_t hscroll;
|
||||
int32_t vscroll;
|
||||
enum android_motionevent_buttons buttons;
|
||||
} inject_scroll_event;
|
||||
struct {
|
||||
enum android_keyevent_action action; // action for the BACK key
|
||||
// screen may only be turned on on ACTION_DOWN
|
||||
} back_or_screen_on;
|
||||
struct {
|
||||
enum sc_copy_key copy_key;
|
||||
enum get_clipboard_copy_key copy_key;
|
||||
} get_clipboard;
|
||||
struct {
|
||||
uint64_t sequence;
|
||||
@@ -85,7 +84,7 @@ struct sc_control_msg {
|
||||
bool paste;
|
||||
} set_clipboard;
|
||||
struct {
|
||||
enum sc_screen_power_mode mode;
|
||||
enum screen_power_mode mode;
|
||||
} set_screen_power_mode;
|
||||
};
|
||||
};
|
||||
@@ -93,12 +92,12 @@ struct sc_control_msg {
|
||||
// buf size must be at least CONTROL_MSG_MAX_SIZE
|
||||
// return the number of bytes written
|
||||
size_t
|
||||
sc_control_msg_serialize(const struct sc_control_msg *msg, unsigned char *buf);
|
||||
control_msg_serialize(const struct control_msg *msg, unsigned char *buf);
|
||||
|
||||
void
|
||||
sc_control_msg_log(const struct sc_control_msg *msg);
|
||||
control_msg_log(const struct control_msg *msg);
|
||||
|
||||
void
|
||||
sc_control_msg_destroy(struct sc_control_msg *msg);
|
||||
control_msg_destroy(struct control_msg *msg);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
#include "util/log.h"
|
||||
|
||||
bool
|
||||
sc_controller_init(struct sc_controller *controller, sc_socket control_socket,
|
||||
struct sc_acksync *acksync) {
|
||||
controller_init(struct controller *controller, sc_socket control_socket,
|
||||
struct sc_acksync *acksync) {
|
||||
cbuf_init(&controller->queue);
|
||||
|
||||
bool ok = receiver_init(&controller->receiver, control_socket, acksync);
|
||||
@@ -34,23 +34,23 @@ sc_controller_init(struct sc_controller *controller, sc_socket control_socket,
|
||||
}
|
||||
|
||||
void
|
||||
sc_controller_destroy(struct sc_controller *controller) {
|
||||
controller_destroy(struct controller *controller) {
|
||||
sc_cond_destroy(&controller->msg_cond);
|
||||
sc_mutex_destroy(&controller->mutex);
|
||||
|
||||
struct sc_control_msg msg;
|
||||
struct control_msg msg;
|
||||
while (cbuf_take(&controller->queue, &msg)) {
|
||||
sc_control_msg_destroy(&msg);
|
||||
control_msg_destroy(&msg);
|
||||
}
|
||||
|
||||
receiver_destroy(&controller->receiver);
|
||||
}
|
||||
|
||||
bool
|
||||
sc_controller_push_msg(struct sc_controller *controller,
|
||||
const struct sc_control_msg *msg) {
|
||||
controller_push_msg(struct controller *controller,
|
||||
const struct control_msg *msg) {
|
||||
if (sc_get_log_level() <= SC_LOG_LEVEL_VERBOSE) {
|
||||
sc_control_msg_log(msg);
|
||||
control_msg_log(msg);
|
||||
}
|
||||
|
||||
sc_mutex_lock(&controller->mutex);
|
||||
@@ -64,10 +64,9 @@ sc_controller_push_msg(struct sc_controller *controller,
|
||||
}
|
||||
|
||||
static bool
|
||||
process_msg(struct sc_controller *controller,
|
||||
const struct sc_control_msg *msg) {
|
||||
static unsigned char serialized_msg[SC_CONTROL_MSG_MAX_SIZE];
|
||||
size_t length = sc_control_msg_serialize(msg, serialized_msg);
|
||||
process_msg(struct controller *controller, const struct control_msg *msg) {
|
||||
static unsigned char serialized_msg[CONTROL_MSG_MAX_SIZE];
|
||||
size_t length = control_msg_serialize(msg, serialized_msg);
|
||||
if (!length) {
|
||||
return false;
|
||||
}
|
||||
@@ -78,7 +77,7 @@ process_msg(struct sc_controller *controller,
|
||||
|
||||
static int
|
||||
run_controller(void *data) {
|
||||
struct sc_controller *controller = data;
|
||||
struct controller *controller = data;
|
||||
|
||||
for (;;) {
|
||||
sc_mutex_lock(&controller->mutex);
|
||||
@@ -90,14 +89,14 @@ run_controller(void *data) {
|
||||
sc_mutex_unlock(&controller->mutex);
|
||||
break;
|
||||
}
|
||||
struct sc_control_msg msg;
|
||||
struct control_msg msg;
|
||||
bool non_empty = cbuf_take(&controller->queue, &msg);
|
||||
assert(non_empty);
|
||||
(void) non_empty;
|
||||
sc_mutex_unlock(&controller->mutex);
|
||||
|
||||
bool ok = process_msg(controller, &msg);
|
||||
sc_control_msg_destroy(&msg);
|
||||
control_msg_destroy(&msg);
|
||||
if (!ok) {
|
||||
LOGD("Could not write msg to socket");
|
||||
break;
|
||||
@@ -107,7 +106,7 @@ run_controller(void *data) {
|
||||
}
|
||||
|
||||
bool
|
||||
sc_controller_start(struct sc_controller *controller) {
|
||||
controller_start(struct controller *controller) {
|
||||
LOGD("Starting controller thread");
|
||||
|
||||
bool ok = sc_thread_create(&controller->thread, run_controller,
|
||||
@@ -118,7 +117,7 @@ sc_controller_start(struct sc_controller *controller) {
|
||||
}
|
||||
|
||||
if (!receiver_start(&controller->receiver)) {
|
||||
sc_controller_stop(controller);
|
||||
controller_stop(controller);
|
||||
sc_thread_join(&controller->thread, NULL);
|
||||
return false;
|
||||
}
|
||||
@@ -127,7 +126,7 @@ sc_controller_start(struct sc_controller *controller) {
|
||||
}
|
||||
|
||||
void
|
||||
sc_controller_stop(struct sc_controller *controller) {
|
||||
controller_stop(struct controller *controller) {
|
||||
sc_mutex_lock(&controller->mutex);
|
||||
controller->stopped = true;
|
||||
sc_cond_signal(&controller->msg_cond);
|
||||
@@ -135,7 +134,7 @@ sc_controller_stop(struct sc_controller *controller) {
|
||||
}
|
||||
|
||||
void
|
||||
sc_controller_join(struct sc_controller *controller) {
|
||||
controller_join(struct controller *controller) {
|
||||
sc_thread_join(&controller->thread, NULL);
|
||||
receiver_join(&controller->receiver);
|
||||
}
|
||||
|
||||
@@ -12,36 +12,36 @@
|
||||
#include "util/net.h"
|
||||
#include "util/thread.h"
|
||||
|
||||
struct sc_control_msg_queue CBUF(struct sc_control_msg, 64);
|
||||
struct control_msg_queue CBUF(struct control_msg, 64);
|
||||
|
||||
struct sc_controller {
|
||||
struct controller {
|
||||
sc_socket control_socket;
|
||||
sc_thread thread;
|
||||
sc_mutex mutex;
|
||||
sc_cond msg_cond;
|
||||
bool stopped;
|
||||
struct sc_control_msg_queue queue;
|
||||
struct control_msg_queue queue;
|
||||
struct receiver receiver;
|
||||
};
|
||||
|
||||
bool
|
||||
sc_controller_init(struct sc_controller *controller, sc_socket control_socket,
|
||||
struct sc_acksync *acksync);
|
||||
controller_init(struct controller *controller, sc_socket control_socket,
|
||||
struct sc_acksync *acksync);
|
||||
|
||||
void
|
||||
sc_controller_destroy(struct sc_controller *controller);
|
||||
controller_destroy(struct controller *controller);
|
||||
|
||||
bool
|
||||
sc_controller_start(struct sc_controller *controller);
|
||||
controller_start(struct controller *controller);
|
||||
|
||||
void
|
||||
sc_controller_stop(struct sc_controller *controller);
|
||||
controller_stop(struct controller *controller);
|
||||
|
||||
void
|
||||
sc_controller_join(struct sc_controller *controller);
|
||||
controller_join(struct controller *controller);
|
||||
|
||||
bool
|
||||
sc_controller_push_msg(struct sc_controller *controller,
|
||||
const struct sc_control_msg *msg);
|
||||
controller_push_msg(struct controller *controller,
|
||||
const struct control_msg *msg);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "trait/packet_sink.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
#define DECODER_MAX_SINKS 2
|
||||
|
||||
178
app/src/file_handler.c
Normal file
178
app/src/file_handler.c
Normal file
@@ -0,0 +1,178 @@
|
||||
#include "file_handler.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "util/log.h"
|
||||
#include "util/process_intr.h"
|
||||
|
||||
#define DEFAULT_PUSH_TARGET "/sdcard/Download/"
|
||||
|
||||
static void
|
||||
file_handler_request_destroy(struct file_handler_request *req) {
|
||||
free(req->file);
|
||||
}
|
||||
|
||||
bool
|
||||
file_handler_init(struct file_handler *file_handler, const char *serial,
|
||||
const char *push_target) {
|
||||
assert(serial);
|
||||
|
||||
cbuf_init(&file_handler->queue);
|
||||
|
||||
bool ok = sc_mutex_init(&file_handler->mutex);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ok = sc_cond_init(&file_handler->event_cond);
|
||||
if (!ok) {
|
||||
sc_mutex_destroy(&file_handler->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
ok = sc_intr_init(&file_handler->intr);
|
||||
if (!ok) {
|
||||
sc_cond_destroy(&file_handler->event_cond);
|
||||
sc_mutex_destroy(&file_handler->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
file_handler->serial = strdup(serial);
|
||||
if (!file_handler->serial) {
|
||||
LOG_OOM();
|
||||
sc_intr_destroy(&file_handler->intr);
|
||||
sc_cond_destroy(&file_handler->event_cond);
|
||||
sc_mutex_destroy(&file_handler->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
// lazy initialization
|
||||
file_handler->initialized = false;
|
||||
|
||||
file_handler->stopped = false;
|
||||
|
||||
file_handler->push_target = push_target ? push_target : DEFAULT_PUSH_TARGET;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
file_handler_destroy(struct file_handler *file_handler) {
|
||||
sc_cond_destroy(&file_handler->event_cond);
|
||||
sc_mutex_destroy(&file_handler->mutex);
|
||||
sc_intr_destroy(&file_handler->intr);
|
||||
free(file_handler->serial);
|
||||
|
||||
struct file_handler_request req;
|
||||
while (cbuf_take(&file_handler->queue, &req)) {
|
||||
file_handler_request_destroy(&req);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
file_handler_request(struct file_handler *file_handler,
|
||||
file_handler_action_t action, char *file) {
|
||||
// start file_handler if it's used for the first time
|
||||
if (!file_handler->initialized) {
|
||||
if (!file_handler_start(file_handler)) {
|
||||
return false;
|
||||
}
|
||||
file_handler->initialized = true;
|
||||
}
|
||||
|
||||
LOGI("Request to %s %s", action == ACTION_INSTALL_APK ? "install" : "push",
|
||||
file);
|
||||
struct file_handler_request req = {
|
||||
.action = action,
|
||||
.file = file,
|
||||
};
|
||||
|
||||
sc_mutex_lock(&file_handler->mutex);
|
||||
bool was_empty = cbuf_is_empty(&file_handler->queue);
|
||||
bool res = cbuf_push(&file_handler->queue, req);
|
||||
if (was_empty) {
|
||||
sc_cond_signal(&file_handler->event_cond);
|
||||
}
|
||||
sc_mutex_unlock(&file_handler->mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
run_file_handler(void *data) {
|
||||
struct file_handler *file_handler = data;
|
||||
struct sc_intr *intr = &file_handler->intr;
|
||||
|
||||
const char *serial = file_handler->serial;
|
||||
assert(serial);
|
||||
|
||||
const char *push_target = file_handler->push_target;
|
||||
assert(push_target);
|
||||
|
||||
for (;;) {
|
||||
sc_mutex_lock(&file_handler->mutex);
|
||||
while (!file_handler->stopped && cbuf_is_empty(&file_handler->queue)) {
|
||||
sc_cond_wait(&file_handler->event_cond, &file_handler->mutex);
|
||||
}
|
||||
if (file_handler->stopped) {
|
||||
// stop immediately, do not process further events
|
||||
sc_mutex_unlock(&file_handler->mutex);
|
||||
break;
|
||||
}
|
||||
struct file_handler_request req;
|
||||
bool non_empty = cbuf_take(&file_handler->queue, &req);
|
||||
assert(non_empty);
|
||||
(void) non_empty;
|
||||
sc_mutex_unlock(&file_handler->mutex);
|
||||
|
||||
if (req.action == ACTION_INSTALL_APK) {
|
||||
LOGI("Installing %s...", req.file);
|
||||
bool ok = adb_install(intr, serial, req.file, 0);
|
||||
if (ok) {
|
||||
LOGI("%s successfully installed", req.file);
|
||||
} else {
|
||||
LOGE("Failed to install %s", req.file);
|
||||
}
|
||||
} else {
|
||||
LOGI("Pushing %s...", req.file);
|
||||
bool ok = adb_push(intr, serial, req.file, push_target, 0);
|
||||
if (ok) {
|
||||
LOGI("%s successfully pushed to %s", req.file, push_target);
|
||||
} else {
|
||||
LOGE("Failed to push %s to %s", req.file, push_target);
|
||||
}
|
||||
}
|
||||
|
||||
file_handler_request_destroy(&req);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
file_handler_start(struct file_handler *file_handler) {
|
||||
LOGD("Starting file_handler thread");
|
||||
|
||||
bool ok = sc_thread_create(&file_handler->thread, run_file_handler,
|
||||
"scrcpy-file", file_handler);
|
||||
if (!ok) {
|
||||
LOGC("Could not start file_handler thread");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
file_handler_stop(struct file_handler *file_handler) {
|
||||
sc_mutex_lock(&file_handler->mutex);
|
||||
file_handler->stopped = true;
|
||||
sc_cond_signal(&file_handler->event_cond);
|
||||
sc_intr_interrupt(&file_handler->intr);
|
||||
sc_mutex_unlock(&file_handler->mutex);
|
||||
}
|
||||
|
||||
void
|
||||
file_handler_join(struct file_handler *file_handler) {
|
||||
sc_thread_join(&file_handler->thread, NULL);
|
||||
}
|
||||
60
app/src/file_handler.h
Normal file
60
app/src/file_handler.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef FILE_HANDLER_H
|
||||
#define FILE_HANDLER_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "util/cbuf.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/intr.h"
|
||||
|
||||
typedef enum {
|
||||
ACTION_INSTALL_APK,
|
||||
ACTION_PUSH_FILE,
|
||||
} file_handler_action_t;
|
||||
|
||||
struct file_handler_request {
|
||||
file_handler_action_t action;
|
||||
char *file;
|
||||
};
|
||||
|
||||
struct file_handler_request_queue CBUF(struct file_handler_request, 16);
|
||||
|
||||
struct file_handler {
|
||||
char *serial;
|
||||
const char *push_target;
|
||||
sc_thread thread;
|
||||
sc_mutex mutex;
|
||||
sc_cond event_cond;
|
||||
bool stopped;
|
||||
bool initialized;
|
||||
struct file_handler_request_queue queue;
|
||||
|
||||
struct sc_intr intr;
|
||||
};
|
||||
|
||||
bool
|
||||
file_handler_init(struct file_handler *file_handler, const char *serial,
|
||||
const char *push_target);
|
||||
|
||||
void
|
||||
file_handler_destroy(struct file_handler *file_handler);
|
||||
|
||||
bool
|
||||
file_handler_start(struct file_handler *file_handler);
|
||||
|
||||
void
|
||||
file_handler_stop(struct file_handler *file_handler);
|
||||
|
||||
void
|
||||
file_handler_join(struct file_handler *file_handler);
|
||||
|
||||
// take ownership of file, and will free() it
|
||||
bool
|
||||
file_handler_request(struct file_handler *file_handler,
|
||||
file_handler_action_t action,
|
||||
char *file);
|
||||
|
||||
#endif
|
||||
@@ -1,178 +0,0 @@
|
||||
#include "file_pusher.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "util/log.h"
|
||||
#include "util/process_intr.h"
|
||||
|
||||
#define DEFAULT_PUSH_TARGET "/sdcard/Download/"
|
||||
|
||||
static void
|
||||
sc_file_pusher_request_destroy(struct sc_file_pusher_request *req) {
|
||||
free(req->file);
|
||||
}
|
||||
|
||||
bool
|
||||
sc_file_pusher_init(struct sc_file_pusher *fp, const char *serial,
|
||||
const char *push_target) {
|
||||
assert(serial);
|
||||
|
||||
cbuf_init(&fp->queue);
|
||||
|
||||
bool ok = sc_mutex_init(&fp->mutex);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ok = sc_cond_init(&fp->event_cond);
|
||||
if (!ok) {
|
||||
sc_mutex_destroy(&fp->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
ok = sc_intr_init(&fp->intr);
|
||||
if (!ok) {
|
||||
sc_cond_destroy(&fp->event_cond);
|
||||
sc_mutex_destroy(&fp->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
fp->serial = strdup(serial);
|
||||
if (!fp->serial) {
|
||||
LOG_OOM();
|
||||
sc_intr_destroy(&fp->intr);
|
||||
sc_cond_destroy(&fp->event_cond);
|
||||
sc_mutex_destroy(&fp->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
// lazy initialization
|
||||
fp->initialized = false;
|
||||
|
||||
fp->stopped = false;
|
||||
|
||||
fp->push_target = push_target ? push_target : DEFAULT_PUSH_TARGET;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
sc_file_pusher_destroy(struct sc_file_pusher *fp) {
|
||||
sc_cond_destroy(&fp->event_cond);
|
||||
sc_mutex_destroy(&fp->mutex);
|
||||
sc_intr_destroy(&fp->intr);
|
||||
free(fp->serial);
|
||||
|
||||
struct sc_file_pusher_request req;
|
||||
while (cbuf_take(&fp->queue, &req)) {
|
||||
sc_file_pusher_request_destroy(&req);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
sc_file_pusher_request(struct sc_file_pusher *fp,
|
||||
enum sc_file_pusher_action action, char *file) {
|
||||
// start file_pusher if it's used for the first time
|
||||
if (!fp->initialized) {
|
||||
if (!sc_file_pusher_start(fp)) {
|
||||
return false;
|
||||
}
|
||||
fp->initialized = true;
|
||||
}
|
||||
|
||||
LOGI("Request to %s %s", action == SC_FILE_PUSHER_ACTION_INSTALL_APK
|
||||
? "install" : "push",
|
||||
file);
|
||||
struct sc_file_pusher_request req = {
|
||||
.action = action,
|
||||
.file = file,
|
||||
};
|
||||
|
||||
sc_mutex_lock(&fp->mutex);
|
||||
bool was_empty = cbuf_is_empty(&fp->queue);
|
||||
bool res = cbuf_push(&fp->queue, req);
|
||||
if (was_empty) {
|
||||
sc_cond_signal(&fp->event_cond);
|
||||
}
|
||||
sc_mutex_unlock(&fp->mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
run_file_pusher(void *data) {
|
||||
struct sc_file_pusher *fp = data;
|
||||
struct sc_intr *intr = &fp->intr;
|
||||
|
||||
const char *serial = fp->serial;
|
||||
assert(serial);
|
||||
|
||||
const char *push_target = fp->push_target;
|
||||
assert(push_target);
|
||||
|
||||
for (;;) {
|
||||
sc_mutex_lock(&fp->mutex);
|
||||
while (!fp->stopped && cbuf_is_empty(&fp->queue)) {
|
||||
sc_cond_wait(&fp->event_cond, &fp->mutex);
|
||||
}
|
||||
if (fp->stopped) {
|
||||
// stop immediately, do not process further events
|
||||
sc_mutex_unlock(&fp->mutex);
|
||||
break;
|
||||
}
|
||||
struct sc_file_pusher_request req;
|
||||
bool non_empty = cbuf_take(&fp->queue, &req);
|
||||
assert(non_empty);
|
||||
(void) non_empty;
|
||||
sc_mutex_unlock(&fp->mutex);
|
||||
|
||||
if (req.action == SC_FILE_PUSHER_ACTION_INSTALL_APK) {
|
||||
LOGI("Installing %s...", req.file);
|
||||
bool ok = adb_install(intr, serial, req.file, 0);
|
||||
if (ok) {
|
||||
LOGI("%s successfully installed", req.file);
|
||||
} else {
|
||||
LOGE("Failed to install %s", req.file);
|
||||
}
|
||||
} else {
|
||||
LOGI("Pushing %s...", req.file);
|
||||
bool ok = adb_push(intr, serial, req.file, push_target, 0);
|
||||
if (ok) {
|
||||
LOGI("%s successfully pushed to %s", req.file, push_target);
|
||||
} else {
|
||||
LOGE("Failed to push %s to %s", req.file, push_target);
|
||||
}
|
||||
}
|
||||
|
||||
sc_file_pusher_request_destroy(&req);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
sc_file_pusher_start(struct sc_file_pusher *fp) {
|
||||
LOGD("Starting file_pusher thread");
|
||||
|
||||
bool ok = sc_thread_create(&fp->thread, run_file_pusher, "scrcpy-file", fp);
|
||||
if (!ok) {
|
||||
LOGC("Could not start file_pusher thread");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
sc_file_pusher_stop(struct sc_file_pusher *fp) {
|
||||
sc_mutex_lock(&fp->mutex);
|
||||
fp->stopped = true;
|
||||
sc_cond_signal(&fp->event_cond);
|
||||
sc_intr_interrupt(&fp->intr);
|
||||
sc_mutex_unlock(&fp->mutex);
|
||||
}
|
||||
|
||||
void
|
||||
sc_file_pusher_join(struct sc_file_pusher *fp) {
|
||||
sc_thread_join(&fp->thread, NULL);
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
#ifndef SC_FILE_PUSHER_H
|
||||
#define SC_FILE_PUSHER_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "util/cbuf.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/intr.h"
|
||||
|
||||
enum sc_file_pusher_action {
|
||||
SC_FILE_PUSHER_ACTION_INSTALL_APK,
|
||||
SC_FILE_PUSHER_ACTION_PUSH_FILE,
|
||||
};
|
||||
|
||||
struct sc_file_pusher_request {
|
||||
enum sc_file_pusher_action action;
|
||||
char *file;
|
||||
};
|
||||
|
||||
struct sc_file_pusher_request_queue CBUF(struct sc_file_pusher_request, 16);
|
||||
|
||||
struct sc_file_pusher {
|
||||
char *serial;
|
||||
const char *push_target;
|
||||
sc_thread thread;
|
||||
sc_mutex mutex;
|
||||
sc_cond event_cond;
|
||||
bool stopped;
|
||||
bool initialized;
|
||||
struct sc_file_pusher_request_queue queue;
|
||||
|
||||
struct sc_intr intr;
|
||||
};
|
||||
|
||||
bool
|
||||
sc_file_pusher_init(struct sc_file_pusher *fp, const char *serial,
|
||||
const char *push_target);
|
||||
|
||||
void
|
||||
sc_file_pusher_destroy(struct sc_file_pusher *fp);
|
||||
|
||||
bool
|
||||
sc_file_pusher_start(struct sc_file_pusher *fp);
|
||||
|
||||
void
|
||||
sc_file_pusher_stop(struct sc_file_pusher *fp);
|
||||
|
||||
void
|
||||
sc_file_pusher_join(struct sc_file_pusher *fp);
|
||||
|
||||
// take ownership of file, and will free() it
|
||||
bool
|
||||
sc_file_pusher_request(struct sc_file_pusher *fp,
|
||||
enum sc_file_pusher_action action, char *file);
|
||||
|
||||
#endif
|
||||
@@ -387,6 +387,15 @@ sc_key_processor_process_key(struct sc_key_processor *kp,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_key_processor_process_text(struct sc_key_processor *kp,
|
||||
const struct sc_text_event *event) {
|
||||
(void) kp;
|
||||
(void) event;
|
||||
|
||||
// Never forward text input via HID (all the keys are injected separately)
|
||||
}
|
||||
|
||||
bool
|
||||
sc_hid_keyboard_init(struct sc_hid_keyboard *kb, struct sc_aoa *aoa) {
|
||||
kb->aoa = aoa;
|
||||
@@ -406,9 +415,7 @@ sc_hid_keyboard_init(struct sc_hid_keyboard *kb, struct sc_aoa *aoa) {
|
||||
|
||||
static const struct sc_key_processor_ops ops = {
|
||||
.process_key = sc_key_processor_process_key,
|
||||
// Never forward text input via HID (all the keys are injected
|
||||
// separately)
|
||||
.process_text = NULL,
|
||||
.process_text = sc_key_processor_process_text,
|
||||
};
|
||||
|
||||
// Clipboard synchronization is requested over the control socket, while HID
|
||||
|
||||
@@ -1,267 +0,0 @@
|
||||
#include "hid_mouse.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "input_events.h"
|
||||
#include "util/log.h"
|
||||
|
||||
/** Downcast mouse processor to hid_mouse */
|
||||
#define DOWNCAST(MP) container_of(MP, struct sc_hid_mouse, mouse_processor)
|
||||
|
||||
#define HID_MOUSE_ACCESSORY_ID 2
|
||||
|
||||
// 1 byte for buttons + padding, 1 byte for X position, 1 byte for Y position
|
||||
#define HID_MOUSE_EVENT_SIZE 4
|
||||
|
||||
/**
|
||||
* Mouse descriptor from the specification:
|
||||
* <https://www.usb.org/sites/default/files/hid1_11.pdf>
|
||||
*
|
||||
* Appendix E (p71): §E.10 Report Descriptor (Mouse)
|
||||
*
|
||||
* The usage tags (like Wheel) are listed in "HID Usage Tables":
|
||||
* <https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf>
|
||||
* §4 Generic Desktop Page (0x01) (p26)
|
||||
*/
|
||||
static const unsigned char mouse_report_desc[] = {
|
||||
// Usage Page (Generic Desktop)
|
||||
0x05, 0x01,
|
||||
// Usage (Mouse)
|
||||
0x09, 0x02,
|
||||
|
||||
// Collection (Application)
|
||||
0xA1, 0x01,
|
||||
|
||||
// Usage (Pointer)
|
||||
0x09, 0x01,
|
||||
|
||||
// Collection (Physical)
|
||||
0xA1, 0x00,
|
||||
|
||||
// Usage Page (Buttons)
|
||||
0x05, 0x09,
|
||||
|
||||
// Usage Minimum (1)
|
||||
0x19, 0x01,
|
||||
// Usage Maximum (5)
|
||||
0x29, 0x05,
|
||||
// Logical Minimum (0)
|
||||
0x15, 0x00,
|
||||
// Logical Maximum (1)
|
||||
0x25, 0x01,
|
||||
// Report Count (5)
|
||||
0x95, 0x05,
|
||||
// Report Size (1)
|
||||
0x75, 0x01,
|
||||
// Input (Data, Variable, Absolute): 5 buttons bits
|
||||
0x81, 0x02,
|
||||
|
||||
// Report Count (1)
|
||||
0x95, 0x01,
|
||||
// Report Size (3)
|
||||
0x75, 0x03,
|
||||
// Input (Constant): 3 bits padding
|
||||
0x81, 0x01,
|
||||
|
||||
// Usage Page (Generic Desktop)
|
||||
0x05, 0x01,
|
||||
// Usage (X)
|
||||
0x09, 0x30,
|
||||
// Usage (Y)
|
||||
0x09, 0x31,
|
||||
// Usage (Wheel)
|
||||
0x09, 0x38,
|
||||
// Local Minimum (-127)
|
||||
0x15, 0x81,
|
||||
// Local Maximum (127)
|
||||
0x25, 0x7F,
|
||||
// Report Size (8)
|
||||
0x75, 0x08,
|
||||
// Report Count (3)
|
||||
0x95, 0x03,
|
||||
// Input (Data, Variable, Relative): 3 position bytes (X, Y, Wheel)
|
||||
0x81, 0x06,
|
||||
|
||||
// End Collection
|
||||
0xC0,
|
||||
|
||||
// End Collection
|
||||
0xC0,
|
||||
};
|
||||
|
||||
/**
|
||||
* A mouse HID event is 3 bytes long:
|
||||
*
|
||||
* - byte 0: buttons state
|
||||
* - byte 1: relative x motion (signed byte from -127 to 127)
|
||||
* - byte 2: relative y motion (signed byte from -127 to 127)
|
||||
*
|
||||
* 7 6 5 4 3 2 1 0
|
||||
* +---------------+
|
||||
* byte 0: |0 0 0 . . . . .| buttons state
|
||||
* +---------------+
|
||||
* ^ ^ ^ ^ ^
|
||||
* | | | | `- left button
|
||||
* | | | `--- right button
|
||||
* | | `----- middle button
|
||||
* | `------- button 4
|
||||
* `--------- button 5
|
||||
*
|
||||
* +---------------+
|
||||
* byte 1: |. . . . . . . .| relative x motion
|
||||
* +---------------+
|
||||
* byte 2: |. . . . . . . .| relative y motion
|
||||
* +---------------+
|
||||
* byte 3: |. . . . . . . .| wheel motion (-1, 0 or 1)
|
||||
* +---------------+
|
||||
*
|
||||
* As an example, here is the report for a motion of (x=5, y=-4) with left
|
||||
* button pressed:
|
||||
*
|
||||
* +---------------+
|
||||
* |0 0 0 0 0 0 0 1| left button pressed
|
||||
* +---------------+
|
||||
* |0 0 0 0 0 1 0 1| horizontal motion (x = 5)
|
||||
* +---------------+
|
||||
* |1 1 1 1 1 1 0 0| relative y motion (y = -4)
|
||||
* +---------------+
|
||||
* |0 0 0 0 0 0 0 0| wheel motion
|
||||
* +---------------+
|
||||
*/
|
||||
|
||||
static bool
|
||||
sc_hid_mouse_event_init(struct sc_hid_event *hid_event) {
|
||||
unsigned char *buffer = calloc(1, HID_MOUSE_EVENT_SIZE);
|
||||
if (!buffer) {
|
||||
LOG_OOM();
|
||||
return false;
|
||||
}
|
||||
|
||||
sc_hid_event_init(hid_event, HID_MOUSE_ACCESSORY_ID, buffer,
|
||||
HID_MOUSE_EVENT_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
static unsigned char
|
||||
buttons_state_to_hid_buttons(uint8_t buttons_state) {
|
||||
unsigned char c = 0;
|
||||
if (buttons_state & SC_MOUSE_BUTTON_LEFT) {
|
||||
c |= 1 << 0;
|
||||
}
|
||||
if (buttons_state & SC_MOUSE_BUTTON_RIGHT) {
|
||||
c |= 1 << 1;
|
||||
}
|
||||
if (buttons_state & SC_MOUSE_BUTTON_MIDDLE) {
|
||||
c |= 1 << 2;
|
||||
}
|
||||
if (buttons_state & SC_MOUSE_BUTTON_X1) {
|
||||
c |= 1 << 3;
|
||||
}
|
||||
if (buttons_state & SC_MOUSE_BUTTON_X2) {
|
||||
c |= 1 << 4;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static void
|
||||
sc_mouse_processor_process_mouse_motion(struct sc_mouse_processor *mp,
|
||||
const struct sc_mouse_motion_event *event) {
|
||||
struct sc_hid_mouse *mouse = DOWNCAST(mp);
|
||||
|
||||
struct sc_hid_event hid_event;
|
||||
if (!sc_hid_mouse_event_init(&hid_event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char *buffer = hid_event.buffer;
|
||||
buffer[0] = buttons_state_to_hid_buttons(event->buttons_state);
|
||||
buffer[1] = CLAMP(event->xrel, -127, 127);
|
||||
buffer[2] = CLAMP(event->yrel, -127, 127);
|
||||
buffer[3] = 0; // wheel coordinates only used for scrolling
|
||||
|
||||
if (!sc_aoa_push_hid_event(mouse->aoa, &hid_event)) {
|
||||
sc_hid_event_destroy(&hid_event);
|
||||
LOGW("Could request HID event");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_mouse_processor_process_mouse_click(struct sc_mouse_processor *mp,
|
||||
const struct sc_mouse_click_event *event) {
|
||||
struct sc_hid_mouse *mouse = DOWNCAST(mp);
|
||||
|
||||
struct sc_hid_event hid_event;
|
||||
if (!sc_hid_mouse_event_init(&hid_event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char *buffer = hid_event.buffer;
|
||||
buffer[0] = buttons_state_to_hid_buttons(event->buttons_state);
|
||||
buffer[1] = 0; // no x motion
|
||||
buffer[2] = 0; // no y motion
|
||||
buffer[3] = 0; // wheel coordinates only used for scrolling
|
||||
|
||||
if (!sc_aoa_push_hid_event(mouse->aoa, &hid_event)) {
|
||||
sc_hid_event_destroy(&hid_event);
|
||||
LOGW("Could request HID event");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp,
|
||||
const struct sc_mouse_scroll_event *event) {
|
||||
struct sc_hid_mouse *mouse = DOWNCAST(mp);
|
||||
|
||||
struct sc_hid_event hid_event;
|
||||
if (!sc_hid_mouse_event_init(&hid_event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char *buffer = hid_event.buffer;
|
||||
buffer[0] = 0; // buttons state irrelevant (and unknown)
|
||||
buffer[1] = 0; // no x motion
|
||||
buffer[2] = 0; // no y motion
|
||||
// In practice, vscroll is always -1, 0 or 1, but in theory other values
|
||||
// are possible
|
||||
buffer[3] = CLAMP(event->vscroll, -127, 127);
|
||||
// Horizontal scrolling ignored
|
||||
|
||||
if (!sc_aoa_push_hid_event(mouse->aoa, &hid_event)) {
|
||||
sc_hid_event_destroy(&hid_event);
|
||||
LOGW("Could request HID event");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
sc_hid_mouse_init(struct sc_hid_mouse *mouse, struct sc_aoa *aoa) {
|
||||
mouse->aoa = aoa;
|
||||
|
||||
bool ok = sc_aoa_setup_hid(aoa, HID_MOUSE_ACCESSORY_ID, mouse_report_desc,
|
||||
ARRAY_LEN(mouse_report_desc));
|
||||
if (!ok) {
|
||||
LOGW("Register HID mouse failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct sc_mouse_processor_ops ops = {
|
||||
.process_mouse_motion = sc_mouse_processor_process_mouse_motion,
|
||||
.process_mouse_click = sc_mouse_processor_process_mouse_click,
|
||||
.process_mouse_scroll = sc_mouse_processor_process_mouse_scroll,
|
||||
// Touch events not supported (coordinates are not relative)
|
||||
.process_touch = NULL,
|
||||
};
|
||||
|
||||
mouse->mouse_processor.ops = &ops;
|
||||
|
||||
mouse->mouse_processor.relative_mode = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
sc_hid_mouse_destroy(struct sc_hid_mouse *mouse) {
|
||||
bool ok = sc_aoa_unregister_hid(mouse->aoa, HID_MOUSE_ACCESSORY_ID);
|
||||
if (!ok) {
|
||||
LOGW("Could not unregister HID mouse");
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#ifndef SC_HID_MOUSE_H
|
||||
#define SC_HID_MOUSE_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "aoa_hid.h"
|
||||
#include "trait/mouse_processor.h"
|
||||
|
||||
struct sc_hid_mouse {
|
||||
struct sc_mouse_processor mouse_processor; // mouse processor trait
|
||||
|
||||
struct sc_aoa *aoa;
|
||||
};
|
||||
|
||||
bool
|
||||
sc_hid_mouse_init(struct sc_hid_mouse *mouse, struct sc_aoa *aoa);
|
||||
|
||||
void
|
||||
sc_hid_mouse_destroy(struct sc_hid_mouse *mouse);
|
||||
|
||||
#endif
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
#include <libavutil/pixfmt.h>
|
||||
@@ -86,7 +85,7 @@ decode_image(const char *path) {
|
||||
|
||||
AVCodecParameters *params = ctx->streams[stream]->codecpar;
|
||||
|
||||
const AVCodec *codec = avcodec_find_decoder(params->codec_id);
|
||||
AVCodec *codec = avcodec_find_decoder(params->codec_id);
|
||||
if (!codec) {
|
||||
LOGE("Could not find image decoder");
|
||||
goto close_input;
|
||||
|
||||
76
app/src/input_events.c
Normal file
76
app/src/input_events.c
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <input_events.h>
|
||||
|
||||
static inline uint16_t
|
||||
sc_mods_state_from_sdl(uint16_t mods_state) {
|
||||
return mods_state;
|
||||
}
|
||||
|
||||
static inline enum sc_keycode
|
||||
sc_keycode_from_sdl(SDL_Keycode keycode) {
|
||||
return (enum sc_keycode) keycode;
|
||||
}
|
||||
|
||||
static inline enum sc_scancode
|
||||
sc_scancode_from_sdl(SDL_Scancode scancode) {
|
||||
return (enum sc_scancode) scancode;
|
||||
}
|
||||
|
||||
static inline enum sc_action
|
||||
sc_action_from_sdl_keyboard_type(uint32_t type) {
|
||||
assert(type == SDL_KEYDOWN || type == SDL_KEYUP);
|
||||
if (type == SDL_KEYDOWN) {
|
||||
return SC_ACTION_DOWN;
|
||||
}
|
||||
return SC_ACTION_UP;
|
||||
}
|
||||
|
||||
static inline enum sc_action
|
||||
sc_action_from_sdl_mousebutton_type(uint32_t type) {
|
||||
assert(type == SDL_MOUSEBUTTONDOWN || type == SDL_MOUSEBUTTONUP);
|
||||
if (type == SDL_MOUSEBUTTONDOWN) {
|
||||
return SC_ACTION_DOWN;
|
||||
}
|
||||
return SC_ACTION_UP;
|
||||
}
|
||||
|
||||
static inline enum sc_mouse_button
|
||||
sc_mouse_button_from_sdl(uint8_t button) {
|
||||
if (button >= SDL_BUTTON_LEFT && button <= SDL_BUTTON_X2) {
|
||||
// SC_MOUSE_BUTTON_* constants are initialized from SDL_BUTTON(index)
|
||||
return SDL_BUTTON(button);
|
||||
}
|
||||
|
||||
return SC_MOUSE_BUTTON_UNKNOWN;
|
||||
}
|
||||
|
||||
static inline uint8_t
|
||||
sc_mouse_buttons_state_from_sdl(uint32_t buttons_state) {
|
||||
assert(buttons_state < 0x100); // fits in uint8_t
|
||||
return buttons_state;
|
||||
}
|
||||
|
||||
void
|
||||
sc_key_event_from_sdl(struct sc_key_event *event,
|
||||
const SDL_KeyboardEvent *sdl) {
|
||||
event->action = sc_action_from_sdl_keyboard_type(sdl->type);
|
||||
event->keycode = sc_keycode_from_sdl(sdl->keysym.sym);
|
||||
event->scancode = sc_scancode_from_sdl(sdl->keysym.scancode);
|
||||
event->repeat = sdl->repeat;
|
||||
event->mods_state = sc_mods_state_from_sdl(sdl->keysym.mod);
|
||||
}
|
||||
|
||||
void
|
||||
sc_text_event_from_sdl(struct sc_text_event *event,
|
||||
const SDL_TextInputEvent *sdl) {
|
||||
event->text = sdl->text;
|
||||
}
|
||||
|
||||
void
|
||||
sc_mouse_click_event_from_sdl(struct sc_mouse_click_event *event,
|
||||
const SDL_MouseButtonEvent *sdl,
|
||||
const SDL_Window *window,
|
||||
struct sc_size screen_size) {
|
||||
event->action = sc_action_from_sdl_mousebutton_type(sdl->type);
|
||||
event->button = sc_mouse_button_from_sdl(sdl->button);
|
||||
event->position.screen_size = screen_size;
|
||||
}
|
||||
@@ -10,37 +10,9 @@
|
||||
|
||||
#include "coords.h"
|
||||
|
||||
/* The representation of input events in scrcpy is very close to the SDL API,
|
||||
* for simplicity.
|
||||
*
|
||||
* This scrcpy input events API is designed to be consumed by input event
|
||||
* processors (sc_key_processor and sc_mouse_processor, see app/src/trait/).
|
||||
*
|
||||
* One major semantic difference between SDL input events and scrcpy input
|
||||
* events is their frame of reference (for mouse and touch events): SDL events
|
||||
* coordinates are expressed in SDL window coordinates (the visible UI), while
|
||||
* scrcpy events are expressed in device frame coordinates.
|
||||
*
|
||||
* In particular, the window may be visually scaled or rotated (with --rotation
|
||||
* or MOD+Left/Right), but this does not impact scrcpy input events (contrary
|
||||
* to SDL input events). This allows to abstract these display details from the
|
||||
* input event processors (and to make them independent from the "screen").
|
||||
*
|
||||
* For many enums below, the values are purposely the same as the SDL
|
||||
* constants (though not all SDL values are represented), so that the
|
||||
/* The values are purposely the same as the SDL constants, so that the
|
||||
* implementation to convert from the SDL version to the scrcpy version is
|
||||
* straightforward.
|
||||
*
|
||||
* In practice, there are 3 levels of input events:
|
||||
* 1. SDL input events (as received from SDL)
|
||||
* 2. scrcpy input events (this API)
|
||||
* 3. the key/mouse processors input events (Android API or HID events)
|
||||
*
|
||||
* An input event is first received (1), then (if accepted) converted to an
|
||||
* scrcpy input event (2), then submitted to the relevant key/mouse processor,
|
||||
* which (if accepted) is converted to an Android event (to be sent to the
|
||||
* server) or to an HID event (to be sent over USB/AOA directly).
|
||||
*/
|
||||
* straightforward */
|
||||
|
||||
enum sc_mod {
|
||||
SC_MOD_LSHIFT = KMOD_LSHIFT,
|
||||
@@ -54,6 +26,7 @@ enum sc_mod {
|
||||
|
||||
SC_MOD_NUM = KMOD_NUM,
|
||||
SC_MOD_CAPS = KMOD_CAPS,
|
||||
SC_MOD_SCROLL = KMOD_SCROLL,
|
||||
};
|
||||
|
||||
enum sc_action {
|
||||
@@ -360,7 +333,6 @@ struct sc_mouse_scroll_event {
|
||||
struct sc_position position;
|
||||
int32_t hscroll;
|
||||
int32_t vscroll;
|
||||
uint8_t buttons_state; // bitwise-OR of sc_mouse_button values
|
||||
};
|
||||
|
||||
struct sc_mouse_motion_event {
|
||||
@@ -370,6 +342,32 @@ struct sc_mouse_motion_event {
|
||||
uint8_t buttons_state; // bitwise-OR of sc_mouse_button values
|
||||
};
|
||||
|
||||
//enum sc_mouse_event_type {
|
||||
// SC_MOUSE_EVENT_TYPE_CLICK,
|
||||
// SC_MOUSE_EVENT_TYPE_MOVE,
|
||||
// SC_MOUSE_EVENT_TYPE_SCROLL,
|
||||
//};
|
||||
//
|
||||
//struct sc_mouse_event {
|
||||
// enum sc_mouse_event_type type;
|
||||
// struct sc_position position;
|
||||
// uint8_t buttons_state; // bitwise-OR of sc_mouse_button values
|
||||
// union {
|
||||
// struct {
|
||||
// enum sc_action action;
|
||||
// enum sc_mouse_button button;
|
||||
// } click;
|
||||
// struct {
|
||||
// int32_t xrel;
|
||||
// int32_t yrel;
|
||||
// } move;
|
||||
// struct {
|
||||
// int32_t h;
|
||||
// int32_t v;
|
||||
// } scroll;
|
||||
// };
|
||||
//};
|
||||
|
||||
struct sc_touch_event {
|
||||
struct sc_position position;
|
||||
enum sc_touch_action action;
|
||||
@@ -377,4 +375,30 @@ struct sc_touch_event {
|
||||
float pressure;
|
||||
};
|
||||
|
||||
|
||||
//void
|
||||
//sc_key_event_from_sdl(struct sc_key_event *event, const SDL_KeyboardEvent *sdl);
|
||||
//
|
||||
//void
|
||||
//sc_text_event_from_sdl(struct sc_text_event *event,
|
||||
// const SDL_TextInputEvent *sdl);
|
||||
//
|
||||
//void
|
||||
//sc_mouse_click_event_from_sdl(struct sc_mouse_click_event *event,
|
||||
// const SDL_MouseButtonEvent *sdl,
|
||||
// const SDL_Window *window,
|
||||
// struct sc_size screen_size);
|
||||
//
|
||||
//void
|
||||
//sc_mouse_wheel_event_from_sdl(struct sc_mouse_wheel_event *event,
|
||||
// const SDL_MouseWheelEvent *sdl);
|
||||
//
|
||||
//void
|
||||
//sc_mouse_motion_event_from_sdl(struct sc_mouse_motion_event *event,
|
||||
// const SDL_MouseMotionEvent *sdl);
|
||||
//
|
||||
//void
|
||||
//sc_touch_event_from_sdl(struct sc_touch_event *event,
|
||||
// const SDL_TouchFingerEvent *sdl);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <SDL2/SDL_keycode.h>
|
||||
|
||||
#include "input_events.h"
|
||||
#include "screen.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static inline uint16_t
|
||||
@@ -64,21 +63,36 @@ sc_mouse_button_from_sdl(uint8_t button) {
|
||||
}
|
||||
|
||||
static inline uint8_t
|
||||
sc_mouse_buttons_state_from_sdl(uint32_t buttons_state,
|
||||
bool forward_all_clicks) {
|
||||
sc_mouse_buttons_state_from_sdl(uint32_t buttons_state) {
|
||||
assert(buttons_state < 0x100); // fits in uint8_t
|
||||
|
||||
uint8_t mask = SC_MOUSE_BUTTON_LEFT;
|
||||
if (forward_all_clicks) {
|
||||
mask |= SC_MOUSE_BUTTON_RIGHT
|
||||
| SC_MOUSE_BUTTON_MIDDLE
|
||||
| SC_MOUSE_BUTTON_X1
|
||||
| SC_MOUSE_BUTTON_X2;
|
||||
}
|
||||
|
||||
return buttons_state & mask;
|
||||
return buttons_state;
|
||||
}
|
||||
|
||||
//void
|
||||
//sc_key_event_from_sdl(struct sc_key_event *event,
|
||||
// const SDL_KeyboardEvent *sdl) {
|
||||
// event->action = sc_action_from_sdl_keyboard_type(sdl->type);
|
||||
// event->keycode = sc_keycode_from_sdl(sdl->keysym.sym);
|
||||
// event->scancode = sc_scancode_from_sdl(sdl->keysym.scancode);
|
||||
// event->repeat = sdl->repeat;
|
||||
// event->mods_state = sc_mods_state_from_sdl(sdl->keysym.mod);
|
||||
//}
|
||||
//
|
||||
//void
|
||||
//sc_text_event_from_sdl(struct sc_text_event *event,
|
||||
// const SDL_TextInputEvent *sdl) {
|
||||
// event->text = sdl->text;
|
||||
//}
|
||||
//
|
||||
//void
|
||||
//sc_mouse_click_event_from_sdl(struct sc_mouse_click_event *event,
|
||||
// const SDL_MouseButtonEvent *sdl,
|
||||
// struct sc_size screen_size) {
|
||||
// event->action = sc_action_from_sdl_mousebutton_type(sdl->type);
|
||||
// event->button = sc_mouse_button_from_sdl(sdl->button);
|
||||
// event->position.screen_size = screen_size;
|
||||
//}
|
||||
|
||||
#define SC_SDL_SHORTCUT_MODS_MASK (KMOD_CTRL | KMOD_ALT | KMOD_GUI)
|
||||
|
||||
static inline uint16_t
|
||||
@@ -106,7 +120,7 @@ to_sdl_mod(unsigned shortcut_mod) {
|
||||
}
|
||||
|
||||
static bool
|
||||
is_shortcut_mod(struct sc_input_manager *im, uint16_t sdl_mod) {
|
||||
is_shortcut_mod(struct input_manager *im, uint16_t sdl_mod) {
|
||||
// keep only the relevant modifier keys
|
||||
sdl_mod &= SC_SDL_SHORTCUT_MODS_MASK;
|
||||
|
||||
@@ -122,22 +136,24 @@ is_shortcut_mod(struct sc_input_manager *im, uint16_t sdl_mod) {
|
||||
}
|
||||
|
||||
void
|
||||
sc_input_manager_init(struct sc_input_manager *im,
|
||||
const struct sc_input_manager_params *params) {
|
||||
assert(!params->controller || (params->kp && params->kp->ops));
|
||||
assert(!params->controller || (params->mp && params->mp->ops));
|
||||
input_manager_init(struct input_manager *im, struct controller *controller,
|
||||
struct screen *screen, struct sc_key_processor *kp,
|
||||
struct sc_mouse_processor *mp,
|
||||
const struct scrcpy_options *options) {
|
||||
assert(!options->control || (kp && kp->ops));
|
||||
assert(!options->control || (mp && mp->ops));
|
||||
|
||||
im->controller = params->controller;
|
||||
im->fp = params->fp;
|
||||
im->screen = params->screen;
|
||||
im->kp = params->kp;
|
||||
im->mp = params->mp;
|
||||
im->controller = controller;
|
||||
im->screen = screen;
|
||||
im->kp = kp;
|
||||
im->mp = mp;
|
||||
|
||||
im->forward_all_clicks = params->forward_all_clicks;
|
||||
im->legacy_paste = params->legacy_paste;
|
||||
im->clipboard_autosync = params->clipboard_autosync;
|
||||
im->control = options->control;
|
||||
im->forward_all_clicks = options->forward_all_clicks;
|
||||
im->legacy_paste = options->legacy_paste;
|
||||
im->clipboard_autosync = options->clipboard_autosync;
|
||||
|
||||
const struct sc_shortcut_mods *shortcut_mods = params->shortcut_mods;
|
||||
const struct sc_shortcut_mods *shortcut_mods = &options->shortcut_mods;
|
||||
assert(shortcut_mods->count);
|
||||
assert(shortcut_mods->count < SC_MAX_SHORTCUT_MODS);
|
||||
for (unsigned i = 0; i < shortcut_mods->count; ++i) {
|
||||
@@ -157,11 +173,11 @@ sc_input_manager_init(struct sc_input_manager *im,
|
||||
}
|
||||
|
||||
static void
|
||||
send_keycode(struct sc_controller *controller, enum android_keycode keycode,
|
||||
send_keycode(struct controller *controller, enum android_keycode keycode,
|
||||
enum sc_action action, const char *name) {
|
||||
// send DOWN event
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
||||
msg.inject_keycode.action = action == SC_ACTION_DOWN
|
||||
? AKEY_EVENT_ACTION_DOWN
|
||||
: AKEY_EVENT_ACTION_UP;
|
||||
@@ -169,100 +185,102 @@ send_keycode(struct sc_controller *controller, enum android_keycode keycode,
|
||||
msg.inject_keycode.metastate = 0;
|
||||
msg.inject_keycode.repeat = 0;
|
||||
|
||||
if (!sc_controller_push_msg(controller, &msg)) {
|
||||
if (!controller_push_msg(controller, &msg)) {
|
||||
LOGW("Could not request 'inject %s'", name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
action_home(struct sc_controller *controller, enum sc_action action) {
|
||||
action_home(struct controller *controller, enum sc_action action) {
|
||||
send_keycode(controller, AKEYCODE_HOME, action, "HOME");
|
||||
}
|
||||
|
||||
static inline void
|
||||
action_back(struct sc_controller *controller, enum sc_action action) {
|
||||
action_back(struct controller *controller, enum sc_action action) {
|
||||
send_keycode(controller, AKEYCODE_BACK, action, "BACK");
|
||||
}
|
||||
|
||||
static inline void
|
||||
action_app_switch(struct sc_controller *controller, enum sc_action action) {
|
||||
action_app_switch(struct controller *controller, enum sc_action action) {
|
||||
send_keycode(controller, AKEYCODE_APP_SWITCH, action, "APP_SWITCH");
|
||||
}
|
||||
|
||||
static inline void
|
||||
action_power(struct sc_controller *controller, enum sc_action action) {
|
||||
action_power(struct controller *controller, enum sc_action action) {
|
||||
send_keycode(controller, AKEYCODE_POWER, action, "POWER");
|
||||
}
|
||||
|
||||
static inline void
|
||||
action_volume_up(struct sc_controller *controller, enum sc_action action) {
|
||||
action_volume_up(struct controller *controller, enum sc_action action) {
|
||||
send_keycode(controller, AKEYCODE_VOLUME_UP, action, "VOLUME_UP");
|
||||
}
|
||||
|
||||
static inline void
|
||||
action_volume_down(struct sc_controller *controller, enum sc_action action) {
|
||||
action_volume_down(struct controller *controller, enum sc_action action) {
|
||||
send_keycode(controller, AKEYCODE_VOLUME_DOWN, action, "VOLUME_DOWN");
|
||||
}
|
||||
|
||||
static inline void
|
||||
action_menu(struct sc_controller *controller, enum sc_action action) {
|
||||
action_menu(struct controller *controller, enum sc_action action) {
|
||||
send_keycode(controller, AKEYCODE_MENU, action, "MENU");
|
||||
}
|
||||
|
||||
// turn the screen on if it was off, press BACK otherwise
|
||||
// If the screen is off, it is turned on only on ACTION_DOWN
|
||||
static void
|
||||
press_back_or_turn_screen_on(struct sc_controller *controller,
|
||||
press_back_or_turn_screen_on(struct controller *controller,
|
||||
enum sc_action action) {
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON;
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON;
|
||||
msg.back_or_screen_on.action = action == SC_ACTION_DOWN
|
||||
? AKEY_EVENT_ACTION_DOWN
|
||||
: AKEY_EVENT_ACTION_UP;
|
||||
|
||||
if (!sc_controller_push_msg(controller, &msg)) {
|
||||
if (!controller_push_msg(controller, &msg)) {
|
||||
LOGW("Could not request 'press back or turn screen on'");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
expand_notification_panel(struct sc_controller *controller) {
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL;
|
||||
expand_notification_panel(struct controller *controller) {
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL;
|
||||
|
||||
if (!sc_controller_push_msg(controller, &msg)) {
|
||||
if (!controller_push_msg(controller, &msg)) {
|
||||
LOGW("Could not request 'expand notification panel'");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
expand_settings_panel(struct sc_controller *controller) {
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL;
|
||||
expand_settings_panel(struct controller *controller) {
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL;
|
||||
|
||||
if (!sc_controller_push_msg(controller, &msg)) {
|
||||
if (!controller_push_msg(controller, &msg)) {
|
||||
LOGW("Could not request 'expand settings panel'");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
collapse_panels(struct sc_controller *controller) {
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS;
|
||||
collapse_panels(struct controller *controller) {
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_COLLAPSE_PANELS;
|
||||
|
||||
if (!sc_controller_push_msg(controller, &msg)) {
|
||||
if (!controller_push_msg(controller, &msg)) {
|
||||
LOGW("Could not request 'collapse notification panel'");
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
get_device_clipboard(struct sc_controller *controller,
|
||||
enum sc_copy_key copy_key) {
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_GET_CLIPBOARD;
|
||||
get_device_clipboard(struct controller *controller,
|
||||
enum get_clipboard_copy_key copy_key) {
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_GET_CLIPBOARD;
|
||||
msg.get_clipboard.copy_key = copy_key;
|
||||
|
||||
if (!sc_controller_push_msg(controller, &msg)) {
|
||||
if (!controller_push_msg(controller, &msg)) {
|
||||
LOGW("Could not request 'get device clipboard'");
|
||||
return false;
|
||||
}
|
||||
@@ -271,7 +289,7 @@ get_device_clipboard(struct sc_controller *controller,
|
||||
}
|
||||
|
||||
static bool
|
||||
set_device_clipboard(struct sc_controller *controller, bool paste,
|
||||
set_device_clipboard(struct controller *controller, bool paste,
|
||||
uint64_t sequence) {
|
||||
char *text = SDL_GetClipboardText();
|
||||
if (!text) {
|
||||
@@ -286,13 +304,13 @@ set_device_clipboard(struct sc_controller *controller, bool paste,
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_SET_CLIPBOARD;
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_SET_CLIPBOARD;
|
||||
msg.set_clipboard.sequence = sequence;
|
||||
msg.set_clipboard.text = text_dup;
|
||||
msg.set_clipboard.paste = paste;
|
||||
|
||||
if (!sc_controller_push_msg(controller, &msg)) {
|
||||
if (!controller_push_msg(controller, &msg)) {
|
||||
free(text_dup);
|
||||
LOGW("Could not request 'set device clipboard'");
|
||||
return false;
|
||||
@@ -302,13 +320,13 @@ set_device_clipboard(struct sc_controller *controller, bool paste,
|
||||
}
|
||||
|
||||
static void
|
||||
set_screen_power_mode(struct sc_controller *controller,
|
||||
enum sc_screen_power_mode mode) {
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE;
|
||||
set_screen_power_mode(struct controller *controller,
|
||||
enum screen_power_mode mode) {
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE;
|
||||
msg.set_screen_power_mode.mode = mode;
|
||||
|
||||
if (!sc_controller_push_msg(controller, &msg)) {
|
||||
if (!controller_push_msg(controller, &msg)) {
|
||||
LOGW("Could not request 'set screen power mode'");
|
||||
}
|
||||
}
|
||||
@@ -330,7 +348,7 @@ switch_fps_counter_state(struct fps_counter *fps_counter) {
|
||||
}
|
||||
|
||||
static void
|
||||
clipboard_paste(struct sc_controller *controller) {
|
||||
clipboard_paste(struct controller *controller) {
|
||||
char *text = SDL_GetClipboardText();
|
||||
if (!text) {
|
||||
LOGW("Could not get clipboard text: %s", SDL_GetError());
|
||||
@@ -349,45 +367,40 @@ clipboard_paste(struct sc_controller *controller) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_INJECT_TEXT;
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
|
||||
msg.inject_text.text = text_dup;
|
||||
if (!sc_controller_push_msg(controller, &msg)) {
|
||||
if (!controller_push_msg(controller, &msg)) {
|
||||
free(text_dup);
|
||||
LOGW("Could not request 'paste clipboard'");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rotate_device(struct sc_controller *controller) {
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_ROTATE_DEVICE;
|
||||
rotate_device(struct controller *controller) {
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_ROTATE_DEVICE;
|
||||
|
||||
if (!sc_controller_push_msg(controller, &msg)) {
|
||||
if (!controller_push_msg(controller, &msg)) {
|
||||
LOGW("Could not request device rotation");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rotate_client_left(struct sc_screen *screen) {
|
||||
rotate_client_left(struct screen *screen) {
|
||||
unsigned new_rotation = (screen->rotation + 1) % 4;
|
||||
sc_screen_set_rotation(screen, new_rotation);
|
||||
screen_set_rotation(screen, new_rotation);
|
||||
}
|
||||
|
||||
static void
|
||||
rotate_client_right(struct sc_screen *screen) {
|
||||
rotate_client_right(struct screen *screen) {
|
||||
unsigned new_rotation = (screen->rotation + 3) % 4;
|
||||
sc_screen_set_rotation(screen, new_rotation);
|
||||
screen_set_rotation(screen, new_rotation);
|
||||
}
|
||||
|
||||
static void
|
||||
sc_input_manager_process_text_input(struct sc_input_manager *im,
|
||||
const SDL_TextInputEvent *event) {
|
||||
if (!im->kp->ops->process_text) {
|
||||
// The key processor does not support text input
|
||||
return;
|
||||
}
|
||||
|
||||
input_manager_process_text_input(struct input_manager *im,
|
||||
const SDL_TextInputEvent *event) {
|
||||
if (is_shortcut_mod(im, SDL_GetModState())) {
|
||||
// A shortcut must never generate text events
|
||||
return;
|
||||
@@ -401,13 +414,13 @@ sc_input_manager_process_text_input(struct sc_input_manager *im,
|
||||
}
|
||||
|
||||
static bool
|
||||
simulate_virtual_finger(struct sc_input_manager *im,
|
||||
simulate_virtual_finger(struct input_manager *im,
|
||||
enum android_motionevent_action action,
|
||||
struct sc_point point) {
|
||||
bool up = action == AMOTION_EVENT_ACTION_UP;
|
||||
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
||||
msg.inject_touch_event.action = action;
|
||||
msg.inject_touch_event.position.screen_size = im->screen->frame_size;
|
||||
msg.inject_touch_event.position.point = point;
|
||||
@@ -415,7 +428,7 @@ simulate_virtual_finger(struct sc_input_manager *im,
|
||||
msg.inject_touch_event.pressure = up ? 0.0f : 1.0f;
|
||||
msg.inject_touch_event.buttons = 0;
|
||||
|
||||
if (!sc_controller_push_msg(im->controller, &msg)) {
|
||||
if (!controller_push_msg(im->controller, &msg)) {
|
||||
LOGW("Could not request 'inject virtual finger event'");
|
||||
return false;
|
||||
}
|
||||
@@ -431,10 +444,12 @@ inverse_point(struct sc_point point, struct sc_size size) {
|
||||
}
|
||||
|
||||
static void
|
||||
sc_input_manager_process_key(struct sc_input_manager *im,
|
||||
const SDL_KeyboardEvent *event) {
|
||||
// controller is NULL if --no-control is requested
|
||||
struct sc_controller *controller = im->controller;
|
||||
input_manager_process_key(struct input_manager *im,
|
||||
const SDL_KeyboardEvent *event) {
|
||||
// control: indicates the state of the command-line option --no-control
|
||||
bool control = im->control;
|
||||
|
||||
struct controller *controller = im->controller;
|
||||
|
||||
SDL_Keycode keycode = event->keysym.sym;
|
||||
uint16_t mod = event->keysym.mod;
|
||||
@@ -460,47 +475,47 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
||||
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
|
||||
switch (keycode) {
|
||||
case SDLK_h:
|
||||
if (controller && !shift && !repeat) {
|
||||
if (control && !shift && !repeat) {
|
||||
action_home(controller, action);
|
||||
}
|
||||
return;
|
||||
case SDLK_b: // fall-through
|
||||
case SDLK_BACKSPACE:
|
||||
if (controller && !shift && !repeat) {
|
||||
if (control && !shift && !repeat) {
|
||||
action_back(controller, action);
|
||||
}
|
||||
return;
|
||||
case SDLK_s:
|
||||
if (controller && !shift && !repeat) {
|
||||
if (control && !shift && !repeat) {
|
||||
action_app_switch(controller, action);
|
||||
}
|
||||
return;
|
||||
case SDLK_m:
|
||||
if (controller && !shift && !repeat) {
|
||||
if (control && !shift && !repeat) {
|
||||
action_menu(controller, action);
|
||||
}
|
||||
return;
|
||||
case SDLK_p:
|
||||
if (controller && !shift && !repeat) {
|
||||
if (control && !shift && !repeat) {
|
||||
action_power(controller, action);
|
||||
}
|
||||
return;
|
||||
case SDLK_o:
|
||||
if (controller && !repeat && down) {
|
||||
enum sc_screen_power_mode mode = shift
|
||||
? SC_SCREEN_POWER_MODE_NORMAL
|
||||
: SC_SCREEN_POWER_MODE_OFF;
|
||||
if (control && !repeat && down) {
|
||||
enum screen_power_mode mode = shift
|
||||
? SCREEN_POWER_MODE_NORMAL
|
||||
: SCREEN_POWER_MODE_OFF;
|
||||
set_screen_power_mode(controller, mode);
|
||||
}
|
||||
return;
|
||||
case SDLK_DOWN:
|
||||
if (controller && !shift) {
|
||||
if (control && !shift) {
|
||||
// forward repeated events
|
||||
action_volume_down(controller, action);
|
||||
}
|
||||
return;
|
||||
case SDLK_UP:
|
||||
if (controller && !shift) {
|
||||
if (control && !shift) {
|
||||
// forward repeated events
|
||||
action_volume_up(controller, action);
|
||||
}
|
||||
@@ -516,17 +531,19 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
||||
}
|
||||
return;
|
||||
case SDLK_c:
|
||||
if (controller && !shift && !repeat && down) {
|
||||
get_device_clipboard(controller, SC_COPY_KEY_COPY);
|
||||
if (control && !shift && !repeat && down) {
|
||||
get_device_clipboard(controller,
|
||||
GET_CLIPBOARD_COPY_KEY_COPY);
|
||||
}
|
||||
return;
|
||||
case SDLK_x:
|
||||
if (controller && !shift && !repeat && down) {
|
||||
get_device_clipboard(controller, SC_COPY_KEY_CUT);
|
||||
if (control && !shift && !repeat && down) {
|
||||
get_device_clipboard(controller,
|
||||
GET_CLIPBOARD_COPY_KEY_CUT);
|
||||
}
|
||||
return;
|
||||
case SDLK_v:
|
||||
if (controller && !repeat && down) {
|
||||
if (control && !repeat && down) {
|
||||
if (shift || im->legacy_paste) {
|
||||
// inject the text as input events
|
||||
clipboard_paste(controller);
|
||||
@@ -540,17 +557,17 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
||||
return;
|
||||
case SDLK_f:
|
||||
if (!shift && !repeat && down) {
|
||||
sc_screen_switch_fullscreen(im->screen);
|
||||
screen_switch_fullscreen(im->screen);
|
||||
}
|
||||
return;
|
||||
case SDLK_w:
|
||||
if (!shift && !repeat && down) {
|
||||
sc_screen_resize_to_fit(im->screen);
|
||||
screen_resize_to_fit(im->screen);
|
||||
}
|
||||
return;
|
||||
case SDLK_g:
|
||||
if (!shift && !repeat && down) {
|
||||
sc_screen_resize_to_pixel_perfect(im->screen);
|
||||
screen_resize_to_pixel_perfect(im->screen);
|
||||
}
|
||||
return;
|
||||
case SDLK_i:
|
||||
@@ -559,7 +576,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
||||
}
|
||||
return;
|
||||
case SDLK_n:
|
||||
if (controller && !repeat && down) {
|
||||
if (control && !repeat && down) {
|
||||
if (shift) {
|
||||
collapse_panels(controller);
|
||||
} else if (im->key_repeat == 0) {
|
||||
@@ -570,7 +587,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
||||
}
|
||||
return;
|
||||
case SDLK_r:
|
||||
if (controller && !shift && !repeat && down) {
|
||||
if (control && !shift && !repeat && down) {
|
||||
rotate_device(controller);
|
||||
}
|
||||
return;
|
||||
@@ -579,7 +596,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!controller) {
|
||||
if (!control) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -620,14 +637,20 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
||||
.mods_state = sc_mods_state_from_sdl(event->keysym.mod),
|
||||
};
|
||||
|
||||
assert(im->kp->ops->process_key);
|
||||
im->kp->ops->process_key(im->kp, &evt, ack_to_wait);
|
||||
}
|
||||
|
||||
static void
|
||||
sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
|
||||
const SDL_MouseMotionEvent *event) {
|
||||
|
||||
input_manager_process_mouse_motion(struct input_manager *im,
|
||||
const SDL_MouseMotionEvent *event) {
|
||||
uint32_t mask = SDL_BUTTON_LMASK;
|
||||
if (im->forward_all_clicks) {
|
||||
mask |= SDL_BUTTON_MMASK | SDL_BUTTON_RMASK;
|
||||
}
|
||||
if (!(event->state & mask)) {
|
||||
// do not send motion events when no click is pressed
|
||||
return;
|
||||
}
|
||||
if (event->which == SDL_TOUCH_MOUSEID) {
|
||||
// simulated from touch events, so it's a duplicate
|
||||
return;
|
||||
@@ -636,41 +659,28 @@ 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),
|
||||
.point = screen_convert_window_to_frame_coords(im->screen,
|
||||
event->x, event->y),
|
||||
},
|
||||
.xrel = event->xrel,
|
||||
.yrel = event->yrel,
|
||||
.buttons_state =
|
||||
sc_mouse_buttons_state_from_sdl(event->state,
|
||||
im->forward_all_clicks),
|
||||
.buttons_state = sc_mouse_buttons_state_from_sdl(event->state),
|
||||
};
|
||||
|
||||
assert(im->mp->ops->process_mouse_motion);
|
||||
im->mp->ops->process_mouse_motion(im->mp, &evt);
|
||||
|
||||
// vfinger must never be used in relative mode
|
||||
assert(!im->mp->relative_mode || !im->vfinger_down);
|
||||
im->mp->ops->process_mouse_motion(im->mp, event);
|
||||
|
||||
if (im->vfinger_down) {
|
||||
assert(!im->mp->relative_mode); // assert one more time
|
||||
struct sc_point mouse =
|
||||
sc_screen_convert_window_to_frame_coords(im->screen, event->x,
|
||||
event->y);
|
||||
screen_convert_window_to_frame_coords(im->screen, event->x,
|
||||
event->y);
|
||||
struct sc_point vfinger = inverse_point(mouse, im->screen->frame_size);
|
||||
simulate_virtual_finger(im, AMOTION_EVENT_ACTION_MOVE, vfinger);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_input_manager_process_touch(struct sc_input_manager *im,
|
||||
const SDL_TouchFingerEvent *event) {
|
||||
if (!im->mp->ops->process_touch) {
|
||||
// The mouse processor does not support touch events
|
||||
return;
|
||||
}
|
||||
|
||||
input_manager_process_touch(struct input_manager *im,
|
||||
const SDL_TouchFingerEvent *event) {
|
||||
int dw;
|
||||
int dh;
|
||||
SDL_GL_GetDrawableSize(im->screen->window, &dw, &dh);
|
||||
@@ -682,21 +692,20 @@ sc_input_manager_process_touch(struct sc_input_manager *im,
|
||||
struct sc_touch_event evt = {
|
||||
.position = {
|
||||
.screen_size = im->screen->frame_size,
|
||||
.point =
|
||||
sc_screen_convert_drawable_to_frame_coords(im->screen, x, y),
|
||||
.point = screen_convert_drawable_to_frame_coords(im->screen, x, y),
|
||||
},
|
||||
.action = sc_touch_action_from_sdl(event->type),
|
||||
.pointer_id = event->fingerId,
|
||||
.pressure = event->pressure,
|
||||
};
|
||||
|
||||
im->mp->ops->process_touch(im->mp, &evt);
|
||||
im->mp->ops->process_touch(im->mp, event);
|
||||
}
|
||||
|
||||
static void
|
||||
sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
||||
const SDL_MouseButtonEvent *event) {
|
||||
struct sc_controller *controller = im->controller;
|
||||
input_manager_process_mouse_button(struct input_manager *im,
|
||||
const SDL_MouseButtonEvent *event) {
|
||||
bool control = im->control;
|
||||
|
||||
if (event->which == SDL_TOUCH_MOUSEID) {
|
||||
// simulated from touch events, so it's a duplicate
|
||||
@@ -705,42 +714,40 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
||||
|
||||
bool down = event->type == SDL_MOUSEBUTTONDOWN;
|
||||
if (!im->forward_all_clicks) {
|
||||
if (controller) {
|
||||
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
|
||||
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
|
||||
|
||||
if (event->button == SDL_BUTTON_X1) {
|
||||
action_app_switch(controller, action);
|
||||
return;
|
||||
}
|
||||
if (event->button == SDL_BUTTON_X2 && down) {
|
||||
if (event->clicks < 2) {
|
||||
expand_notification_panel(controller);
|
||||
} else {
|
||||
expand_settings_panel(controller);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (event->button == SDL_BUTTON_RIGHT) {
|
||||
press_back_or_turn_screen_on(controller, action);
|
||||
return;
|
||||
}
|
||||
if (event->button == SDL_BUTTON_MIDDLE) {
|
||||
action_home(controller, action);
|
||||
return;
|
||||
if (control && event->button == SDL_BUTTON_X1) {
|
||||
action_app_switch(im->controller, action);
|
||||
return;
|
||||
}
|
||||
if (control && event->button == SDL_BUTTON_X2 && down) {
|
||||
if (event->clicks < 2) {
|
||||
expand_notification_panel(im->controller);
|
||||
} else {
|
||||
expand_settings_panel(im->controller);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (control && event->button == SDL_BUTTON_RIGHT) {
|
||||
press_back_or_turn_screen_on(im->controller, action);
|
||||
return;
|
||||
}
|
||||
if (control && event->button == SDL_BUTTON_MIDDLE) {
|
||||
action_home(im->controller, action);
|
||||
return;
|
||||
}
|
||||
|
||||
// double-click on black borders resize to fit the device screen
|
||||
if (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);
|
||||
screen_hidpi_scale_coords(im->screen, &x, &y);
|
||||
SDL_Rect *r = &im->screen->rect;
|
||||
bool outside = x < r->x || x >= r->x + r->w
|
||||
|| y < r->y || y >= r->y + r->h;
|
||||
if (outside) {
|
||||
if (down) {
|
||||
sc_screen_resize_to_fit(im->screen);
|
||||
screen_resize_to_fit(im->screen);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -748,7 +755,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
||||
// otherwise, send the click event to the device
|
||||
}
|
||||
|
||||
if (!controller) {
|
||||
if (!control) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -757,25 +764,15 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
||||
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),
|
||||
.point = screen_convert_window_to_frame_coords(im->screen, event->x,
|
||||
event->y),
|
||||
},
|
||||
.action = sc_action_from_sdl_mousebutton_type(event->type),
|
||||
.button = sc_mouse_button_from_sdl(event->button),
|
||||
.buttons_state =
|
||||
sc_mouse_buttons_state_from_sdl(sdl_buttons_state,
|
||||
im->forward_all_clicks),
|
||||
.buttons_state = sc_mouse_buttons_state_from_sdl(sdl_buttons_state),
|
||||
};
|
||||
|
||||
assert(im->mp->ops->process_mouse_click);
|
||||
im->mp->ops->process_mouse_click(im->mp, &evt);
|
||||
|
||||
if (im->mp->relative_mode) {
|
||||
assert(!im->vfinger_down); // vfinger must not be used in relative mode
|
||||
// No pinch-to-zoom simulation
|
||||
return;
|
||||
}
|
||||
im->mp->ops->process_mouse_button(im->mp, event);
|
||||
|
||||
// Pinch-to-zoom simulation.
|
||||
//
|
||||
@@ -787,12 +784,11 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
||||
// In other words, the center of the rotation/scaling is the center of the
|
||||
// screen.
|
||||
#define CTRL_PRESSED (SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL))
|
||||
if (event->button == SDL_BUTTON_LEFT &&
|
||||
((down && !im->vfinger_down && CTRL_PRESSED) ||
|
||||
(!down && im->vfinger_down))) {
|
||||
if ((down && !im->vfinger_down && CTRL_PRESSED)
|
||||
|| (!down && im->vfinger_down)) {
|
||||
struct sc_point mouse =
|
||||
sc_screen_convert_window_to_frame_coords(im->screen, event->x,
|
||||
event->y);
|
||||
screen_convert_window_to_frame_coords(im->screen, event->x,
|
||||
event->y);
|
||||
struct sc_point vfinger = inverse_point(mouse, im->screen->frame_size);
|
||||
enum android_motionevent_action action = down
|
||||
? AMOTION_EVENT_ACTION_DOWN
|
||||
@@ -805,108 +801,65 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
||||
}
|
||||
|
||||
static void
|
||||
sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
|
||||
const SDL_MouseWheelEvent *event) {
|
||||
if (!im->mp->ops->process_mouse_scroll) {
|
||||
// The mouse processor does not support scroll events
|
||||
return;
|
||||
}
|
||||
|
||||
input_manager_process_mouse_wheel(struct input_manager *im,
|
||||
const SDL_MouseWheelEvent *event) {
|
||||
// mouse_x and mouse_y are expressed in pixels relative to the window
|
||||
int mouse_x;
|
||||
int mouse_y;
|
||||
uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y);
|
||||
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),
|
||||
.point = screen_convert_window_to_frame_coords(im->screen,
|
||||
mouse_x, mouse_y),
|
||||
},
|
||||
.hscroll = event->x,
|
||||
.vscroll = event->y,
|
||||
.buttons_state =
|
||||
sc_mouse_buttons_state_from_sdl(buttons, im->forward_all_clicks),
|
||||
};
|
||||
|
||||
im->mp->ops->process_mouse_scroll(im->mp, &evt);
|
||||
im->mp->ops->process_mouse_wheel(im->mp, event);
|
||||
}
|
||||
|
||||
static bool
|
||||
is_apk(const char *file) {
|
||||
const char *ext = strrchr(file, '.');
|
||||
return ext && !strcmp(ext, ".apk");
|
||||
}
|
||||
|
||||
static void
|
||||
sc_input_manager_process_file(struct sc_input_manager *im,
|
||||
const SDL_DropEvent *event) {
|
||||
char *file = strdup(event->file);
|
||||
SDL_free(event->file);
|
||||
if (!file) {
|
||||
LOG_OOM();
|
||||
return;
|
||||
}
|
||||
|
||||
enum sc_file_pusher_action action;
|
||||
if (is_apk(file)) {
|
||||
action = SC_FILE_PUSHER_ACTION_INSTALL_APK;
|
||||
} else {
|
||||
action = SC_FILE_PUSHER_ACTION_PUSH_FILE;
|
||||
}
|
||||
bool ok = sc_file_pusher_request(im->fp, action, file);
|
||||
if (!ok) {
|
||||
free(file);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sc_input_manager_handle_event(struct sc_input_manager *im, SDL_Event *event) {
|
||||
bool control = im->controller;
|
||||
bool
|
||||
input_manager_handle_event(struct input_manager *im, SDL_Event *event) {
|
||||
switch (event->type) {
|
||||
case SDL_TEXTINPUT:
|
||||
if (!control) {
|
||||
break;
|
||||
if (!im->control) {
|
||||
return true;
|
||||
}
|
||||
sc_input_manager_process_text_input(im, &event->text);
|
||||
break;
|
||||
input_manager_process_text_input(im, &event->text);
|
||||
return true;
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
// some key events do not interact with the device, so process the
|
||||
// event even if control is disabled
|
||||
sc_input_manager_process_key(im, &event->key);
|
||||
break;
|
||||
input_manager_process_key(im, &event->key);
|
||||
return true;
|
||||
case SDL_MOUSEMOTION:
|
||||
if (!control) {
|
||||
if (!im->control) {
|
||||
break;
|
||||
}
|
||||
sc_input_manager_process_mouse_motion(im, &event->motion);
|
||||
break;
|
||||
input_manager_process_mouse_motion(im, &event->motion);
|
||||
return true;
|
||||
case SDL_MOUSEWHEEL:
|
||||
if (!control) {
|
||||
if (!im->control) {
|
||||
break;
|
||||
}
|
||||
sc_input_manager_process_mouse_wheel(im, &event->wheel);
|
||||
break;
|
||||
input_manager_process_mouse_wheel(im, &event->wheel);
|
||||
return true;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
// some mouse events do not interact with the device, so process
|
||||
// the event even if control is disabled
|
||||
sc_input_manager_process_mouse_button(im, &event->button);
|
||||
break;
|
||||
input_manager_process_mouse_button(im, &event->button);
|
||||
return true;
|
||||
case SDL_FINGERMOTION:
|
||||
case SDL_FINGERDOWN:
|
||||
case SDL_FINGERUP:
|
||||
if (!control) {
|
||||
break;
|
||||
}
|
||||
sc_input_manager_process_touch(im, &event->tfinger);
|
||||
break;
|
||||
case SDL_DROPFILE: {
|
||||
if (!control) {
|
||||
break;
|
||||
}
|
||||
sc_input_manager_process_file(im, &event->drop);
|
||||
}
|
||||
input_manager_process_touch(im, &event->tfinger);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -8,20 +8,20 @@
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "controller.h"
|
||||
#include "file_pusher.h"
|
||||
#include "fps_counter.h"
|
||||
#include "options.h"
|
||||
#include "screen.h"
|
||||
#include "trait/key_processor.h"
|
||||
#include "trait/mouse_processor.h"
|
||||
|
||||
struct sc_input_manager {
|
||||
struct sc_controller *controller;
|
||||
struct sc_file_pusher *fp;
|
||||
struct sc_screen *screen;
|
||||
struct input_manager {
|
||||
struct controller *controller;
|
||||
struct screen *screen;
|
||||
|
||||
struct sc_key_processor *kp;
|
||||
struct sc_mouse_processor *mp;
|
||||
|
||||
bool control;
|
||||
bool forward_all_clicks;
|
||||
bool legacy_paste;
|
||||
bool clipboard_autosync;
|
||||
@@ -43,24 +43,13 @@ struct sc_input_manager {
|
||||
uint64_t next_sequence; // used for request acknowledgements
|
||||
};
|
||||
|
||||
struct sc_input_manager_params {
|
||||
struct sc_controller *controller;
|
||||
struct sc_file_pusher *fp;
|
||||
struct sc_screen *screen;
|
||||
struct sc_key_processor *kp;
|
||||
struct sc_mouse_processor *mp;
|
||||
|
||||
bool forward_all_clicks;
|
||||
bool legacy_paste;
|
||||
bool clipboard_autosync;
|
||||
const struct sc_shortcut_mods *shortcut_mods;
|
||||
};
|
||||
|
||||
void
|
||||
sc_input_manager_init(struct sc_input_manager *im,
|
||||
const struct sc_input_manager_params *params);
|
||||
input_manager_init(struct input_manager *im, struct controller *controller,
|
||||
struct screen *screen, struct sc_key_processor *kp,
|
||||
struct sc_mouse_processor *mp,
|
||||
const struct scrcpy_options *options);
|
||||
|
||||
void
|
||||
sc_input_manager_handle_event(struct sc_input_manager *im, SDL_Event *event);
|
||||
bool
|
||||
input_manager_handle_event(struct input_manager *im, SDL_Event *event);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,13 +12,20 @@
|
||||
/** Downcast key processor to sc_keyboard_inject */
|
||||
#define DOWNCAST(KP) container_of(KP, struct sc_keyboard_inject, key_processor)
|
||||
|
||||
static enum android_keyevent_action
|
||||
convert_keycode_action(enum sc_action action) {
|
||||
if (action == SC_ACTION_DOWN) {
|
||||
return AKEY_EVENT_ACTION_DOWN;
|
||||
static bool
|
||||
convert_keycode_action(enum sc_action from, enum android_keyevent_action *to) {
|
||||
static const struct sc_intmap_entry actions[] = {
|
||||
{SC_ACTION_DOWN, AKEY_EVENT_ACTION_DOWN},
|
||||
{SC_ACTION_UP, AKEY_EVENT_ACTION_UP},
|
||||
};
|
||||
|
||||
const struct sc_intmap_entry *entry = SC_INTMAP_FIND_ENTRY(actions, from);
|
||||
if (entry) {
|
||||
*to = entry->value;
|
||||
return true;
|
||||
}
|
||||
assert(action == SC_ACTION_UP);
|
||||
return AKEY_EVENT_ACTION_UP;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -246,16 +253,19 @@ convert_meta_state(uint16_t mod) {
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_input_key(const struct sc_key_event *event, struct sc_control_msg *msg,
|
||||
convert_input_key(const struct sc_key_event *event, struct control_msg *msg,
|
||||
enum sc_key_inject_mode key_inject_mode, uint32_t repeat) {
|
||||
msg->type = SC_CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
||||
msg->type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
||||
|
||||
if (!convert_keycode_action(event->action, &msg->inject_keycode.action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!convert_keycode(event->keycode, &msg->inject_keycode.keycode,
|
||||
event->mods_state, key_inject_mode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
msg->inject_keycode.action = convert_keycode_action(event->action);
|
||||
msg->inject_keycode.repeat = repeat;
|
||||
msg->inject_keycode.metastate = convert_meta_state(event->mods_state);
|
||||
|
||||
@@ -282,9 +292,9 @@ sc_key_processor_process_key(struct sc_key_processor *kp,
|
||||
ki->repeat = 0;
|
||||
}
|
||||
|
||||
struct sc_control_msg msg;
|
||||
struct control_msg msg;
|
||||
if (convert_input_key(event, &msg, ki->key_inject_mode, ki->repeat)) {
|
||||
if (!sc_controller_push_msg(ki->controller, &msg)) {
|
||||
if (!controller_push_msg(ki->controller, &msg)) {
|
||||
LOGW("Could not request 'inject keycode'");
|
||||
}
|
||||
}
|
||||
@@ -309,14 +319,14 @@ sc_key_processor_process_text(struct sc_key_processor *kp,
|
||||
}
|
||||
}
|
||||
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_INJECT_TEXT;
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
|
||||
msg.inject_text.text = strdup(event->text);
|
||||
if (!msg.inject_text.text) {
|
||||
LOGW("Could not strdup input text");
|
||||
return;
|
||||
}
|
||||
if (!sc_controller_push_msg(ki->controller, &msg)) {
|
||||
if (!controller_push_msg(ki->controller, &msg)) {
|
||||
free(msg.inject_text.text);
|
||||
LOGW("Could not request 'inject text'");
|
||||
}
|
||||
@@ -324,12 +334,11 @@ sc_key_processor_process_text(struct sc_key_processor *kp,
|
||||
|
||||
void
|
||||
sc_keyboard_inject_init(struct sc_keyboard_inject *ki,
|
||||
struct sc_controller *controller,
|
||||
enum sc_key_inject_mode key_inject_mode,
|
||||
bool forward_key_repeat) {
|
||||
struct controller *controller,
|
||||
const struct scrcpy_options *options) {
|
||||
ki->controller = controller;
|
||||
ki->key_inject_mode = key_inject_mode;
|
||||
ki->forward_key_repeat = forward_key_repeat;
|
||||
ki->key_inject_mode = options->key_inject_mode;
|
||||
ki->forward_key_repeat = options->forward_key_repeat;
|
||||
|
||||
ki->repeat = 0;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
struct sc_keyboard_inject {
|
||||
struct sc_key_processor key_processor; // key processor trait
|
||||
|
||||
struct sc_controller *controller;
|
||||
struct controller *controller;
|
||||
|
||||
// SDL reports repeated events as a boolean, but Android expects the actual
|
||||
// number of repetitions. This variable keeps track of the count.
|
||||
@@ -24,8 +24,7 @@ struct sc_keyboard_inject {
|
||||
|
||||
void
|
||||
sc_keyboard_inject_init(struct sc_keyboard_inject *ki,
|
||||
struct sc_controller *controller,
|
||||
enum sc_key_inject_mode key_inject_mode,
|
||||
bool forward_key_repeat);
|
||||
struct controller *controller,
|
||||
const struct scrcpy_options *options);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "mouse_inject.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <SDL2/SDL_events.h>
|
||||
|
||||
#include "android/input.h"
|
||||
#include "control_msg.h"
|
||||
#include "controller.h"
|
||||
#include "input_events.h"
|
||||
#include "util/intmap.h"
|
||||
#include "util/log.h"
|
||||
|
||||
@@ -15,147 +15,210 @@
|
||||
static enum android_motionevent_buttons
|
||||
convert_mouse_buttons(uint32_t state) {
|
||||
enum android_motionevent_buttons buttons = 0;
|
||||
if (state & SC_MOUSE_BUTTON_LEFT) {
|
||||
if (state & SDL_BUTTON_LMASK) {
|
||||
buttons |= AMOTION_EVENT_BUTTON_PRIMARY;
|
||||
}
|
||||
if (state & SC_MOUSE_BUTTON_RIGHT) {
|
||||
if (state & SDL_BUTTON_RMASK) {
|
||||
buttons |= AMOTION_EVENT_BUTTON_SECONDARY;
|
||||
}
|
||||
if (state & SC_MOUSE_BUTTON_MIDDLE) {
|
||||
if (state & SDL_BUTTON_MMASK) {
|
||||
buttons |= AMOTION_EVENT_BUTTON_TERTIARY;
|
||||
}
|
||||
if (state & SC_MOUSE_BUTTON_X1) {
|
||||
if (state & SDL_BUTTON_X1MASK) {
|
||||
buttons |= AMOTION_EVENT_BUTTON_BACK;
|
||||
}
|
||||
if (state & SC_MOUSE_BUTTON_X2) {
|
||||
if (state & SDL_BUTTON_X2MASK) {
|
||||
buttons |= AMOTION_EVENT_BUTTON_FORWARD;
|
||||
}
|
||||
return buttons;
|
||||
}
|
||||
|
||||
static enum android_motionevent_action
|
||||
convert_mouse_action(enum sc_action action) {
|
||||
if (action == SC_ACTION_DOWN) {
|
||||
return AMOTION_EVENT_ACTION_DOWN;
|
||||
static bool
|
||||
convert_mouse_action(SDL_EventType from, enum android_motionevent_action *to) {
|
||||
static const struct sc_intmap_entry actions[] = {
|
||||
{SDL_MOUSEBUTTONDOWN, AMOTION_EVENT_ACTION_DOWN},
|
||||
{SDL_MOUSEBUTTONUP, AMOTION_EVENT_ACTION_UP},
|
||||
};
|
||||
|
||||
const struct sc_intmap_entry *entry = SC_INTMAP_FIND_ENTRY(actions, from);
|
||||
if (entry) {
|
||||
*to = entry->value;
|
||||
return true;
|
||||
}
|
||||
assert(action == SC_ACTION_UP);
|
||||
return AMOTION_EVENT_ACTION_UP;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static enum android_motionevent_action
|
||||
convert_touch_action(enum sc_touch_action action) {
|
||||
switch (action) {
|
||||
case SC_TOUCH_ACTION_MOVE:
|
||||
return AMOTION_EVENT_ACTION_MOVE;
|
||||
case SC_TOUCH_ACTION_DOWN:
|
||||
return AMOTION_EVENT_ACTION_DOWN;
|
||||
default:
|
||||
assert(action == SC_TOUCH_ACTION_UP);
|
||||
return AMOTION_EVENT_ACTION_UP;
|
||||
static bool
|
||||
convert_touch_action(SDL_EventType from, enum android_motionevent_action *to) {
|
||||
static const struct sc_intmap_entry actions[] = {
|
||||
{SDL_FINGERMOTION, AMOTION_EVENT_ACTION_MOVE},
|
||||
{SDL_FINGERDOWN, AMOTION_EVENT_ACTION_DOWN},
|
||||
{SDL_FINGERUP, AMOTION_EVENT_ACTION_UP},
|
||||
};
|
||||
|
||||
const struct sc_intmap_entry *entry = SC_INTMAP_FIND_ENTRY(actions, from);
|
||||
if (entry) {
|
||||
*to = entry->value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_mouse_motion(const SDL_MouseMotionEvent *from, struct screen *screen,
|
||||
struct control_msg *to) {
|
||||
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
||||
to->inject_touch_event.action = AMOTION_EVENT_ACTION_MOVE;
|
||||
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
|
||||
to->inject_touch_event.position.screen_size = screen->frame_size;
|
||||
to->inject_touch_event.position.point =
|
||||
screen_convert_window_to_frame_coords(screen, from->x, from->y);
|
||||
to->inject_touch_event.pressure = 1.f;
|
||||
to->inject_touch_event.buttons = convert_mouse_buttons(from->state);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_touch(const SDL_TouchFingerEvent *from, struct screen *screen,
|
||||
struct control_msg *to) {
|
||||
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
||||
|
||||
if (!convert_touch_action(from->type, &to->inject_touch_event.action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
to->inject_touch_event.pointer_id = from->fingerId;
|
||||
to->inject_touch_event.position.screen_size = screen->frame_size;
|
||||
|
||||
int dw;
|
||||
int dh;
|
||||
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
|
||||
|
||||
// SDL touch event coordinates are normalized in the range [0; 1]
|
||||
int32_t x = from->x * dw;
|
||||
int32_t y = from->y * dh;
|
||||
to->inject_touch_event.position.point =
|
||||
screen_convert_drawable_to_frame_coords(screen, x, y);
|
||||
|
||||
to->inject_touch_event.pressure = from->pressure;
|
||||
to->inject_touch_event.buttons = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_mouse_button(const SDL_MouseButtonEvent *from, struct screen *screen,
|
||||
struct control_msg *to) {
|
||||
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
||||
|
||||
if (!convert_mouse_action(from->type, &to->inject_touch_event.action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
|
||||
to->inject_touch_event.position.screen_size = screen->frame_size;
|
||||
to->inject_touch_event.position.point =
|
||||
screen_convert_window_to_frame_coords(screen, from->x, from->y);
|
||||
to->inject_touch_event.pressure =
|
||||
from->type == SDL_MOUSEBUTTONDOWN ? 1.f : 0.f;
|
||||
to->inject_touch_event.buttons =
|
||||
convert_mouse_buttons(SDL_BUTTON(from->button));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct screen *screen,
|
||||
struct control_msg *to) {
|
||||
|
||||
// mouse_x and mouse_y are expressed in pixels relative to the window
|
||||
int mouse_x;
|
||||
int mouse_y;
|
||||
SDL_GetMouseState(&mouse_x, &mouse_y);
|
||||
|
||||
struct sc_position position = {
|
||||
.screen_size = screen->frame_size,
|
||||
.point = screen_convert_window_to_frame_coords(screen,
|
||||
mouse_x, mouse_y),
|
||||
};
|
||||
|
||||
to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT;
|
||||
|
||||
to->inject_scroll_event.position = position;
|
||||
to->inject_scroll_event.hscroll = from->x;
|
||||
to->inject_scroll_event.vscroll = from->y;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
sc_mouse_processor_process_mouse_motion(struct sc_mouse_processor *mp,
|
||||
const struct sc_mouse_motion_event *event) {
|
||||
if (!event->buttons_state) {
|
||||
// Do not send motion events when no click is pressed
|
||||
const SDL_MouseMotionEvent *event) {
|
||||
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
||||
|
||||
struct control_msg msg;
|
||||
if (!convert_mouse_motion(event, mi->screen, &msg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
||||
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||
.inject_touch_event = {
|
||||
.action = AMOTION_EVENT_ACTION_MOVE,
|
||||
.pointer_id = POINTER_ID_MOUSE,
|
||||
.position = event->position,
|
||||
.pressure = 1.f,
|
||||
.buttons = convert_mouse_buttons(event->buttons_state),
|
||||
},
|
||||
};
|
||||
|
||||
if (!sc_controller_push_msg(mi->controller, &msg)) {
|
||||
if (!controller_push_msg(mi->controller, &msg)) {
|
||||
LOGW("Could not request 'inject mouse motion event'");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_mouse_processor_process_mouse_click(struct sc_mouse_processor *mp,
|
||||
const struct sc_mouse_click_event *event) {
|
||||
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
||||
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||
.inject_touch_event = {
|
||||
.action = convert_mouse_action(event->action),
|
||||
.pointer_id = POINTER_ID_MOUSE,
|
||||
.position = event->position,
|
||||
.pressure = event->action == SC_ACTION_DOWN ? 1.f : 0.f,
|
||||
.buttons = convert_mouse_buttons(event->buttons_state),
|
||||
},
|
||||
};
|
||||
|
||||
if (!sc_controller_push_msg(mi->controller, &msg)) {
|
||||
LOGW("Could not request 'inject mouse click event'");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp,
|
||||
const struct sc_mouse_scroll_event *event) {
|
||||
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
||||
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
||||
.inject_scroll_event = {
|
||||
.position = event->position,
|
||||
.hscroll = event->hscroll,
|
||||
.vscroll = event->vscroll,
|
||||
.buttons = convert_mouse_buttons(event->buttons_state),
|
||||
},
|
||||
};
|
||||
|
||||
if (!sc_controller_push_msg(mi->controller, &msg)) {
|
||||
LOGW("Could not request 'inject mouse scroll event'");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_mouse_processor_process_touch(struct sc_mouse_processor *mp,
|
||||
const struct sc_touch_event *event) {
|
||||
const SDL_TouchFingerEvent *event) {
|
||||
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
||||
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||
.inject_touch_event = {
|
||||
.action = convert_touch_action(event->action),
|
||||
.pointer_id = event->pointer_id,
|
||||
.position = event->position,
|
||||
.pressure = event->pressure,
|
||||
.buttons = 0,
|
||||
},
|
||||
};
|
||||
struct control_msg msg;
|
||||
if (convert_touch(event, mi->screen, &msg)) {
|
||||
if (!controller_push_msg(mi->controller, &msg)) {
|
||||
LOGW("Could not request 'inject touch event'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sc_controller_push_msg(mi->controller, &msg)) {
|
||||
LOGW("Could not request 'inject touch event'");
|
||||
static void
|
||||
sc_mouse_processor_process_mouse_button(struct sc_mouse_processor *mp,
|
||||
const SDL_MouseButtonEvent *event) {
|
||||
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
||||
|
||||
struct control_msg msg;
|
||||
if (convert_mouse_button(event, mi->screen, &msg)) {
|
||||
if (!controller_push_msg(mi->controller, &msg)) {
|
||||
LOGW("Could not request 'inject mouse button event'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_mouse_processor_process_mouse_wheel(struct sc_mouse_processor *mp,
|
||||
const SDL_MouseWheelEvent *event) {
|
||||
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
||||
|
||||
struct control_msg msg;
|
||||
if (convert_mouse_wheel(event, mi->screen, &msg)) {
|
||||
if (!controller_push_msg(mi->controller, &msg)) {
|
||||
LOGW("Could not request 'inject mouse wheel event'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sc_mouse_inject_init(struct sc_mouse_inject *mi,
|
||||
struct sc_controller *controller) {
|
||||
sc_mouse_inject_init(struct sc_mouse_inject *mi, struct controller *controller,
|
||||
struct screen *screen) {
|
||||
mi->controller = controller;
|
||||
mi->screen = screen;
|
||||
|
||||
static const struct sc_mouse_processor_ops ops = {
|
||||
.process_mouse_motion = sc_mouse_processor_process_mouse_motion,
|
||||
.process_mouse_click = sc_mouse_processor_process_mouse_click,
|
||||
.process_mouse_scroll = sc_mouse_processor_process_mouse_scroll,
|
||||
.process_touch = sc_mouse_processor_process_touch,
|
||||
.process_mouse_button = sc_mouse_processor_process_mouse_button,
|
||||
.process_mouse_wheel = sc_mouse_processor_process_mouse_wheel,
|
||||
};
|
||||
|
||||
mi->mouse_processor.ops = &ops;
|
||||
|
||||
mi->mouse_processor.relative_mode = false;
|
||||
}
|
||||
|
||||
@@ -12,11 +12,12 @@
|
||||
struct sc_mouse_inject {
|
||||
struct sc_mouse_processor mouse_processor; // mouse processor trait
|
||||
|
||||
struct sc_controller *controller;
|
||||
struct controller *controller;
|
||||
struct screen *screen;
|
||||
};
|
||||
|
||||
void
|
||||
sc_mouse_inject_init(struct sc_mouse_inject *mi,
|
||||
struct sc_controller *controller);
|
||||
sc_mouse_inject_init(struct sc_mouse_inject *mi, struct controller *controller,
|
||||
struct screen *screen);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -54,7 +54,6 @@ const struct scrcpy_options scrcpy_options_default = {
|
||||
.legacy_paste = false,
|
||||
.power_off_on_close = false,
|
||||
.clipboard_autosync = true,
|
||||
.downsize_on_error = true,
|
||||
.tcpip = false,
|
||||
.tcpip_dst = NULL,
|
||||
};
|
||||
|
||||
@@ -38,11 +38,6 @@ 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.
|
||||
@@ -95,7 +90,6 @@ 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;
|
||||
@@ -129,7 +123,6 @@ struct scrcpy_options {
|
||||
bool legacy_paste;
|
||||
bool power_off_on_close;
|
||||
bool clipboard_autosync;
|
||||
bool downsize_on_error;
|
||||
bool tcpip;
|
||||
const char *tcpip_dst;
|
||||
};
|
||||
|
||||
312
app/src/scrcpy.c
312
app/src/scrcpy.c
@@ -16,10 +16,10 @@
|
||||
#include "controller.h"
|
||||
#include "decoder.h"
|
||||
#include "events.h"
|
||||
#include "file_pusher.h"
|
||||
#include "file_handler.h"
|
||||
#include "input_manager.h"
|
||||
#ifdef HAVE_AOA_HID
|
||||
# include "hid_keyboard.h"
|
||||
# include "hid_mouse.h"
|
||||
#endif
|
||||
#include "keyboard_inject.h"
|
||||
#include "mouse_inject.h"
|
||||
@@ -36,15 +36,15 @@
|
||||
|
||||
struct scrcpy {
|
||||
struct sc_server server;
|
||||
struct sc_screen screen;
|
||||
struct screen screen;
|
||||
struct stream stream;
|
||||
struct decoder decoder;
|
||||
struct recorder recorder;
|
||||
#ifdef HAVE_V4L2
|
||||
struct sc_v4l2_sink v4l2_sink;
|
||||
#endif
|
||||
struct sc_controller controller;
|
||||
struct sc_file_pusher file_pusher;
|
||||
struct controller controller;
|
||||
struct file_handler file_handler;
|
||||
#ifdef HAVE_AOA_HID
|
||||
struct sc_aoa aoa;
|
||||
// sequence/ack helper to synchronize clipboard and Ctrl+v via HID
|
||||
@@ -56,12 +56,8 @@ struct scrcpy {
|
||||
struct sc_hid_keyboard keyboard_hid;
|
||||
#endif
|
||||
};
|
||||
union {
|
||||
struct sc_mouse_inject mouse_inject;
|
||||
#ifdef HAVE_AOA_HID
|
||||
struct sc_hid_mouse mouse_hid;
|
||||
#endif
|
||||
};
|
||||
struct sc_mouse_inject mouse_inject;
|
||||
struct input_manager input_manager;
|
||||
};
|
||||
|
||||
static inline void
|
||||
@@ -149,18 +145,73 @@ sdl_configure(bool display, bool disable_screensaver) {
|
||||
}
|
||||
|
||||
static bool
|
||||
event_loop(struct scrcpy *s) {
|
||||
is_apk(const char *file) {
|
||||
const char *ext = strrchr(file, '.');
|
||||
return ext && !strcmp(ext, ".apk");
|
||||
}
|
||||
|
||||
enum event_result {
|
||||
EVENT_RESULT_CONTINUE,
|
||||
EVENT_RESULT_STOPPED_BY_USER,
|
||||
EVENT_RESULT_STOPPED_BY_EOS,
|
||||
};
|
||||
|
||||
static enum event_result
|
||||
handle_event(struct scrcpy *s, const struct scrcpy_options *options,
|
||||
SDL_Event *event) {
|
||||
switch (event->type) {
|
||||
case EVENT_STREAM_STOPPED:
|
||||
LOGD("Video stream stopped");
|
||||
return EVENT_RESULT_STOPPED_BY_EOS;
|
||||
case SDL_QUIT:
|
||||
LOGD("User requested to quit");
|
||||
return EVENT_RESULT_STOPPED_BY_USER;
|
||||
case SDL_DROPFILE: {
|
||||
if (!options->control) {
|
||||
break;
|
||||
}
|
||||
char *file = strdup(event->drop.file);
|
||||
SDL_free(event->drop.file);
|
||||
if (!file) {
|
||||
LOGW("Could not strdup drop filename\n");
|
||||
break;
|
||||
}
|
||||
|
||||
file_handler_action_t action;
|
||||
if (is_apk(file)) {
|
||||
action = ACTION_INSTALL_APK;
|
||||
} else {
|
||||
action = ACTION_PUSH_FILE;
|
||||
}
|
||||
file_handler_request(&s->file_handler, action, file);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
bool consumed = screen_handle_event(&s->screen, event);
|
||||
if (consumed) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
consumed = input_manager_handle_event(&s->input_manager, event);
|
||||
(void) consumed;
|
||||
|
||||
end:
|
||||
return EVENT_RESULT_CONTINUE;
|
||||
}
|
||||
|
||||
static bool
|
||||
event_loop(struct scrcpy *s, const struct scrcpy_options *options) {
|
||||
SDL_Event event;
|
||||
while (SDL_WaitEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case EVENT_STREAM_STOPPED:
|
||||
enum event_result result = handle_event(s, options, &event);
|
||||
switch (result) {
|
||||
case EVENT_RESULT_STOPPED_BY_USER:
|
||||
return true;
|
||||
case EVENT_RESULT_STOPPED_BY_EOS:
|
||||
LOGW("Device disconnected");
|
||||
return false;
|
||||
case SDL_QUIT:
|
||||
LOGD("User requested to quit");
|
||||
return true;
|
||||
default:
|
||||
sc_screen_handle_event(&s->screen, &event);
|
||||
case EVENT_RESULT_CONTINUE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -277,7 +328,7 @@ scrcpy(struct scrcpy_options *options) {
|
||||
bool ret = false;
|
||||
|
||||
bool server_started = false;
|
||||
bool file_pusher_initialized = false;
|
||||
bool file_handler_initialized = false;
|
||||
bool recorder_initialized = false;
|
||||
#ifdef HAVE_V4L2
|
||||
bool v4l2_sink_initialized = false;
|
||||
@@ -285,8 +336,6 @@ 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;
|
||||
@@ -314,7 +363,6 @@ scrcpy(struct scrcpy_options *options) {
|
||||
.force_adb_forward = options->force_adb_forward,
|
||||
.power_off_on_close = options->power_off_on_close,
|
||||
.clipboard_autosync = options->clipboard_autosync,
|
||||
.downsize_on_error = options->downsize_on_error,
|
||||
.tcpip = options->tcpip,
|
||||
.tcpip_dst = options->tcpip_dst,
|
||||
};
|
||||
@@ -357,15 +405,12 @@ scrcpy(struct scrcpy_options *options) {
|
||||
const char *serial = s->server.params.serial;
|
||||
assert(serial);
|
||||
|
||||
struct sc_file_pusher *fp = NULL;
|
||||
|
||||
if (options->display && options->control) {
|
||||
if (!sc_file_pusher_init(&s->file_pusher, serial,
|
||||
options->push_target)) {
|
||||
if (!file_handler_init(&s->file_handler, serial,
|
||||
options->push_target)) {
|
||||
goto end;
|
||||
}
|
||||
fp = &s->file_pusher;
|
||||
file_pusher_initialized = true;
|
||||
file_handler_initialized = true;
|
||||
}
|
||||
|
||||
struct decoder *dec = NULL;
|
||||
@@ -405,142 +450,44 @@ scrcpy(struct scrcpy_options *options) {
|
||||
stream_add_sink(&s->stream, &rec->packet_sink);
|
||||
}
|
||||
|
||||
struct sc_controller *controller = NULL;
|
||||
struct sc_key_processor *kp = NULL;
|
||||
struct sc_mouse_processor *mp = NULL;
|
||||
|
||||
if (options->control) {
|
||||
#ifdef HAVE_AOA_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) {
|
||||
if (options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_HID) {
|
||||
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->key_inject_mode,
|
||||
options->forward_key_repeat);
|
||||
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 (!sc_controller_init(&s->controller, s->server.control_socket,
|
||||
acksync)) {
|
||||
if (!controller_init(&s->controller, s->server.control_socket,
|
||||
acksync)) {
|
||||
goto end;
|
||||
}
|
||||
controller_initialized = true;
|
||||
|
||||
if (!sc_controller_start(&s->controller)) {
|
||||
if (!controller_start(&s->controller)) {
|
||||
goto end;
|
||||
}
|
||||
controller_started = true;
|
||||
controller = &s->controller;
|
||||
|
||||
if (options->turn_screen_off) {
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE;
|
||||
msg.set_screen_power_mode.mode = SC_SCREEN_POWER_MODE_OFF;
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE;
|
||||
msg.set_screen_power_mode.mode = SCREEN_POWER_MODE_OFF;
|
||||
|
||||
if (!sc_controller_push_msg(&s->controller, &msg)) {
|
||||
if (!controller_push_msg(&s->controller, &msg)) {
|
||||
LOGW("Could not request 'set screen power mode'");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// There is a controller if and only if control is enabled
|
||||
assert(options->control == !!controller);
|
||||
|
||||
if (options->display) {
|
||||
const char *window_title =
|
||||
options->window_title ? options->window_title : info->device_name;
|
||||
|
||||
struct sc_screen_params screen_params = {
|
||||
.controller = controller,
|
||||
.fp = fp,
|
||||
.kp = kp,
|
||||
.mp = mp,
|
||||
.forward_all_clicks = options->forward_all_clicks,
|
||||
.legacy_paste = options->legacy_paste,
|
||||
.clipboard_autosync = options->clipboard_autosync,
|
||||
.shortcut_mods = &options->shortcut_mods,
|
||||
struct screen_params screen_params = {
|
||||
.window_title = window_title,
|
||||
.frame_size = info->frame_size,
|
||||
.always_on_top = options->always_on_top,
|
||||
@@ -555,7 +502,7 @@ aoa_hid_end:
|
||||
.buffering_time = options->display_buffer,
|
||||
};
|
||||
|
||||
if (!sc_screen_init(&s->screen, &screen_params)) {
|
||||
if (!screen_init(&s->screen, &screen_params)) {
|
||||
goto end;
|
||||
}
|
||||
screen_initialized = true;
|
||||
@@ -583,24 +530,77 @@ aoa_hid_end:
|
||||
}
|
||||
stream_started = true;
|
||||
|
||||
ret = event_loop(s);
|
||||
struct sc_key_processor *kp = NULL;
|
||||
struct sc_mouse_processor *mp = NULL;
|
||||
|
||||
if (options->control) {
|
||||
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, &s->screen);
|
||||
mp = &s->mouse_inject.mouse_processor;
|
||||
}
|
||||
|
||||
input_manager_init(&s->input_manager, &s->controller, &s->screen, kp, mp,
|
||||
options);
|
||||
|
||||
ret = event_loop(s, options);
|
||||
LOGD("quit...");
|
||||
|
||||
// Close the window immediately on closing, because screen_destroy() may
|
||||
// only be called once the stream thread is joined (it may take time)
|
||||
sc_screen_hide_window(&s->screen);
|
||||
screen_hide_window(&s->screen);
|
||||
|
||||
end:
|
||||
// The stream is not stopped explicitly, because it will stop by itself on
|
||||
// end-of-stream
|
||||
#ifdef HAVE_AOA_HID
|
||||
if (aoa_hid_initialized) {
|
||||
if (hid_keyboard_initialized) {
|
||||
sc_hid_keyboard_destroy(&s->keyboard_hid);
|
||||
}
|
||||
if (hid_mouse_initialized) {
|
||||
sc_hid_mouse_destroy(&s->mouse_hid);
|
||||
}
|
||||
sc_hid_keyboard_destroy(&s->keyboard_hid);
|
||||
sc_aoa_stop(&s->aoa);
|
||||
}
|
||||
if (acksync) {
|
||||
@@ -608,13 +608,13 @@ end:
|
||||
}
|
||||
#endif
|
||||
if (controller_started) {
|
||||
sc_controller_stop(&s->controller);
|
||||
controller_stop(&s->controller);
|
||||
}
|
||||
if (file_pusher_initialized) {
|
||||
sc_file_pusher_stop(&s->file_pusher);
|
||||
if (file_handler_initialized) {
|
||||
file_handler_stop(&s->file_handler);
|
||||
}
|
||||
if (screen_initialized) {
|
||||
sc_screen_interrupt(&s->screen);
|
||||
screen_interrupt(&s->screen);
|
||||
}
|
||||
|
||||
if (server_started) {
|
||||
@@ -644,24 +644,24 @@ end:
|
||||
// Destroy the screen only after the stream is guaranteed to be finished,
|
||||
// because otherwise the screen could receive new frames after destruction
|
||||
if (screen_initialized) {
|
||||
sc_screen_join(&s->screen);
|
||||
sc_screen_destroy(&s->screen);
|
||||
screen_join(&s->screen);
|
||||
screen_destroy(&s->screen);
|
||||
}
|
||||
|
||||
if (controller_started) {
|
||||
sc_controller_join(&s->controller);
|
||||
controller_join(&s->controller);
|
||||
}
|
||||
if (controller_initialized) {
|
||||
sc_controller_destroy(&s->controller);
|
||||
controller_destroy(&s->controller);
|
||||
}
|
||||
|
||||
if (recorder_initialized) {
|
||||
recorder_destroy(&s->recorder);
|
||||
}
|
||||
|
||||
if (file_pusher_initialized) {
|
||||
sc_file_pusher_join(&s->file_pusher);
|
||||
sc_file_pusher_destroy(&s->file_pusher);
|
||||
if (file_handler_initialized) {
|
||||
file_handler_join(&s->file_handler);
|
||||
file_handler_destroy(&s->file_handler);
|
||||
}
|
||||
|
||||
sc_server_destroy(&s->server);
|
||||
|
||||
278
app/src/screen.c
278
app/src/screen.c
@@ -12,7 +12,7 @@
|
||||
|
||||
#define DISPLAY_MARGINS 96
|
||||
|
||||
#define DOWNCAST(SINK) container_of(SINK, struct sc_screen, frame_sink)
|
||||
#define DOWNCAST(SINK) container_of(SINK, struct screen, frame_sink)
|
||||
|
||||
static inline struct sc_size
|
||||
get_rotated_size(struct sc_size size, int rotation) {
|
||||
@@ -29,7 +29,7 @@ get_rotated_size(struct sc_size size, int rotation) {
|
||||
|
||||
// get the window size in a struct sc_size
|
||||
static struct sc_size
|
||||
get_window_size(const struct sc_screen *screen) {
|
||||
get_window_size(const struct screen *screen) {
|
||||
int width;
|
||||
int height;
|
||||
SDL_GetWindowSize(screen->window, &width, &height);
|
||||
@@ -41,7 +41,7 @@ get_window_size(const struct sc_screen *screen) {
|
||||
}
|
||||
|
||||
static struct sc_point
|
||||
get_window_position(const struct sc_screen *screen) {
|
||||
get_window_position(const struct screen *screen) {
|
||||
int x;
|
||||
int y;
|
||||
SDL_GetWindowPosition(screen->window, &x, &y);
|
||||
@@ -54,7 +54,7 @@ get_window_position(const struct sc_screen *screen) {
|
||||
|
||||
// set the window size to be applied when fullscreen is disabled
|
||||
static void
|
||||
set_window_size(struct sc_screen *screen, struct sc_size new_size) {
|
||||
set_window_size(struct screen *screen, struct sc_size new_size) {
|
||||
assert(!screen->fullscreen);
|
||||
assert(!screen->maximized);
|
||||
SDL_SetWindowSize(screen->window, new_size.width, new_size.height);
|
||||
@@ -156,25 +156,8 @@ get_initial_optimal_size(struct sc_size content_size, uint16_t req_width,
|
||||
return window_size;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
sc_screen_is_relative_mode(struct sc_screen *screen) {
|
||||
// screen->im.mp may be NULL if --no-control
|
||||
return screen->im.mp && screen->im.mp->relative_mode;
|
||||
}
|
||||
|
||||
static void
|
||||
sc_screen_capture_mouse(struct sc_screen *screen, bool capture) {
|
||||
if (SDL_SetRelativeMouseMode(capture)) {
|
||||
LOGE("Could not set relative mouse mode to %s: %s",
|
||||
capture ? "true" : "false", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
screen->mouse_captured = capture;
|
||||
}
|
||||
|
||||
static void
|
||||
sc_screen_update_content_rect(struct sc_screen *screen) {
|
||||
screen_update_content_rect(struct screen *screen) {
|
||||
int dw;
|
||||
int dh;
|
||||
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
|
||||
@@ -211,7 +194,7 @@ sc_screen_update_content_rect(struct sc_screen *screen) {
|
||||
}
|
||||
|
||||
static inline SDL_Texture *
|
||||
create_texture(struct sc_screen *screen) {
|
||||
create_texture(struct screen *screen) {
|
||||
SDL_Renderer *renderer = screen->renderer;
|
||||
struct sc_size size = screen->frame_size;
|
||||
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12,
|
||||
@@ -242,9 +225,9 @@ create_texture(struct sc_screen *screen) {
|
||||
// Set the update_content_rect flag if the window or content size may have
|
||||
// changed, so that the content rectangle is recomputed
|
||||
static void
|
||||
sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
|
||||
screen_render(struct screen *screen, bool update_content_rect) {
|
||||
if (update_content_rect) {
|
||||
sc_screen_update_content_rect(screen);
|
||||
screen_update_content_rect(screen);
|
||||
}
|
||||
|
||||
SDL_RenderClear(screen->renderer);
|
||||
@@ -288,20 +271,20 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
|
||||
// <https://stackoverflow.com/a/40693139/1987178>
|
||||
static int
|
||||
event_watcher(void *data, SDL_Event *event) {
|
||||
struct sc_screen *screen = data;
|
||||
struct screen *screen = data;
|
||||
if (event->type == SDL_WINDOWEVENT
|
||||
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||
// In practice, it seems to always be called from the same thread in
|
||||
// that specific case. Anyway, it's just a workaround.
|
||||
sc_screen_render(screen, true);
|
||||
screen_render(screen, true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool
|
||||
sc_screen_frame_sink_open(struct sc_frame_sink *sink) {
|
||||
struct sc_screen *screen = DOWNCAST(sink);
|
||||
screen_frame_sink_open(struct sc_frame_sink *sink) {
|
||||
struct screen *screen = DOWNCAST(sink);
|
||||
(void) screen;
|
||||
#ifndef NDEBUG
|
||||
screen->open = true;
|
||||
@@ -312,8 +295,8 @@ sc_screen_frame_sink_open(struct sc_frame_sink *sink) {
|
||||
}
|
||||
|
||||
static void
|
||||
sc_screen_frame_sink_close(struct sc_frame_sink *sink) {
|
||||
struct sc_screen *screen = DOWNCAST(sink);
|
||||
screen_frame_sink_close(struct sc_frame_sink *sink) {
|
||||
struct screen *screen = DOWNCAST(sink);
|
||||
(void) screen;
|
||||
#ifndef NDEBUG
|
||||
screen->open = false;
|
||||
@@ -323,8 +306,8 @@ sc_screen_frame_sink_close(struct sc_frame_sink *sink) {
|
||||
}
|
||||
|
||||
static bool
|
||||
sc_screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) {
|
||||
struct sc_screen *screen = DOWNCAST(sink);
|
||||
screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) {
|
||||
struct screen *screen = DOWNCAST(sink);
|
||||
return sc_video_buffer_push(&screen->vb, frame);
|
||||
}
|
||||
|
||||
@@ -332,7 +315,7 @@ static void
|
||||
sc_video_buffer_on_new_frame(struct sc_video_buffer *vb, bool previous_skipped,
|
||||
void *userdata) {
|
||||
(void) vb;
|
||||
struct sc_screen *screen = userdata;
|
||||
struct screen *screen = userdata;
|
||||
|
||||
// event_failed implies previous_skipped (the previous frame may not have
|
||||
// been consumed if the event was not sent)
|
||||
@@ -365,21 +348,12 @@ sc_video_buffer_on_new_frame(struct sc_video_buffer *vb, bool previous_skipped,
|
||||
}
|
||||
|
||||
bool
|
||||
sc_screen_init(struct sc_screen *screen,
|
||||
const struct sc_screen_params *params) {
|
||||
screen_init(struct screen *screen, const struct screen_params *params) {
|
||||
screen->resize_pending = false;
|
||||
screen->has_frame = false;
|
||||
screen->fullscreen = false;
|
||||
screen->maximized = false;
|
||||
screen->event_failed = false;
|
||||
screen->mouse_captured = false;
|
||||
screen->mouse_capture_key_pressed = 0;
|
||||
|
||||
screen->req.x = params->window_x;
|
||||
screen->req.y = params->window_y;
|
||||
screen->req.width = params->window_width;
|
||||
screen->req.height = params->window_height;
|
||||
screen->req.fullscreen = params->fullscreen;
|
||||
|
||||
static const struct sc_video_buffer_callbacks cbs = {
|
||||
.on_new_frame = sc_video_buffer_on_new_frame,
|
||||
@@ -409,6 +383,9 @@ sc_screen_init(struct sc_screen *screen,
|
||||
get_rotated_size(screen->frame_size, screen->rotation);
|
||||
screen->content_size = content_size;
|
||||
|
||||
struct sc_size window_size =
|
||||
get_initial_optimal_size(content_size,params->window_width,
|
||||
params->window_height);
|
||||
uint32_t window_flags = SDL_WINDOW_HIDDEN
|
||||
| SDL_WINDOW_RESIZABLE
|
||||
| SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
@@ -419,9 +396,13 @@ sc_screen_init(struct sc_screen *screen,
|
||||
window_flags |= SDL_WINDOW_BORDERLESS;
|
||||
}
|
||||
|
||||
// The window will be positioned and sized on first video frame
|
||||
screen->window =
|
||||
SDL_CreateWindow(params->window_title, 0, 0, 0, 0, window_flags);
|
||||
int x = params->window_x != SC_WINDOW_POSITION_UNDEFINED
|
||||
? params->window_x : (int) SDL_WINDOWPOS_UNDEFINED;
|
||||
int y = params->window_y != SC_WINDOW_POSITION_UNDEFINED
|
||||
? params->window_y : (int) SDL_WINDOWPOS_UNDEFINED;
|
||||
screen->window = SDL_CreateWindow(params->window_title, x, y,
|
||||
window_size.width, window_size.height,
|
||||
window_flags);
|
||||
if (!screen->window) {
|
||||
LOGC("Could not create window: %s", SDL_GetError());
|
||||
goto error_destroy_fps_counter;
|
||||
@@ -489,28 +470,25 @@ sc_screen_init(struct sc_screen *screen,
|
||||
goto error_destroy_texture;
|
||||
}
|
||||
|
||||
struct sc_input_manager_params im_params = {
|
||||
.controller = params->controller,
|
||||
.fp = params->fp,
|
||||
.screen = screen,
|
||||
.kp = params->kp,
|
||||
.mp = params->mp,
|
||||
.forward_all_clicks = params->forward_all_clicks,
|
||||
.legacy_paste = params->legacy_paste,
|
||||
.clipboard_autosync = params->clipboard_autosync,
|
||||
.shortcut_mods = params->shortcut_mods,
|
||||
};
|
||||
// Reset the window size to trigger a SIZE_CHANGED event, to workaround
|
||||
// HiDPI issues with some SDL renderers when several displays having
|
||||
// different HiDPI scaling are connected
|
||||
SDL_SetWindowSize(screen->window, window_size.width, window_size.height);
|
||||
|
||||
sc_input_manager_init(&screen->im, &im_params);
|
||||
screen_update_content_rect(screen);
|
||||
|
||||
if (params->fullscreen) {
|
||||
screen_switch_fullscreen(screen);
|
||||
}
|
||||
|
||||
#ifdef CONTINUOUS_RESIZING_WORKAROUND
|
||||
SDL_AddEventWatch(event_watcher, screen);
|
||||
#endif
|
||||
|
||||
static const struct sc_frame_sink_ops ops = {
|
||||
.open = sc_screen_frame_sink_open,
|
||||
.close = sc_screen_frame_sink_close,
|
||||
.push = sc_screen_frame_sink_push,
|
||||
.open = screen_frame_sink_open,
|
||||
.close = screen_frame_sink_close,
|
||||
.push = screen_frame_sink_push,
|
||||
};
|
||||
|
||||
screen->frame_sink.ops = &ops;
|
||||
@@ -539,45 +517,29 @@ error_destroy_video_buffer:
|
||||
}
|
||||
|
||||
static void
|
||||
sc_screen_show_initial_window(struct sc_screen *screen) {
|
||||
int x = screen->req.x != SC_WINDOW_POSITION_UNDEFINED
|
||||
? screen->req.x : (int) SDL_WINDOWPOS_CENTERED;
|
||||
int y = screen->req.y != SC_WINDOW_POSITION_UNDEFINED
|
||||
? screen->req.y : (int) SDL_WINDOWPOS_CENTERED;
|
||||
|
||||
struct sc_size window_size =
|
||||
get_initial_optimal_size(screen->content_size, screen->req.width,
|
||||
screen->req.height);
|
||||
|
||||
set_window_size(screen, window_size);
|
||||
SDL_SetWindowPosition(screen->window, x, y);
|
||||
|
||||
if (screen->req.fullscreen) {
|
||||
sc_screen_switch_fullscreen(screen);
|
||||
}
|
||||
|
||||
screen_show_window(struct screen *screen) {
|
||||
SDL_ShowWindow(screen->window);
|
||||
}
|
||||
|
||||
void
|
||||
sc_screen_hide_window(struct sc_screen *screen) {
|
||||
screen_hide_window(struct screen *screen) {
|
||||
SDL_HideWindow(screen->window);
|
||||
}
|
||||
|
||||
void
|
||||
sc_screen_interrupt(struct sc_screen *screen) {
|
||||
screen_interrupt(struct screen *screen) {
|
||||
sc_video_buffer_stop(&screen->vb);
|
||||
fps_counter_interrupt(&screen->fps_counter);
|
||||
}
|
||||
|
||||
void
|
||||
sc_screen_join(struct sc_screen *screen) {
|
||||
screen_join(struct screen *screen) {
|
||||
sc_video_buffer_join(&screen->vb);
|
||||
fps_counter_join(&screen->fps_counter);
|
||||
}
|
||||
|
||||
void
|
||||
sc_screen_destroy(struct sc_screen *screen) {
|
||||
screen_destroy(struct screen *screen) {
|
||||
#ifndef NDEBUG
|
||||
assert(!screen->open);
|
||||
#endif
|
||||
@@ -590,7 +552,7 @@ sc_screen_destroy(struct sc_screen *screen) {
|
||||
}
|
||||
|
||||
static void
|
||||
resize_for_content(struct sc_screen *screen, struct sc_size old_content_size,
|
||||
resize_for_content(struct screen *screen, struct sc_size old_content_size,
|
||||
struct sc_size new_content_size) {
|
||||
struct sc_size window_size = get_window_size(screen);
|
||||
struct sc_size target_size = {
|
||||
@@ -604,7 +566,7 @@ resize_for_content(struct sc_screen *screen, struct sc_size old_content_size,
|
||||
}
|
||||
|
||||
static void
|
||||
set_content_size(struct sc_screen *screen, struct sc_size new_content_size) {
|
||||
set_content_size(struct screen *screen, struct sc_size new_content_size) {
|
||||
if (!screen->fullscreen && !screen->maximized) {
|
||||
resize_for_content(screen, screen->content_size, new_content_size);
|
||||
} else if (!screen->resize_pending) {
|
||||
@@ -618,7 +580,7 @@ set_content_size(struct sc_screen *screen, struct sc_size new_content_size) {
|
||||
}
|
||||
|
||||
static void
|
||||
apply_pending_resize(struct sc_screen *screen) {
|
||||
apply_pending_resize(struct screen *screen) {
|
||||
assert(!screen->fullscreen);
|
||||
assert(!screen->maximized);
|
||||
if (screen->resize_pending) {
|
||||
@@ -629,7 +591,7 @@ apply_pending_resize(struct sc_screen *screen) {
|
||||
}
|
||||
|
||||
void
|
||||
sc_screen_set_rotation(struct sc_screen *screen, unsigned rotation) {
|
||||
screen_set_rotation(struct screen *screen, unsigned rotation) {
|
||||
assert(rotation < 4);
|
||||
if (rotation == screen->rotation) {
|
||||
return;
|
||||
@@ -643,12 +605,12 @@ sc_screen_set_rotation(struct sc_screen *screen, unsigned rotation) {
|
||||
screen->rotation = rotation;
|
||||
LOGI("Display rotation set to %u", rotation);
|
||||
|
||||
sc_screen_render(screen, true);
|
||||
screen_render(screen, true);
|
||||
}
|
||||
|
||||
// recreate the texture and resize the window if the frame size has changed
|
||||
static bool
|
||||
prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) {
|
||||
prepare_for_frame(struct screen *screen, struct sc_size new_frame_size) {
|
||||
if (screen->frame_size.width != new_frame_size.width
|
||||
|| screen->frame_size.height != new_frame_size.height) {
|
||||
// frame dimension changed, destroy texture
|
||||
@@ -660,7 +622,7 @@ prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) {
|
||||
get_rotated_size(new_frame_size, screen->rotation);
|
||||
set_content_size(screen, new_content_size);
|
||||
|
||||
sc_screen_update_content_rect(screen);
|
||||
screen_update_content_rect(screen);
|
||||
|
||||
LOGI("New texture: %" PRIu16 "x%" PRIu16,
|
||||
screen->frame_size.width, screen->frame_size.height);
|
||||
@@ -676,7 +638,7 @@ prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) {
|
||||
|
||||
// write the frame into the texture
|
||||
static void
|
||||
update_texture(struct sc_screen *screen, const AVFrame *frame) {
|
||||
update_texture(struct screen *screen, const AVFrame *frame) {
|
||||
SDL_UpdateYUVTexture(screen->texture, NULL,
|
||||
frame->data[0], frame->linesize[0],
|
||||
frame->data[1], frame->linesize[1],
|
||||
@@ -690,7 +652,7 @@ update_texture(struct sc_screen *screen, const AVFrame *frame) {
|
||||
}
|
||||
|
||||
static bool
|
||||
sc_screen_update_frame(struct sc_screen *screen) {
|
||||
screen_update_frame(struct screen *screen) {
|
||||
av_frame_unref(screen->frame);
|
||||
sc_video_buffer_consume(&screen->vb, screen->frame);
|
||||
AVFrame *frame = screen->frame;
|
||||
@@ -703,23 +665,12 @@ sc_screen_update_frame(struct sc_screen *screen) {
|
||||
}
|
||||
update_texture(screen, frame);
|
||||
|
||||
if (!screen->has_frame) {
|
||||
screen->has_frame = true;
|
||||
// this is the very first frame, show the window
|
||||
sc_screen_show_initial_window(screen);
|
||||
|
||||
if (sc_screen_is_relative_mode(screen)) {
|
||||
// Capture mouse on start
|
||||
sc_screen_capture_mouse(screen, true);
|
||||
}
|
||||
}
|
||||
|
||||
sc_screen_render(screen, false);
|
||||
screen_render(screen, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
sc_screen_switch_fullscreen(struct sc_screen *screen) {
|
||||
screen_switch_fullscreen(struct screen *screen) {
|
||||
uint32_t new_mode = screen->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
if (SDL_SetWindowFullscreen(screen->window, new_mode)) {
|
||||
LOGW("Could not switch fullscreen mode: %s", SDL_GetError());
|
||||
@@ -732,11 +683,11 @@ sc_screen_switch_fullscreen(struct sc_screen *screen) {
|
||||
}
|
||||
|
||||
LOGD("Switched to %s mode", screen->fullscreen ? "fullscreen" : "windowed");
|
||||
sc_screen_render(screen, true);
|
||||
screen_render(screen, true);
|
||||
}
|
||||
|
||||
void
|
||||
sc_screen_resize_to_fit(struct sc_screen *screen) {
|
||||
screen_resize_to_fit(struct screen *screen) {
|
||||
if (screen->fullscreen || screen->maximized) {
|
||||
return;
|
||||
}
|
||||
@@ -760,7 +711,7 @@ sc_screen_resize_to_fit(struct sc_screen *screen) {
|
||||
}
|
||||
|
||||
void
|
||||
sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) {
|
||||
screen_resize_to_pixel_perfect(struct screen *screen) {
|
||||
if (screen->fullscreen) {
|
||||
return;
|
||||
}
|
||||
@@ -776,34 +727,31 @@ sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) {
|
||||
content_size.height);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
sc_screen_is_mouse_capture_key(SDL_Keycode key) {
|
||||
return key == SDLK_LALT || key == SDLK_LGUI || key == SDLK_RGUI;
|
||||
}
|
||||
|
||||
void
|
||||
sc_screen_handle_event(struct sc_screen *screen, SDL_Event *event) {
|
||||
bool relative_mode = sc_screen_is_relative_mode(screen);
|
||||
|
||||
bool
|
||||
screen_handle_event(struct screen *screen, SDL_Event *event) {
|
||||
switch (event->type) {
|
||||
case EVENT_NEW_FRAME: {
|
||||
bool ok = sc_screen_update_frame(screen);
|
||||
case EVENT_NEW_FRAME:
|
||||
if (!screen->has_frame) {
|
||||
screen->has_frame = true;
|
||||
// this is the very first frame, show the window
|
||||
screen_show_window(screen);
|
||||
}
|
||||
bool ok = screen_update_frame(screen);
|
||||
if (!ok) {
|
||||
LOGW("Frame update failed\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
return true;
|
||||
case SDL_WINDOWEVENT:
|
||||
if (!screen->has_frame) {
|
||||
// Do nothing
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
switch (event->window.event) {
|
||||
case SDL_WINDOWEVENT_EXPOSED:
|
||||
sc_screen_render(screen, true);
|
||||
screen_render(screen, true);
|
||||
break;
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
sc_screen_render(screen, true);
|
||||
screen_render(screen, true);
|
||||
break;
|
||||
case SDL_WINDOWEVENT_MAXIMIZED:
|
||||
screen->maximized = true;
|
||||
@@ -819,80 +767,18 @@ sc_screen_handle_event(struct sc_screen *screen, SDL_Event *event) {
|
||||
}
|
||||
screen->maximized = false;
|
||||
apply_pending_resize(screen);
|
||||
sc_screen_render(screen, true);
|
||||
break;
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
if (relative_mode) {
|
||||
sc_screen_capture_mouse(screen, false);
|
||||
}
|
||||
screen_render(screen, true);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
case SDL_KEYDOWN:
|
||||
if (relative_mode) {
|
||||
SDL_Keycode key = event->key.keysym.sym;
|
||||
if (sc_screen_is_mouse_capture_key(key)) {
|
||||
if (!screen->mouse_capture_key_pressed) {
|
||||
screen->mouse_capture_key_pressed = key;
|
||||
} else {
|
||||
// Another mouse capture key has been pressed, cancel
|
||||
// mouse (un)capture
|
||||
screen->mouse_capture_key_pressed = 0;
|
||||
}
|
||||
// Mouse capture keys are never forwarded to the device
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
if (relative_mode) {
|
||||
SDL_Keycode key = event->key.keysym.sym;
|
||||
SDL_Keycode cap = screen->mouse_capture_key_pressed;
|
||||
screen->mouse_capture_key_pressed = 0;
|
||||
if (sc_screen_is_mouse_capture_key(key)) {
|
||||
if (key == cap) {
|
||||
// A mouse capture key has been pressed then released:
|
||||
// toggle the capture mouse mode
|
||||
sc_screen_capture_mouse(screen,
|
||||
!screen->mouse_captured);
|
||||
}
|
||||
// Mouse capture keys are never forwarded to the device
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEWHEEL:
|
||||
case SDL_MOUSEMOTION:
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
if (relative_mode && !screen->mouse_captured) {
|
||||
// Do not forward to input manager, the mouse will be captured
|
||||
// on SDL_MOUSEBUTTONUP
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case SDL_FINGERMOTION:
|
||||
case SDL_FINGERDOWN:
|
||||
case SDL_FINGERUP:
|
||||
if (relative_mode) {
|
||||
// Touch events are not compatible with relative mode
|
||||
// (coordinates are not relative)
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
if (relative_mode && !screen->mouse_captured) {
|
||||
sc_screen_capture_mouse(screen, true);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
|
||||
sc_input_manager_handle_event(&screen->im, event);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sc_point
|
||||
sc_screen_convert_drawable_to_frame_coords(struct sc_screen *screen,
|
||||
int32_t x, int32_t y) {
|
||||
screen_convert_drawable_to_frame_coords(struct screen *screen,
|
||||
int32_t x, int32_t y) {
|
||||
unsigned rotation = screen->rotation;
|
||||
assert(rotation < 4);
|
||||
|
||||
@@ -928,14 +814,14 @@ sc_screen_convert_drawable_to_frame_coords(struct sc_screen *screen,
|
||||
}
|
||||
|
||||
struct sc_point
|
||||
sc_screen_convert_window_to_frame_coords(struct sc_screen *screen,
|
||||
int32_t x, int32_t y) {
|
||||
sc_screen_hidpi_scale_coords(screen, &x, &y);
|
||||
return sc_screen_convert_drawable_to_frame_coords(screen, x, y);
|
||||
screen_convert_window_to_frame_coords(struct screen *screen,
|
||||
int32_t x, int32_t y) {
|
||||
screen_hidpi_scale_coords(screen, &x, &y);
|
||||
return screen_convert_drawable_to_frame_coords(screen, x, y);
|
||||
}
|
||||
|
||||
void
|
||||
sc_screen_hidpi_scale_coords(struct sc_screen *screen, int32_t *x, int32_t *y) {
|
||||
screen_hidpi_scale_coords(struct screen *screen, int32_t *x, int32_t *y) {
|
||||
// take the HiDPI scaling (dw/ww and dh/wh) into account
|
||||
int ww, wh, dw, dh;
|
||||
SDL_GetWindowSize(screen->window, &ww, &wh);
|
||||
|
||||
@@ -7,36 +7,22 @@
|
||||
#include <SDL2/SDL.h>
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
#include "controller.h"
|
||||
#include "coords.h"
|
||||
#include "fps_counter.h"
|
||||
#include "input_manager.h"
|
||||
#include "opengl.h"
|
||||
#include "trait/key_processor.h"
|
||||
#include "trait/frame_sink.h"
|
||||
#include "trait/mouse_processor.h"
|
||||
#include "video_buffer.h"
|
||||
|
||||
struct sc_screen {
|
||||
struct screen {
|
||||
struct sc_frame_sink frame_sink; // frame sink trait
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool open; // track the open/close state to assert correct behavior
|
||||
#endif
|
||||
|
||||
struct sc_input_manager im;
|
||||
struct sc_video_buffer vb;
|
||||
struct fps_counter fps_counter;
|
||||
|
||||
// The initial requested window properties
|
||||
struct {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
bool fullscreen;
|
||||
} req;
|
||||
|
||||
SDL_Window *window;
|
||||
SDL_Renderer *renderer;
|
||||
SDL_Texture *texture;
|
||||
@@ -60,33 +46,18 @@ struct sc_screen {
|
||||
|
||||
bool event_failed; // in case SDL_PushEvent() returned an error
|
||||
|
||||
bool mouse_captured; // only relevant in relative mouse mode
|
||||
// To enable/disable mouse capture, a mouse capture key (LALT, LGUI or
|
||||
// RGUI) must be pressed. This variable tracks the pressed capture key.
|
||||
SDL_Keycode mouse_capture_key_pressed;
|
||||
|
||||
AVFrame *frame;
|
||||
};
|
||||
|
||||
struct sc_screen_params {
|
||||
struct sc_controller *controller;
|
||||
struct sc_file_pusher *fp;
|
||||
struct sc_key_processor *kp;
|
||||
struct sc_mouse_processor *mp;
|
||||
|
||||
bool forward_all_clicks;
|
||||
bool legacy_paste;
|
||||
bool clipboard_autosync;
|
||||
const struct sc_shortcut_mods *shortcut_mods;
|
||||
|
||||
struct screen_params {
|
||||
const char *window_title;
|
||||
struct sc_size frame_size;
|
||||
bool always_on_top;
|
||||
|
||||
int16_t window_x; // accepts SC_WINDOW_POSITION_UNDEFINED
|
||||
int16_t window_y; // accepts SC_WINDOW_POSITION_UNDEFINED
|
||||
uint16_t window_width;
|
||||
uint16_t window_height;
|
||||
int16_t window_x;
|
||||
int16_t window_y;
|
||||
uint16_t window_width; // accepts SC_WINDOW_POSITION_UNDEFINED
|
||||
uint16_t window_height; // accepts SC_WINDOW_POSITION_UNDEFINED
|
||||
|
||||
bool window_borderless;
|
||||
|
||||
@@ -100,65 +71,65 @@ struct sc_screen_params {
|
||||
|
||||
// initialize screen, create window, renderer and texture (window is hidden)
|
||||
bool
|
||||
sc_screen_init(struct sc_screen *screen, const struct sc_screen_params *params);
|
||||
screen_init(struct screen *screen, const struct screen_params *params);
|
||||
|
||||
// request to interrupt any inner thread
|
||||
// must be called before screen_join()
|
||||
void
|
||||
sc_screen_interrupt(struct sc_screen *screen);
|
||||
screen_interrupt(struct screen *screen);
|
||||
|
||||
// join any inner thread
|
||||
void
|
||||
sc_screen_join(struct sc_screen *screen);
|
||||
screen_join(struct screen *screen);
|
||||
|
||||
// destroy window, renderer and texture (if any)
|
||||
void
|
||||
sc_screen_destroy(struct sc_screen *screen);
|
||||
screen_destroy(struct screen *screen);
|
||||
|
||||
// hide the window
|
||||
//
|
||||
// It is used to hide the window immediately on closing without waiting for
|
||||
// screen_destroy()
|
||||
void
|
||||
sc_screen_hide_window(struct sc_screen *screen);
|
||||
screen_hide_window(struct screen *screen);
|
||||
|
||||
// switch the fullscreen mode
|
||||
void
|
||||
sc_screen_switch_fullscreen(struct sc_screen *screen);
|
||||
screen_switch_fullscreen(struct screen *screen);
|
||||
|
||||
// resize window to optimal size (remove black borders)
|
||||
void
|
||||
sc_screen_resize_to_fit(struct sc_screen *screen);
|
||||
screen_resize_to_fit(struct screen *screen);
|
||||
|
||||
// resize window to 1:1 (pixel-perfect)
|
||||
void
|
||||
sc_screen_resize_to_pixel_perfect(struct sc_screen *screen);
|
||||
screen_resize_to_pixel_perfect(struct screen *screen);
|
||||
|
||||
// set the display rotation (0, 1, 2 or 3, x90 degrees counterclockwise)
|
||||
void
|
||||
sc_screen_set_rotation(struct sc_screen *screen, unsigned rotation);
|
||||
screen_set_rotation(struct screen *screen, unsigned rotation);
|
||||
|
||||
// react to SDL events
|
||||
void
|
||||
sc_screen_handle_event(struct sc_screen *screen, SDL_Event *event);
|
||||
bool
|
||||
screen_handle_event(struct screen *screen, SDL_Event *event);
|
||||
|
||||
// convert point from window coordinates to frame coordinates
|
||||
// x and y are expressed in pixels
|
||||
struct sc_point
|
||||
sc_screen_convert_window_to_frame_coords(struct sc_screen *screen,
|
||||
int32_t x, int32_t y);
|
||||
screen_convert_window_to_frame_coords(struct screen *screen,
|
||||
int32_t x, int32_t y);
|
||||
|
||||
// convert point from drawable coordinates to frame coordinates
|
||||
// x and y are expressed in pixels
|
||||
struct sc_point
|
||||
sc_screen_convert_drawable_to_frame_coords(struct sc_screen *screen,
|
||||
int32_t x, int32_t y);
|
||||
screen_convert_drawable_to_frame_coords(struct screen *screen,
|
||||
int32_t x, int32_t y);
|
||||
|
||||
// Convert coordinates from window to drawable.
|
||||
// Events are expressed in window coordinates, but content is expressed in
|
||||
// drawable coordinates. They are the same if HiDPI scaling is 1, but differ
|
||||
// otherwise.
|
||||
void
|
||||
sc_screen_hidpi_scale_coords(struct sc_screen *screen, int32_t *x, int32_t *y);
|
||||
screen_hidpi_scale_coords(struct screen *screen, int32_t *x, int32_t *y);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -188,6 +188,7 @@ execute_server(struct sc_server *server,
|
||||
} \
|
||||
cmd[count++] = p; \
|
||||
}
|
||||
#define STRBOOL(v) (v ? "true" : "false")
|
||||
|
||||
ADD_PARAM("log_level=%s", log_level_to_server_string(params->log_level));
|
||||
ADD_PARAM("bit_rate=%" PRIu32, params->bit_rate);
|
||||
@@ -203,23 +204,23 @@ execute_server(struct sc_server *server,
|
||||
params->lock_video_orientation);
|
||||
}
|
||||
if (server->tunnel.forward) {
|
||||
ADD_PARAM("tunnel_forward=true");
|
||||
ADD_PARAM("tunnel_forward=%s", STRBOOL(server->tunnel.forward));
|
||||
}
|
||||
if (params->crop) {
|
||||
ADD_PARAM("crop=%s", params->crop);
|
||||
}
|
||||
if (!params->control) {
|
||||
// By default, control is true
|
||||
ADD_PARAM("control=false");
|
||||
ADD_PARAM("control=%s", STRBOOL(params->control));
|
||||
}
|
||||
if (params->display_id) {
|
||||
ADD_PARAM("display_id=%" PRIu32, params->display_id);
|
||||
}
|
||||
if (params->show_touches) {
|
||||
ADD_PARAM("show_touches=true");
|
||||
ADD_PARAM("show_touches=%s", STRBOOL(params->show_touches));
|
||||
}
|
||||
if (params->stay_awake) {
|
||||
ADD_PARAM("stay_awake=true");
|
||||
ADD_PARAM("stay_awake=%s", STRBOOL(params->stay_awake));
|
||||
}
|
||||
if (params->codec_options) {
|
||||
ADD_PARAM("codec_options=%s", params->codec_options);
|
||||
@@ -228,15 +229,11 @@ execute_server(struct sc_server *server,
|
||||
ADD_PARAM("encoder_name=%s", params->encoder_name);
|
||||
}
|
||||
if (params->power_off_on_close) {
|
||||
ADD_PARAM("power_off_on_close=true");
|
||||
ADD_PARAM("power_off_on_close=%s", STRBOOL(params->power_off_on_close));
|
||||
}
|
||||
if (!params->clipboard_autosync) {
|
||||
// By default, clipboard_autosync is true
|
||||
ADD_PARAM("clipboard_autosync=false");
|
||||
}
|
||||
if (!params->downsize_on_error) {
|
||||
// By default, downsize_on_error is true
|
||||
ADD_PARAM("downsize_on_error=false");
|
||||
ADD_PARAM("clipboard_autosync=%s", STRBOOL(params->clipboard_autosync));
|
||||
}
|
||||
|
||||
#undef ADD_PARAM
|
||||
|
||||
@@ -42,7 +42,6 @@ struct sc_server_params {
|
||||
bool force_adb_forward;
|
||||
bool power_off_on_close;
|
||||
bool clipboard_autosync;
|
||||
bool downsize_on_error;
|
||||
bool tcpip;
|
||||
const char *tcpip_dst;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "stream.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -191,7 +192,7 @@ static int
|
||||
run_stream(void *data) {
|
||||
struct stream *stream = data;
|
||||
|
||||
const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);
|
||||
AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);
|
||||
if (!codec) {
|
||||
LOGE("H.264 decoder not found");
|
||||
goto end;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
#include "trait/packet_sink.h"
|
||||
|
||||
@@ -29,24 +29,17 @@ struct sc_key_processor {
|
||||
struct sc_key_processor_ops {
|
||||
|
||||
/**
|
||||
* Process a keyboard event
|
||||
* Process the keyboard event
|
||||
*
|
||||
* The `sequence` number (if different from `SC_SEQUENCE_INVALID`) indicates
|
||||
* the acknowledgement number to wait for before injecting this event.
|
||||
* This allows to ensure that the device clipboard is set before injecting
|
||||
* Ctrl+v on the device.
|
||||
*
|
||||
* This function is mandatory.
|
||||
*/
|
||||
void
|
||||
(*process_key)(struct sc_key_processor *kp,
|
||||
const struct sc_key_event *event, uint64_t ack_to_wait);
|
||||
|
||||
/**
|
||||
* Process an input text
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void
|
||||
(*process_text)(struct sc_key_processor *kp,
|
||||
const struct sc_text_event *event);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "input_events.h"
|
||||
#include <SDL2/SDL_events.h>
|
||||
|
||||
/**
|
||||
* Mouse processor trait.
|
||||
@@ -16,51 +16,24 @@
|
||||
*/
|
||||
struct sc_mouse_processor {
|
||||
const struct sc_mouse_processor_ops *ops;
|
||||
|
||||
/**
|
||||
* If set, the mouse processor works in relative mode (the absolute
|
||||
* position is irrelevant). In particular, it indicates that the mouse
|
||||
* pointer must be "captured" by the UI.
|
||||
*/
|
||||
bool relative_mode;
|
||||
};
|
||||
|
||||
struct sc_mouse_processor_ops {
|
||||
/**
|
||||
* Process a mouse motion event
|
||||
*
|
||||
* This function is mandatory.
|
||||
*/
|
||||
void
|
||||
(*process_mouse_motion)(struct sc_mouse_processor *mp,
|
||||
const struct sc_mouse_motion_event *event);
|
||||
const SDL_MouseMotionEvent *event);
|
||||
|
||||
/**
|
||||
* Process a mouse click event
|
||||
*
|
||||
* This function is mandatory.
|
||||
*/
|
||||
void
|
||||
(*process_mouse_click)(struct sc_mouse_processor *mp,
|
||||
const struct sc_mouse_click_event *event);
|
||||
|
||||
/**
|
||||
* Process a mouse scroll event
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void
|
||||
(*process_mouse_scroll)(struct sc_mouse_processor *mp,
|
||||
const struct sc_mouse_scroll_event *event);
|
||||
|
||||
/**
|
||||
* Process a touch event
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void
|
||||
(*process_touch)(struct sc_mouse_processor *mp,
|
||||
const struct sc_touch_event *event);
|
||||
const SDL_TouchFingerEvent *event);
|
||||
|
||||
void
|
||||
(*process_mouse_button)(struct sc_mouse_processor *mp,
|
||||
const SDL_MouseButtonEvent *event);
|
||||
|
||||
void
|
||||
(*process_mouse_wheel)(struct sc_mouse_processor *mp,
|
||||
const SDL_MouseWheelEvent *event);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -83,7 +83,6 @@ unwrap(sc_socket socket) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef HAVE_SOCK_CLOEXEC // avoid unused-function warning
|
||||
static inline bool
|
||||
sc_raw_socket_close(sc_raw_socket raw_sock) {
|
||||
#ifndef _WIN32
|
||||
@@ -92,7 +91,6 @@ sc_raw_socket_close(sc_raw_socket raw_sock) {
|
||||
return !closesocket(raw_sock);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_SOCK_CLOEXEC
|
||||
// If SOCK_CLOEXEC does not exist, the flag must be set manually once the
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
#include "coords.h"
|
||||
#include "trait/frame_sink.h"
|
||||
#include "video_buffer.h"
|
||||
#include "util/tick.h"
|
||||
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
struct sc_v4l2_sink {
|
||||
struct sc_frame_sink frame_sink; // frame sink trait
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ static void test_get_ip_single_line() {
|
||||
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
|
||||
assert(ip);
|
||||
assert(!strcmp(ip, "192.168.12.34"));
|
||||
free(ip);
|
||||
}
|
||||
|
||||
static void test_get_ip_single_line_without_eol() {
|
||||
@@ -21,7 +20,6 @@ static void test_get_ip_single_line_without_eol() {
|
||||
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
|
||||
assert(ip);
|
||||
assert(!strcmp(ip, "192.168.12.34"));
|
||||
free(ip);
|
||||
}
|
||||
|
||||
static void test_get_ip_single_line_with_trailing_space() {
|
||||
@@ -31,7 +29,6 @@ static void test_get_ip_single_line_with_trailing_space() {
|
||||
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
|
||||
assert(ip);
|
||||
assert(!strcmp(ip, "192.168.12.34"));
|
||||
free(ip);
|
||||
}
|
||||
|
||||
static void test_get_ip_multiline_first_ok() {
|
||||
@@ -43,7 +40,6 @@ static void test_get_ip_multiline_first_ok() {
|
||||
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
|
||||
assert(ip);
|
||||
assert(!strcmp(ip, "192.168.1.2"));
|
||||
free(ip);
|
||||
}
|
||||
|
||||
static void test_get_ip_multiline_second_ok() {
|
||||
@@ -55,7 +51,6 @@ static void test_get_ip_multiline_second_ok() {
|
||||
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
|
||||
assert(ip);
|
||||
assert(!strcmp(ip, "192.168.1.3"));
|
||||
free(ip);
|
||||
}
|
||||
|
||||
static void test_get_ip_no_wlan() {
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#include "control_msg.h"
|
||||
|
||||
static void test_serialize_inject_keycode(void) {
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
||||
.inject_keycode = {
|
||||
.action = AKEY_EVENT_ACTION_UP,
|
||||
.keycode = AKEYCODE_ENTER,
|
||||
@@ -16,12 +16,12 @@ static void test_serialize_inject_keycode(void) {
|
||||
},
|
||||
};
|
||||
|
||||
unsigned char buf[SC_CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 14);
|
||||
|
||||
const unsigned char expected[] = {
|
||||
SC_CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
||||
CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
||||
0x01, // AKEY_EVENT_ACTION_UP
|
||||
0x00, 0x00, 0x00, 0x42, // AKEYCODE_ENTER
|
||||
0x00, 0x00, 0x00, 0X05, // repeat
|
||||
@@ -31,19 +31,19 @@ static void test_serialize_inject_keycode(void) {
|
||||
}
|
||||
|
||||
static void test_serialize_inject_text(void) {
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_INJECT_TEXT,
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_INJECT_TEXT,
|
||||
.inject_text = {
|
||||
.text = "hello, world!",
|
||||
},
|
||||
};
|
||||
|
||||
unsigned char buf[SC_CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 18);
|
||||
|
||||
const unsigned char expected[] = {
|
||||
SC_CONTROL_MSG_TYPE_INJECT_TEXT,
|
||||
CONTROL_MSG_TYPE_INJECT_TEXT,
|
||||
0x00, 0x00, 0x00, 0x0d, // text length
|
||||
'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', // text
|
||||
};
|
||||
@@ -51,31 +51,31 @@ static void test_serialize_inject_text(void) {
|
||||
}
|
||||
|
||||
static void test_serialize_inject_text_long(void) {
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_INJECT_TEXT;
|
||||
char text[SC_CONTROL_MSG_INJECT_TEXT_MAX_LENGTH + 1];
|
||||
memset(text, 'a', SC_CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
|
||||
text[SC_CONTROL_MSG_INJECT_TEXT_MAX_LENGTH] = '\0';
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
|
||||
char text[CONTROL_MSG_INJECT_TEXT_MAX_LENGTH + 1];
|
||||
memset(text, 'a', CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
|
||||
text[CONTROL_MSG_INJECT_TEXT_MAX_LENGTH] = '\0';
|
||||
msg.inject_text.text = text;
|
||||
|
||||
unsigned char buf[SC_CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||
assert(size == 5 + SC_CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
|
||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 5 + CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
|
||||
|
||||
unsigned char expected[5 + SC_CONTROL_MSG_INJECT_TEXT_MAX_LENGTH];
|
||||
expected[0] = SC_CONTROL_MSG_TYPE_INJECT_TEXT;
|
||||
unsigned char expected[5 + CONTROL_MSG_INJECT_TEXT_MAX_LENGTH];
|
||||
expected[0] = CONTROL_MSG_TYPE_INJECT_TEXT;
|
||||
expected[1] = 0x00;
|
||||
expected[2] = 0x00;
|
||||
expected[3] = 0x01;
|
||||
expected[4] = 0x2c; // text length (32 bits)
|
||||
memset(&expected[5], 'a', SC_CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
|
||||
memset(&expected[5], 'a', CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
|
||||
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
|
||||
static void test_serialize_inject_touch_event(void) {
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||
.inject_touch_event = {
|
||||
.action = AMOTION_EVENT_ACTION_DOWN,
|
||||
.pointer_id = UINT64_C(0x1234567887654321),
|
||||
@@ -94,12 +94,12 @@ static void test_serialize_inject_touch_event(void) {
|
||||
},
|
||||
};
|
||||
|
||||
unsigned char buf[SC_CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 28);
|
||||
|
||||
const unsigned char expected[] = {
|
||||
SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||
CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||
0x00, // AKEY_EVENT_ACTION_DOWN
|
||||
0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21, // pointer id
|
||||
0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0xc8, // 100 200
|
||||
@@ -111,8 +111,8 @@ static void test_serialize_inject_touch_event(void) {
|
||||
}
|
||||
|
||||
static void test_serialize_inject_scroll_event(void) {
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
||||
.inject_scroll_event = {
|
||||
.position = {
|
||||
.point = {
|
||||
@@ -126,111 +126,109 @@ static void test_serialize_inject_scroll_event(void) {
|
||||
},
|
||||
.hscroll = 1,
|
||||
.vscroll = -1,
|
||||
.buttons = 1,
|
||||
},
|
||||
};
|
||||
|
||||
unsigned char buf[SC_CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||
assert(size == 25);
|
||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 21);
|
||||
|
||||
const unsigned char expected[] = {
|
||||
SC_CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
||||
CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
||||
0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x04, 0x02, // 260 1026
|
||||
0x04, 0x38, 0x07, 0x80, // 1080 1920
|
||||
0x00, 0x00, 0x00, 0x01, // 1
|
||||
0xFF, 0xFF, 0xFF, 0xFF, // -1
|
||||
0x00, 0x00, 0x00, 0x01, // 1
|
||||
};
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
|
||||
static void test_serialize_back_or_screen_on(void) {
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
|
||||
.back_or_screen_on = {
|
||||
.action = AKEY_EVENT_ACTION_UP,
|
||||
},
|
||||
};
|
||||
|
||||
unsigned char buf[SC_CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 2);
|
||||
|
||||
const unsigned char expected[] = {
|
||||
SC_CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
|
||||
CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
|
||||
0x01, // AKEY_EVENT_ACTION_UP
|
||||
};
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
|
||||
static void test_serialize_expand_notification_panel(void) {
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL,
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL,
|
||||
};
|
||||
|
||||
unsigned char buf[SC_CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 1);
|
||||
|
||||
const unsigned char expected[] = {
|
||||
SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL,
|
||||
CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL,
|
||||
};
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
|
||||
static void test_serialize_expand_settings_panel(void) {
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL,
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL,
|
||||
};
|
||||
|
||||
unsigned char buf[SC_CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 1);
|
||||
|
||||
const unsigned char expected[] = {
|
||||
SC_CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL,
|
||||
CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL,
|
||||
};
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
|
||||
static void test_serialize_collapse_panels(void) {
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS,
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_COLLAPSE_PANELS,
|
||||
};
|
||||
|
||||
unsigned char buf[SC_CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 1);
|
||||
|
||||
const unsigned char expected[] = {
|
||||
SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS,
|
||||
CONTROL_MSG_TYPE_COLLAPSE_PANELS,
|
||||
};
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
|
||||
static void test_serialize_get_clipboard(void) {
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_GET_CLIPBOARD,
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_GET_CLIPBOARD,
|
||||
.get_clipboard = {
|
||||
.copy_key = SC_COPY_KEY_COPY,
|
||||
.copy_key = GET_CLIPBOARD_COPY_KEY_COPY,
|
||||
},
|
||||
};
|
||||
|
||||
unsigned char buf[SC_CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 2);
|
||||
|
||||
const unsigned char expected[] = {
|
||||
SC_CONTROL_MSG_TYPE_GET_CLIPBOARD,
|
||||
SC_COPY_KEY_COPY,
|
||||
CONTROL_MSG_TYPE_GET_CLIPBOARD,
|
||||
GET_CLIPBOARD_COPY_KEY_COPY,
|
||||
};
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
|
||||
static void test_serialize_set_clipboard(void) {
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||
.set_clipboard = {
|
||||
.sequence = UINT64_C(0x0102030405060708),
|
||||
.paste = true,
|
||||
@@ -238,12 +236,12 @@ static void test_serialize_set_clipboard(void) {
|
||||
},
|
||||
};
|
||||
|
||||
unsigned char buf[SC_CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 27);
|
||||
|
||||
const unsigned char expected[] = {
|
||||
SC_CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||
CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // sequence
|
||||
1, // paste
|
||||
0x00, 0x00, 0x00, 0x0d, // text length
|
||||
@@ -253,8 +251,8 @@ static void test_serialize_set_clipboard(void) {
|
||||
}
|
||||
|
||||
static void test_serialize_set_clipboard_long(void) {
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||
.set_clipboard = {
|
||||
.sequence = UINT64_C(0x0102030405060708),
|
||||
.paste = true,
|
||||
@@ -262,60 +260,60 @@ static void test_serialize_set_clipboard_long(void) {
|
||||
},
|
||||
};
|
||||
|
||||
char text[SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH + 1];
|
||||
memset(text, 'a', SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH);
|
||||
text[SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH] = '\0';
|
||||
char text[CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH + 1];
|
||||
memset(text, 'a', CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH);
|
||||
text[CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH] = '\0';
|
||||
msg.set_clipboard.text = text;
|
||||
|
||||
unsigned char buf[SC_CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||
assert(size == SC_CONTROL_MSG_MAX_SIZE);
|
||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = control_msg_serialize(&msg, buf);
|
||||
assert(size == CONTROL_MSG_MAX_SIZE);
|
||||
|
||||
unsigned char expected[SC_CONTROL_MSG_MAX_SIZE] = {
|
||||
SC_CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||
unsigned char expected[CONTROL_MSG_MAX_SIZE] = {
|
||||
CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // sequence
|
||||
1, // paste
|
||||
// text length
|
||||
SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH >> 24,
|
||||
(SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH >> 16) & 0xff,
|
||||
(SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH >> 8) & 0xff,
|
||||
SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH & 0xff,
|
||||
CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH >> 24,
|
||||
(CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH >> 16) & 0xff,
|
||||
(CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH >> 8) & 0xff,
|
||||
CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH & 0xff,
|
||||
};
|
||||
memset(expected + 14, 'a', SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH);
|
||||
memset(expected + 14, 'a', CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH);
|
||||
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
|
||||
static void test_serialize_set_screen_power_mode(void) {
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
|
||||
.set_screen_power_mode = {
|
||||
.mode = SC_SCREEN_POWER_MODE_NORMAL,
|
||||
.mode = SCREEN_POWER_MODE_NORMAL,
|
||||
},
|
||||
};
|
||||
|
||||
unsigned char buf[SC_CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 2);
|
||||
|
||||
const unsigned char expected[] = {
|
||||
SC_CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
|
||||
0x02, // SC_SCREEN_POWER_MODE_NORMAL
|
||||
CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
|
||||
0x02, // SCREEN_POWER_MODE_NORMAL
|
||||
};
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
|
||||
static void test_serialize_rotate_device(void) {
|
||||
struct sc_control_msg msg = {
|
||||
.type = SC_CONTROL_MSG_TYPE_ROTATE_DEVICE,
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_ROTATE_DEVICE,
|
||||
};
|
||||
|
||||
unsigned char buf[SC_CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 1);
|
||||
|
||||
const unsigned char expected[] = {
|
||||
SC_CONTROL_MSG_TYPE_ROTATE_DEVICE,
|
||||
CONTROL_MSG_TYPE_ROTATE_DEVICE,
|
||||
};
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@ cpu = 'i686'
|
||||
endian = 'little'
|
||||
|
||||
[properties]
|
||||
ffmpeg_avcodec = 'avcodec-58'
|
||||
ffmpeg_avformat = 'avformat-58'
|
||||
ffmpeg_avutil = 'avutil-56'
|
||||
prebuilt_ffmpeg = 'ffmpeg-4.3.1-win32-shared'
|
||||
prebuilt_ffmpeg_shared = 'ffmpeg-4.3.1-win32-shared'
|
||||
prebuilt_ffmpeg_dev = 'ffmpeg-4.3.1-win32-dev'
|
||||
prebuilt_sdl2 = 'SDL2-2.0.18/i686-w64-mingw32'
|
||||
|
||||
@@ -16,8 +16,6 @@ cpu = 'x86_64'
|
||||
endian = 'little'
|
||||
|
||||
[properties]
|
||||
ffmpeg_avcodec = 'avcodec-59'
|
||||
ffmpeg_avformat = 'avformat-59'
|
||||
ffmpeg_avutil = 'avutil-57'
|
||||
prebuilt_ffmpeg = 'ffmpeg-5.0-full_build-shared'
|
||||
prebuilt_ffmpeg_shared = 'ffmpeg-4.3.1-win64-shared'
|
||||
prebuilt_ffmpeg_dev = 'ffmpeg-4.3.1-win64-dev'
|
||||
prebuilt_sdl2 = 'SDL2-2.0.18/x86_64-w64-mingw32'
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
@cmd
|
||||
@@ -1,26 +1,33 @@
|
||||
.PHONY: prepare-win32 prepare-win64 \
|
||||
prepare-ffmpeg-win32 \
|
||||
prepare-ffmpeg-win64 \
|
||||
prepare-ffmpeg-shared-win32 \
|
||||
prepare-ffmpeg-dev-win32 \
|
||||
prepare-ffmpeg-shared-win64 \
|
||||
prepare-ffmpeg-dev-win64 \
|
||||
prepare-sdl2 \
|
||||
prepare-adb
|
||||
|
||||
prepare-win32: prepare-sdl2 prepare-ffmpeg-win32 prepare-adb
|
||||
prepare-win64: prepare-sdl2 prepare-ffmpeg-win64 prepare-adb
|
||||
prepare-win32: prepare-sdl2 prepare-ffmpeg-shared-win32 prepare-ffmpeg-dev-win32 prepare-adb
|
||||
prepare-win64: prepare-sdl2 prepare-ffmpeg-shared-win64 prepare-ffmpeg-dev-win64 prepare-adb
|
||||
|
||||
# Use old FFmpeg version for win32, there are no new prebuilts
|
||||
prepare-ffmpeg-win32:
|
||||
prepare-ffmpeg-shared-win32:
|
||||
@./prepare-dep https://github.com/Genymobile/scrcpy/releases/download/v1.16/ffmpeg-4.3.1-win32-shared.zip \
|
||||
357af9901a456f4dcbacd107e83a934d344c9cb07ddad8aaf80612eeab7d26d2 \
|
||||
ffmpeg-4.3.1-win32-shared
|
||||
|
||||
prepare-ffmpeg-dev-win32:
|
||||
@./prepare-dep https://github.com/Genymobile/scrcpy/releases/download/v1.16/ffmpeg-4.3.1-win32-dev.zip \
|
||||
230efb08e9bcf225bd474da29676c70e591fc94d8790a740ca801408fddcb78b \
|
||||
ffmpeg-4.3.1-win32-dev
|
||||
ln -sf ../ffmpeg-4.3.1-win32-dev/include ffmpeg-4.3.1-win32-shared/
|
||||
|
||||
prepare-ffmpeg-win64:
|
||||
@./prepare-dep https://github.com/GyanD/codexffmpeg/releases/download/5.0/ffmpeg-5.0-full_build-shared.7z \
|
||||
e5900f6cecd4c438d398bd2fc308736c10b857cd8dd61c11bcfb05bff5d1211a \
|
||||
ffmpeg-5.0-full_build-shared
|
||||
prepare-ffmpeg-shared-win64:
|
||||
@./prepare-dep https://github.com/Genymobile/scrcpy/releases/download/v1.16/ffmpeg-4.3.1-win64-shared.zip \
|
||||
dd29b7f92f48dead4dd940492c7509138c0f99db445076d0a597007298a79940 \
|
||||
ffmpeg-4.3.1-win64-shared
|
||||
|
||||
prepare-ffmpeg-dev-win64:
|
||||
@./prepare-dep https://github.com/Genymobile/scrcpy/releases/download/v1.16/ffmpeg-4.3.1-win64-dev.zip \
|
||||
2e8038242cf8e1bd095c2978f196ff0462b122cc6ef7e74626a6af15459d8b81 \
|
||||
ffmpeg-4.3.1-win64-dev
|
||||
|
||||
prepare-sdl2:
|
||||
@./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.18-mingw.tar.gz \
|
||||
|
||||
@@ -34,9 +34,6 @@ extract() {
|
||||
elif [[ "$file" == *.tar.gz ]]
|
||||
then
|
||||
tar xf "$file"
|
||||
elif [[ "$file" == *.7z ]]
|
||||
then
|
||||
7z x "$file"
|
||||
else
|
||||
echo "Unsupported file: $file"
|
||||
return 1
|
||||
|
||||
12
release.mk
12
release.mk
@@ -93,7 +93,6 @@ dist-win32: build-server build-win32
|
||||
cp data/scrcpy-console.bat "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp data/icon.png "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp data/open_a_terminal_here.bat "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
@@ -111,12 +110,11 @@ dist-win64: build-server build-win64
|
||||
cp data/scrcpy-console.bat "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp data/icon.png "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp data/open_a_terminal_here.bat "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp prebuilt-deps/ffmpeg-5.0-full_build-shared/bin/avutil-57.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-5.0-full_build-shared/bin/avcodec-59.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-5.0-full_build-shared/bin/avformat-59.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-5.0-full_build-shared/bin/swresample-4.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-5.0-full_build-shared/bin/swscale-6.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/swresample-3.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/swscale-5.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
|
||||
@@ -71,13 +71,12 @@ public final class ControlMessage {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public static ControlMessage createInjectScrollEvent(Position position, int hScroll, int vScroll, int buttons) {
|
||||
public static ControlMessage createInjectScrollEvent(Position position, int hScroll, int vScroll) {
|
||||
ControlMessage msg = new ControlMessage();
|
||||
msg.type = TYPE_INJECT_SCROLL_EVENT;
|
||||
msg.position = position;
|
||||
msg.hScroll = hScroll;
|
||||
msg.vScroll = vScroll;
|
||||
msg.buttons = buttons;
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ public class ControlMessageReader {
|
||||
|
||||
static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 13;
|
||||
static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 27;
|
||||
static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 24;
|
||||
static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
|
||||
static final int BACK_OR_SCREEN_ON_LENGTH = 1;
|
||||
static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
||||
static final int GET_CLIPBOARD_LENGTH = 1;
|
||||
@@ -154,8 +154,7 @@ public class ControlMessageReader {
|
||||
Position position = readPosition(buffer);
|
||||
int hScroll = buffer.getInt();
|
||||
int vScroll = buffer.getInt();
|
||||
int buttons = buffer.getInt();
|
||||
return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll, buttons);
|
||||
return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll);
|
||||
}
|
||||
|
||||
private ControlMessage parseBackOrScreenOnEvent() {
|
||||
|
||||
@@ -98,7 +98,7 @@ public class Controller {
|
||||
break;
|
||||
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
||||
if (device.supportsInputEvents()) {
|
||||
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll(), msg.getButtons());
|
||||
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
|
||||
}
|
||||
break;
|
||||
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
|
||||
@@ -221,7 +221,7 @@ public class Controller {
|
||||
return device.injectEvent(event, Device.INJECT_MODE_ASYNC);
|
||||
}
|
||||
|
||||
private boolean injectScroll(Position position, int hScroll, int vScroll, int buttons) {
|
||||
private boolean injectScroll(Position position, int hScroll, int vScroll) {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
Point point = device.getPhysicalPoint(position);
|
||||
if (point == null) {
|
||||
@@ -239,7 +239,7 @@ public class Controller {
|
||||
coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll);
|
||||
|
||||
MotionEvent event = MotionEvent
|
||||
.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0,
|
||||
.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, 0, 1f, 1f, DEFAULT_DEVICE_ID, 0,
|
||||
InputDevice.SOURCE_MOUSE, 0);
|
||||
return device.injectEvent(event, Device.INJECT_MODE_ASYNC);
|
||||
}
|
||||
|
||||
@@ -46,17 +46,15 @@ public final class DesktopConnection implements Closeable {
|
||||
return localSocket;
|
||||
}
|
||||
|
||||
public static DesktopConnection open(boolean tunnelForward, boolean control, boolean sendDummyByte) throws IOException {
|
||||
public static DesktopConnection open(Device device, boolean tunnelForward, boolean control) throws IOException {
|
||||
LocalSocket videoSocket;
|
||||
LocalSocket controlSocket = null;
|
||||
if (tunnelForward) {
|
||||
LocalServerSocket localServerSocket = new LocalServerSocket(SOCKET_NAME);
|
||||
try {
|
||||
videoSocket = localServerSocket.accept();
|
||||
if (sendDummyByte) {
|
||||
// send one byte so the client may read() to detect a connection error
|
||||
videoSocket.getOutputStream().write(0);
|
||||
}
|
||||
// send one byte so the client may read() to detect a connection error
|
||||
videoSocket.getOutputStream().write(0);
|
||||
if (control) {
|
||||
try {
|
||||
controlSocket = localServerSocket.accept();
|
||||
@@ -80,7 +78,10 @@ public final class DesktopConnection implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
return new DesktopConnection(videoSocket, controlSocket);
|
||||
DesktopConnection connection = new DesktopConnection(videoSocket, controlSocket);
|
||||
Size videoSize = device.getScreenInfo().getVideoSize();
|
||||
connection.send(Device.getDeviceName(), videoSize.getWidth(), videoSize.getHeight());
|
||||
return connection;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
@@ -94,7 +95,7 @@ public final class DesktopConnection implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
public void sendDeviceMeta(String deviceName, int width, int height) throws IOException {
|
||||
private void send(String deviceName, int width, int height) throws IOException {
|
||||
byte[] buffer = new byte[DEVICE_NAME_FIELD_LENGTH + 4];
|
||||
|
||||
byte[] deviceNameBytes = deviceName.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
@@ -42,11 +42,6 @@ public final class Device {
|
||||
void onClipboardTextChanged(String text);
|
||||
}
|
||||
|
||||
private final Size deviceSize;
|
||||
private final Rect crop;
|
||||
private int maxSize;
|
||||
private final int lockVideoOrientation;
|
||||
|
||||
private ScreenInfo screenInfo;
|
||||
private RotationListener rotationListener;
|
||||
private ClipboardListener clipboardListener;
|
||||
@@ -74,12 +69,7 @@ public final class Device {
|
||||
|
||||
int displayInfoFlags = displayInfo.getFlags();
|
||||
|
||||
deviceSize = displayInfo.getSize();
|
||||
crop = options.getCrop();
|
||||
maxSize = options.getMaxSize();
|
||||
lockVideoOrientation = options.getLockVideoOrientation();
|
||||
|
||||
screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), deviceSize, crop, maxSize, lockVideoOrientation);
|
||||
screenInfo = ScreenInfo.computeScreenInfo(displayInfo, options.getCrop(), options.getMaxSize(), options.getLockVideoOrientation());
|
||||
layerStack = displayInfo.getLayerStack();
|
||||
|
||||
SERVICE_MANAGER.getWindowManager().registerRotationWatcher(new IRotationWatcher.Stub() {
|
||||
@@ -133,11 +123,6 @@ public final class Device {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void setMaxSize(int newMaxSize) {
|
||||
maxSize = newMaxSize;
|
||||
screenInfo = ScreenInfo.computeScreenInfo(screenInfo.getReverseVideoRotation(), deviceSize, crop, newMaxSize, lockVideoOrientation);
|
||||
}
|
||||
|
||||
public synchronized ScreenInfo getScreenInfo() {
|
||||
return screenInfo;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ public class Options {
|
||||
private int lockVideoOrientation = -1;
|
||||
private boolean tunnelForward;
|
||||
private Rect crop;
|
||||
private boolean sendFrameMeta = true; // send PTS so that the client may record properly
|
||||
private boolean control = true;
|
||||
private int displayId;
|
||||
private boolean showTouches;
|
||||
@@ -20,12 +21,6 @@ public class Options {
|
||||
private String encoderName;
|
||||
private boolean powerOffScreenOnClose;
|
||||
private boolean clipboardAutosync = true;
|
||||
private boolean downsizeOnError = true;
|
||||
|
||||
// Options not used by the scrcpy client, but useful to use scrcpy-server directly
|
||||
private boolean sendDeviceMeta = true; // send device name and size
|
||||
private boolean sendFrameMeta = true; // send PTS so that the client may record properly
|
||||
private boolean sendDummyByte = true; // write a byte on start to detect connection issues
|
||||
|
||||
public Ln.Level getLogLevel() {
|
||||
return logLevel;
|
||||
@@ -83,6 +78,14 @@ public class Options {
|
||||
this.crop = crop;
|
||||
}
|
||||
|
||||
public boolean getSendFrameMeta() {
|
||||
return sendFrameMeta;
|
||||
}
|
||||
|
||||
public void setSendFrameMeta(boolean sendFrameMeta) {
|
||||
this.sendFrameMeta = sendFrameMeta;
|
||||
}
|
||||
|
||||
public boolean getControl() {
|
||||
return control;
|
||||
}
|
||||
@@ -146,36 +149,4 @@ public class Options {
|
||||
public void setClipboardAutosync(boolean clipboardAutosync) {
|
||||
this.clipboardAutosync = clipboardAutosync;
|
||||
}
|
||||
|
||||
public boolean getDownsizeOnError() {
|
||||
return downsizeOnError;
|
||||
}
|
||||
|
||||
public void setDownsizeOnError(boolean downsizeOnError) {
|
||||
this.downsizeOnError = downsizeOnError;
|
||||
}
|
||||
|
||||
public boolean getSendDeviceMeta() {
|
||||
return sendDeviceMeta;
|
||||
}
|
||||
|
||||
public void setSendDeviceMeta(boolean sendDeviceMeta) {
|
||||
this.sendDeviceMeta = sendDeviceMeta;
|
||||
}
|
||||
|
||||
public boolean getSendFrameMeta() {
|
||||
return sendFrameMeta;
|
||||
}
|
||||
|
||||
public void setSendFrameMeta(boolean sendFrameMeta) {
|
||||
this.sendFrameMeta = sendFrameMeta;
|
||||
}
|
||||
|
||||
public boolean getSendDummyByte() {
|
||||
return sendDummyByte;
|
||||
}
|
||||
|
||||
public void setSendDummyByte(boolean sendDummyByte) {
|
||||
this.sendDummyByte = sendDummyByte;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,32 +25,24 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||
private static final int REPEAT_FRAME_DELAY_US = 100_000; // repeat after 100ms
|
||||
private static final String KEY_MAX_FPS_TO_ENCODER = "max-fps-to-encoder";
|
||||
|
||||
// Keep the values in descending order
|
||||
private static final int[] MAX_SIZE_FALLBACK = {2560, 1920, 1600, 1280, 1024, 800};
|
||||
|
||||
private static final int NO_PTS = -1;
|
||||
|
||||
private final AtomicBoolean rotationChanged = new AtomicBoolean();
|
||||
private final ByteBuffer headerBuffer = ByteBuffer.allocate(12);
|
||||
|
||||
private final String encoderName;
|
||||
private final List<CodecOption> codecOptions;
|
||||
private final int bitRate;
|
||||
private final int maxFps;
|
||||
private final boolean sendFrameMeta;
|
||||
private final boolean downsizeOnError;
|
||||
private String encoderName;
|
||||
private List<CodecOption> codecOptions;
|
||||
private int bitRate;
|
||||
private int maxFps;
|
||||
private boolean sendFrameMeta;
|
||||
private long ptsOrigin;
|
||||
|
||||
private boolean firstFrameSent;
|
||||
|
||||
public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, List<CodecOption> codecOptions, String encoderName,
|
||||
boolean downsizeOnError) {
|
||||
public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, List<CodecOption> codecOptions, String encoderName) {
|
||||
this.sendFrameMeta = sendFrameMeta;
|
||||
this.bitRate = bitRate;
|
||||
this.maxFps = maxFps;
|
||||
this.codecOptions = codecOptions;
|
||||
this.encoderName = encoderName;
|
||||
this.downsizeOnError = downsizeOnError;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -99,23 +91,6 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||
alive = encode(codec, fd);
|
||||
// do not call stop() on exception, it would trigger an IllegalStateException
|
||||
codec.stop();
|
||||
} catch (IllegalStateException e) {
|
||||
Ln.e("Encoding error: " + e.getClass().getName() + ": " + e.getMessage());
|
||||
if (!downsizeOnError || firstFrameSent) {
|
||||
// Fail immediately
|
||||
throw e;
|
||||
}
|
||||
|
||||
int newMaxSize = chooseMaxSizeFallback(screenInfo.getVideoSize());
|
||||
if (newMaxSize == 0) {
|
||||
// Definitively fail
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Retry with a smaller device size
|
||||
Ln.i("Retrying with -m" + newMaxSize + "...");
|
||||
device.setMaxSize(newMaxSize);
|
||||
alive = true;
|
||||
} finally {
|
||||
destroyDisplay(display);
|
||||
codec.release();
|
||||
@@ -127,18 +102,6 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||
}
|
||||
}
|
||||
|
||||
private static int chooseMaxSizeFallback(Size failedSize) {
|
||||
int currentMaxSize = Math.max(failedSize.getWidth(), failedSize.getHeight());
|
||||
for (int value : MAX_SIZE_FALLBACK) {
|
||||
if (value < currentMaxSize) {
|
||||
// We found a smaller value to reduce the video size
|
||||
return value;
|
||||
}
|
||||
}
|
||||
// No fallback, fail definitively
|
||||
return 0;
|
||||
}
|
||||
|
||||
private boolean encode(MediaCodec codec, FileDescriptor fd) throws IOException {
|
||||
boolean eof = false;
|
||||
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
||||
@@ -159,10 +122,6 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||
}
|
||||
|
||||
IO.writeFully(fd, codecBuffer);
|
||||
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
|
||||
// If this is not a config packet, then it contains a frame
|
||||
firstFrameSent = true;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (outputBufferId >= 0) {
|
||||
|
||||
@@ -80,12 +80,15 @@ public final class ScreenInfo {
|
||||
return new ScreenInfo(newContentRect, newUnlockedVideoSize, newDeviceRotation, lockedVideoOrientation);
|
||||
}
|
||||
|
||||
public static ScreenInfo computeScreenInfo(int rotation, Size deviceSize, Rect crop, int maxSize, int lockedVideoOrientation) {
|
||||
public static ScreenInfo computeScreenInfo(DisplayInfo displayInfo, Rect crop, int maxSize, int lockedVideoOrientation) {
|
||||
int rotation = displayInfo.getRotation();
|
||||
|
||||
if (lockedVideoOrientation == Device.LOCK_VIDEO_ORIENTATION_INITIAL) {
|
||||
// The user requested to lock the video orientation to the current orientation
|
||||
lockedVideoOrientation = rotation;
|
||||
}
|
||||
|
||||
Size deviceSize = displayInfo.getSize();
|
||||
Rect contentRect = new Rect(0, 0, deviceSize.getWidth(), deviceSize.getHeight());
|
||||
if (crop != null) {
|
||||
if (rotation % 2 != 0) { // 180s preserve dimensions
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.genymobile.scrcpy;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.media.MediaCodec;
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.Build;
|
||||
@@ -19,7 +20,6 @@ public final class Server {
|
||||
private static void initAndCleanUp(Options options) {
|
||||
boolean mustDisableShowTouchesOnCleanUp = false;
|
||||
int restoreStayOn = -1;
|
||||
boolean restoreNormalPowerMode = options.getControl(); // only restore power mode if control is enabled
|
||||
if (options.getShowTouches() || options.getStayAwake()) {
|
||||
Settings settings = Device.getSettings();
|
||||
if (options.getShowTouches()) {
|
||||
@@ -52,7 +52,7 @@ public final class Server {
|
||||
}
|
||||
|
||||
try {
|
||||
CleanUp.configure(options.getDisplayId(), restoreStayOn, mustDisableShowTouchesOnCleanUp, restoreNormalPowerMode, options.getPowerOffScreenOnClose());
|
||||
CleanUp.configure(options.getDisplayId(), restoreStayOn, mustDisableShowTouchesOnCleanUp, true, options.getPowerOffScreenOnClose());
|
||||
} catch (IOException e) {
|
||||
Ln.e("Could not configure cleanup", e);
|
||||
}
|
||||
@@ -67,15 +67,10 @@ public final class Server {
|
||||
|
||||
boolean tunnelForward = options.isTunnelForward();
|
||||
boolean control = options.getControl();
|
||||
boolean sendDummyByte = options.getSendDummyByte();
|
||||
|
||||
try (DesktopConnection connection = DesktopConnection.open(tunnelForward, control, sendDummyByte)) {
|
||||
if (options.getSendDeviceMeta()) {
|
||||
Size videoSize = device.getScreenInfo().getVideoSize();
|
||||
connection.sendDeviceMeta(Device.getDeviceName(), videoSize.getWidth(), videoSize.getHeight());
|
||||
}
|
||||
try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward, control)) {
|
||||
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), codecOptions,
|
||||
options.getEncoderName(), options.getDownsizeOnError());
|
||||
options.getEncoderName());
|
||||
|
||||
Thread controllerThread = null;
|
||||
Thread deviceMessageSenderThread = null;
|
||||
@@ -205,6 +200,10 @@ public final class Server {
|
||||
Rect crop = parseCrop(value);
|
||||
options.setCrop(crop);
|
||||
break;
|
||||
case "send_frame_meta":
|
||||
boolean sendFrameMeta = Boolean.parseBoolean(value);
|
||||
options.setSendFrameMeta(sendFrameMeta);
|
||||
break;
|
||||
case "control":
|
||||
boolean control = Boolean.parseBoolean(value);
|
||||
options.setControl(control);
|
||||
@@ -238,29 +237,6 @@ public final class Server {
|
||||
boolean clipboardAutosync = Boolean.parseBoolean(value);
|
||||
options.setClipboardAutosync(clipboardAutosync);
|
||||
break;
|
||||
case "downsize_on_error":
|
||||
boolean downsizeOnError = Boolean.parseBoolean(value);
|
||||
options.setDownsizeOnError(downsizeOnError);
|
||||
break;
|
||||
case "send_device_meta":
|
||||
boolean sendDeviceMeta = Boolean.parseBoolean(value);
|
||||
options.setSendDeviceMeta(sendDeviceMeta);
|
||||
break;
|
||||
case "send_frame_meta":
|
||||
boolean sendFrameMeta = Boolean.parseBoolean(value);
|
||||
options.setSendFrameMeta(sendFrameMeta);
|
||||
break;
|
||||
case "send_dummy_byte":
|
||||
boolean sendDummyByte = Boolean.parseBoolean(value);
|
||||
options.setSendDummyByte(sendDummyByte);
|
||||
break;
|
||||
case "raw_video_stream":
|
||||
boolean rawVideoStream = Boolean.parseBoolean(value);
|
||||
if (rawVideoStream) {
|
||||
options.setSendDeviceMeta(false);
|
||||
options.setSendFrameMeta(false);
|
||||
options.setSendDummyByte(false);
|
||||
}
|
||||
default:
|
||||
Ln.w("Unknown server option: " + key);
|
||||
break;
|
||||
@@ -287,6 +263,16 @@ public final class Server {
|
||||
}
|
||||
|
||||
private static void suggestFix(Throwable e) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (e instanceof MediaCodec.CodecException) {
|
||||
MediaCodec.CodecException mce = (MediaCodec.CodecException) e;
|
||||
if (mce.getErrorCode() == 0xfffffc0e) {
|
||||
Ln.e("The hardware encoder is not able to encode at the given definition.");
|
||||
Ln.e("Try with a lower definition:");
|
||||
Ln.e(" scrcpy -m 1024");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e instanceof InvalidDisplayIdException) {
|
||||
InvalidDisplayIdException idie = (InvalidDisplayIdException) e;
|
||||
int[] displayIds = idie.getAvailableDisplayIds();
|
||||
|
||||
@@ -16,7 +16,6 @@ public final class InputManager {
|
||||
|
||||
private final IInterface manager;
|
||||
private Method injectInputEventMethod;
|
||||
private boolean alternativeInjectInputEventMethod;
|
||||
|
||||
private static Method setDisplayIdMethod;
|
||||
|
||||
@@ -26,12 +25,7 @@ public final class InputManager {
|
||||
|
||||
private Method getInjectInputEventMethod() throws NoSuchMethodException {
|
||||
if (injectInputEventMethod == null) {
|
||||
try {
|
||||
injectInputEventMethod = manager.getClass().getMethod("injectInputEvent", InputEvent.class, int.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
injectInputEventMethod = manager.getClass().getMethod("injectInputEvent", InputEvent.class, int.class, int.class);
|
||||
alternativeInjectInputEventMethod = true;
|
||||
}
|
||||
injectInputEventMethod = manager.getClass().getMethod("injectInputEvent", InputEvent.class, int.class);
|
||||
}
|
||||
return injectInputEventMethod;
|
||||
}
|
||||
@@ -39,10 +33,6 @@ public final class InputManager {
|
||||
public boolean injectInputEvent(InputEvent inputEvent, int mode) {
|
||||
try {
|
||||
Method method = getInjectInputEventMethod();
|
||||
if (alternativeInjectInputEventMethod) {
|
||||
// See <https://github.com/Genymobile/scrcpy/issues/2250>
|
||||
return (boolean) method.invoke(manager, inputEvent, mode, 0);
|
||||
}
|
||||
return (boolean) method.invoke(manager, inputEvent, mode);
|
||||
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||
Ln.e("Could not invoke method", e);
|
||||
|
||||
@@ -128,7 +128,6 @@ public class ControlMessageReaderTest {
|
||||
dos.writeShort(1920);
|
||||
dos.writeInt(1);
|
||||
dos.writeInt(-1);
|
||||
dos.writeInt(1);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
@@ -145,7 +144,6 @@ public class ControlMessageReaderTest {
|
||||
Assert.assertEquals(1920, event.getPosition().getScreenSize().getHeight());
|
||||
Assert.assertEquals(1, event.getHScroll());
|
||||
Assert.assertEquals(-1, event.getVScroll());
|
||||
Assert.assertEquals(1, event.getButtons());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user