Compare commits
86 Commits
hidmouse.1
...
raw_video_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93ac6a347e | ||
|
|
45a5e560df | ||
|
|
3ba32c2a0d | ||
|
|
6b21f4ae13 | ||
|
|
31a5d0c2bf | ||
|
|
f289d206ea | ||
|
|
ca516f4318 | ||
|
|
5d6076bffd | ||
|
|
e0bce1725b | ||
|
|
a9429efa34 | ||
|
|
063d103dd6 | ||
|
|
4bf9c057fe | ||
|
|
17c97820b2 | ||
|
|
8c7f0ed5ea | ||
|
|
ac038f276e | ||
|
|
1f65b1bf87 | ||
|
|
d41a46dc95 | ||
|
|
308a1f8192 | ||
|
|
241a587e61 | ||
|
|
7e35bfe382 | ||
|
|
855819bbd8 | ||
|
|
557daf280e | ||
|
|
0b8e926330 | ||
|
|
0ec3361bc9 | ||
|
|
81ff7ebd06 | ||
|
|
1ffe312369 | ||
|
|
ebef027c4f | ||
|
|
8e4e7d42f1 | ||
|
|
b066dc0bbf | ||
|
|
262506c733 | ||
|
|
2eb6fe7d81 | ||
|
|
3a0ba7d0a4 | ||
|
|
75c5dc6859 | ||
|
|
fa30f9806a | ||
|
|
4fb61ac83d | ||
|
|
8fa9e6b01a | ||
|
|
0ec64baad4 | ||
|
|
15bf27afdd | ||
|
|
26b4104844 | ||
|
|
723faa5dee | ||
|
|
162043911e | ||
|
|
117fe32626 | ||
|
|
b7a06278fe | ||
|
|
b3ff1f6b3b | ||
|
|
a2495c5ef1 | ||
|
|
37c7827d46 | ||
|
|
5e8fa56e7a | ||
|
|
60bf133ac2 | ||
|
|
1c71bd16be | ||
|
|
afa4a1b728 | ||
|
|
3a4d5c7f18 | ||
|
|
5f7ddff8ae | ||
|
|
2a0c2e5e99 | ||
|
|
a6644e831b | ||
|
|
75655194fb | ||
|
|
43aff4af73 | ||
|
|
cba84f6999 | ||
|
|
ed2e45ee29 | ||
|
|
aee1b39790 | ||
|
|
17d01b5bf7 | ||
|
|
40fca82b60 | ||
|
|
643293752d | ||
|
|
b5855e5deb | ||
|
|
924375487e | ||
|
|
7121a0dc53 | ||
|
|
f04812fc71 | ||
|
|
5ce1ccde85 | ||
|
|
6102a0b5bb | ||
|
|
2b34e1224e | ||
|
|
a9d23400cd | ||
|
|
cca3c953da | ||
|
|
57f1655d4b | ||
|
|
bc674721dc | ||
|
|
63e29b1782 | ||
|
|
3c15cbdaf8 | ||
|
|
96e0e89740 | ||
|
|
a1f2f5fbd3 | ||
|
|
9460bdd87b | ||
|
|
b4b638e8fe | ||
|
|
e4396e34c2 | ||
|
|
b8fed50639 | ||
|
|
d540c72e7c | ||
|
|
cd5891fee6 | ||
|
|
26ee7ce566 | ||
|
|
ba28d817fb | ||
|
|
37124e1452 |
3
FAQ.md
3
FAQ.md
@@ -219,6 +219,9 @@ scrcpy -m 1024
|
|||||||
scrcpy -m 800
|
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).
|
You could also try another [encoder](README.md#encoder).
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
31
README.md
31
README.md
@@ -31,6 +31,8 @@ Its features include:
|
|||||||
- device screen [as a webcam (V4L2)](#v4l2loopback) (Linux-only)
|
- device screen [as a webcam (V4L2)](#v4l2loopback) (Linux-only)
|
||||||
- [physical keyboard simulation (HID)](#physical-keyboard-simulation-hid)
|
- [physical keyboard simulation (HID)](#physical-keyboard-simulation-hid)
|
||||||
(Linux-only)
|
(Linux-only)
|
||||||
|
- [physical mouse simulation (HID)](#physical-mouse-simulation-hid)
|
||||||
|
(Linux-only)
|
||||||
- and more…
|
- and more…
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
@@ -815,6 +817,35 @@ a physical keyboard is connected).
|
|||||||
|
|
||||||
[Physical keyboard]: https://github.com/Genymobile/scrcpy/pull/2632#issuecomment-923756915
|
[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
|
#### Text injection preference
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ src = [
|
|||||||
'src/decoder.c',
|
'src/decoder.c',
|
||||||
'src/device_msg.c',
|
'src/device_msg.c',
|
||||||
'src/icon.c',
|
'src/icon.c',
|
||||||
'src/file_handler.c',
|
'src/file_pusher.c',
|
||||||
'src/fps_counter.c',
|
'src/fps_counter.c',
|
||||||
'src/frame_buffer.c',
|
'src/frame_buffer.c',
|
||||||
'src/input_manager.c',
|
'src/input_manager.c',
|
||||||
@@ -118,15 +118,20 @@ else
|
|||||||
include_directories: include_directories(sdl2_include_dir)
|
include_directories: include_directories(sdl2_include_dir)
|
||||||
)
|
)
|
||||||
|
|
||||||
prebuilt_ffmpeg_shared = meson.get_cross_property('prebuilt_ffmpeg_shared')
|
prebuilt_ffmpeg = meson.get_cross_property('prebuilt_ffmpeg')
|
||||||
prebuilt_ffmpeg_dev = meson.get_cross_property('prebuilt_ffmpeg_dev')
|
ffmpeg_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_ffmpeg + '/bin'
|
||||||
ffmpeg_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_ffmpeg_shared + '/bin'
|
ffmpeg_include_dir = '../prebuilt-deps/' + prebuilt_ffmpeg + '/include'
|
||||||
ffmpeg_include_dir = '../prebuilt-deps/' + prebuilt_ffmpeg_dev + '/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')
|
||||||
|
|
||||||
ffmpeg = declare_dependency(
|
ffmpeg = declare_dependency(
|
||||||
dependencies: [
|
dependencies: [
|
||||||
cc.find_library('avcodec-58', dirs: ffmpeg_bin_dir),
|
cc.find_library(ffmpeg_avcodec, dirs: ffmpeg_bin_dir),
|
||||||
cc.find_library('avformat-58', dirs: ffmpeg_bin_dir),
|
cc.find_library(ffmpeg_avformat, dirs: ffmpeg_bin_dir),
|
||||||
cc.find_library('avutil-56', dirs: ffmpeg_bin_dir),
|
cc.find_library(ffmpeg_avutil, dirs: ffmpeg_bin_dir),
|
||||||
],
|
],
|
||||||
include_directories: include_directories(ffmpeg_include_dir)
|
include_directories: include_directories(ffmpeg_include_dir)
|
||||||
)
|
)
|
||||||
|
|||||||
20
app/scrcpy.1
20
app/scrcpy.1
@@ -96,6 +96,8 @@ 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).
|
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
|
.TP
|
||||||
.B \-\-legacy\-paste
|
.B \-\-legacy\-paste
|
||||||
Inject computer clipboard text as a sequence of key events on Ctrl+v (like MOD+Shift+v).
|
Inject computer clipboard text as a sequence of key events on Ctrl+v (like MOD+Shift+v).
|
||||||
@@ -120,12 +122,30 @@ Limit both the width and height of the video to \fIvalue\fR. The other dimension
|
|||||||
|
|
||||||
Default is 0 (unlimited).
|
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
|
.TP
|
||||||
.B \-\-no\-clipboard\-autosync
|
.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.
|
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.
|
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
|
.TP
|
||||||
.B \-n, \-\-no\-control
|
.B \-n, \-\-no\-control
|
||||||
Disable device control (mirror the device in read\-only).
|
Disable device control (mirror the device in read\-only).
|
||||||
|
|||||||
@@ -56,14 +56,13 @@ accept_device(libusb_device *device, const char *serial) {
|
|||||||
// devices available on the computer have permission restrictions
|
// devices available on the computer have permission restrictions
|
||||||
|
|
||||||
struct libusb_device_descriptor desc;
|
struct libusb_device_descriptor desc;
|
||||||
libusb_get_device_descriptor(device, &desc);
|
int result = libusb_get_device_descriptor(device, &desc);
|
||||||
|
if (result < 0 || !desc.iSerialNumber) {
|
||||||
if (!desc.iSerialNumber) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
libusb_device_handle *handle;
|
libusb_device_handle *handle;
|
||||||
int result = libusb_open(device, &handle);
|
result = libusb_open(device, &handle);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -131,31 +130,22 @@ sc_aoa_init(struct sc_aoa *aoa, const char *serial,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!sc_cond_init(&aoa->event_cond)) {
|
if (!sc_cond_init(&aoa->event_cond)) {
|
||||||
sc_mutex_destroy(&aoa->mutex);
|
goto error_destroy_mutex;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (libusb_init(&aoa->usb_context) != LIBUSB_SUCCESS) {
|
if (libusb_init(&aoa->usb_context) != LIBUSB_SUCCESS) {
|
||||||
sc_cond_destroy(&aoa->event_cond);
|
goto error_destroy_cond;
|
||||||
sc_mutex_destroy(&aoa->mutex);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
aoa->usb_device = sc_aoa_find_usb_device(serial);
|
aoa->usb_device = sc_aoa_find_usb_device(serial);
|
||||||
if (!aoa->usb_device) {
|
if (!aoa->usb_device) {
|
||||||
LOGW("USB device of serial %s not found", serial);
|
LOGW("USB device of serial %s not found", serial);
|
||||||
libusb_exit(aoa->usb_context);
|
goto error_exit_libusb;
|
||||||
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) {
|
if (sc_aoa_open_usb_handle(aoa->usb_device, &aoa->usb_handle) < 0) {
|
||||||
LOGW("Open USB handle failed");
|
LOGW("Open USB handle failed");
|
||||||
libusb_unref_device(aoa->usb_device);
|
goto error_unref_device;
|
||||||
libusb_exit(aoa->usb_context);
|
|
||||||
sc_cond_destroy(&aoa->event_cond);
|
|
||||||
sc_mutex_destroy(&aoa->mutex);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +153,16 @@ sc_aoa_init(struct sc_aoa *aoa, const char *serial,
|
|||||||
aoa->acksync = acksync;
|
aoa->acksync = acksync;
|
||||||
|
|
||||||
return true;
|
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
|
void
|
||||||
@@ -343,8 +343,8 @@ run_aoa_thread(void *data) {
|
|||||||
|
|
||||||
if (ack_to_wait != SC_SEQUENCE_INVALID) {
|
if (ack_to_wait != SC_SEQUENCE_INVALID) {
|
||||||
LOGD("Waiting ack from server sequence=%" PRIu64_, ack_to_wait);
|
LOGD("Waiting ack from server sequence=%" PRIu64_, ack_to_wait);
|
||||||
// Do not block the loop indefinitely if the ack never comes (it should
|
// Do not block the loop indefinitely if the ack never comes (it
|
||||||
// never happen)
|
// should never happen)
|
||||||
sc_tick deadline = sc_tick_now() + SC_TICK_FROM_MS(500);
|
sc_tick deadline = sc_tick_now() + SC_TICK_FROM_MS(500);
|
||||||
enum sc_acksync_wait_result result =
|
enum sc_acksync_wait_result result =
|
||||||
sc_acksync_wait(aoa->acksync, ack_to_wait, deadline);
|
sc_acksync_wait(aoa->acksync, ack_to_wait, deadline);
|
||||||
|
|||||||
@@ -52,6 +52,7 @@
|
|||||||
#define OPT_NO_CLIPBOARD_AUTOSYNC 1032
|
#define OPT_NO_CLIPBOARD_AUTOSYNC 1032
|
||||||
#define OPT_TCPIP 1033
|
#define OPT_TCPIP 1033
|
||||||
#define OPT_RAW_KEY_EVENTS 1034
|
#define OPT_RAW_KEY_EVENTS 1034
|
||||||
|
#define OPT_NO_DOWNSIZE_ON_ERROR 1035
|
||||||
|
|
||||||
struct sc_option {
|
struct sc_option {
|
||||||
char shortopt;
|
char shortopt;
|
||||||
@@ -178,7 +179,8 @@ static const struct sc_option options[] = {
|
|||||||
"directly: `adb shell am start -a "
|
"directly: `adb shell am start -a "
|
||||||
"android.settings.HARD_KEYBOARD_SETTINGS`.\n"
|
"android.settings.HARD_KEYBOARD_SETTINGS`.\n"
|
||||||
"However, the option is only available when the HID keyboard "
|
"However, the option is only available when the HID keyboard "
|
||||||
"is enabled (or a physical keyboard is connected).",
|
"is enabled (or a physical keyboard is connected).\n"
|
||||||
|
"Also see --hid-mouse.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.shortopt = 'h',
|
.shortopt = 'h',
|
||||||
@@ -223,7 +225,8 @@ static const struct sc_option options[] = {
|
|||||||
"LAlt, LSuper or RSuper toggle the capture mode, to give "
|
"LAlt, LSuper or RSuper toggle the capture mode, to give "
|
||||||
"control of the mouse back to the computer.\n"
|
"control of the mouse back to the computer.\n"
|
||||||
"It may only work over USB, and is currently only supported "
|
"It may only work over USB, and is currently only supported "
|
||||||
"on Linux.\n",
|
"on Linux.\n"
|
||||||
|
"Also see --hid-keyboard.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.shortopt = 'm',
|
.shortopt = 'm',
|
||||||
@@ -234,6 +237,13 @@ static const struct sc_option options[] = {
|
|||||||
"is preserved.\n"
|
"is preserved.\n"
|
||||||
"Default is 0 (unlimited).",
|
"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_id = OPT_NO_CLIPBOARD_AUTOSYNC,
|
||||||
.longopt = "no-clipboard-autosync",
|
.longopt = "no-clipboard-autosync",
|
||||||
@@ -406,8 +416,8 @@ static const struct sc_option options[] = {
|
|||||||
"frames. This increases latency to compensate for jitter.\n"
|
"frames. This increases latency to compensate for jitter.\n"
|
||||||
"This option is similar to --display-buffer, but specific to "
|
"This option is similar to --display-buffer, but specific to "
|
||||||
"V4L2 sink.\n"
|
"V4L2 sink.\n"
|
||||||
"This feature is only available on Linux.\n"
|
"Default is 0 (no buffering).\n"
|
||||||
"Default is 0 (no buffering).",
|
"This option is only available on Linux.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.shortopt = 'V',
|
.shortopt = 'V',
|
||||||
@@ -1310,12 +1320,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
case 'K':
|
case 'K':
|
||||||
#ifdef HAVE_AOA_HID
|
#ifdef HAVE_AOA_HID
|
||||||
opts->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_HID;
|
opts->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_HID;
|
||||||
|
break;
|
||||||
#else
|
#else
|
||||||
LOGE("HID over AOA (-K/--hid-keyboard) is not supported on "
|
LOGE("HID over AOA (-K/--hid-keyboard) is not supported on "
|
||||||
"this platform. It is only available on Linux.");
|
"this platform. It is only available on Linux.");
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
break;
|
|
||||||
case OPT_MAX_FPS:
|
case OPT_MAX_FPS:
|
||||||
if (!parse_max_fps(optarg, &opts->max_fps)) {
|
if (!parse_max_fps(optarg, &opts->max_fps)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1329,12 +1339,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
case 'M':
|
case 'M':
|
||||||
#ifdef HAVE_AOA_HID
|
#ifdef HAVE_AOA_HID
|
||||||
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_HID;
|
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_HID;
|
||||||
|
break;
|
||||||
#else
|
#else
|
||||||
LOGE("HID over AOA (-M/--hid-mouse) is not supported on this"
|
LOGE("HID over AOA (-M/--hid-mouse) is not supported on this"
|
||||||
"platform. It is only available on Linux.");
|
"platform. It is only available on Linux.");
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
break;
|
|
||||||
case OPT_LOCK_VIDEO_ORIENTATION:
|
case OPT_LOCK_VIDEO_ORIENTATION:
|
||||||
if (!parse_lock_video_orientation(optarg,
|
if (!parse_lock_video_orientation(optarg,
|
||||||
&opts->lock_video_orientation)) {
|
&opts->lock_video_orientation)) {
|
||||||
@@ -1487,24 +1497,27 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
opts->tcpip = true;
|
opts->tcpip = true;
|
||||||
opts->tcpip_dst = optarg;
|
opts->tcpip_dst = optarg;
|
||||||
break;
|
break;
|
||||||
|
case OPT_NO_DOWNSIZE_ON_ERROR:
|
||||||
|
opts->downsize_on_error = false;
|
||||||
|
break;
|
||||||
case OPT_V4L2_SINK:
|
case OPT_V4L2_SINK:
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
opts->v4l2_device = optarg;
|
opts->v4l2_device = optarg;
|
||||||
|
break;
|
||||||
#else
|
#else
|
||||||
LOGE("V4L2 (--v4l2-sink) is only available on Linux.");
|
LOGE("V4L2 (--v4l2-sink) is only available on Linux.");
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
break;
|
|
||||||
case OPT_V4L2_BUFFER:
|
case OPT_V4L2_BUFFER:
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
if (!parse_buffering_time(optarg, &opts->v4l2_buffer)) {
|
if (!parse_buffering_time(optarg, &opts->v4l2_buffer)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
#else
|
#else
|
||||||
LOGE("V4L2 (--v4l2-buffer) is only available on Linux.");
|
LOGE("V4L2 (--v4l2-buffer) is only available on Linux.");
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
// getopt prints the error message on stderr
|
// getopt prints the error message on stderr
|
||||||
return false;
|
return false;
|
||||||
@@ -1532,11 +1545,18 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->v4l2_device && opts->lock_video_orientation
|
if (opts->v4l2_device) {
|
||||||
== SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) {
|
if (opts->lock_video_orientation ==
|
||||||
LOGI("Video orientation is locked for v4l2 sink. "
|
SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) {
|
||||||
"See --lock-video-orientation.");
|
LOGI("Video orientation is locked for v4l2 sink. "
|
||||||
opts->lock_video_orientation = SC_LOCK_VIDEO_ORIENTATION_INITIAL;
|
"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_buffer && !opts->v4l2_device) {
|
if (opts->v4l2_buffer && !opts->v4l2_device) {
|
||||||
@@ -1571,14 +1591,23 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!opts->control && opts->turn_screen_off) {
|
if (!opts->control) {
|
||||||
LOGE("Could not request to turn screen off if control is disabled");
|
if (opts->turn_screen_off) {
|
||||||
return false;
|
LOGE("Could not request to turn screen off if control is disabled");
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
if (!opts->control && opts->stay_awake) {
|
if (opts->stay_awake) {
|
||||||
LOGE("Could not request to stay awake if control is disabled");
|
LOGE("Could not request to stay awake if control is disabled");
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ to_fixed_point_16(float f) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
sc_control_msg_serialize(const struct sc_control_msg *msg, unsigned char *buf) {
|
||||||
buf[0] = msg->type;
|
buf[0] = msg->type;
|
||||||
switch (msg->type) {
|
switch (msg->type) {
|
||||||
case CONTROL_MSG_TYPE_INJECT_KEYCODE:
|
case CONTROL_MSG_TYPE_INJECT_KEYCODE:
|
||||||
@@ -151,7 +151,7 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
control_msg_log(const struct control_msg *msg) {
|
sc_control_msg_log(const struct sc_control_msg *msg) {
|
||||||
#define LOG_CMSG(fmt, ...) LOGV("input: " fmt, ## __VA_ARGS__)
|
#define LOG_CMSG(fmt, ...) LOGV("input: " fmt, ## __VA_ARGS__)
|
||||||
switch (msg->type) {
|
switch (msg->type) {
|
||||||
case CONTROL_MSG_TYPE_INJECT_KEYCODE:
|
case CONTROL_MSG_TYPE_INJECT_KEYCODE:
|
||||||
@@ -237,7 +237,7 @@ control_msg_log(const struct control_msg *msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
control_msg_destroy(struct control_msg *msg) {
|
sc_control_msg_destroy(struct sc_control_msg *msg) {
|
||||||
switch (msg->type) {
|
switch (msg->type) {
|
||||||
case CONTROL_MSG_TYPE_INJECT_TEXT:
|
case CONTROL_MSG_TYPE_INJECT_TEXT:
|
||||||
free(msg->inject_text.text);
|
free(msg->inject_text.text);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
#define POINTER_ID_MOUSE UINT64_C(-1)
|
#define POINTER_ID_MOUSE UINT64_C(-1)
|
||||||
#define POINTER_ID_VIRTUAL_FINGER UINT64_C(-2)
|
#define POINTER_ID_VIRTUAL_FINGER UINT64_C(-2)
|
||||||
|
|
||||||
enum control_msg_type {
|
enum sc_control_msg_type {
|
||||||
CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
||||||
CONTROL_MSG_TYPE_INJECT_TEXT,
|
CONTROL_MSG_TYPE_INJECT_TEXT,
|
||||||
CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||||
@@ -47,8 +47,8 @@ enum get_clipboard_copy_key {
|
|||||||
GET_CLIPBOARD_COPY_KEY_CUT,
|
GET_CLIPBOARD_COPY_KEY_CUT,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct control_msg {
|
struct sc_control_msg {
|
||||||
enum control_msg_type type;
|
enum sc_control_msg_type type;
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
enum android_keyevent_action action;
|
enum android_keyevent_action action;
|
||||||
@@ -93,12 +93,12 @@ struct control_msg {
|
|||||||
// buf size must be at least CONTROL_MSG_MAX_SIZE
|
// buf size must be at least CONTROL_MSG_MAX_SIZE
|
||||||
// return the number of bytes written
|
// return the number of bytes written
|
||||||
size_t
|
size_t
|
||||||
control_msg_serialize(const struct control_msg *msg, unsigned char *buf);
|
sc_control_msg_serialize(const struct sc_control_msg *msg, unsigned char *buf);
|
||||||
|
|
||||||
void
|
void
|
||||||
control_msg_log(const struct control_msg *msg);
|
sc_control_msg_log(const struct sc_control_msg *msg);
|
||||||
|
|
||||||
void
|
void
|
||||||
control_msg_destroy(struct control_msg *msg);
|
sc_control_msg_destroy(struct sc_control_msg *msg);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
bool
|
bool
|
||||||
controller_init(struct controller *controller, sc_socket control_socket,
|
sc_controller_init(struct sc_controller *controller, sc_socket control_socket,
|
||||||
struct sc_acksync *acksync) {
|
struct sc_acksync *acksync) {
|
||||||
cbuf_init(&controller->queue);
|
cbuf_init(&controller->queue);
|
||||||
|
|
||||||
bool ok = receiver_init(&controller->receiver, control_socket, acksync);
|
bool ok = receiver_init(&controller->receiver, control_socket, acksync);
|
||||||
@@ -34,23 +34,23 @@ controller_init(struct controller *controller, sc_socket control_socket,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
controller_destroy(struct controller *controller) {
|
sc_controller_destroy(struct sc_controller *controller) {
|
||||||
sc_cond_destroy(&controller->msg_cond);
|
sc_cond_destroy(&controller->msg_cond);
|
||||||
sc_mutex_destroy(&controller->mutex);
|
sc_mutex_destroy(&controller->mutex);
|
||||||
|
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
while (cbuf_take(&controller->queue, &msg)) {
|
while (cbuf_take(&controller->queue, &msg)) {
|
||||||
control_msg_destroy(&msg);
|
sc_control_msg_destroy(&msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
receiver_destroy(&controller->receiver);
|
receiver_destroy(&controller->receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
controller_push_msg(struct controller *controller,
|
sc_controller_push_msg(struct sc_controller *controller,
|
||||||
const struct control_msg *msg) {
|
const struct sc_control_msg *msg) {
|
||||||
if (sc_get_log_level() <= SC_LOG_LEVEL_VERBOSE) {
|
if (sc_get_log_level() <= SC_LOG_LEVEL_VERBOSE) {
|
||||||
control_msg_log(msg);
|
sc_control_msg_log(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
sc_mutex_lock(&controller->mutex);
|
sc_mutex_lock(&controller->mutex);
|
||||||
@@ -64,9 +64,10 @@ controller_push_msg(struct controller *controller,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
process_msg(struct controller *controller, const struct control_msg *msg) {
|
process_msg(struct sc_controller *controller,
|
||||||
|
const struct sc_control_msg *msg) {
|
||||||
static unsigned char serialized_msg[CONTROL_MSG_MAX_SIZE];
|
static unsigned char serialized_msg[CONTROL_MSG_MAX_SIZE];
|
||||||
size_t length = control_msg_serialize(msg, serialized_msg);
|
size_t length = sc_control_msg_serialize(msg, serialized_msg);
|
||||||
if (!length) {
|
if (!length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -77,7 +78,7 @@ process_msg(struct controller *controller, const struct control_msg *msg) {
|
|||||||
|
|
||||||
static int
|
static int
|
||||||
run_controller(void *data) {
|
run_controller(void *data) {
|
||||||
struct controller *controller = data;
|
struct sc_controller *controller = data;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
sc_mutex_lock(&controller->mutex);
|
sc_mutex_lock(&controller->mutex);
|
||||||
@@ -89,14 +90,14 @@ run_controller(void *data) {
|
|||||||
sc_mutex_unlock(&controller->mutex);
|
sc_mutex_unlock(&controller->mutex);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
bool non_empty = cbuf_take(&controller->queue, &msg);
|
bool non_empty = cbuf_take(&controller->queue, &msg);
|
||||||
assert(non_empty);
|
assert(non_empty);
|
||||||
(void) non_empty;
|
(void) non_empty;
|
||||||
sc_mutex_unlock(&controller->mutex);
|
sc_mutex_unlock(&controller->mutex);
|
||||||
|
|
||||||
bool ok = process_msg(controller, &msg);
|
bool ok = process_msg(controller, &msg);
|
||||||
control_msg_destroy(&msg);
|
sc_control_msg_destroy(&msg);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGD("Could not write msg to socket");
|
LOGD("Could not write msg to socket");
|
||||||
break;
|
break;
|
||||||
@@ -106,7 +107,7 @@ run_controller(void *data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
controller_start(struct controller *controller) {
|
sc_controller_start(struct sc_controller *controller) {
|
||||||
LOGD("Starting controller thread");
|
LOGD("Starting controller thread");
|
||||||
|
|
||||||
bool ok = sc_thread_create(&controller->thread, run_controller,
|
bool ok = sc_thread_create(&controller->thread, run_controller,
|
||||||
@@ -117,7 +118,7 @@ controller_start(struct controller *controller) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!receiver_start(&controller->receiver)) {
|
if (!receiver_start(&controller->receiver)) {
|
||||||
controller_stop(controller);
|
sc_controller_stop(controller);
|
||||||
sc_thread_join(&controller->thread, NULL);
|
sc_thread_join(&controller->thread, NULL);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -126,7 +127,7 @@ controller_start(struct controller *controller) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
controller_stop(struct controller *controller) {
|
sc_controller_stop(struct sc_controller *controller) {
|
||||||
sc_mutex_lock(&controller->mutex);
|
sc_mutex_lock(&controller->mutex);
|
||||||
controller->stopped = true;
|
controller->stopped = true;
|
||||||
sc_cond_signal(&controller->msg_cond);
|
sc_cond_signal(&controller->msg_cond);
|
||||||
@@ -134,7 +135,7 @@ controller_stop(struct controller *controller) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
controller_join(struct controller *controller) {
|
sc_controller_join(struct sc_controller *controller) {
|
||||||
sc_thread_join(&controller->thread, NULL);
|
sc_thread_join(&controller->thread, NULL);
|
||||||
receiver_join(&controller->receiver);
|
receiver_join(&controller->receiver);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,36 +12,36 @@
|
|||||||
#include "util/net.h"
|
#include "util/net.h"
|
||||||
#include "util/thread.h"
|
#include "util/thread.h"
|
||||||
|
|
||||||
struct control_msg_queue CBUF(struct control_msg, 64);
|
struct sc_control_msg_queue CBUF(struct sc_control_msg, 64);
|
||||||
|
|
||||||
struct controller {
|
struct sc_controller {
|
||||||
sc_socket control_socket;
|
sc_socket control_socket;
|
||||||
sc_thread thread;
|
sc_thread thread;
|
||||||
sc_mutex mutex;
|
sc_mutex mutex;
|
||||||
sc_cond msg_cond;
|
sc_cond msg_cond;
|
||||||
bool stopped;
|
bool stopped;
|
||||||
struct control_msg_queue queue;
|
struct sc_control_msg_queue queue;
|
||||||
struct receiver receiver;
|
struct receiver receiver;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
controller_init(struct controller *controller, sc_socket control_socket,
|
sc_controller_init(struct sc_controller *controller, sc_socket control_socket,
|
||||||
struct sc_acksync *acksync);
|
struct sc_acksync *acksync);
|
||||||
|
|
||||||
void
|
void
|
||||||
controller_destroy(struct controller *controller);
|
sc_controller_destroy(struct sc_controller *controller);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
controller_start(struct controller *controller);
|
sc_controller_start(struct sc_controller *controller);
|
||||||
|
|
||||||
void
|
void
|
||||||
controller_stop(struct controller *controller);
|
sc_controller_stop(struct sc_controller *controller);
|
||||||
|
|
||||||
void
|
void
|
||||||
controller_join(struct controller *controller);
|
sc_controller_join(struct sc_controller *controller);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
controller_push_msg(struct controller *controller,
|
sc_controller_push_msg(struct sc_controller *controller,
|
||||||
const struct control_msg *msg);
|
const struct sc_control_msg *msg);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "trait/packet_sink.h"
|
#include "trait/packet_sink.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
|
|
||||||
#define DECODER_MAX_SINKS 2
|
#define DECODER_MAX_SINKS 2
|
||||||
|
|||||||
@@ -1,178 +0,0 @@
|
|||||||
#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);
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
#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
|
|
||||||
178
app/src/file_pusher.c
Normal file
178
app/src/file_pusher.c
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
59
app/src/file_pusher.h
Normal file
59
app/src/file_pusher.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#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
|
||||||
@@ -262,6 +262,6 @@ void
|
|||||||
sc_hid_mouse_destroy(struct sc_hid_mouse *mouse) {
|
sc_hid_mouse_destroy(struct sc_hid_mouse *mouse) {
|
||||||
bool ok = sc_aoa_unregister_hid(mouse->aoa, HID_MOUSE_ACCESSORY_ID);
|
bool ok = sc_aoa_unregister_hid(mouse->aoa, HID_MOUSE_ACCESSORY_ID);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGW("Could not unregister HID");
|
LOGW("Could not unregister HID mouse");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#ifndef HID_MOUSE_H
|
#ifndef SC_HID_MOUSE_H
|
||||||
#define HID_MOUSE_H
|
#define SC_HID_MOUSE_H
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
#include <libavutil/pixdesc.h>
|
#include <libavutil/pixdesc.h>
|
||||||
#include <libavutil/pixfmt.h>
|
#include <libavutil/pixfmt.h>
|
||||||
@@ -85,7 +86,7 @@ decode_image(const char *path) {
|
|||||||
|
|
||||||
AVCodecParameters *params = ctx->streams[stream]->codecpar;
|
AVCodecParameters *params = ctx->streams[stream]->codecpar;
|
||||||
|
|
||||||
AVCodec *codec = avcodec_find_decoder(params->codec_id);
|
const AVCodec *codec = avcodec_find_decoder(params->codec_id);
|
||||||
if (!codec) {
|
if (!codec) {
|
||||||
LOGE("Could not find image decoder");
|
LOGE("Could not find image decoder");
|
||||||
goto close_input;
|
goto close_input;
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ enum sc_mod {
|
|||||||
|
|
||||||
SC_MOD_NUM = KMOD_NUM,
|
SC_MOD_NUM = KMOD_NUM,
|
||||||
SC_MOD_CAPS = KMOD_CAPS,
|
SC_MOD_CAPS = KMOD_CAPS,
|
||||||
SC_MOD_SCROLL = KMOD_SCROLL,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum sc_action {
|
enum sc_action {
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ to_sdl_mod(unsigned shortcut_mod) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_shortcut_mod(struct input_manager *im, uint16_t sdl_mod) {
|
is_shortcut_mod(struct sc_input_manager *im, uint16_t sdl_mod) {
|
||||||
// keep only the relevant modifier keys
|
// keep only the relevant modifier keys
|
||||||
sdl_mod &= SC_SDL_SHORTCUT_MODS_MASK;
|
sdl_mod &= SC_SDL_SHORTCUT_MODS_MASK;
|
||||||
|
|
||||||
@@ -122,17 +122,17 @@ is_shortcut_mod(struct input_manager *im, uint16_t sdl_mod) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
input_manager_init(struct input_manager *im,
|
sc_input_manager_init(struct sc_input_manager *im,
|
||||||
const struct input_manager_params *params) {
|
const struct sc_input_manager_params *params) {
|
||||||
assert(!params->control || (params->kp && params->kp->ops));
|
assert(!params->controller || (params->kp && params->kp->ops));
|
||||||
assert(!params->control || (params->mp && params->mp->ops));
|
assert(!params->controller || (params->mp && params->mp->ops));
|
||||||
|
|
||||||
im->controller = params->controller;
|
im->controller = params->controller;
|
||||||
|
im->fp = params->fp;
|
||||||
im->screen = params->screen;
|
im->screen = params->screen;
|
||||||
im->kp = params->kp;
|
im->kp = params->kp;
|
||||||
im->mp = params->mp;
|
im->mp = params->mp;
|
||||||
|
|
||||||
im->control = params->control;
|
|
||||||
im->forward_all_clicks = params->forward_all_clicks;
|
im->forward_all_clicks = params->forward_all_clicks;
|
||||||
im->legacy_paste = params->legacy_paste;
|
im->legacy_paste = params->legacy_paste;
|
||||||
im->clipboard_autosync = params->clipboard_autosync;
|
im->clipboard_autosync = params->clipboard_autosync;
|
||||||
@@ -157,10 +157,10 @@ input_manager_init(struct input_manager *im,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
send_keycode(struct controller *controller, enum android_keycode keycode,
|
send_keycode(struct sc_controller *controller, enum android_keycode keycode,
|
||||||
enum sc_action action, const char *name) {
|
enum sc_action action, const char *name) {
|
||||||
// send DOWN event
|
// send DOWN event
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
msg.type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
msg.type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
||||||
msg.inject_keycode.action = action == SC_ACTION_DOWN
|
msg.inject_keycode.action = action == SC_ACTION_DOWN
|
||||||
? AKEY_EVENT_ACTION_DOWN
|
? AKEY_EVENT_ACTION_DOWN
|
||||||
@@ -169,102 +169,100 @@ send_keycode(struct controller *controller, enum android_keycode keycode,
|
|||||||
msg.inject_keycode.metastate = 0;
|
msg.inject_keycode.metastate = 0;
|
||||||
msg.inject_keycode.repeat = 0;
|
msg.inject_keycode.repeat = 0;
|
||||||
|
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!sc_controller_push_msg(controller, &msg)) {
|
||||||
LOGW("Could not request 'inject %s'", name);
|
LOGW("Could not request 'inject %s'", name);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
action_home(struct controller *controller, enum sc_action action) {
|
action_home(struct sc_controller *controller, enum sc_action action) {
|
||||||
send_keycode(controller, AKEYCODE_HOME, action, "HOME");
|
send_keycode(controller, AKEYCODE_HOME, action, "HOME");
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
action_back(struct controller *controller, enum sc_action action) {
|
action_back(struct sc_controller *controller, enum sc_action action) {
|
||||||
send_keycode(controller, AKEYCODE_BACK, action, "BACK");
|
send_keycode(controller, AKEYCODE_BACK, action, "BACK");
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
action_app_switch(struct controller *controller, enum sc_action action) {
|
action_app_switch(struct sc_controller *controller, enum sc_action action) {
|
||||||
send_keycode(controller, AKEYCODE_APP_SWITCH, action, "APP_SWITCH");
|
send_keycode(controller, AKEYCODE_APP_SWITCH, action, "APP_SWITCH");
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
action_power(struct controller *controller, enum sc_action action) {
|
action_power(struct sc_controller *controller, enum sc_action action) {
|
||||||
send_keycode(controller, AKEYCODE_POWER, action, "POWER");
|
send_keycode(controller, AKEYCODE_POWER, action, "POWER");
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
action_volume_up(struct controller *controller, enum sc_action action) {
|
action_volume_up(struct sc_controller *controller, enum sc_action action) {
|
||||||
send_keycode(controller, AKEYCODE_VOLUME_UP, action, "VOLUME_UP");
|
send_keycode(controller, AKEYCODE_VOLUME_UP, action, "VOLUME_UP");
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
action_volume_down(struct controller *controller, enum sc_action action) {
|
action_volume_down(struct sc_controller *controller, enum sc_action action) {
|
||||||
send_keycode(controller, AKEYCODE_VOLUME_DOWN, action, "VOLUME_DOWN");
|
send_keycode(controller, AKEYCODE_VOLUME_DOWN, action, "VOLUME_DOWN");
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
action_menu(struct controller *controller, enum sc_action action) {
|
action_menu(struct sc_controller *controller, enum sc_action action) {
|
||||||
send_keycode(controller, AKEYCODE_MENU, action, "MENU");
|
send_keycode(controller, AKEYCODE_MENU, action, "MENU");
|
||||||
}
|
}
|
||||||
|
|
||||||
// turn the screen on if it was off, press BACK otherwise
|
// turn the screen on if it was off, press BACK otherwise
|
||||||
// If the screen is off, it is turned on only on ACTION_DOWN
|
// If the screen is off, it is turned on only on ACTION_DOWN
|
||||||
static void
|
static void
|
||||||
press_back_or_turn_screen_on(struct controller *controller,
|
press_back_or_turn_screen_on(struct sc_controller *controller,
|
||||||
enum sc_action action) {
|
enum sc_action action) {
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
msg.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON;
|
msg.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON;
|
||||||
msg.back_or_screen_on.action = action == SC_ACTION_DOWN
|
msg.back_or_screen_on.action = action == SC_ACTION_DOWN
|
||||||
? AKEY_EVENT_ACTION_DOWN
|
? AKEY_EVENT_ACTION_DOWN
|
||||||
: AKEY_EVENT_ACTION_UP;
|
: AKEY_EVENT_ACTION_UP;
|
||||||
|
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!sc_controller_push_msg(controller, &msg)) {
|
||||||
LOGW("Could not request 'press back or turn screen on'");
|
LOGW("Could not request 'press back or turn screen on'");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
expand_notification_panel(struct controller *controller) {
|
expand_notification_panel(struct sc_controller *controller) {
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
msg.type = CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL;
|
msg.type = CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL;
|
||||||
|
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!sc_controller_push_msg(controller, &msg)) {
|
||||||
LOGW("Could not request 'expand notification panel'");
|
LOGW("Could not request 'expand notification panel'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
expand_settings_panel(struct controller *controller) {
|
expand_settings_panel(struct sc_controller *controller) {
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
msg.type = CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL;
|
msg.type = CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL;
|
||||||
|
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!sc_controller_push_msg(controller, &msg)) {
|
||||||
LOGW("Could not request 'expand settings panel'");
|
LOGW("Could not request 'expand settings panel'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
collapse_panels(struct controller *controller) {
|
collapse_panels(struct sc_controller *controller) {
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
msg.type = CONTROL_MSG_TYPE_COLLAPSE_PANELS;
|
msg.type = CONTROL_MSG_TYPE_COLLAPSE_PANELS;
|
||||||
|
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!sc_controller_push_msg(controller, &msg)) {
|
||||||
LOGW("Could not request 'collapse notification panel'");
|
LOGW("Could not request 'collapse notification panel'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
get_device_clipboard(struct controller *controller,
|
get_device_clipboard(struct sc_controller *controller,
|
||||||
enum get_clipboard_copy_key copy_key) {
|
enum get_clipboard_copy_key copy_key) {
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
msg.type = CONTROL_MSG_TYPE_GET_CLIPBOARD;
|
msg.type = CONTROL_MSG_TYPE_GET_CLIPBOARD;
|
||||||
msg.get_clipboard.copy_key = copy_key;
|
msg.get_clipboard.copy_key = copy_key;
|
||||||
|
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!sc_controller_push_msg(controller, &msg)) {
|
||||||
LOGW("Could not request 'get device clipboard'");
|
LOGW("Could not request 'get device clipboard'");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -273,7 +271,7 @@ get_device_clipboard(struct controller *controller,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
set_device_clipboard(struct controller *controller, bool paste,
|
set_device_clipboard(struct sc_controller *controller, bool paste,
|
||||||
uint64_t sequence) {
|
uint64_t sequence) {
|
||||||
char *text = SDL_GetClipboardText();
|
char *text = SDL_GetClipboardText();
|
||||||
if (!text) {
|
if (!text) {
|
||||||
@@ -288,13 +286,13 @@ set_device_clipboard(struct controller *controller, bool paste,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
msg.type = CONTROL_MSG_TYPE_SET_CLIPBOARD;
|
msg.type = CONTROL_MSG_TYPE_SET_CLIPBOARD;
|
||||||
msg.set_clipboard.sequence = sequence;
|
msg.set_clipboard.sequence = sequence;
|
||||||
msg.set_clipboard.text = text_dup;
|
msg.set_clipboard.text = text_dup;
|
||||||
msg.set_clipboard.paste = paste;
|
msg.set_clipboard.paste = paste;
|
||||||
|
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!sc_controller_push_msg(controller, &msg)) {
|
||||||
free(text_dup);
|
free(text_dup);
|
||||||
LOGW("Could not request 'set device clipboard'");
|
LOGW("Could not request 'set device clipboard'");
|
||||||
return false;
|
return false;
|
||||||
@@ -304,13 +302,13 @@ set_device_clipboard(struct controller *controller, bool paste,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
set_screen_power_mode(struct controller *controller,
|
set_screen_power_mode(struct sc_controller *controller,
|
||||||
enum screen_power_mode mode) {
|
enum screen_power_mode mode) {
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
msg.type = CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE;
|
msg.type = CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE;
|
||||||
msg.set_screen_power_mode.mode = mode;
|
msg.set_screen_power_mode.mode = mode;
|
||||||
|
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!sc_controller_push_msg(controller, &msg)) {
|
||||||
LOGW("Could not request 'set screen power mode'");
|
LOGW("Could not request 'set screen power mode'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -332,7 +330,7 @@ switch_fps_counter_state(struct fps_counter *fps_counter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
clipboard_paste(struct controller *controller) {
|
clipboard_paste(struct sc_controller *controller) {
|
||||||
char *text = SDL_GetClipboardText();
|
char *text = SDL_GetClipboardText();
|
||||||
if (!text) {
|
if (!text) {
|
||||||
LOGW("Could not get clipboard text: %s", SDL_GetError());
|
LOGW("Could not get clipboard text: %s", SDL_GetError());
|
||||||
@@ -351,40 +349,40 @@ clipboard_paste(struct controller *controller) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
|
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
|
||||||
msg.inject_text.text = text_dup;
|
msg.inject_text.text = text_dup;
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!sc_controller_push_msg(controller, &msg)) {
|
||||||
free(text_dup);
|
free(text_dup);
|
||||||
LOGW("Could not request 'paste clipboard'");
|
LOGW("Could not request 'paste clipboard'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rotate_device(struct controller *controller) {
|
rotate_device(struct sc_controller *controller) {
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
msg.type = CONTROL_MSG_TYPE_ROTATE_DEVICE;
|
msg.type = CONTROL_MSG_TYPE_ROTATE_DEVICE;
|
||||||
|
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!sc_controller_push_msg(controller, &msg)) {
|
||||||
LOGW("Could not request device rotation");
|
LOGW("Could not request device rotation");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rotate_client_left(struct screen *screen) {
|
rotate_client_left(struct sc_screen *screen) {
|
||||||
unsigned new_rotation = (screen->rotation + 1) % 4;
|
unsigned new_rotation = (screen->rotation + 1) % 4;
|
||||||
screen_set_rotation(screen, new_rotation);
|
sc_screen_set_rotation(screen, new_rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rotate_client_right(struct screen *screen) {
|
rotate_client_right(struct sc_screen *screen) {
|
||||||
unsigned new_rotation = (screen->rotation + 3) % 4;
|
unsigned new_rotation = (screen->rotation + 3) % 4;
|
||||||
screen_set_rotation(screen, new_rotation);
|
sc_screen_set_rotation(screen, new_rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
input_manager_process_text_input(struct input_manager *im,
|
sc_input_manager_process_text_input(struct sc_input_manager *im,
|
||||||
const SDL_TextInputEvent *event) {
|
const SDL_TextInputEvent *event) {
|
||||||
if (!im->kp->ops->process_text) {
|
if (!im->kp->ops->process_text) {
|
||||||
// The key processor does not support text input
|
// The key processor does not support text input
|
||||||
return;
|
return;
|
||||||
@@ -403,12 +401,12 @@ input_manager_process_text_input(struct input_manager *im,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
simulate_virtual_finger(struct input_manager *im,
|
simulate_virtual_finger(struct sc_input_manager *im,
|
||||||
enum android_motionevent_action action,
|
enum android_motionevent_action action,
|
||||||
struct sc_point point) {
|
struct sc_point point) {
|
||||||
bool up = action == AMOTION_EVENT_ACTION_UP;
|
bool up = action == AMOTION_EVENT_ACTION_UP;
|
||||||
|
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
msg.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
msg.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
||||||
msg.inject_touch_event.action = action;
|
msg.inject_touch_event.action = action;
|
||||||
msg.inject_touch_event.position.screen_size = im->screen->frame_size;
|
msg.inject_touch_event.position.screen_size = im->screen->frame_size;
|
||||||
@@ -417,7 +415,7 @@ simulate_virtual_finger(struct input_manager *im,
|
|||||||
msg.inject_touch_event.pressure = up ? 0.0f : 1.0f;
|
msg.inject_touch_event.pressure = up ? 0.0f : 1.0f;
|
||||||
msg.inject_touch_event.buttons = 0;
|
msg.inject_touch_event.buttons = 0;
|
||||||
|
|
||||||
if (!controller_push_msg(im->controller, &msg)) {
|
if (!sc_controller_push_msg(im->controller, &msg)) {
|
||||||
LOGW("Could not request 'inject virtual finger event'");
|
LOGW("Could not request 'inject virtual finger event'");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -433,12 +431,10 @@ inverse_point(struct sc_point point, struct sc_size size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
input_manager_process_key(struct input_manager *im,
|
sc_input_manager_process_key(struct sc_input_manager *im,
|
||||||
const SDL_KeyboardEvent *event) {
|
const SDL_KeyboardEvent *event) {
|
||||||
// control: indicates the state of the command-line option --no-control
|
// controller is NULL if --no-control is requested
|
||||||
bool control = im->control;
|
struct sc_controller *controller = im->controller;
|
||||||
|
|
||||||
struct controller *controller = im->controller;
|
|
||||||
|
|
||||||
SDL_Keycode keycode = event->keysym.sym;
|
SDL_Keycode keycode = event->keysym.sym;
|
||||||
uint16_t mod = event->keysym.mod;
|
uint16_t mod = event->keysym.mod;
|
||||||
@@ -464,33 +460,33 @@ input_manager_process_key(struct input_manager *im,
|
|||||||
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
|
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
|
||||||
switch (keycode) {
|
switch (keycode) {
|
||||||
case SDLK_h:
|
case SDLK_h:
|
||||||
if (control && !shift && !repeat) {
|
if (controller && !shift && !repeat) {
|
||||||
action_home(controller, action);
|
action_home(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_b: // fall-through
|
case SDLK_b: // fall-through
|
||||||
case SDLK_BACKSPACE:
|
case SDLK_BACKSPACE:
|
||||||
if (control && !shift && !repeat) {
|
if (controller && !shift && !repeat) {
|
||||||
action_back(controller, action);
|
action_back(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_s:
|
case SDLK_s:
|
||||||
if (control && !shift && !repeat) {
|
if (controller && !shift && !repeat) {
|
||||||
action_app_switch(controller, action);
|
action_app_switch(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_m:
|
case SDLK_m:
|
||||||
if (control && !shift && !repeat) {
|
if (controller && !shift && !repeat) {
|
||||||
action_menu(controller, action);
|
action_menu(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_p:
|
case SDLK_p:
|
||||||
if (control && !shift && !repeat) {
|
if (controller && !shift && !repeat) {
|
||||||
action_power(controller, action);
|
action_power(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_o:
|
case SDLK_o:
|
||||||
if (control && !repeat && down) {
|
if (controller && !repeat && down) {
|
||||||
enum screen_power_mode mode = shift
|
enum screen_power_mode mode = shift
|
||||||
? SCREEN_POWER_MODE_NORMAL
|
? SCREEN_POWER_MODE_NORMAL
|
||||||
: SCREEN_POWER_MODE_OFF;
|
: SCREEN_POWER_MODE_OFF;
|
||||||
@@ -498,13 +494,13 @@ input_manager_process_key(struct input_manager *im,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_DOWN:
|
case SDLK_DOWN:
|
||||||
if (control && !shift) {
|
if (controller && !shift) {
|
||||||
// forward repeated events
|
// forward repeated events
|
||||||
action_volume_down(controller, action);
|
action_volume_down(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_UP:
|
case SDLK_UP:
|
||||||
if (control && !shift) {
|
if (controller && !shift) {
|
||||||
// forward repeated events
|
// forward repeated events
|
||||||
action_volume_up(controller, action);
|
action_volume_up(controller, action);
|
||||||
}
|
}
|
||||||
@@ -520,19 +516,19 @@ input_manager_process_key(struct input_manager *im,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_c:
|
case SDLK_c:
|
||||||
if (control && !shift && !repeat && down) {
|
if (controller && !shift && !repeat && down) {
|
||||||
get_device_clipboard(controller,
|
get_device_clipboard(controller,
|
||||||
GET_CLIPBOARD_COPY_KEY_COPY);
|
GET_CLIPBOARD_COPY_KEY_COPY);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_x:
|
case SDLK_x:
|
||||||
if (control && !shift && !repeat && down) {
|
if (controller && !shift && !repeat && down) {
|
||||||
get_device_clipboard(controller,
|
get_device_clipboard(controller,
|
||||||
GET_CLIPBOARD_COPY_KEY_CUT);
|
GET_CLIPBOARD_COPY_KEY_CUT);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_v:
|
case SDLK_v:
|
||||||
if (control && !repeat && down) {
|
if (controller && !repeat && down) {
|
||||||
if (shift || im->legacy_paste) {
|
if (shift || im->legacy_paste) {
|
||||||
// inject the text as input events
|
// inject the text as input events
|
||||||
clipboard_paste(controller);
|
clipboard_paste(controller);
|
||||||
@@ -546,17 +542,17 @@ input_manager_process_key(struct input_manager *im,
|
|||||||
return;
|
return;
|
||||||
case SDLK_f:
|
case SDLK_f:
|
||||||
if (!shift && !repeat && down) {
|
if (!shift && !repeat && down) {
|
||||||
screen_switch_fullscreen(im->screen);
|
sc_screen_switch_fullscreen(im->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_w:
|
case SDLK_w:
|
||||||
if (!shift && !repeat && down) {
|
if (!shift && !repeat && down) {
|
||||||
screen_resize_to_fit(im->screen);
|
sc_screen_resize_to_fit(im->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_g:
|
case SDLK_g:
|
||||||
if (!shift && !repeat && down) {
|
if (!shift && !repeat && down) {
|
||||||
screen_resize_to_pixel_perfect(im->screen);
|
sc_screen_resize_to_pixel_perfect(im->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_i:
|
case SDLK_i:
|
||||||
@@ -565,7 +561,7 @@ input_manager_process_key(struct input_manager *im,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_n:
|
case SDLK_n:
|
||||||
if (control && !repeat && down) {
|
if (controller && !repeat && down) {
|
||||||
if (shift) {
|
if (shift) {
|
||||||
collapse_panels(controller);
|
collapse_panels(controller);
|
||||||
} else if (im->key_repeat == 0) {
|
} else if (im->key_repeat == 0) {
|
||||||
@@ -576,7 +572,7 @@ input_manager_process_key(struct input_manager *im,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_r:
|
case SDLK_r:
|
||||||
if (control && !shift && !repeat && down) {
|
if (controller && !shift && !repeat && down) {
|
||||||
rotate_device(controller);
|
rotate_device(controller);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -585,7 +581,7 @@ input_manager_process_key(struct input_manager *im,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!control) {
|
if (!controller) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -631,17 +627,9 @@ input_manager_process_key(struct input_manager *im,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
input_manager_process_mouse_motion(struct input_manager *im,
|
sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
|
||||||
const SDL_MouseMotionEvent *event) {
|
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) {
|
if (event->which == SDL_TOUCH_MOUSEID) {
|
||||||
// simulated from touch events, so it's a duplicate
|
// simulated from touch events, so it's a duplicate
|
||||||
return;
|
return;
|
||||||
@@ -650,8 +638,9 @@ input_manager_process_mouse_motion(struct input_manager *im,
|
|||||||
struct sc_mouse_motion_event evt = {
|
struct sc_mouse_motion_event evt = {
|
||||||
.position = {
|
.position = {
|
||||||
.screen_size = im->screen->frame_size,
|
.screen_size = im->screen->frame_size,
|
||||||
.point = screen_convert_window_to_frame_coords(im->screen,
|
.point = sc_screen_convert_window_to_frame_coords(im->screen,
|
||||||
event->x, event->y),
|
event->x,
|
||||||
|
event->y),
|
||||||
},
|
},
|
||||||
.xrel = event->xrel,
|
.xrel = event->xrel,
|
||||||
.yrel = event->yrel,
|
.yrel = event->yrel,
|
||||||
@@ -663,18 +652,22 @@ input_manager_process_mouse_motion(struct input_manager *im,
|
|||||||
assert(im->mp->ops->process_mouse_motion);
|
assert(im->mp->ops->process_mouse_motion);
|
||||||
im->mp->ops->process_mouse_motion(im->mp, &evt);
|
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);
|
||||||
|
|
||||||
if (im->vfinger_down) {
|
if (im->vfinger_down) {
|
||||||
|
assert(!im->mp->relative_mode); // assert one more time
|
||||||
struct sc_point mouse =
|
struct sc_point mouse =
|
||||||
screen_convert_window_to_frame_coords(im->screen, event->x,
|
sc_screen_convert_window_to_frame_coords(im->screen, event->x,
|
||||||
event->y);
|
event->y);
|
||||||
struct sc_point vfinger = inverse_point(mouse, im->screen->frame_size);
|
struct sc_point vfinger = inverse_point(mouse, im->screen->frame_size);
|
||||||
simulate_virtual_finger(im, AMOTION_EVENT_ACTION_MOVE, vfinger);
|
simulate_virtual_finger(im, AMOTION_EVENT_ACTION_MOVE, vfinger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
input_manager_process_touch(struct input_manager *im,
|
sc_input_manager_process_touch(struct sc_input_manager *im,
|
||||||
const SDL_TouchFingerEvent *event) {
|
const SDL_TouchFingerEvent *event) {
|
||||||
if (!im->mp->ops->process_touch) {
|
if (!im->mp->ops->process_touch) {
|
||||||
// The mouse processor does not support touch events
|
// The mouse processor does not support touch events
|
||||||
return;
|
return;
|
||||||
@@ -691,7 +684,8 @@ input_manager_process_touch(struct input_manager *im,
|
|||||||
struct sc_touch_event evt = {
|
struct sc_touch_event evt = {
|
||||||
.position = {
|
.position = {
|
||||||
.screen_size = im->screen->frame_size,
|
.screen_size = im->screen->frame_size,
|
||||||
.point = screen_convert_drawable_to_frame_coords(im->screen, x, y),
|
.point =
|
||||||
|
sc_screen_convert_drawable_to_frame_coords(im->screen, x, y),
|
||||||
},
|
},
|
||||||
.action = sc_touch_action_from_sdl(event->type),
|
.action = sc_touch_action_from_sdl(event->type),
|
||||||
.pointer_id = event->fingerId,
|
.pointer_id = event->fingerId,
|
||||||
@@ -702,9 +696,9 @@ input_manager_process_touch(struct input_manager *im,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
input_manager_process_mouse_button(struct input_manager *im,
|
sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
||||||
const SDL_MouseButtonEvent *event) {
|
const SDL_MouseButtonEvent *event) {
|
||||||
bool control = im->control;
|
struct sc_controller *controller = im->controller;
|
||||||
|
|
||||||
if (event->which == SDL_TOUCH_MOUSEID) {
|
if (event->which == SDL_TOUCH_MOUSEID) {
|
||||||
// simulated from touch events, so it's a duplicate
|
// simulated from touch events, so it's a duplicate
|
||||||
@@ -713,40 +707,42 @@ input_manager_process_mouse_button(struct input_manager *im,
|
|||||||
|
|
||||||
bool down = event->type == SDL_MOUSEBUTTONDOWN;
|
bool down = event->type == SDL_MOUSEBUTTONDOWN;
|
||||||
if (!im->forward_all_clicks) {
|
if (!im->forward_all_clicks) {
|
||||||
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
|
if (controller) {
|
||||||
|
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
|
||||||
|
|
||||||
if (control && event->button == SDL_BUTTON_X1) {
|
if (event->button == SDL_BUTTON_X1) {
|
||||||
action_app_switch(im->controller, action);
|
action_app_switch(controller, action);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (control && event->button == SDL_BUTTON_X2 && down) {
|
if (event->button == SDL_BUTTON_X2 && down) {
|
||||||
if (event->clicks < 2) {
|
if (event->clicks < 2) {
|
||||||
expand_notification_panel(im->controller);
|
expand_notification_panel(controller);
|
||||||
} else {
|
} else {
|
||||||
expand_settings_panel(im->controller);
|
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;
|
||||||
}
|
}
|
||||||
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
|
// double-click on black borders resize to fit the device screen
|
||||||
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
|
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
|
||||||
int32_t x = event->x;
|
int32_t x = event->x;
|
||||||
int32_t y = event->y;
|
int32_t y = event->y;
|
||||||
screen_hidpi_scale_coords(im->screen, &x, &y);
|
sc_screen_hidpi_scale_coords(im->screen, &x, &y);
|
||||||
SDL_Rect *r = &im->screen->rect;
|
SDL_Rect *r = &im->screen->rect;
|
||||||
bool outside = x < r->x || x >= r->x + r->w
|
bool outside = x < r->x || x >= r->x + r->w
|
||||||
|| y < r->y || y >= r->y + r->h;
|
|| y < r->y || y >= r->y + r->h;
|
||||||
if (outside) {
|
if (outside) {
|
||||||
if (down) {
|
if (down) {
|
||||||
screen_resize_to_fit(im->screen);
|
sc_screen_resize_to_fit(im->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -754,7 +750,7 @@ input_manager_process_mouse_button(struct input_manager *im,
|
|||||||
// otherwise, send the click event to the device
|
// otherwise, send the click event to the device
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!control) {
|
if (!controller) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -763,8 +759,9 @@ input_manager_process_mouse_button(struct input_manager *im,
|
|||||||
struct sc_mouse_click_event evt = {
|
struct sc_mouse_click_event evt = {
|
||||||
.position = {
|
.position = {
|
||||||
.screen_size = im->screen->frame_size,
|
.screen_size = im->screen->frame_size,
|
||||||
.point = screen_convert_window_to_frame_coords(im->screen, event->x,
|
.point = sc_screen_convert_window_to_frame_coords(im->screen,
|
||||||
event->y),
|
event->x,
|
||||||
|
event->y),
|
||||||
},
|
},
|
||||||
.action = sc_action_from_sdl_mousebutton_type(event->type),
|
.action = sc_action_from_sdl_mousebutton_type(event->type),
|
||||||
.button = sc_mouse_button_from_sdl(event->button),
|
.button = sc_mouse_button_from_sdl(event->button),
|
||||||
@@ -792,11 +789,12 @@ input_manager_process_mouse_button(struct input_manager *im,
|
|||||||
// In other words, the center of the rotation/scaling is the center of the
|
// In other words, the center of the rotation/scaling is the center of the
|
||||||
// screen.
|
// screen.
|
||||||
#define CTRL_PRESSED (SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL))
|
#define CTRL_PRESSED (SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL))
|
||||||
if ((down && !im->vfinger_down && CTRL_PRESSED)
|
if (event->button == SDL_BUTTON_LEFT &&
|
||||||
|| (!down && im->vfinger_down)) {
|
((down && !im->vfinger_down && CTRL_PRESSED) ||
|
||||||
|
(!down && im->vfinger_down))) {
|
||||||
struct sc_point mouse =
|
struct sc_point mouse =
|
||||||
screen_convert_window_to_frame_coords(im->screen, event->x,
|
sc_screen_convert_window_to_frame_coords(im->screen, event->x,
|
||||||
event->y);
|
event->y);
|
||||||
struct sc_point vfinger = inverse_point(mouse, im->screen->frame_size);
|
struct sc_point vfinger = inverse_point(mouse, im->screen->frame_size);
|
||||||
enum android_motionevent_action action = down
|
enum android_motionevent_action action = down
|
||||||
? AMOTION_EVENT_ACTION_DOWN
|
? AMOTION_EVENT_ACTION_DOWN
|
||||||
@@ -809,8 +807,8 @@ input_manager_process_mouse_button(struct input_manager *im,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
input_manager_process_mouse_wheel(struct input_manager *im,
|
sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
|
||||||
const SDL_MouseWheelEvent *event) {
|
const SDL_MouseWheelEvent *event) {
|
||||||
if (!im->mp->ops->process_mouse_scroll) {
|
if (!im->mp->ops->process_mouse_scroll) {
|
||||||
// The mouse processor does not support scroll events
|
// The mouse processor does not support scroll events
|
||||||
return;
|
return;
|
||||||
@@ -824,8 +822,8 @@ input_manager_process_mouse_wheel(struct input_manager *im,
|
|||||||
struct sc_mouse_scroll_event evt = {
|
struct sc_mouse_scroll_event evt = {
|
||||||
.position = {
|
.position = {
|
||||||
.screen_size = im->screen->frame_size,
|
.screen_size = im->screen->frame_size,
|
||||||
.point = screen_convert_window_to_frame_coords(im->screen,
|
.point = sc_screen_convert_window_to_frame_coords(im->screen,
|
||||||
mouse_x, mouse_y),
|
mouse_x, mouse_y),
|
||||||
},
|
},
|
||||||
.hscroll = event->x,
|
.hscroll = event->x,
|
||||||
.vscroll = event->y,
|
.vscroll = event->y,
|
||||||
@@ -836,45 +834,81 @@ input_manager_process_mouse_wheel(struct input_manager *im,
|
|||||||
im->mp->ops->process_mouse_scroll(im->mp, &evt);
|
im->mp->ops->process_mouse_scroll(im->mp, &evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
static bool
|
||||||
input_manager_handle_event(struct input_manager *im, SDL_Event *event) {
|
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;
|
||||||
switch (event->type) {
|
switch (event->type) {
|
||||||
case SDL_TEXTINPUT:
|
case SDL_TEXTINPUT:
|
||||||
if (!im->control) {
|
if (!control) {
|
||||||
return true;
|
break;
|
||||||
}
|
}
|
||||||
input_manager_process_text_input(im, &event->text);
|
sc_input_manager_process_text_input(im, &event->text);
|
||||||
return true;
|
break;
|
||||||
case SDL_KEYDOWN:
|
case SDL_KEYDOWN:
|
||||||
case SDL_KEYUP:
|
case SDL_KEYUP:
|
||||||
// some key events do not interact with the device, so process the
|
// some key events do not interact with the device, so process the
|
||||||
// event even if control is disabled
|
// event even if control is disabled
|
||||||
input_manager_process_key(im, &event->key);
|
sc_input_manager_process_key(im, &event->key);
|
||||||
return true;
|
break;
|
||||||
case SDL_MOUSEMOTION:
|
case SDL_MOUSEMOTION:
|
||||||
if (!im->control) {
|
if (!control) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
input_manager_process_mouse_motion(im, &event->motion);
|
sc_input_manager_process_mouse_motion(im, &event->motion);
|
||||||
return true;
|
break;
|
||||||
case SDL_MOUSEWHEEL:
|
case SDL_MOUSEWHEEL:
|
||||||
if (!im->control) {
|
if (!control) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
input_manager_process_mouse_wheel(im, &event->wheel);
|
sc_input_manager_process_mouse_wheel(im, &event->wheel);
|
||||||
return true;
|
break;
|
||||||
case SDL_MOUSEBUTTONDOWN:
|
case SDL_MOUSEBUTTONDOWN:
|
||||||
case SDL_MOUSEBUTTONUP:
|
case SDL_MOUSEBUTTONUP:
|
||||||
// some mouse events do not interact with the device, so process
|
// some mouse events do not interact with the device, so process
|
||||||
// the event even if control is disabled
|
// the event even if control is disabled
|
||||||
input_manager_process_mouse_button(im, &event->button);
|
sc_input_manager_process_mouse_button(im, &event->button);
|
||||||
return true;
|
break;
|
||||||
case SDL_FINGERMOTION:
|
case SDL_FINGERMOTION:
|
||||||
case SDL_FINGERDOWN:
|
case SDL_FINGERDOWN:
|
||||||
case SDL_FINGERUP:
|
case SDL_FINGERUP:
|
||||||
input_manager_process_touch(im, &event->tfinger);
|
if (!control) {
|
||||||
return true;
|
break;
|
||||||
|
}
|
||||||
|
sc_input_manager_process_touch(im, &event->tfinger);
|
||||||
|
break;
|
||||||
|
case SDL_DROPFILE: {
|
||||||
|
if (!control) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sc_input_manager_process_file(im, &event->drop);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,21 +8,20 @@
|
|||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
|
#include "file_pusher.h"
|
||||||
#include "fps_counter.h"
|
#include "fps_counter.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "trait/key_processor.h"
|
#include "trait/key_processor.h"
|
||||||
#include "trait/mouse_processor.h"
|
#include "trait/mouse_processor.h"
|
||||||
|
|
||||||
struct screen; // forward declaration to avoid cycle
|
struct sc_input_manager {
|
||||||
|
struct sc_controller *controller;
|
||||||
struct input_manager {
|
struct sc_file_pusher *fp;
|
||||||
struct controller *controller;
|
struct sc_screen *screen;
|
||||||
struct screen *screen;
|
|
||||||
|
|
||||||
struct sc_key_processor *kp;
|
struct sc_key_processor *kp;
|
||||||
struct sc_mouse_processor *mp;
|
struct sc_mouse_processor *mp;
|
||||||
|
|
||||||
bool control;
|
|
||||||
bool forward_all_clicks;
|
bool forward_all_clicks;
|
||||||
bool legacy_paste;
|
bool legacy_paste;
|
||||||
bool clipboard_autosync;
|
bool clipboard_autosync;
|
||||||
@@ -44,13 +43,13 @@ struct input_manager {
|
|||||||
uint64_t next_sequence; // used for request acknowledgements
|
uint64_t next_sequence; // used for request acknowledgements
|
||||||
};
|
};
|
||||||
|
|
||||||
struct input_manager_params {
|
struct sc_input_manager_params {
|
||||||
struct controller *controller;
|
struct sc_controller *controller;
|
||||||
struct screen *screen;
|
struct sc_file_pusher *fp;
|
||||||
|
struct sc_screen *screen;
|
||||||
struct sc_key_processor *kp;
|
struct sc_key_processor *kp;
|
||||||
struct sc_mouse_processor *mp;
|
struct sc_mouse_processor *mp;
|
||||||
|
|
||||||
bool control;
|
|
||||||
bool forward_all_clicks;
|
bool forward_all_clicks;
|
||||||
bool legacy_paste;
|
bool legacy_paste;
|
||||||
bool clipboard_autosync;
|
bool clipboard_autosync;
|
||||||
@@ -58,10 +57,10 @@ struct input_manager_params {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
input_manager_init(struct input_manager *im,
|
sc_input_manager_init(struct sc_input_manager *im,
|
||||||
const struct input_manager_params *params);
|
const struct sc_input_manager_params *params);
|
||||||
|
|
||||||
bool
|
void
|
||||||
input_manager_handle_event(struct input_manager *im, SDL_Event *event);
|
sc_input_manager_handle_event(struct sc_input_manager *im, SDL_Event *event);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ convert_meta_state(uint16_t mod) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
convert_input_key(const struct sc_key_event *event, struct control_msg *msg,
|
convert_input_key(const struct sc_key_event *event, struct sc_control_msg *msg,
|
||||||
enum sc_key_inject_mode key_inject_mode, uint32_t repeat) {
|
enum sc_key_inject_mode key_inject_mode, uint32_t repeat) {
|
||||||
msg->type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
msg->type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
||||||
|
|
||||||
@@ -282,9 +282,9 @@ sc_key_processor_process_key(struct sc_key_processor *kp,
|
|||||||
ki->repeat = 0;
|
ki->repeat = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
if (convert_input_key(event, &msg, ki->key_inject_mode, ki->repeat)) {
|
if (convert_input_key(event, &msg, ki->key_inject_mode, ki->repeat)) {
|
||||||
if (!controller_push_msg(ki->controller, &msg)) {
|
if (!sc_controller_push_msg(ki->controller, &msg)) {
|
||||||
LOGW("Could not request 'inject keycode'");
|
LOGW("Could not request 'inject keycode'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -309,14 +309,14 @@ sc_key_processor_process_text(struct sc_key_processor *kp,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
|
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
|
||||||
msg.inject_text.text = strdup(event->text);
|
msg.inject_text.text = strdup(event->text);
|
||||||
if (!msg.inject_text.text) {
|
if (!msg.inject_text.text) {
|
||||||
LOGW("Could not strdup input text");
|
LOGW("Could not strdup input text");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!controller_push_msg(ki->controller, &msg)) {
|
if (!sc_controller_push_msg(ki->controller, &msg)) {
|
||||||
free(msg.inject_text.text);
|
free(msg.inject_text.text);
|
||||||
LOGW("Could not request 'inject text'");
|
LOGW("Could not request 'inject text'");
|
||||||
}
|
}
|
||||||
@@ -324,11 +324,12 @@ sc_key_processor_process_text(struct sc_key_processor *kp,
|
|||||||
|
|
||||||
void
|
void
|
||||||
sc_keyboard_inject_init(struct sc_keyboard_inject *ki,
|
sc_keyboard_inject_init(struct sc_keyboard_inject *ki,
|
||||||
struct controller *controller,
|
struct sc_controller *controller,
|
||||||
const struct scrcpy_options *options) {
|
enum sc_key_inject_mode key_inject_mode,
|
||||||
|
bool forward_key_repeat) {
|
||||||
ki->controller = controller;
|
ki->controller = controller;
|
||||||
ki->key_inject_mode = options->key_inject_mode;
|
ki->key_inject_mode = key_inject_mode;
|
||||||
ki->forward_key_repeat = options->forward_key_repeat;
|
ki->forward_key_repeat = forward_key_repeat;
|
||||||
|
|
||||||
ki->repeat = 0;
|
ki->repeat = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
struct sc_keyboard_inject {
|
struct sc_keyboard_inject {
|
||||||
struct sc_key_processor key_processor; // key processor trait
|
struct sc_key_processor key_processor; // key processor trait
|
||||||
|
|
||||||
struct controller *controller;
|
struct sc_controller *controller;
|
||||||
|
|
||||||
// SDL reports repeated events as a boolean, but Android expects the actual
|
// SDL reports repeated events as a boolean, but Android expects the actual
|
||||||
// number of repetitions. This variable keeps track of the count.
|
// number of repetitions. This variable keeps track of the count.
|
||||||
@@ -24,7 +24,8 @@ struct sc_keyboard_inject {
|
|||||||
|
|
||||||
void
|
void
|
||||||
sc_keyboard_inject_init(struct sc_keyboard_inject *ki,
|
sc_keyboard_inject_init(struct sc_keyboard_inject *ki,
|
||||||
struct controller *controller,
|
struct sc_controller *controller,
|
||||||
const struct scrcpy_options *options);
|
enum sc_key_inject_mode key_inject_mode,
|
||||||
|
bool forward_key_repeat);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -58,9 +58,14 @@ convert_touch_action(enum sc_touch_action action) {
|
|||||||
static void
|
static void
|
||||||
sc_mouse_processor_process_mouse_motion(struct sc_mouse_processor *mp,
|
sc_mouse_processor_process_mouse_motion(struct sc_mouse_processor *mp,
|
||||||
const struct sc_mouse_motion_event *event) {
|
const struct sc_mouse_motion_event *event) {
|
||||||
|
if (!event->buttons_state) {
|
||||||
|
// Do not send motion events when no click is pressed
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
||||||
|
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||||
.inject_touch_event = {
|
.inject_touch_event = {
|
||||||
.action = AMOTION_EVENT_ACTION_MOVE,
|
.action = AMOTION_EVENT_ACTION_MOVE,
|
||||||
@@ -71,7 +76,7 @@ sc_mouse_processor_process_mouse_motion(struct sc_mouse_processor *mp,
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!controller_push_msg(mi->controller, &msg)) {
|
if (!sc_controller_push_msg(mi->controller, &msg)) {
|
||||||
LOGW("Could not request 'inject mouse motion event'");
|
LOGW("Could not request 'inject mouse motion event'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +86,7 @@ sc_mouse_processor_process_mouse_click(struct sc_mouse_processor *mp,
|
|||||||
const struct sc_mouse_click_event *event) {
|
const struct sc_mouse_click_event *event) {
|
||||||
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
||||||
|
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||||
.inject_touch_event = {
|
.inject_touch_event = {
|
||||||
.action = convert_mouse_action(event->action),
|
.action = convert_mouse_action(event->action),
|
||||||
@@ -92,7 +97,7 @@ sc_mouse_processor_process_mouse_click(struct sc_mouse_processor *mp,
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!controller_push_msg(mi->controller, &msg)) {
|
if (!sc_controller_push_msg(mi->controller, &msg)) {
|
||||||
LOGW("Could not request 'inject mouse click event'");
|
LOGW("Could not request 'inject mouse click event'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,7 +107,7 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp,
|
|||||||
const struct sc_mouse_scroll_event *event) {
|
const struct sc_mouse_scroll_event *event) {
|
||||||
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
||||||
|
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
.type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
||||||
.inject_scroll_event = {
|
.inject_scroll_event = {
|
||||||
.position = event->position,
|
.position = event->position,
|
||||||
@@ -112,7 +117,7 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp,
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!controller_push_msg(mi->controller, &msg)) {
|
if (!sc_controller_push_msg(mi->controller, &msg)) {
|
||||||
LOGW("Could not request 'inject mouse scroll event'");
|
LOGW("Could not request 'inject mouse scroll event'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,7 +127,7 @@ sc_mouse_processor_process_touch(struct sc_mouse_processor *mp,
|
|||||||
const struct sc_touch_event *event) {
|
const struct sc_touch_event *event) {
|
||||||
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
||||||
|
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||||
.inject_touch_event = {
|
.inject_touch_event = {
|
||||||
.action = convert_touch_action(event->action),
|
.action = convert_touch_action(event->action),
|
||||||
@@ -133,14 +138,14 @@ sc_mouse_processor_process_touch(struct sc_mouse_processor *mp,
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!controller_push_msg(mi->controller, &msg)) {
|
if (!sc_controller_push_msg(mi->controller, &msg)) {
|
||||||
LOGW("Could not request 'inject touch event'");
|
LOGW("Could not request 'inject touch event'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
sc_mouse_inject_init(struct sc_mouse_inject *mi,
|
sc_mouse_inject_init(struct sc_mouse_inject *mi,
|
||||||
struct controller *controller) {
|
struct sc_controller *controller) {
|
||||||
mi->controller = controller;
|
mi->controller = controller;
|
||||||
|
|
||||||
static const struct sc_mouse_processor_ops ops = {
|
static const struct sc_mouse_processor_ops ops = {
|
||||||
|
|||||||
@@ -12,10 +12,11 @@
|
|||||||
struct sc_mouse_inject {
|
struct sc_mouse_inject {
|
||||||
struct sc_mouse_processor mouse_processor; // mouse processor trait
|
struct sc_mouse_processor mouse_processor; // mouse processor trait
|
||||||
|
|
||||||
struct controller *controller;
|
struct sc_controller *controller;
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
sc_mouse_inject_init(struct sc_mouse_inject *mi, struct controller *controller);
|
sc_mouse_inject_init(struct sc_mouse_inject *mi,
|
||||||
|
struct sc_controller *controller);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ const struct scrcpy_options scrcpy_options_default = {
|
|||||||
.legacy_paste = false,
|
.legacy_paste = false,
|
||||||
.power_off_on_close = false,
|
.power_off_on_close = false,
|
||||||
.clipboard_autosync = true,
|
.clipboard_autosync = true,
|
||||||
|
.downsize_on_error = true,
|
||||||
.tcpip = false,
|
.tcpip = false,
|
||||||
.tcpip_dst = NULL,
|
.tcpip_dst = NULL,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ struct scrcpy_options {
|
|||||||
bool legacy_paste;
|
bool legacy_paste;
|
||||||
bool power_off_on_close;
|
bool power_off_on_close;
|
||||||
bool clipboard_autosync;
|
bool clipboard_autosync;
|
||||||
|
bool downsize_on_error;
|
||||||
bool tcpip;
|
bool tcpip;
|
||||||
const char *tcpip_dst;
|
const char *tcpip_dst;
|
||||||
};
|
};
|
||||||
|
|||||||
141
app/src/scrcpy.c
141
app/src/scrcpy.c
@@ -16,7 +16,7 @@
|
|||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
#include "decoder.h"
|
#include "decoder.h"
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
#include "file_handler.h"
|
#include "file_pusher.h"
|
||||||
#ifdef HAVE_AOA_HID
|
#ifdef HAVE_AOA_HID
|
||||||
# include "hid_keyboard.h"
|
# include "hid_keyboard.h"
|
||||||
# include "hid_mouse.h"
|
# include "hid_mouse.h"
|
||||||
@@ -36,15 +36,15 @@
|
|||||||
|
|
||||||
struct scrcpy {
|
struct scrcpy {
|
||||||
struct sc_server server;
|
struct sc_server server;
|
||||||
struct screen screen;
|
struct sc_screen screen;
|
||||||
struct stream stream;
|
struct stream stream;
|
||||||
struct decoder decoder;
|
struct decoder decoder;
|
||||||
struct recorder recorder;
|
struct recorder recorder;
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
struct sc_v4l2_sink v4l2_sink;
|
struct sc_v4l2_sink v4l2_sink;
|
||||||
#endif
|
#endif
|
||||||
struct controller controller;
|
struct sc_controller controller;
|
||||||
struct file_handler file_handler;
|
struct sc_file_pusher file_pusher;
|
||||||
#ifdef HAVE_AOA_HID
|
#ifdef HAVE_AOA_HID
|
||||||
struct sc_aoa aoa;
|
struct sc_aoa aoa;
|
||||||
// sequence/ack helper to synchronize clipboard and Ctrl+v via HID
|
// sequence/ack helper to synchronize clipboard and Ctrl+v via HID
|
||||||
@@ -149,68 +149,18 @@ sdl_configure(bool display, bool disable_screensaver) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_apk(const char *file) {
|
event_loop(struct scrcpy *s) {
|
||||||
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);
|
|
||||||
(void) consumed;
|
|
||||||
|
|
||||||
end:
|
|
||||||
return EVENT_RESULT_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
event_loop(struct scrcpy *s, const struct scrcpy_options *options) {
|
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
while (SDL_WaitEvent(&event)) {
|
while (SDL_WaitEvent(&event)) {
|
||||||
enum event_result result = handle_event(s, options, &event);
|
switch (event.type) {
|
||||||
switch (result) {
|
case EVENT_STREAM_STOPPED:
|
||||||
case EVENT_RESULT_STOPPED_BY_USER:
|
|
||||||
return true;
|
|
||||||
case EVENT_RESULT_STOPPED_BY_EOS:
|
|
||||||
LOGW("Device disconnected");
|
LOGW("Device disconnected");
|
||||||
return false;
|
return false;
|
||||||
case EVENT_RESULT_CONTINUE:
|
case SDL_QUIT:
|
||||||
|
LOGD("User requested to quit");
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
sc_screen_handle_event(&s->screen, &event);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,7 +277,7 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
bool server_started = false;
|
bool server_started = false;
|
||||||
bool file_handler_initialized = false;
|
bool file_pusher_initialized = false;
|
||||||
bool recorder_initialized = false;
|
bool recorder_initialized = false;
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
bool v4l2_sink_initialized = false;
|
bool v4l2_sink_initialized = false;
|
||||||
@@ -364,6 +314,7 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
.force_adb_forward = options->force_adb_forward,
|
.force_adb_forward = options->force_adb_forward,
|
||||||
.power_off_on_close = options->power_off_on_close,
|
.power_off_on_close = options->power_off_on_close,
|
||||||
.clipboard_autosync = options->clipboard_autosync,
|
.clipboard_autosync = options->clipboard_autosync,
|
||||||
|
.downsize_on_error = options->downsize_on_error,
|
||||||
.tcpip = options->tcpip,
|
.tcpip = options->tcpip,
|
||||||
.tcpip_dst = options->tcpip_dst,
|
.tcpip_dst = options->tcpip_dst,
|
||||||
};
|
};
|
||||||
@@ -406,12 +357,15 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
const char *serial = s->server.params.serial;
|
const char *serial = s->server.params.serial;
|
||||||
assert(serial);
|
assert(serial);
|
||||||
|
|
||||||
|
struct sc_file_pusher *fp = NULL;
|
||||||
|
|
||||||
if (options->display && options->control) {
|
if (options->display && options->control) {
|
||||||
if (!file_handler_init(&s->file_handler, serial,
|
if (!sc_file_pusher_init(&s->file_pusher, serial,
|
||||||
options->push_target)) {
|
options->push_target)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
file_handler_initialized = true;
|
fp = &s->file_pusher;
|
||||||
|
file_pusher_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct decoder *dec = NULL;
|
struct decoder *dec = NULL;
|
||||||
@@ -451,6 +405,7 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
stream_add_sink(&s->stream, &rec->packet_sink);
|
stream_add_sink(&s->stream, &rec->packet_sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct sc_controller *controller = NULL;
|
||||||
struct sc_key_processor *kp = NULL;
|
struct sc_key_processor *kp = NULL;
|
||||||
struct sc_mouse_processor *mp = NULL;
|
struct sc_mouse_processor *mp = NULL;
|
||||||
|
|
||||||
@@ -535,7 +490,8 @@ aoa_hid_end:
|
|||||||
// keyboard_input_mode may have been reset if HID mode failed
|
// keyboard_input_mode may have been reset if HID mode failed
|
||||||
if (options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_INJECT) {
|
if (options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_INJECT) {
|
||||||
sc_keyboard_inject_init(&s->keyboard_inject, &s->controller,
|
sc_keyboard_inject_init(&s->keyboard_inject, &s->controller,
|
||||||
options);
|
options->key_inject_mode,
|
||||||
|
options->forward_key_repeat);
|
||||||
kp = &s->keyboard_inject.key_processor;
|
kp = &s->keyboard_inject.key_processor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,38 +501,42 @@ aoa_hid_end:
|
|||||||
mp = &s->mouse_inject.mouse_processor;
|
mp = &s->mouse_inject.mouse_processor;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!controller_init(&s->controller, s->server.control_socket,
|
if (!sc_controller_init(&s->controller, s->server.control_socket,
|
||||||
acksync)) {
|
acksync)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
controller_initialized = true;
|
controller_initialized = true;
|
||||||
|
|
||||||
if (!controller_start(&s->controller)) {
|
if (!sc_controller_start(&s->controller)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
controller_started = true;
|
controller_started = true;
|
||||||
|
controller = &s->controller;
|
||||||
|
|
||||||
if (options->turn_screen_off) {
|
if (options->turn_screen_off) {
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
msg.type = CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE;
|
msg.type = CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE;
|
||||||
msg.set_screen_power_mode.mode = SCREEN_POWER_MODE_OFF;
|
msg.set_screen_power_mode.mode = SCREEN_POWER_MODE_OFF;
|
||||||
|
|
||||||
if (!controller_push_msg(&s->controller, &msg)) {
|
if (!sc_controller_push_msg(&s->controller, &msg)) {
|
||||||
LOGW("Could not request 'set screen power mode'");
|
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) {
|
if (options->display) {
|
||||||
const char *window_title =
|
const char *window_title =
|
||||||
options->window_title ? options->window_title : info->device_name;
|
options->window_title ? options->window_title : info->device_name;
|
||||||
|
|
||||||
struct screen_params screen_params = {
|
struct sc_screen_params screen_params = {
|
||||||
.controller = &s->controller,
|
.controller = controller,
|
||||||
|
.fp = fp,
|
||||||
.kp = kp,
|
.kp = kp,
|
||||||
.mp = mp,
|
.mp = mp,
|
||||||
.control = options->control,
|
|
||||||
.forward_all_clicks = options->forward_all_clicks,
|
.forward_all_clicks = options->forward_all_clicks,
|
||||||
.legacy_paste = options->legacy_paste,
|
.legacy_paste = options->legacy_paste,
|
||||||
.clipboard_autosync = options->clipboard_autosync,
|
.clipboard_autosync = options->clipboard_autosync,
|
||||||
@@ -595,7 +555,7 @@ aoa_hid_end:
|
|||||||
.buffering_time = options->display_buffer,
|
.buffering_time = options->display_buffer,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!screen_init(&s->screen, &screen_params)) {
|
if (!sc_screen_init(&s->screen, &screen_params)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
screen_initialized = true;
|
screen_initialized = true;
|
||||||
@@ -623,12 +583,12 @@ aoa_hid_end:
|
|||||||
}
|
}
|
||||||
stream_started = true;
|
stream_started = true;
|
||||||
|
|
||||||
ret = event_loop(s, options);
|
ret = event_loop(s);
|
||||||
LOGD("quit...");
|
LOGD("quit...");
|
||||||
|
|
||||||
// Close the window immediately on closing, because screen_destroy() may
|
// Close the window immediately on closing, because screen_destroy() may
|
||||||
// only be called once the stream thread is joined (it may take time)
|
// only be called once the stream thread is joined (it may take time)
|
||||||
screen_hide_window(&s->screen);
|
sc_screen_hide_window(&s->screen);
|
||||||
|
|
||||||
end:
|
end:
|
||||||
// The stream is not stopped explicitly, because it will stop by itself on
|
// The stream is not stopped explicitly, because it will stop by itself on
|
||||||
@@ -638,6 +598,9 @@ end:
|
|||||||
if (hid_keyboard_initialized) {
|
if (hid_keyboard_initialized) {
|
||||||
sc_hid_keyboard_destroy(&s->keyboard_hid);
|
sc_hid_keyboard_destroy(&s->keyboard_hid);
|
||||||
}
|
}
|
||||||
|
if (hid_mouse_initialized) {
|
||||||
|
sc_hid_mouse_destroy(&s->mouse_hid);
|
||||||
|
}
|
||||||
sc_aoa_stop(&s->aoa);
|
sc_aoa_stop(&s->aoa);
|
||||||
}
|
}
|
||||||
if (acksync) {
|
if (acksync) {
|
||||||
@@ -645,13 +608,13 @@ end:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (controller_started) {
|
if (controller_started) {
|
||||||
controller_stop(&s->controller);
|
sc_controller_stop(&s->controller);
|
||||||
}
|
}
|
||||||
if (file_handler_initialized) {
|
if (file_pusher_initialized) {
|
||||||
file_handler_stop(&s->file_handler);
|
sc_file_pusher_stop(&s->file_pusher);
|
||||||
}
|
}
|
||||||
if (screen_initialized) {
|
if (screen_initialized) {
|
||||||
screen_interrupt(&s->screen);
|
sc_screen_interrupt(&s->screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server_started) {
|
if (server_started) {
|
||||||
@@ -681,24 +644,24 @@ end:
|
|||||||
// Destroy the screen only after the stream is guaranteed to be finished,
|
// Destroy the screen only after the stream is guaranteed to be finished,
|
||||||
// because otherwise the screen could receive new frames after destruction
|
// because otherwise the screen could receive new frames after destruction
|
||||||
if (screen_initialized) {
|
if (screen_initialized) {
|
||||||
screen_join(&s->screen);
|
sc_screen_join(&s->screen);
|
||||||
screen_destroy(&s->screen);
|
sc_screen_destroy(&s->screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (controller_started) {
|
if (controller_started) {
|
||||||
controller_join(&s->controller);
|
sc_controller_join(&s->controller);
|
||||||
}
|
}
|
||||||
if (controller_initialized) {
|
if (controller_initialized) {
|
||||||
controller_destroy(&s->controller);
|
sc_controller_destroy(&s->controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recorder_initialized) {
|
if (recorder_initialized) {
|
||||||
recorder_destroy(&s->recorder);
|
recorder_destroy(&s->recorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_handler_initialized) {
|
if (file_pusher_initialized) {
|
||||||
file_handler_join(&s->file_handler);
|
sc_file_pusher_join(&s->file_pusher);
|
||||||
file_handler_destroy(&s->file_handler);
|
sc_file_pusher_destroy(&s->file_pusher);
|
||||||
}
|
}
|
||||||
|
|
||||||
sc_server_destroy(&s->server);
|
sc_server_destroy(&s->server);
|
||||||
|
|||||||
246
app/src/screen.c
246
app/src/screen.c
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
#define DISPLAY_MARGINS 96
|
#define DISPLAY_MARGINS 96
|
||||||
|
|
||||||
#define DOWNCAST(SINK) container_of(SINK, struct screen, frame_sink)
|
#define DOWNCAST(SINK) container_of(SINK, struct sc_screen, frame_sink)
|
||||||
|
|
||||||
static inline struct sc_size
|
static inline struct sc_size
|
||||||
get_rotated_size(struct sc_size size, int rotation) {
|
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
|
// get the window size in a struct sc_size
|
||||||
static struct sc_size
|
static struct sc_size
|
||||||
get_window_size(const struct screen *screen) {
|
get_window_size(const struct sc_screen *screen) {
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
SDL_GetWindowSize(screen->window, &width, &height);
|
SDL_GetWindowSize(screen->window, &width, &height);
|
||||||
@@ -41,7 +41,7 @@ get_window_size(const struct screen *screen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct sc_point
|
static struct sc_point
|
||||||
get_window_position(const struct screen *screen) {
|
get_window_position(const struct sc_screen *screen) {
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
SDL_GetWindowPosition(screen->window, &x, &y);
|
SDL_GetWindowPosition(screen->window, &x, &y);
|
||||||
@@ -54,7 +54,7 @@ get_window_position(const struct screen *screen) {
|
|||||||
|
|
||||||
// set the window size to be applied when fullscreen is disabled
|
// set the window size to be applied when fullscreen is disabled
|
||||||
static void
|
static void
|
||||||
set_window_size(struct screen *screen, struct sc_size new_size) {
|
set_window_size(struct sc_screen *screen, struct sc_size new_size) {
|
||||||
assert(!screen->fullscreen);
|
assert(!screen->fullscreen);
|
||||||
assert(!screen->maximized);
|
assert(!screen->maximized);
|
||||||
SDL_SetWindowSize(screen->window, new_size.width, new_size.height);
|
SDL_SetWindowSize(screen->window, new_size.width, new_size.height);
|
||||||
@@ -156,8 +156,14 @@ get_initial_optimal_size(struct sc_size content_size, uint16_t req_width,
|
|||||||
return window_size;
|
return window_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline bool
|
||||||
screen_capture_mouse(struct screen *screen, bool capture) {
|
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)) {
|
if (SDL_SetRelativeMouseMode(capture)) {
|
||||||
LOGE("Could not set relative mouse mode to %s: %s",
|
LOGE("Could not set relative mouse mode to %s: %s",
|
||||||
capture ? "true" : "false", SDL_GetError());
|
capture ? "true" : "false", SDL_GetError());
|
||||||
@@ -168,7 +174,7 @@ screen_capture_mouse(struct screen *screen, bool capture) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
screen_update_content_rect(struct screen *screen) {
|
sc_screen_update_content_rect(struct sc_screen *screen) {
|
||||||
int dw;
|
int dw;
|
||||||
int dh;
|
int dh;
|
||||||
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
|
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
|
||||||
@@ -205,7 +211,7 @@ screen_update_content_rect(struct screen *screen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline SDL_Texture *
|
static inline SDL_Texture *
|
||||||
create_texture(struct screen *screen) {
|
create_texture(struct sc_screen *screen) {
|
||||||
SDL_Renderer *renderer = screen->renderer;
|
SDL_Renderer *renderer = screen->renderer;
|
||||||
struct sc_size size = screen->frame_size;
|
struct sc_size size = screen->frame_size;
|
||||||
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12,
|
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12,
|
||||||
@@ -236,9 +242,9 @@ create_texture(struct screen *screen) {
|
|||||||
// Set the update_content_rect flag if the window or content size may have
|
// Set the update_content_rect flag if the window or content size may have
|
||||||
// changed, so that the content rectangle is recomputed
|
// changed, so that the content rectangle is recomputed
|
||||||
static void
|
static void
|
||||||
screen_render(struct screen *screen, bool update_content_rect) {
|
sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
|
||||||
if (update_content_rect) {
|
if (update_content_rect) {
|
||||||
screen_update_content_rect(screen);
|
sc_screen_update_content_rect(screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_RenderClear(screen->renderer);
|
SDL_RenderClear(screen->renderer);
|
||||||
@@ -282,20 +288,20 @@ screen_render(struct screen *screen, bool update_content_rect) {
|
|||||||
// <https://stackoverflow.com/a/40693139/1987178>
|
// <https://stackoverflow.com/a/40693139/1987178>
|
||||||
static int
|
static int
|
||||||
event_watcher(void *data, SDL_Event *event) {
|
event_watcher(void *data, SDL_Event *event) {
|
||||||
struct screen *screen = data;
|
struct sc_screen *screen = data;
|
||||||
if (event->type == SDL_WINDOWEVENT
|
if (event->type == SDL_WINDOWEVENT
|
||||||
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
|
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||||
// In practice, it seems to always be called from the same thread in
|
// In practice, it seems to always be called from the same thread in
|
||||||
// that specific case. Anyway, it's just a workaround.
|
// that specific case. Anyway, it's just a workaround.
|
||||||
screen_render(screen, true);
|
sc_screen_render(screen, true);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
screen_frame_sink_open(struct sc_frame_sink *sink) {
|
sc_screen_frame_sink_open(struct sc_frame_sink *sink) {
|
||||||
struct screen *screen = DOWNCAST(sink);
|
struct sc_screen *screen = DOWNCAST(sink);
|
||||||
(void) screen;
|
(void) screen;
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
screen->open = true;
|
screen->open = true;
|
||||||
@@ -306,8 +312,8 @@ screen_frame_sink_open(struct sc_frame_sink *sink) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
screen_frame_sink_close(struct sc_frame_sink *sink) {
|
sc_screen_frame_sink_close(struct sc_frame_sink *sink) {
|
||||||
struct screen *screen = DOWNCAST(sink);
|
struct sc_screen *screen = DOWNCAST(sink);
|
||||||
(void) screen;
|
(void) screen;
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
screen->open = false;
|
screen->open = false;
|
||||||
@@ -317,8 +323,8 @@ screen_frame_sink_close(struct sc_frame_sink *sink) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) {
|
sc_screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) {
|
||||||
struct screen *screen = DOWNCAST(sink);
|
struct sc_screen *screen = DOWNCAST(sink);
|
||||||
return sc_video_buffer_push(&screen->vb, frame);
|
return sc_video_buffer_push(&screen->vb, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,7 +332,7 @@ static void
|
|||||||
sc_video_buffer_on_new_frame(struct sc_video_buffer *vb, bool previous_skipped,
|
sc_video_buffer_on_new_frame(struct sc_video_buffer *vb, bool previous_skipped,
|
||||||
void *userdata) {
|
void *userdata) {
|
||||||
(void) vb;
|
(void) vb;
|
||||||
struct screen *screen = userdata;
|
struct sc_screen *screen = userdata;
|
||||||
|
|
||||||
// event_failed implies previous_skipped (the previous frame may not have
|
// event_failed implies previous_skipped (the previous frame may not have
|
||||||
// been consumed if the event was not sent)
|
// been consumed if the event was not sent)
|
||||||
@@ -359,7 +365,8 @@ sc_video_buffer_on_new_frame(struct sc_video_buffer *vb, bool previous_skipped,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
screen_init(struct screen *screen, const struct screen_params *params) {
|
sc_screen_init(struct sc_screen *screen,
|
||||||
|
const struct sc_screen_params *params) {
|
||||||
screen->resize_pending = false;
|
screen->resize_pending = false;
|
||||||
screen->has_frame = false;
|
screen->has_frame = false;
|
||||||
screen->fullscreen = false;
|
screen->fullscreen = false;
|
||||||
@@ -368,6 +375,12 @@ screen_init(struct screen *screen, const struct screen_params *params) {
|
|||||||
screen->mouse_captured = false;
|
screen->mouse_captured = false;
|
||||||
screen->mouse_capture_key_pressed = 0;
|
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 = {
|
static const struct sc_video_buffer_callbacks cbs = {
|
||||||
.on_new_frame = sc_video_buffer_on_new_frame,
|
.on_new_frame = sc_video_buffer_on_new_frame,
|
||||||
};
|
};
|
||||||
@@ -396,9 +409,6 @@ screen_init(struct screen *screen, const struct screen_params *params) {
|
|||||||
get_rotated_size(screen->frame_size, screen->rotation);
|
get_rotated_size(screen->frame_size, screen->rotation);
|
||||||
screen->content_size = content_size;
|
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
|
uint32_t window_flags = SDL_WINDOW_HIDDEN
|
||||||
| SDL_WINDOW_RESIZABLE
|
| SDL_WINDOW_RESIZABLE
|
||||||
| SDL_WINDOW_ALLOW_HIGHDPI;
|
| SDL_WINDOW_ALLOW_HIGHDPI;
|
||||||
@@ -409,13 +419,9 @@ screen_init(struct screen *screen, const struct screen_params *params) {
|
|||||||
window_flags |= SDL_WINDOW_BORDERLESS;
|
window_flags |= SDL_WINDOW_BORDERLESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int x = params->window_x != SC_WINDOW_POSITION_UNDEFINED
|
// The window will be positioned and sized on first video frame
|
||||||
? params->window_x : (int) SDL_WINDOWPOS_UNDEFINED;
|
screen->window =
|
||||||
int y = params->window_y != SC_WINDOW_POSITION_UNDEFINED
|
SDL_CreateWindow(params->window_title, 0, 0, 0, 0, window_flags);
|
||||||
? 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) {
|
if (!screen->window) {
|
||||||
LOGC("Could not create window: %s", SDL_GetError());
|
LOGC("Could not create window: %s", SDL_GetError());
|
||||||
goto error_destroy_fps_counter;
|
goto error_destroy_fps_counter;
|
||||||
@@ -483,39 +489,28 @@ screen_init(struct screen *screen, const struct screen_params *params) {
|
|||||||
goto error_destroy_texture;
|
goto error_destroy_texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct input_manager_params im_params = {
|
struct sc_input_manager_params im_params = {
|
||||||
.controller = params->controller,
|
.controller = params->controller,
|
||||||
|
.fp = params->fp,
|
||||||
.screen = screen,
|
.screen = screen,
|
||||||
.kp = params->kp,
|
.kp = params->kp,
|
||||||
.mp = params->mp,
|
.mp = params->mp,
|
||||||
.control = params->control,
|
|
||||||
.forward_all_clicks = params->forward_all_clicks,
|
.forward_all_clicks = params->forward_all_clicks,
|
||||||
.legacy_paste = params->legacy_paste,
|
.legacy_paste = params->legacy_paste,
|
||||||
.clipboard_autosync = params->clipboard_autosync,
|
.clipboard_autosync = params->clipboard_autosync,
|
||||||
.shortcut_mods = params->shortcut_mods,
|
.shortcut_mods = params->shortcut_mods,
|
||||||
};
|
};
|
||||||
|
|
||||||
input_manager_init(&screen->im, &im_params);
|
sc_input_manager_init(&screen->im, &im_params);
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
screen_update_content_rect(screen);
|
|
||||||
|
|
||||||
if (params->fullscreen) {
|
|
||||||
screen_switch_fullscreen(screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONTINUOUS_RESIZING_WORKAROUND
|
#ifdef CONTINUOUS_RESIZING_WORKAROUND
|
||||||
SDL_AddEventWatch(event_watcher, screen);
|
SDL_AddEventWatch(event_watcher, screen);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct sc_frame_sink_ops ops = {
|
static const struct sc_frame_sink_ops ops = {
|
||||||
.open = screen_frame_sink_open,
|
.open = sc_screen_frame_sink_open,
|
||||||
.close = screen_frame_sink_close,
|
.close = sc_screen_frame_sink_close,
|
||||||
.push = screen_frame_sink_push,
|
.push = sc_screen_frame_sink_push,
|
||||||
};
|
};
|
||||||
|
|
||||||
screen->frame_sink.ops = &ops;
|
screen->frame_sink.ops = &ops;
|
||||||
@@ -544,29 +539,45 @@ error_destroy_video_buffer:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
screen_show_window(struct screen *screen) {
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
SDL_ShowWindow(screen->window);
|
SDL_ShowWindow(screen->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_hide_window(struct screen *screen) {
|
sc_screen_hide_window(struct sc_screen *screen) {
|
||||||
SDL_HideWindow(screen->window);
|
SDL_HideWindow(screen->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_interrupt(struct screen *screen) {
|
sc_screen_interrupt(struct sc_screen *screen) {
|
||||||
sc_video_buffer_stop(&screen->vb);
|
sc_video_buffer_stop(&screen->vb);
|
||||||
fps_counter_interrupt(&screen->fps_counter);
|
fps_counter_interrupt(&screen->fps_counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_join(struct screen *screen) {
|
sc_screen_join(struct sc_screen *screen) {
|
||||||
sc_video_buffer_join(&screen->vb);
|
sc_video_buffer_join(&screen->vb);
|
||||||
fps_counter_join(&screen->fps_counter);
|
fps_counter_join(&screen->fps_counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_destroy(struct screen *screen) {
|
sc_screen_destroy(struct sc_screen *screen) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
assert(!screen->open);
|
assert(!screen->open);
|
||||||
#endif
|
#endif
|
||||||
@@ -579,7 +590,7 @@ screen_destroy(struct screen *screen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
resize_for_content(struct screen *screen, struct sc_size old_content_size,
|
resize_for_content(struct sc_screen *screen, struct sc_size old_content_size,
|
||||||
struct sc_size new_content_size) {
|
struct sc_size new_content_size) {
|
||||||
struct sc_size window_size = get_window_size(screen);
|
struct sc_size window_size = get_window_size(screen);
|
||||||
struct sc_size target_size = {
|
struct sc_size target_size = {
|
||||||
@@ -593,7 +604,7 @@ resize_for_content(struct screen *screen, struct sc_size old_content_size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
set_content_size(struct screen *screen, struct sc_size new_content_size) {
|
set_content_size(struct sc_screen *screen, struct sc_size new_content_size) {
|
||||||
if (!screen->fullscreen && !screen->maximized) {
|
if (!screen->fullscreen && !screen->maximized) {
|
||||||
resize_for_content(screen, screen->content_size, new_content_size);
|
resize_for_content(screen, screen->content_size, new_content_size);
|
||||||
} else if (!screen->resize_pending) {
|
} else if (!screen->resize_pending) {
|
||||||
@@ -607,7 +618,7 @@ set_content_size(struct screen *screen, struct sc_size new_content_size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
apply_pending_resize(struct screen *screen) {
|
apply_pending_resize(struct sc_screen *screen) {
|
||||||
assert(!screen->fullscreen);
|
assert(!screen->fullscreen);
|
||||||
assert(!screen->maximized);
|
assert(!screen->maximized);
|
||||||
if (screen->resize_pending) {
|
if (screen->resize_pending) {
|
||||||
@@ -618,7 +629,7 @@ apply_pending_resize(struct screen *screen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_set_rotation(struct screen *screen, unsigned rotation) {
|
sc_screen_set_rotation(struct sc_screen *screen, unsigned rotation) {
|
||||||
assert(rotation < 4);
|
assert(rotation < 4);
|
||||||
if (rotation == screen->rotation) {
|
if (rotation == screen->rotation) {
|
||||||
return;
|
return;
|
||||||
@@ -632,12 +643,12 @@ screen_set_rotation(struct screen *screen, unsigned rotation) {
|
|||||||
screen->rotation = rotation;
|
screen->rotation = rotation;
|
||||||
LOGI("Display rotation set to %u", rotation);
|
LOGI("Display rotation set to %u", rotation);
|
||||||
|
|
||||||
screen_render(screen, true);
|
sc_screen_render(screen, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// recreate the texture and resize the window if the frame size has changed
|
// recreate the texture and resize the window if the frame size has changed
|
||||||
static bool
|
static bool
|
||||||
prepare_for_frame(struct screen *screen, struct sc_size new_frame_size) {
|
prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) {
|
||||||
if (screen->frame_size.width != new_frame_size.width
|
if (screen->frame_size.width != new_frame_size.width
|
||||||
|| screen->frame_size.height != new_frame_size.height) {
|
|| screen->frame_size.height != new_frame_size.height) {
|
||||||
// frame dimension changed, destroy texture
|
// frame dimension changed, destroy texture
|
||||||
@@ -649,7 +660,7 @@ prepare_for_frame(struct screen *screen, struct sc_size new_frame_size) {
|
|||||||
get_rotated_size(new_frame_size, screen->rotation);
|
get_rotated_size(new_frame_size, screen->rotation);
|
||||||
set_content_size(screen, new_content_size);
|
set_content_size(screen, new_content_size);
|
||||||
|
|
||||||
screen_update_content_rect(screen);
|
sc_screen_update_content_rect(screen);
|
||||||
|
|
||||||
LOGI("New texture: %" PRIu16 "x%" PRIu16,
|
LOGI("New texture: %" PRIu16 "x%" PRIu16,
|
||||||
screen->frame_size.width, screen->frame_size.height);
|
screen->frame_size.width, screen->frame_size.height);
|
||||||
@@ -665,7 +676,7 @@ prepare_for_frame(struct screen *screen, struct sc_size new_frame_size) {
|
|||||||
|
|
||||||
// write the frame into the texture
|
// write the frame into the texture
|
||||||
static void
|
static void
|
||||||
update_texture(struct screen *screen, const AVFrame *frame) {
|
update_texture(struct sc_screen *screen, const AVFrame *frame) {
|
||||||
SDL_UpdateYUVTexture(screen->texture, NULL,
|
SDL_UpdateYUVTexture(screen->texture, NULL,
|
||||||
frame->data[0], frame->linesize[0],
|
frame->data[0], frame->linesize[0],
|
||||||
frame->data[1], frame->linesize[1],
|
frame->data[1], frame->linesize[1],
|
||||||
@@ -679,7 +690,7 @@ update_texture(struct screen *screen, const AVFrame *frame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
screen_update_frame(struct screen *screen) {
|
sc_screen_update_frame(struct sc_screen *screen) {
|
||||||
av_frame_unref(screen->frame);
|
av_frame_unref(screen->frame);
|
||||||
sc_video_buffer_consume(&screen->vb, screen->frame);
|
sc_video_buffer_consume(&screen->vb, screen->frame);
|
||||||
AVFrame *frame = screen->frame;
|
AVFrame *frame = screen->frame;
|
||||||
@@ -692,12 +703,23 @@ screen_update_frame(struct screen *screen) {
|
|||||||
}
|
}
|
||||||
update_texture(screen, frame);
|
update_texture(screen, frame);
|
||||||
|
|
||||||
screen_render(screen, false);
|
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);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_switch_fullscreen(struct screen *screen) {
|
sc_screen_switch_fullscreen(struct sc_screen *screen) {
|
||||||
uint32_t new_mode = screen->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP;
|
uint32_t new_mode = screen->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||||
if (SDL_SetWindowFullscreen(screen->window, new_mode)) {
|
if (SDL_SetWindowFullscreen(screen->window, new_mode)) {
|
||||||
LOGW("Could not switch fullscreen mode: %s", SDL_GetError());
|
LOGW("Could not switch fullscreen mode: %s", SDL_GetError());
|
||||||
@@ -710,11 +732,11 @@ screen_switch_fullscreen(struct screen *screen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LOGD("Switched to %s mode", screen->fullscreen ? "fullscreen" : "windowed");
|
LOGD("Switched to %s mode", screen->fullscreen ? "fullscreen" : "windowed");
|
||||||
screen_render(screen, true);
|
sc_screen_render(screen, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_resize_to_fit(struct screen *screen) {
|
sc_screen_resize_to_fit(struct sc_screen *screen) {
|
||||||
if (screen->fullscreen || screen->maximized) {
|
if (screen->fullscreen || screen->maximized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -738,7 +760,7 @@ screen_resize_to_fit(struct screen *screen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_resize_to_pixel_perfect(struct screen *screen) {
|
sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) {
|
||||||
if (screen->fullscreen) {
|
if (screen->fullscreen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -755,35 +777,33 @@ screen_resize_to_pixel_perfect(struct screen *screen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
screen_is_mouse_capture_key(SDL_Keycode key) {
|
sc_screen_is_mouse_capture_key(SDL_Keycode key) {
|
||||||
return key == SDLK_LALT || key == SDLK_LGUI || key == SDLK_RGUI;
|
return key == SDLK_LALT || key == SDLK_LGUI || key == SDLK_RGUI;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
screen_handle_event(struct screen *screen, SDL_Event *event) {
|
sc_screen_handle_event(struct sc_screen *screen, SDL_Event *event) {
|
||||||
|
bool relative_mode = sc_screen_is_relative_mode(screen);
|
||||||
|
|
||||||
switch (event->type) {
|
switch (event->type) {
|
||||||
case EVENT_NEW_FRAME:
|
case EVENT_NEW_FRAME: {
|
||||||
if (!screen->has_frame) {
|
bool ok = sc_screen_update_frame(screen);
|
||||||
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) {
|
if (!ok) {
|
||||||
LOGW("Frame update failed\n");
|
LOGW("Frame update failed\n");
|
||||||
}
|
}
|
||||||
return true;
|
return;
|
||||||
|
}
|
||||||
case SDL_WINDOWEVENT:
|
case SDL_WINDOWEVENT:
|
||||||
if (!screen->has_frame) {
|
if (!screen->has_frame) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
switch (event->window.event) {
|
switch (event->window.event) {
|
||||||
case SDL_WINDOWEVENT_EXPOSED:
|
case SDL_WINDOWEVENT_EXPOSED:
|
||||||
screen_render(screen, true);
|
sc_screen_render(screen, true);
|
||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||||
screen_render(screen, true);
|
sc_screen_render(screen, true);
|
||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT_MAXIMIZED:
|
case SDL_WINDOWEVENT_MAXIMIZED:
|
||||||
screen->maximized = true;
|
screen->maximized = true;
|
||||||
@@ -799,78 +819,80 @@ screen_handle_event(struct screen *screen, SDL_Event *event) {
|
|||||||
}
|
}
|
||||||
screen->maximized = false;
|
screen->maximized = false;
|
||||||
apply_pending_resize(screen);
|
apply_pending_resize(screen);
|
||||||
screen_render(screen, true);
|
sc_screen_render(screen, true);
|
||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||||
if (screen->im.mp->relative_mode) {
|
if (relative_mode) {
|
||||||
screen_capture_mouse(screen, false);
|
sc_screen_capture_mouse(screen, false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return true;
|
return;
|
||||||
case SDL_KEYDOWN:
|
case SDL_KEYDOWN:
|
||||||
if (screen->im.mp->relative_mode) {
|
if (relative_mode) {
|
||||||
SDL_Keycode key = event->key.keysym.sym;
|
SDL_Keycode key = event->key.keysym.sym;
|
||||||
if (screen_is_mouse_capture_key(key)) {
|
if (sc_screen_is_mouse_capture_key(key)) {
|
||||||
if (!screen->mouse_capture_key_pressed) {
|
if (!screen->mouse_capture_key_pressed) {
|
||||||
screen->mouse_capture_key_pressed = key;
|
screen->mouse_capture_key_pressed = key;
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
// Another mouse capture key has been pressed, cancel
|
// Another mouse capture key has been pressed, cancel
|
||||||
// mouse (un)capture
|
// mouse (un)capture
|
||||||
screen->mouse_capture_key_pressed = 0;
|
screen->mouse_capture_key_pressed = 0;
|
||||||
// Do not return, the event must be forwarded to the
|
|
||||||
// input manager
|
|
||||||
}
|
}
|
||||||
|
// Mouse capture keys are never forwarded to the device
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_KEYUP:
|
case SDL_KEYUP:
|
||||||
if (screen->im.mp->relative_mode) {
|
if (relative_mode) {
|
||||||
SDL_Keycode key = event->key.keysym.sym;
|
SDL_Keycode key = event->key.keysym.sym;
|
||||||
SDL_Keycode cap = screen->mouse_capture_key_pressed;
|
SDL_Keycode cap = screen->mouse_capture_key_pressed;
|
||||||
screen->mouse_capture_key_pressed = 0;
|
screen->mouse_capture_key_pressed = 0;
|
||||||
if (key == cap) {
|
if (sc_screen_is_mouse_capture_key(key)) {
|
||||||
// A mouse capture key has been pressed then released:
|
if (key == cap) {
|
||||||
// toggle the capture mouse mode
|
// A mouse capture key has been pressed then released:
|
||||||
screen_capture_mouse(screen, !screen->mouse_captured);
|
// toggle the capture mouse mode
|
||||||
return true;
|
sc_screen_capture_mouse(screen,
|
||||||
|
!screen->mouse_captured);
|
||||||
|
}
|
||||||
|
// Mouse capture keys are never forwarded to the device
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// Do not return, the event must be forwarded to the input
|
|
||||||
// manager
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_MOUSEWHEEL:
|
case SDL_MOUSEWHEEL:
|
||||||
case SDL_MOUSEMOTION:
|
case SDL_MOUSEMOTION:
|
||||||
case SDL_MOUSEBUTTONDOWN:
|
case SDL_MOUSEBUTTONDOWN:
|
||||||
if (screen->im.mp->relative_mode && !screen->mouse_captured) {
|
if (relative_mode && !screen->mouse_captured) {
|
||||||
// Do not forward to input manager, the mouse will be captured
|
// Do not forward to input manager, the mouse will be captured
|
||||||
// on SDL_MOUSEBUTTONUP
|
// on SDL_MOUSEBUTTONUP
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_FINGERMOTION:
|
case SDL_FINGERMOTION:
|
||||||
case SDL_FINGERDOWN:
|
case SDL_FINGERDOWN:
|
||||||
case SDL_FINGERUP:
|
case SDL_FINGERUP:
|
||||||
if (screen->im.mp->relative_mode) {
|
if (relative_mode) {
|
||||||
// Touch events are not compatible with relative mode
|
// Touch events are not compatible with relative mode
|
||||||
// (coordinates are not relative)
|
// (coordinates are not relative)
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_MOUSEBUTTONUP:
|
case SDL_MOUSEBUTTONUP:
|
||||||
if (screen->im.mp->relative_mode && !screen->mouse_captured) {
|
if (relative_mode && !screen->mouse_captured) {
|
||||||
screen_capture_mouse(screen, true);
|
sc_screen_capture_mouse(screen, true);
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return input_manager_handle_event(&screen->im, event);
|
sc_input_manager_handle_event(&screen->im, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sc_point
|
struct sc_point
|
||||||
screen_convert_drawable_to_frame_coords(struct screen *screen,
|
sc_screen_convert_drawable_to_frame_coords(struct sc_screen *screen,
|
||||||
int32_t x, int32_t y) {
|
int32_t x, int32_t y) {
|
||||||
unsigned rotation = screen->rotation;
|
unsigned rotation = screen->rotation;
|
||||||
assert(rotation < 4);
|
assert(rotation < 4);
|
||||||
|
|
||||||
@@ -906,14 +928,14 @@ screen_convert_drawable_to_frame_coords(struct screen *screen,
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct sc_point
|
struct sc_point
|
||||||
screen_convert_window_to_frame_coords(struct screen *screen,
|
sc_screen_convert_window_to_frame_coords(struct sc_screen *screen,
|
||||||
int32_t x, int32_t y) {
|
int32_t x, int32_t y) {
|
||||||
screen_hidpi_scale_coords(screen, &x, &y);
|
sc_screen_hidpi_scale_coords(screen, &x, &y);
|
||||||
return screen_convert_drawable_to_frame_coords(screen, x, y);
|
return sc_screen_convert_drawable_to_frame_coords(screen, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_hidpi_scale_coords(struct screen *screen, int32_t *x, int32_t *y) {
|
sc_screen_hidpi_scale_coords(struct sc_screen *screen, int32_t *x, int32_t *y) {
|
||||||
// take the HiDPI scaling (dw/ww and dh/wh) into account
|
// take the HiDPI scaling (dw/ww and dh/wh) into account
|
||||||
int ww, wh, dw, dh;
|
int ww, wh, dw, dh;
|
||||||
SDL_GetWindowSize(screen->window, &ww, &wh);
|
SDL_GetWindowSize(screen->window, &ww, &wh);
|
||||||
|
|||||||
@@ -17,17 +17,26 @@
|
|||||||
#include "trait/mouse_processor.h"
|
#include "trait/mouse_processor.h"
|
||||||
#include "video_buffer.h"
|
#include "video_buffer.h"
|
||||||
|
|
||||||
struct screen {
|
struct sc_screen {
|
||||||
struct sc_frame_sink frame_sink; // frame sink trait
|
struct sc_frame_sink frame_sink; // frame sink trait
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
bool open; // track the open/close state to assert correct behavior
|
bool open; // track the open/close state to assert correct behavior
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct input_manager im;
|
struct sc_input_manager im;
|
||||||
struct sc_video_buffer vb;
|
struct sc_video_buffer vb;
|
||||||
struct fps_counter fps_counter;
|
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_Window *window;
|
||||||
SDL_Renderer *renderer;
|
SDL_Renderer *renderer;
|
||||||
SDL_Texture *texture;
|
SDL_Texture *texture;
|
||||||
@@ -59,12 +68,12 @@ struct screen {
|
|||||||
AVFrame *frame;
|
AVFrame *frame;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct screen_params {
|
struct sc_screen_params {
|
||||||
struct controller *controller;
|
struct sc_controller *controller;
|
||||||
|
struct sc_file_pusher *fp;
|
||||||
struct sc_key_processor *kp;
|
struct sc_key_processor *kp;
|
||||||
struct sc_mouse_processor *mp;
|
struct sc_mouse_processor *mp;
|
||||||
|
|
||||||
bool control;
|
|
||||||
bool forward_all_clicks;
|
bool forward_all_clicks;
|
||||||
bool legacy_paste;
|
bool legacy_paste;
|
||||||
bool clipboard_autosync;
|
bool clipboard_autosync;
|
||||||
@@ -74,10 +83,10 @@ struct screen_params {
|
|||||||
struct sc_size frame_size;
|
struct sc_size frame_size;
|
||||||
bool always_on_top;
|
bool always_on_top;
|
||||||
|
|
||||||
int16_t window_x;
|
int16_t window_x; // accepts SC_WINDOW_POSITION_UNDEFINED
|
||||||
int16_t window_y;
|
int16_t window_y; // accepts SC_WINDOW_POSITION_UNDEFINED
|
||||||
uint16_t window_width; // accepts SC_WINDOW_POSITION_UNDEFINED
|
uint16_t window_width;
|
||||||
uint16_t window_height; // accepts SC_WINDOW_POSITION_UNDEFINED
|
uint16_t window_height;
|
||||||
|
|
||||||
bool window_borderless;
|
bool window_borderless;
|
||||||
|
|
||||||
@@ -91,65 +100,65 @@ struct screen_params {
|
|||||||
|
|
||||||
// initialize screen, create window, renderer and texture (window is hidden)
|
// initialize screen, create window, renderer and texture (window is hidden)
|
||||||
bool
|
bool
|
||||||
screen_init(struct screen *screen, const struct screen_params *params);
|
sc_screen_init(struct sc_screen *screen, const struct sc_screen_params *params);
|
||||||
|
|
||||||
// request to interrupt any inner thread
|
// request to interrupt any inner thread
|
||||||
// must be called before screen_join()
|
// must be called before screen_join()
|
||||||
void
|
void
|
||||||
screen_interrupt(struct screen *screen);
|
sc_screen_interrupt(struct sc_screen *screen);
|
||||||
|
|
||||||
// join any inner thread
|
// join any inner thread
|
||||||
void
|
void
|
||||||
screen_join(struct screen *screen);
|
sc_screen_join(struct sc_screen *screen);
|
||||||
|
|
||||||
// destroy window, renderer and texture (if any)
|
// destroy window, renderer and texture (if any)
|
||||||
void
|
void
|
||||||
screen_destroy(struct screen *screen);
|
sc_screen_destroy(struct sc_screen *screen);
|
||||||
|
|
||||||
// hide the window
|
// hide the window
|
||||||
//
|
//
|
||||||
// It is used to hide the window immediately on closing without waiting for
|
// It is used to hide the window immediately on closing without waiting for
|
||||||
// screen_destroy()
|
// screen_destroy()
|
||||||
void
|
void
|
||||||
screen_hide_window(struct screen *screen);
|
sc_screen_hide_window(struct sc_screen *screen);
|
||||||
|
|
||||||
// switch the fullscreen mode
|
// switch the fullscreen mode
|
||||||
void
|
void
|
||||||
screen_switch_fullscreen(struct screen *screen);
|
sc_screen_switch_fullscreen(struct sc_screen *screen);
|
||||||
|
|
||||||
// resize window to optimal size (remove black borders)
|
// resize window to optimal size (remove black borders)
|
||||||
void
|
void
|
||||||
screen_resize_to_fit(struct screen *screen);
|
sc_screen_resize_to_fit(struct sc_screen *screen);
|
||||||
|
|
||||||
// resize window to 1:1 (pixel-perfect)
|
// resize window to 1:1 (pixel-perfect)
|
||||||
void
|
void
|
||||||
screen_resize_to_pixel_perfect(struct screen *screen);
|
sc_screen_resize_to_pixel_perfect(struct sc_screen *screen);
|
||||||
|
|
||||||
// set the display rotation (0, 1, 2 or 3, x90 degrees counterclockwise)
|
// set the display rotation (0, 1, 2 or 3, x90 degrees counterclockwise)
|
||||||
void
|
void
|
||||||
screen_set_rotation(struct screen *screen, unsigned rotation);
|
sc_screen_set_rotation(struct sc_screen *screen, unsigned rotation);
|
||||||
|
|
||||||
// react to SDL events
|
// react to SDL events
|
||||||
bool
|
void
|
||||||
screen_handle_event(struct screen *screen, SDL_Event *event);
|
sc_screen_handle_event(struct sc_screen *screen, SDL_Event *event);
|
||||||
|
|
||||||
// convert point from window coordinates to frame coordinates
|
// convert point from window coordinates to frame coordinates
|
||||||
// x and y are expressed in pixels
|
// x and y are expressed in pixels
|
||||||
struct sc_point
|
struct sc_point
|
||||||
screen_convert_window_to_frame_coords(struct screen *screen,
|
sc_screen_convert_window_to_frame_coords(struct sc_screen *screen,
|
||||||
int32_t x, int32_t y);
|
int32_t x, int32_t y);
|
||||||
|
|
||||||
// convert point from drawable coordinates to frame coordinates
|
// convert point from drawable coordinates to frame coordinates
|
||||||
// x and y are expressed in pixels
|
// x and y are expressed in pixels
|
||||||
struct sc_point
|
struct sc_point
|
||||||
screen_convert_drawable_to_frame_coords(struct screen *screen,
|
sc_screen_convert_drawable_to_frame_coords(struct sc_screen *screen,
|
||||||
int32_t x, int32_t y);
|
int32_t x, int32_t y);
|
||||||
|
|
||||||
// Convert coordinates from window to drawable.
|
// Convert coordinates from window to drawable.
|
||||||
// Events are expressed in window coordinates, but content is expressed in
|
// Events are expressed in window coordinates, but content is expressed in
|
||||||
// drawable coordinates. They are the same if HiDPI scaling is 1, but differ
|
// drawable coordinates. They are the same if HiDPI scaling is 1, but differ
|
||||||
// otherwise.
|
// otherwise.
|
||||||
void
|
void
|
||||||
screen_hidpi_scale_coords(struct screen *screen, int32_t *x, int32_t *y);
|
sc_screen_hidpi_scale_coords(struct sc_screen *screen, int32_t *x, int32_t *y);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -188,7 +188,6 @@ execute_server(struct sc_server *server,
|
|||||||
} \
|
} \
|
||||||
cmd[count++] = p; \
|
cmd[count++] = p; \
|
||||||
}
|
}
|
||||||
#define STRBOOL(v) (v ? "true" : "false")
|
|
||||||
|
|
||||||
ADD_PARAM("log_level=%s", log_level_to_server_string(params->log_level));
|
ADD_PARAM("log_level=%s", log_level_to_server_string(params->log_level));
|
||||||
ADD_PARAM("bit_rate=%" PRIu32, params->bit_rate);
|
ADD_PARAM("bit_rate=%" PRIu32, params->bit_rate);
|
||||||
@@ -204,23 +203,23 @@ execute_server(struct sc_server *server,
|
|||||||
params->lock_video_orientation);
|
params->lock_video_orientation);
|
||||||
}
|
}
|
||||||
if (server->tunnel.forward) {
|
if (server->tunnel.forward) {
|
||||||
ADD_PARAM("tunnel_forward=%s", STRBOOL(server->tunnel.forward));
|
ADD_PARAM("tunnel_forward=true");
|
||||||
}
|
}
|
||||||
if (params->crop) {
|
if (params->crop) {
|
||||||
ADD_PARAM("crop=%s", params->crop);
|
ADD_PARAM("crop=%s", params->crop);
|
||||||
}
|
}
|
||||||
if (!params->control) {
|
if (!params->control) {
|
||||||
// By default, control is true
|
// By default, control is true
|
||||||
ADD_PARAM("control=%s", STRBOOL(params->control));
|
ADD_PARAM("control=false");
|
||||||
}
|
}
|
||||||
if (params->display_id) {
|
if (params->display_id) {
|
||||||
ADD_PARAM("display_id=%" PRIu32, params->display_id);
|
ADD_PARAM("display_id=%" PRIu32, params->display_id);
|
||||||
}
|
}
|
||||||
if (params->show_touches) {
|
if (params->show_touches) {
|
||||||
ADD_PARAM("show_touches=%s", STRBOOL(params->show_touches));
|
ADD_PARAM("show_touches=true");
|
||||||
}
|
}
|
||||||
if (params->stay_awake) {
|
if (params->stay_awake) {
|
||||||
ADD_PARAM("stay_awake=%s", STRBOOL(params->stay_awake));
|
ADD_PARAM("stay_awake=true");
|
||||||
}
|
}
|
||||||
if (params->codec_options) {
|
if (params->codec_options) {
|
||||||
ADD_PARAM("codec_options=%s", params->codec_options);
|
ADD_PARAM("codec_options=%s", params->codec_options);
|
||||||
@@ -229,11 +228,15 @@ execute_server(struct sc_server *server,
|
|||||||
ADD_PARAM("encoder_name=%s", params->encoder_name);
|
ADD_PARAM("encoder_name=%s", params->encoder_name);
|
||||||
}
|
}
|
||||||
if (params->power_off_on_close) {
|
if (params->power_off_on_close) {
|
||||||
ADD_PARAM("power_off_on_close=%s", STRBOOL(params->power_off_on_close));
|
ADD_PARAM("power_off_on_close=true");
|
||||||
}
|
}
|
||||||
if (!params->clipboard_autosync) {
|
if (!params->clipboard_autosync) {
|
||||||
// By default, clipboard_autosync is true
|
// By default, clipboard_autosync is true
|
||||||
ADD_PARAM("clipboard_autosync=%s", STRBOOL(params->clipboard_autosync));
|
ADD_PARAM("clipboard_autosync=false");
|
||||||
|
}
|
||||||
|
if (!params->downsize_on_error) {
|
||||||
|
// By default, downsize_on_error is true
|
||||||
|
ADD_PARAM("downsize_on_error=false");
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef ADD_PARAM
|
#undef ADD_PARAM
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ struct sc_server_params {
|
|||||||
bool force_adb_forward;
|
bool force_adb_forward;
|
||||||
bool power_off_on_close;
|
bool power_off_on_close;
|
||||||
bool clipboard_autosync;
|
bool clipboard_autosync;
|
||||||
|
bool downsize_on_error;
|
||||||
bool tcpip;
|
bool tcpip;
|
||||||
const char *tcpip_dst;
|
const char *tcpip_dst;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "stream.h"
|
#include "stream.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <libavformat/avformat.h>
|
|
||||||
#include <libavutil/time.h>
|
#include <libavutil/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@@ -192,7 +191,7 @@ static int
|
|||||||
run_stream(void *data) {
|
run_stream(void *data) {
|
||||||
struct stream *stream = data;
|
struct stream *stream = data;
|
||||||
|
|
||||||
AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);
|
const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);
|
||||||
if (!codec) {
|
if (!codec) {
|
||||||
LOGE("H.264 decoder not found");
|
LOGE("H.264 decoder not found");
|
||||||
goto end;
|
goto end;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
|
|
||||||
#include "trait/packet_sink.h"
|
#include "trait/packet_sink.h"
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ unwrap(sc_socket socket) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef HAVE_SOCK_CLOEXEC // avoid unused-function warning
|
||||||
static inline bool
|
static inline bool
|
||||||
sc_raw_socket_close(sc_raw_socket raw_sock) {
|
sc_raw_socket_close(sc_raw_socket raw_sock) {
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
@@ -91,6 +92,7 @@ sc_raw_socket_close(sc_raw_socket raw_sock) {
|
|||||||
return !closesocket(raw_sock);
|
return !closesocket(raw_sock);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef HAVE_SOCK_CLOEXEC
|
#ifndef HAVE_SOCK_CLOEXEC
|
||||||
// If SOCK_CLOEXEC does not exist, the flag must be set manually once the
|
// If SOCK_CLOEXEC does not exist, the flag must be set manually once the
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
|
||||||
#include "coords.h"
|
#include "coords.h"
|
||||||
#include "trait/frame_sink.h"
|
#include "trait/frame_sink.h"
|
||||||
#include "video_buffer.h"
|
#include "video_buffer.h"
|
||||||
#include "util/tick.h"
|
#include "util/tick.h"
|
||||||
|
|
||||||
#include <libavformat/avformat.h>
|
|
||||||
|
|
||||||
struct sc_v4l2_sink {
|
struct sc_v4l2_sink {
|
||||||
struct sc_frame_sink frame_sink; // frame sink trait
|
struct sc_frame_sink frame_sink; // frame sink trait
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include "control_msg.h"
|
#include "control_msg.h"
|
||||||
|
|
||||||
static void test_serialize_inject_keycode(void) {
|
static void test_serialize_inject_keycode(void) {
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
.type = CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
||||||
.inject_keycode = {
|
.inject_keycode = {
|
||||||
.action = AKEY_EVENT_ACTION_UP,
|
.action = AKEY_EVENT_ACTION_UP,
|
||||||
@@ -17,7 +17,7 @@ static void test_serialize_inject_keycode(void) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||||
size_t size = control_msg_serialize(&msg, buf);
|
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||||
assert(size == 14);
|
assert(size == 14);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
@@ -31,7 +31,7 @@ static void test_serialize_inject_keycode(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_serialize_inject_text(void) {
|
static void test_serialize_inject_text(void) {
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_INJECT_TEXT,
|
.type = CONTROL_MSG_TYPE_INJECT_TEXT,
|
||||||
.inject_text = {
|
.inject_text = {
|
||||||
.text = "hello, world!",
|
.text = "hello, world!",
|
||||||
@@ -39,7 +39,7 @@ static void test_serialize_inject_text(void) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||||
size_t size = control_msg_serialize(&msg, buf);
|
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||||
assert(size == 18);
|
assert(size == 18);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
@@ -51,7 +51,7 @@ static void test_serialize_inject_text(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_serialize_inject_text_long(void) {
|
static void test_serialize_inject_text_long(void) {
|
||||||
struct control_msg msg;
|
struct sc_control_msg msg;
|
||||||
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
|
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
|
||||||
char text[CONTROL_MSG_INJECT_TEXT_MAX_LENGTH + 1];
|
char text[CONTROL_MSG_INJECT_TEXT_MAX_LENGTH + 1];
|
||||||
memset(text, 'a', CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
|
memset(text, 'a', CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
|
||||||
@@ -59,7 +59,7 @@ static void test_serialize_inject_text_long(void) {
|
|||||||
msg.inject_text.text = text;
|
msg.inject_text.text = text;
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||||
size_t size = control_msg_serialize(&msg, buf);
|
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||||
assert(size == 5 + CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
|
assert(size == 5 + CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
|
||||||
|
|
||||||
unsigned char expected[5 + CONTROL_MSG_INJECT_TEXT_MAX_LENGTH];
|
unsigned char expected[5 + CONTROL_MSG_INJECT_TEXT_MAX_LENGTH];
|
||||||
@@ -74,7 +74,7 @@ static void test_serialize_inject_text_long(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_serialize_inject_touch_event(void) {
|
static void test_serialize_inject_touch_event(void) {
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||||
.inject_touch_event = {
|
.inject_touch_event = {
|
||||||
.action = AMOTION_EVENT_ACTION_DOWN,
|
.action = AMOTION_EVENT_ACTION_DOWN,
|
||||||
@@ -95,7 +95,7 @@ static void test_serialize_inject_touch_event(void) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||||
size_t size = control_msg_serialize(&msg, buf);
|
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||||
assert(size == 28);
|
assert(size == 28);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
@@ -111,7 +111,7 @@ static void test_serialize_inject_touch_event(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_serialize_inject_scroll_event(void) {
|
static void test_serialize_inject_scroll_event(void) {
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
.type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
||||||
.inject_scroll_event = {
|
.inject_scroll_event = {
|
||||||
.position = {
|
.position = {
|
||||||
@@ -131,7 +131,7 @@ static void test_serialize_inject_scroll_event(void) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||||
size_t size = control_msg_serialize(&msg, buf);
|
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||||
assert(size == 25);
|
assert(size == 25);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
@@ -146,7 +146,7 @@ static void test_serialize_inject_scroll_event(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_serialize_back_or_screen_on(void) {
|
static void test_serialize_back_or_screen_on(void) {
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
|
.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
|
||||||
.back_or_screen_on = {
|
.back_or_screen_on = {
|
||||||
.action = AKEY_EVENT_ACTION_UP,
|
.action = AKEY_EVENT_ACTION_UP,
|
||||||
@@ -154,7 +154,7 @@ static void test_serialize_back_or_screen_on(void) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||||
size_t size = control_msg_serialize(&msg, buf);
|
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||||
assert(size == 2);
|
assert(size == 2);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
@@ -165,12 +165,12 @@ static void test_serialize_back_or_screen_on(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_serialize_expand_notification_panel(void) {
|
static void test_serialize_expand_notification_panel(void) {
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL,
|
.type = CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL,
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||||
size_t size = control_msg_serialize(&msg, buf);
|
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||||
assert(size == 1);
|
assert(size == 1);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
@@ -180,12 +180,12 @@ static void test_serialize_expand_notification_panel(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_serialize_expand_settings_panel(void) {
|
static void test_serialize_expand_settings_panel(void) {
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL,
|
.type = CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL,
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||||
size_t size = control_msg_serialize(&msg, buf);
|
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||||
assert(size == 1);
|
assert(size == 1);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
@@ -195,12 +195,12 @@ static void test_serialize_expand_settings_panel(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_serialize_collapse_panels(void) {
|
static void test_serialize_collapse_panels(void) {
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_COLLAPSE_PANELS,
|
.type = CONTROL_MSG_TYPE_COLLAPSE_PANELS,
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||||
size_t size = control_msg_serialize(&msg, buf);
|
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||||
assert(size == 1);
|
assert(size == 1);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
@@ -210,7 +210,7 @@ static void test_serialize_collapse_panels(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_serialize_get_clipboard(void) {
|
static void test_serialize_get_clipboard(void) {
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_GET_CLIPBOARD,
|
.type = CONTROL_MSG_TYPE_GET_CLIPBOARD,
|
||||||
.get_clipboard = {
|
.get_clipboard = {
|
||||||
.copy_key = GET_CLIPBOARD_COPY_KEY_COPY,
|
.copy_key = GET_CLIPBOARD_COPY_KEY_COPY,
|
||||||
@@ -218,7 +218,7 @@ static void test_serialize_get_clipboard(void) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||||
size_t size = control_msg_serialize(&msg, buf);
|
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||||
assert(size == 2);
|
assert(size == 2);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
@@ -229,7 +229,7 @@ static void test_serialize_get_clipboard(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_serialize_set_clipboard(void) {
|
static void test_serialize_set_clipboard(void) {
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
.type = CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||||
.set_clipboard = {
|
.set_clipboard = {
|
||||||
.sequence = UINT64_C(0x0102030405060708),
|
.sequence = UINT64_C(0x0102030405060708),
|
||||||
@@ -239,7 +239,7 @@ static void test_serialize_set_clipboard(void) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||||
size_t size = control_msg_serialize(&msg, buf);
|
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||||
assert(size == 27);
|
assert(size == 27);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
@@ -253,7 +253,7 @@ static void test_serialize_set_clipboard(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_serialize_set_clipboard_long(void) {
|
static void test_serialize_set_clipboard_long(void) {
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
.type = CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||||
.set_clipboard = {
|
.set_clipboard = {
|
||||||
.sequence = UINT64_C(0x0102030405060708),
|
.sequence = UINT64_C(0x0102030405060708),
|
||||||
@@ -268,7 +268,7 @@ static void test_serialize_set_clipboard_long(void) {
|
|||||||
msg.set_clipboard.text = text;
|
msg.set_clipboard.text = text;
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||||
size_t size = control_msg_serialize(&msg, buf);
|
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||||
assert(size == CONTROL_MSG_MAX_SIZE);
|
assert(size == CONTROL_MSG_MAX_SIZE);
|
||||||
|
|
||||||
unsigned char expected[CONTROL_MSG_MAX_SIZE] = {
|
unsigned char expected[CONTROL_MSG_MAX_SIZE] = {
|
||||||
@@ -287,7 +287,7 @@ static void test_serialize_set_clipboard_long(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_serialize_set_screen_power_mode(void) {
|
static void test_serialize_set_screen_power_mode(void) {
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
|
.type = CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
|
||||||
.set_screen_power_mode = {
|
.set_screen_power_mode = {
|
||||||
.mode = SCREEN_POWER_MODE_NORMAL,
|
.mode = SCREEN_POWER_MODE_NORMAL,
|
||||||
@@ -295,7 +295,7 @@ static void test_serialize_set_screen_power_mode(void) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||||
size_t size = control_msg_serialize(&msg, buf);
|
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||||
assert(size == 2);
|
assert(size == 2);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
@@ -306,12 +306,12 @@ static void test_serialize_set_screen_power_mode(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_serialize_rotate_device(void) {
|
static void test_serialize_rotate_device(void) {
|
||||||
struct control_msg msg = {
|
struct sc_control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_ROTATE_DEVICE,
|
.type = CONTROL_MSG_TYPE_ROTATE_DEVICE,
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||||
size_t size = control_msg_serialize(&msg, buf);
|
size_t size = sc_control_msg_serialize(&msg, buf);
|
||||||
assert(size == 1);
|
assert(size == 1);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ cpu = 'i686'
|
|||||||
endian = 'little'
|
endian = 'little'
|
||||||
|
|
||||||
[properties]
|
[properties]
|
||||||
prebuilt_ffmpeg_shared = 'ffmpeg-4.3.1-win32-shared'
|
ffmpeg_avcodec = 'avcodec-58'
|
||||||
prebuilt_ffmpeg_dev = 'ffmpeg-4.3.1-win32-dev'
|
ffmpeg_avformat = 'avformat-58'
|
||||||
|
ffmpeg_avutil = 'avutil-56'
|
||||||
|
prebuilt_ffmpeg = 'ffmpeg-4.3.1-win32-shared'
|
||||||
prebuilt_sdl2 = 'SDL2-2.0.18/i686-w64-mingw32'
|
prebuilt_sdl2 = 'SDL2-2.0.18/i686-w64-mingw32'
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ cpu = 'x86_64'
|
|||||||
endian = 'little'
|
endian = 'little'
|
||||||
|
|
||||||
[properties]
|
[properties]
|
||||||
prebuilt_ffmpeg_shared = 'ffmpeg-4.3.1-win64-shared'
|
ffmpeg_avcodec = 'avcodec-59'
|
||||||
prebuilt_ffmpeg_dev = 'ffmpeg-4.3.1-win64-dev'
|
ffmpeg_avformat = 'avformat-59'
|
||||||
|
ffmpeg_avutil = 'avutil-57'
|
||||||
|
prebuilt_ffmpeg = 'ffmpeg-5.0-full_build-shared'
|
||||||
prebuilt_sdl2 = 'SDL2-2.0.18/x86_64-w64-mingw32'
|
prebuilt_sdl2 = 'SDL2-2.0.18/x86_64-w64-mingw32'
|
||||||
|
|||||||
@@ -1,33 +1,26 @@
|
|||||||
.PHONY: prepare-win32 prepare-win64 \
|
.PHONY: prepare-win32 prepare-win64 \
|
||||||
prepare-ffmpeg-shared-win32 \
|
prepare-ffmpeg-win32 \
|
||||||
prepare-ffmpeg-dev-win32 \
|
prepare-ffmpeg-win64 \
|
||||||
prepare-ffmpeg-shared-win64 \
|
|
||||||
prepare-ffmpeg-dev-win64 \
|
|
||||||
prepare-sdl2 \
|
prepare-sdl2 \
|
||||||
prepare-adb
|
prepare-adb
|
||||||
|
|
||||||
prepare-win32: prepare-sdl2 prepare-ffmpeg-shared-win32 prepare-ffmpeg-dev-win32 prepare-adb
|
prepare-win32: prepare-sdl2 prepare-ffmpeg-win32 prepare-adb
|
||||||
prepare-win64: prepare-sdl2 prepare-ffmpeg-shared-win64 prepare-ffmpeg-dev-win64 prepare-adb
|
prepare-win64: prepare-sdl2 prepare-ffmpeg-win64 prepare-adb
|
||||||
|
|
||||||
prepare-ffmpeg-shared-win32:
|
# Use old FFmpeg version for win32, there are no new prebuilts
|
||||||
|
prepare-ffmpeg-win32:
|
||||||
@./prepare-dep https://github.com/Genymobile/scrcpy/releases/download/v1.16/ffmpeg-4.3.1-win32-shared.zip \
|
@./prepare-dep https://github.com/Genymobile/scrcpy/releases/download/v1.16/ffmpeg-4.3.1-win32-shared.zip \
|
||||||
357af9901a456f4dcbacd107e83a934d344c9cb07ddad8aaf80612eeab7d26d2 \
|
357af9901a456f4dcbacd107e83a934d344c9cb07ddad8aaf80612eeab7d26d2 \
|
||||||
ffmpeg-4.3.1-win32-shared
|
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 \
|
@./prepare-dep https://github.com/Genymobile/scrcpy/releases/download/v1.16/ffmpeg-4.3.1-win32-dev.zip \
|
||||||
230efb08e9bcf225bd474da29676c70e591fc94d8790a740ca801408fddcb78b \
|
230efb08e9bcf225bd474da29676c70e591fc94d8790a740ca801408fddcb78b \
|
||||||
ffmpeg-4.3.1-win32-dev
|
ffmpeg-4.3.1-win32-dev
|
||||||
|
ln -sf ../ffmpeg-4.3.1-win32-dev/include ffmpeg-4.3.1-win32-shared/
|
||||||
|
|
||||||
prepare-ffmpeg-shared-win64:
|
prepare-ffmpeg-win64:
|
||||||
@./prepare-dep https://github.com/Genymobile/scrcpy/releases/download/v1.16/ffmpeg-4.3.1-win64-shared.zip \
|
@./prepare-dep https://github.com/GyanD/codexffmpeg/releases/download/5.0/ffmpeg-5.0-full_build-shared.7z \
|
||||||
dd29b7f92f48dead4dd940492c7509138c0f99db445076d0a597007298a79940 \
|
e5900f6cecd4c438d398bd2fc308736c10b857cd8dd61c11bcfb05bff5d1211a \
|
||||||
ffmpeg-4.3.1-win64-shared
|
ffmpeg-5.0-full_build-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-sdl2:
|
||||||
@./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.18-mingw.tar.gz \
|
@./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.18-mingw.tar.gz \
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ extract() {
|
|||||||
elif [[ "$file" == *.tar.gz ]]
|
elif [[ "$file" == *.tar.gz ]]
|
||||||
then
|
then
|
||||||
tar xf "$file"
|
tar xf "$file"
|
||||||
|
elif [[ "$file" == *.7z ]]
|
||||||
|
then
|
||||||
|
7z x "$file"
|
||||||
else
|
else
|
||||||
echo "Unsupported file: $file"
|
echo "Unsupported file: $file"
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
10
release.mk
10
release.mk
@@ -110,11 +110,11 @@ dist-win64: build-server build-win64
|
|||||||
cp data/scrcpy-console.bat "$(DIST)/$(WIN64_TARGET_DIR)"
|
cp data/scrcpy-console.bat "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||||
cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN64_TARGET_DIR)"
|
cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||||
cp data/icon.png "$(DIST)/$(WIN64_TARGET_DIR)"
|
cp data/icon.png "$(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-5.0-full_build-shared/bin/avutil-57.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-5.0-full_build-shared/bin/avcodec-59.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-5.0-full_build-shared/bin/avformat-59.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-5.0-full_build-shared/bin/swresample-4.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/ffmpeg-5.0-full_build-shared/bin/swscale-6.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||||
cp prebuilt-deps/platform-tools/adb.exe "$(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/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||||
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||||
|
|||||||
@@ -46,15 +46,17 @@ public final class DesktopConnection implements Closeable {
|
|||||||
return localSocket;
|
return localSocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DesktopConnection open(Device device, boolean tunnelForward, boolean control) throws IOException {
|
public static DesktopConnection open(boolean tunnelForward, boolean control, boolean sendDummyByte) throws IOException {
|
||||||
LocalSocket videoSocket;
|
LocalSocket videoSocket;
|
||||||
LocalSocket controlSocket = null;
|
LocalSocket controlSocket = null;
|
||||||
if (tunnelForward) {
|
if (tunnelForward) {
|
||||||
LocalServerSocket localServerSocket = new LocalServerSocket(SOCKET_NAME);
|
LocalServerSocket localServerSocket = new LocalServerSocket(SOCKET_NAME);
|
||||||
try {
|
try {
|
||||||
videoSocket = localServerSocket.accept();
|
videoSocket = localServerSocket.accept();
|
||||||
// send one byte so the client may read() to detect a connection error
|
if (sendDummyByte) {
|
||||||
videoSocket.getOutputStream().write(0);
|
// send one byte so the client may read() to detect a connection error
|
||||||
|
videoSocket.getOutputStream().write(0);
|
||||||
|
}
|
||||||
if (control) {
|
if (control) {
|
||||||
try {
|
try {
|
||||||
controlSocket = localServerSocket.accept();
|
controlSocket = localServerSocket.accept();
|
||||||
@@ -78,10 +80,7 @@ public final class DesktopConnection implements Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DesktopConnection connection = new DesktopConnection(videoSocket, controlSocket);
|
return new DesktopConnection(videoSocket, controlSocket);
|
||||||
Size videoSize = device.getScreenInfo().getVideoSize();
|
|
||||||
connection.send(Device.getDeviceName(), videoSize.getWidth(), videoSize.getHeight());
|
|
||||||
return connection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
@@ -95,7 +94,7 @@ public final class DesktopConnection implements Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void send(String deviceName, int width, int height) throws IOException {
|
public void sendDeviceMeta(String deviceName, int width, int height) throws IOException {
|
||||||
byte[] buffer = new byte[DEVICE_NAME_FIELD_LENGTH + 4];
|
byte[] buffer = new byte[DEVICE_NAME_FIELD_LENGTH + 4];
|
||||||
|
|
||||||
byte[] deviceNameBytes = deviceName.getBytes(StandardCharsets.UTF_8);
|
byte[] deviceNameBytes = deviceName.getBytes(StandardCharsets.UTF_8);
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ public final class Device {
|
|||||||
void onClipboardTextChanged(String text);
|
void onClipboardTextChanged(String text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final Size deviceSize;
|
||||||
|
private final Rect crop;
|
||||||
|
private int maxSize;
|
||||||
|
private final int lockVideoOrientation;
|
||||||
|
|
||||||
private ScreenInfo screenInfo;
|
private ScreenInfo screenInfo;
|
||||||
private RotationListener rotationListener;
|
private RotationListener rotationListener;
|
||||||
private ClipboardListener clipboardListener;
|
private ClipboardListener clipboardListener;
|
||||||
@@ -69,7 +74,12 @@ public final class Device {
|
|||||||
|
|
||||||
int displayInfoFlags = displayInfo.getFlags();
|
int displayInfoFlags = displayInfo.getFlags();
|
||||||
|
|
||||||
screenInfo = ScreenInfo.computeScreenInfo(displayInfo, options.getCrop(), options.getMaxSize(), options.getLockVideoOrientation());
|
deviceSize = displayInfo.getSize();
|
||||||
|
crop = options.getCrop();
|
||||||
|
maxSize = options.getMaxSize();
|
||||||
|
lockVideoOrientation = options.getLockVideoOrientation();
|
||||||
|
|
||||||
|
screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), deviceSize, crop, maxSize, lockVideoOrientation);
|
||||||
layerStack = displayInfo.getLayerStack();
|
layerStack = displayInfo.getLayerStack();
|
||||||
|
|
||||||
SERVICE_MANAGER.getWindowManager().registerRotationWatcher(new IRotationWatcher.Stub() {
|
SERVICE_MANAGER.getWindowManager().registerRotationWatcher(new IRotationWatcher.Stub() {
|
||||||
@@ -123,6 +133,11 @@ 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() {
|
public synchronized ScreenInfo getScreenInfo() {
|
||||||
return screenInfo;
|
return screenInfo;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ public class Options {
|
|||||||
private int lockVideoOrientation = -1;
|
private int lockVideoOrientation = -1;
|
||||||
private boolean tunnelForward;
|
private boolean tunnelForward;
|
||||||
private Rect crop;
|
private Rect crop;
|
||||||
private boolean sendFrameMeta = true; // send PTS so that the client may record properly
|
|
||||||
private boolean control = true;
|
private boolean control = true;
|
||||||
private int displayId;
|
private int displayId;
|
||||||
private boolean showTouches;
|
private boolean showTouches;
|
||||||
@@ -21,6 +20,12 @@ public class Options {
|
|||||||
private String encoderName;
|
private String encoderName;
|
||||||
private boolean powerOffScreenOnClose;
|
private boolean powerOffScreenOnClose;
|
||||||
private boolean clipboardAutosync = true;
|
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() {
|
public Ln.Level getLogLevel() {
|
||||||
return logLevel;
|
return logLevel;
|
||||||
@@ -78,14 +83,6 @@ public class Options {
|
|||||||
this.crop = crop;
|
this.crop = crop;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getSendFrameMeta() {
|
|
||||||
return sendFrameMeta;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSendFrameMeta(boolean sendFrameMeta) {
|
|
||||||
this.sendFrameMeta = sendFrameMeta;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getControl() {
|
public boolean getControl() {
|
||||||
return control;
|
return control;
|
||||||
}
|
}
|
||||||
@@ -149,4 +146,36 @@ public class Options {
|
|||||||
public void setClipboardAutosync(boolean clipboardAutosync) {
|
public void setClipboardAutosync(boolean clipboardAutosync) {
|
||||||
this.clipboardAutosync = 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,24 +25,32 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
private static final int REPEAT_FRAME_DELAY_US = 100_000; // repeat after 100ms
|
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";
|
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 static final int NO_PTS = -1;
|
||||||
|
|
||||||
private final AtomicBoolean rotationChanged = new AtomicBoolean();
|
private final AtomicBoolean rotationChanged = new AtomicBoolean();
|
||||||
private final ByteBuffer headerBuffer = ByteBuffer.allocate(12);
|
private final ByteBuffer headerBuffer = ByteBuffer.allocate(12);
|
||||||
|
|
||||||
private String encoderName;
|
private final String encoderName;
|
||||||
private List<CodecOption> codecOptions;
|
private final List<CodecOption> codecOptions;
|
||||||
private int bitRate;
|
private final int bitRate;
|
||||||
private int maxFps;
|
private final int maxFps;
|
||||||
private boolean sendFrameMeta;
|
private final boolean sendFrameMeta;
|
||||||
|
private final boolean downsizeOnError;
|
||||||
private long ptsOrigin;
|
private long ptsOrigin;
|
||||||
|
|
||||||
public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, List<CodecOption> codecOptions, String encoderName) {
|
private boolean firstFrameSent;
|
||||||
|
|
||||||
|
public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, List<CodecOption> codecOptions, String encoderName,
|
||||||
|
boolean downsizeOnError) {
|
||||||
this.sendFrameMeta = sendFrameMeta;
|
this.sendFrameMeta = sendFrameMeta;
|
||||||
this.bitRate = bitRate;
|
this.bitRate = bitRate;
|
||||||
this.maxFps = maxFps;
|
this.maxFps = maxFps;
|
||||||
this.codecOptions = codecOptions;
|
this.codecOptions = codecOptions;
|
||||||
this.encoderName = encoderName;
|
this.encoderName = encoderName;
|
||||||
|
this.downsizeOnError = downsizeOnError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -91,6 +99,23 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
alive = encode(codec, fd);
|
alive = encode(codec, fd);
|
||||||
// do not call stop() on exception, it would trigger an IllegalStateException
|
// do not call stop() on exception, it would trigger an IllegalStateException
|
||||||
codec.stop();
|
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 {
|
} finally {
|
||||||
destroyDisplay(display);
|
destroyDisplay(display);
|
||||||
codec.release();
|
codec.release();
|
||||||
@@ -102,6 +127,18 @@ 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 {
|
private boolean encode(MediaCodec codec, FileDescriptor fd) throws IOException {
|
||||||
boolean eof = false;
|
boolean eof = false;
|
||||||
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
||||||
@@ -122,6 +159,10 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
IO.writeFully(fd, codecBuffer);
|
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 {
|
} finally {
|
||||||
if (outputBufferId >= 0) {
|
if (outputBufferId >= 0) {
|
||||||
|
|||||||
@@ -80,15 +80,12 @@ public final class ScreenInfo {
|
|||||||
return new ScreenInfo(newContentRect, newUnlockedVideoSize, newDeviceRotation, lockedVideoOrientation);
|
return new ScreenInfo(newContentRect, newUnlockedVideoSize, newDeviceRotation, lockedVideoOrientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ScreenInfo computeScreenInfo(DisplayInfo displayInfo, Rect crop, int maxSize, int lockedVideoOrientation) {
|
public static ScreenInfo computeScreenInfo(int rotation, Size deviceSize, Rect crop, int maxSize, int lockedVideoOrientation) {
|
||||||
int rotation = displayInfo.getRotation();
|
|
||||||
|
|
||||||
if (lockedVideoOrientation == Device.LOCK_VIDEO_ORIENTATION_INITIAL) {
|
if (lockedVideoOrientation == Device.LOCK_VIDEO_ORIENTATION_INITIAL) {
|
||||||
// The user requested to lock the video orientation to the current orientation
|
// The user requested to lock the video orientation to the current orientation
|
||||||
lockedVideoOrientation = rotation;
|
lockedVideoOrientation = rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
Size deviceSize = displayInfo.getSize();
|
|
||||||
Rect contentRect = new Rect(0, 0, deviceSize.getWidth(), deviceSize.getHeight());
|
Rect contentRect = new Rect(0, 0, deviceSize.getWidth(), deviceSize.getHeight());
|
||||||
if (crop != null) {
|
if (crop != null) {
|
||||||
if (rotation % 2 != 0) { // 180s preserve dimensions
|
if (rotation % 2 != 0) { // 180s preserve dimensions
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.genymobile.scrcpy;
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.media.MediaCodec;
|
|
||||||
import android.media.MediaCodecInfo;
|
import android.media.MediaCodecInfo;
|
||||||
import android.os.BatteryManager;
|
import android.os.BatteryManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -67,10 +66,15 @@ public final class Server {
|
|||||||
|
|
||||||
boolean tunnelForward = options.isTunnelForward();
|
boolean tunnelForward = options.isTunnelForward();
|
||||||
boolean control = options.getControl();
|
boolean control = options.getControl();
|
||||||
|
boolean sendDummyByte = options.getSendDummyByte();
|
||||||
|
|
||||||
try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward, control)) {
|
try (DesktopConnection connection = DesktopConnection.open(tunnelForward, control, sendDummyByte)) {
|
||||||
|
if (options.getSendDeviceMeta()) {
|
||||||
|
Size videoSize = device.getScreenInfo().getVideoSize();
|
||||||
|
connection.sendDeviceMeta(Device.getDeviceName(), videoSize.getWidth(), videoSize.getHeight());
|
||||||
|
}
|
||||||
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), codecOptions,
|
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), codecOptions,
|
||||||
options.getEncoderName());
|
options.getEncoderName(), options.getDownsizeOnError());
|
||||||
|
|
||||||
Thread controllerThread = null;
|
Thread controllerThread = null;
|
||||||
Thread deviceMessageSenderThread = null;
|
Thread deviceMessageSenderThread = null;
|
||||||
@@ -200,10 +204,6 @@ public final class Server {
|
|||||||
Rect crop = parseCrop(value);
|
Rect crop = parseCrop(value);
|
||||||
options.setCrop(crop);
|
options.setCrop(crop);
|
||||||
break;
|
break;
|
||||||
case "send_frame_meta":
|
|
||||||
boolean sendFrameMeta = Boolean.parseBoolean(value);
|
|
||||||
options.setSendFrameMeta(sendFrameMeta);
|
|
||||||
break;
|
|
||||||
case "control":
|
case "control":
|
||||||
boolean control = Boolean.parseBoolean(value);
|
boolean control = Boolean.parseBoolean(value);
|
||||||
options.setControl(control);
|
options.setControl(control);
|
||||||
@@ -237,6 +237,29 @@ public final class Server {
|
|||||||
boolean clipboardAutosync = Boolean.parseBoolean(value);
|
boolean clipboardAutosync = Boolean.parseBoolean(value);
|
||||||
options.setClipboardAutosync(clipboardAutosync);
|
options.setClipboardAutosync(clipboardAutosync);
|
||||||
break;
|
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:
|
default:
|
||||||
Ln.w("Unknown server option: " + key);
|
Ln.w("Unknown server option: " + key);
|
||||||
break;
|
break;
|
||||||
@@ -263,16 +286,6 @@ public final class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void suggestFix(Throwable e) {
|
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) {
|
if (e instanceof InvalidDisplayIdException) {
|
||||||
InvalidDisplayIdException idie = (InvalidDisplayIdException) e;
|
InvalidDisplayIdException idie = (InvalidDisplayIdException) e;
|
||||||
int[] displayIds = idie.getAvailableDisplayIds();
|
int[] displayIds = idie.getAvailableDisplayIds();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ public final class InputManager {
|
|||||||
|
|
||||||
private final IInterface manager;
|
private final IInterface manager;
|
||||||
private Method injectInputEventMethod;
|
private Method injectInputEventMethod;
|
||||||
|
private boolean alternativeInjectInputEventMethod;
|
||||||
|
|
||||||
private static Method setDisplayIdMethod;
|
private static Method setDisplayIdMethod;
|
||||||
|
|
||||||
@@ -25,7 +26,12 @@ public final class InputManager {
|
|||||||
|
|
||||||
private Method getInjectInputEventMethod() throws NoSuchMethodException {
|
private Method getInjectInputEventMethod() throws NoSuchMethodException {
|
||||||
if (injectInputEventMethod == null) {
|
if (injectInputEventMethod == null) {
|
||||||
injectInputEventMethod = manager.getClass().getMethod("injectInputEvent", InputEvent.class, int.class);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return injectInputEventMethod;
|
return injectInputEventMethod;
|
||||||
}
|
}
|
||||||
@@ -33,6 +39,10 @@ public final class InputManager {
|
|||||||
public boolean injectInputEvent(InputEvent inputEvent, int mode) {
|
public boolean injectInputEvent(InputEvent inputEvent, int mode) {
|
||||||
try {
|
try {
|
||||||
Method method = getInjectInputEventMethod();
|
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);
|
return (boolean) method.invoke(manager, inputEvent, mode);
|
||||||
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
Ln.e("Could not invoke method", e);
|
Ln.e("Could not invoke method", e);
|
||||||
|
|||||||
Reference in New Issue
Block a user