Compare commits

..

43 Commits

Author SHA1 Message Date
b26ad41da7 wip 2021-12-29 01:21:40 +01:00
bc84a130a4 wip 2021-12-29 00:37:49 +01:00
3c663e1c0f wip 2021-12-28 23:46:24 +01:00
05ef36545c wip 2021-12-28 23:46:24 +01:00
ac32280a43 wip 2021-12-28 23:46:24 +01:00
93c49ae446 wip 2021-12-28 23:46:24 +01:00
15d40b6d16 input_events 2021-12-28 23:46:24 +01:00
c520d9f0e7 Rename SC_MOD_* to SC_SHORTCUT_MOD_*
This will avoid conflicts with new SC_MOD_* constants.
2021-12-28 23:46:24 +01:00
826ddf1a6e Document HID keyboard events 2021-12-28 12:23:03 +01:00
6261bb0b5a Ignore display bounds on resize-to-fit
The "resize to fit" feature (MOD+w or double-click on black borders)
computed the "optimal size" using the same function computing the
initial window size on start.

However, on "resize to fit", only the black borders must be removed (the
content size must be preserved), so the display bounds must not be
considered.
2021-12-20 19:24:28 +01:00
2f091beeaa Simplify sc_size assignment
Assign the whole struct instead of each field separately.
2021-12-20 19:24:28 +01:00
720c3064df Upgrade SDL (2.0.18) for Windows
Include the latest version of SDL in Windows releases.
2021-12-20 19:16:28 +01:00
ad11c5babb Set DPI awareness for Windows
Add a windows manifest to set the DPI awareness by default:
<https://docs.microsoft.com/en-us/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process>

Refs #40 <https://github.com/Genymobile/scrcpy/issues/40>
Fixes #2865 <https://github.com/Genymobile/scrcpy/issues/2865>
2021-12-20 19:13:02 +01:00
feb250a973 Fix typos reported by codespell 2021-12-15 18:27:45 +01:00
d049671908 Fix adb server hang
Since commit 0426708544, the server is run
in a dedicated thread. For SDL, many signals, including SIGINT and
SIGTERM, are masked for new threads. As a result, if the adb server is
not already running, adb commands invoked by scrcpy will start an adb
server that ignores those signals and cannot be terminated at system
shutdown.

Fixes #2873 <https://github.com/Genymobile/scrcpy/issues/2873>
PR #2870 <https://github.com/Genymobile/scrcpy/pull/2870>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-12-11 19:09:11 +01:00
0685c491cd Improve crossbuild configuration
Use meson native features to detect crossbuild, and remove the
user-provided option crossbuild_windows.
2021-12-10 19:50:58 +01:00
892cfe943e Add script to bump version
The version must now be bumped at 4 different places. Add a script to
bump automatically:

    ./bump_version 1.23.4
2021-12-10 19:50:17 +01:00
29570ee819 Add metadata to scrcpy.exe for Windows
Refs <https://stackoverflow.com/a/708382/1987178>
2021-12-09 23:38:13 +01:00
cfcbc2ac21 Add icon to scrcpy.exe
The icon will be associated to scrcpy.exe in the Windows explorer.

The .ico was created using imagemagick:

    convert icon.png icon.ico

It is included as a binary for simplicity.

Refs #2815 <https://github.com/Genymobile/scrcpy/issues/2815>
2021-12-09 23:38:13 +01:00
2cb4e04209 Update copyright date to 2021 in manpage
December, it's time to update to 2021!
2021-12-09 23:37:40 +01:00
878ffffc36 Update environment variables section in manpage
Use the same content as the section printed with --help.
2021-12-09 21:47:05 +01:00
f0361fc8b3 Add environment variables in help
Print the list of environment variables used by scrcpy in --help.
2021-12-09 21:45:39 +01:00
b5d4ec61fc Move newline generation in help
If we removed the shortcuts intro, we would not need the additional '\n'
of the section title, so it should be printed along with the shortcuts
intro.
2021-12-09 21:43:54 +01:00
3ada5c51bc Rename scrcpy threads
Prefix the name of threads by "scrcpy-". This improves readability in
the output of `top -H` for example.

Limit the thread names to 16 bytes, because it is limited on some
platforms.
2021-12-09 21:32:11 +01:00
09c55b0f93 Set "low delay" decoder flag
I don't really know the concrete benefits, but scrcpy definitely wants
low delay decoding.

Suggested-by: François Cartegnie <fcvlcdev@free.fr>
2021-12-08 23:53:54 +01:00
682a691173 Use timers with microsecond precision
SDL only provides millisecond precision. Use system timers to get a
better precision.
2021-12-08 23:45:45 +01:00
ddb9396743 Interrupt and close sockets on server stop
The sockets were never interrupted or closed by the client since recent
changes to run the server from a dedicated thread (see commit
0426708544).

As a side effect, the server could never terminate properly (it was
waiting on socket blocking calls), so it was always killed by the client
after the WATCHDOG_DELAY.

Interrupt the sockets on stop to give the servera chance to terminate
property, then close them.
2021-12-08 23:44:23 +01:00
cabcbc2b15 Do not create control socket if no control
If --no-control is enabled, then it is not necessary to create a second
communication socket between the client and the server.

This also facilitates the use of the server alone (without the client)
to receive only the raw video stream.
2021-12-08 23:41:38 +01:00
80fe12a95f Require libavcodec >= 57.37
In ffmpeg/doc/APIchanges:

> 2016-04-21 - 7fc329e - lavc 57.37.100 - avcodec.h
>   Add a new audio/video encoding and decoding API with decoupled input
>   and output -- avcodec_send_packet(), avcodec_receive_frame(),
>   avcodec_send_frame() and avcodec_receive_packet().

Refs de9b79ec2d

Refs #2862 <https://github.com/Genymobile/scrcpy/issues/2862>
2021-12-07 00:04:35 +01:00
099c546580 Require libavformat >= 57.33
In ffmpeg/doc/APIchanges:

> 2016-04-11 - 6f69f7a / 9200514 - lavf 57.33.100 / 57.5.0 - avformat.h
>   Add AVStream.codecpar, deprecate AVStream.codec.

Refs 5d9e96dc4e

Refs #2862 <https://github.com/Genymobile/scrcpy/issues/2862>
2021-12-07 00:04:27 +01:00
dca2c5f94f Require SDL >= 2.0.5
Icon loading uses SDL_CreateRGBSurfaceWithFormatFrom(), available since
SDL 2.0.5 (in 2016).

Refs #2862 <https://github.com/Genymobile/scrcpy/issues/2862>
2021-12-06 23:49:55 +01:00
90cf956f57 Remove spurious ';' 2021-12-04 09:29:30 +01:00
36c8778d2d Add missing comma
Thank you clang:

    ../app/src/control_msg.c:45:5: warning: suspicious concatenation of
    string literals in an array initialization; did you mean to separate
    the elements with a comma? [-Wstring-concatenation]
        "hover-exit",
        ^
2021-12-04 09:25:58 +01:00
ae90ef22db Add a unit test for clipboard text length
This would have catched the possible memcpy() overflow fixed by the
previous commit.

Refs #2859 <https://github.com/Genymobile/scrcpy/pull/2859>
2021-12-04 09:22:35 +01:00
d80bc25eba Fix overflow in memcpy
In function ‘memcpy’,
    inlined from ‘control_msg_serialize.constprop’ at ../app/src/control_msg.c:77:5,
    inlined from ‘run_controller’ at ../app/src/controller.c:69:12:
        /usr/include/x86_64-linux-gnu/bits/string_fortified.h:34:10:
        warning: ‘__builtin___memcpy_chk’ writing 262138 bytes into a region
        of size 262130 overflows the destination [-Wstringop-overflow=]
   return __builtin___memcpy_chk (__dest, __src, __len, __bos0 (__dest));

Refs 901d837165
PR #2859 <https://github.com/Genymobile/scrcpy/pull/2859>

Signed-off-by: Yu-Chen Lin <npes87184@gmail.com>
Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-12-04 09:18:55 +01:00
daa06abd34 Fix comment in control message serialization
Refs 245999aec4

Signed-off-by: Yu-Chen Lin <npes87184@gmail.com>
Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-12-04 09:18:43 +01:00
94702a4309 Fix memset() size in tests
The memset() size was 1 byte too long. It was harmless because the last
'a' was overwritten by '\0`.
2021-12-04 09:12:20 +01:00
65fbec9643 Mention SCRCPY_ICON_PATH env var in manpage 2021-12-03 21:47:31 +01:00
a208400133 Remove useless intermediate variable
Now that PLATFORM is correctly used in the if-condition,
PLATFORM_VERSION is useless.

PR #2850 <https://github.com/Genymobile/scrcpy/pull/2850>
2021-12-03 21:47:00 +01:00
ab00210b37 Fix script to build without gradle
The PLATFORM variable is assigned either from $ANDROID_PLATFORM or gets
a default value (currently $PLATFORM_VERSION).

The check to use either dx (SDK < 31) or d8 (SDK >= 31) must be based on
the actual $PLATFORM, not the default $PLATFORM_VERSION.

Refs 52138fd921
Refs <57d30780dd/debian/patches/0002-Workaround-broken-script.patch>

PR #2850 <https://github.com/Genymobile/scrcpy/pull/2850>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-12-03 21:46:49 +01:00
64a04b8d4a Fix process execution on Windows 7
According to this bug report on Firefox:
<https://bugzilla.mozilla.org/show_bug.cgi?id=1460995>

> CreateProcess fails with ERROR_NO_SYSTEM_RESOURCES on Windows 7. It
> looks like the reason why is because PROC_THREAD_ATTRIBUTE_HANDLE_LIST
> doesn't like console handles.

To avoid the problem, do not pass console handles to
PROC_THREAD_ATTRIBUTE_HANDLE_LIST.

Refs #2783 <https://github.com/Genymobile/scrcpy/pull/2783>
Refs f801d8b312
Fixes #2838 <https://github.com/Genymobile/scrcpy/issues/2838>
PR #2840 <https://github.com/Genymobile/scrcpy/pull/2840>
2021-12-01 18:02:35 +01:00
86c91e183d Log CreateProcessW() error code on Windows
Refs #2838 <https://github.com/Genymobile/scrcpy/issues/2838>
2021-11-30 09:41:47 +01:00
cb8713eb1f Update links to v1.21 2021-11-29 22:24:06 +01:00
51 changed files with 1273 additions and 341 deletions

View File

@ -270,10 +270,10 @@ install` must be run as root)._
#### Option 2: Use prebuilt server
- [`scrcpy-server-v1.20`][direct-scrcpy-server]
_(SHA-256: b20aee4951f99b060c4a44000ba94de973f9604758ef62beb253b371aad3df34)_
- [`scrcpy-server-v1.21`][direct-scrcpy-server]
_(SHA-256: dbcccab523ee26796e55ea33652649e4b7af498edae9aa75e4d4d7869c0ab848)_
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.20/scrcpy-server-v1.20
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.21/scrcpy-server-v1.21
Download the prebuilt server somewhere, and specify its path during the Meson
configuration:

View File

@ -1,4 +1,4 @@
# scrcpy (v1.20)
# scrcpy (v1.21)
<img src="data/icon.svg" width="128" height="128" alt="scrcpy" align="right" />
@ -101,10 +101,10 @@ process][BUILD_simple]).
For Windows, for simplicity, a prebuilt archive with all the dependencies
(including `adb`) is available:
- [`scrcpy-win64-v1.20.zip`][direct-win64]
_(SHA-256: 548532b616288bcaeceff6881ad5e6f0928e5ae2b48c380385f03627401cfdba)_
- [`scrcpy-win64-v1.21.zip`][direct-win64]
_(SHA-256: fdab0c1421353b592a9bbcebd6e252675eadccca65cca8105686feaa9c1ded53)_
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.20/scrcpy-win64-v1.20.zip
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.21/scrcpy-win64-v1.21.zip
It is also available in [Chocolatey]:

View File

@ -14,6 +14,7 @@ src = [
'src/file_handler.c',
'src/fps_counter.c',
'src/frame_buffer.c',
'src/input_events.c',
'src/input_manager.c',
'src/keyboard_inject.c',
'src/mouse_inject.c',
@ -49,9 +50,11 @@ conf.set('_XOPEN_SOURCE', '700')
conf.set('_GNU_SOURCE', true)
if host_machine.system() == 'windows'
windows = import('windows')
src += [
'src/sys/win/file.c',
'src/sys/win/process.c',
windows.compile_resources('scrcpy-windows.rc'),
]
conf.set('_WIN32_WINNT', '0x0600')
conf.set('WINVER', '0x0600')
@ -80,14 +83,16 @@ endif
cc = meson.get_compiler('c')
if not get_option('crossbuild_windows')
crossbuild_windows = meson.is_cross_build() and host_machine.system() == 'windows'
if not crossbuild_windows
# native build
dependencies = [
dependency('libavformat'),
dependency('libavcodec'),
dependency('libavformat', version: '>= 57.33'),
dependency('libavcodec', version: '>= 57.37'),
dependency('libavutil'),
dependency('sdl2'),
dependency('sdl2', version: '>= 2.0.5'),
]
if v4l2_support

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:application>
<asmv3:windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>

23
app/scrcpy-windows.rc Normal file
View File

@ -0,0 +1,23 @@
#include <winuser.h>
0 ICON "../data/icon.ico"
1 RT_MANIFEST "scrcpy-windows.manifest"
2 VERSIONINFO
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "FileDescription", "Display and control your Android device"
VALUE "InternalName", "scrcpy"
VALUE "LegalCopyright", "Romain Vimont, Genymobile"
VALUE "OriginalFilename", "scrcpy.exe"
VALUE "ProductName", "scrcpy"
VALUE "ProductVersion", "1.21"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END

View File

@ -413,11 +413,15 @@ Push file to device (see \fB\-\-push\-target\fR)
.TP
.B ADB
Specify the path to adb.
Path to adb.
.TP
.B SCRCPY_ICON_PATH
Path to the program icon.
.TP
.B SCRCPY_SERVER_PATH
Specify the path to server binary.
Path to the server binary.
.SH AUTHORS
@ -442,7 +446,7 @@ Copyright \(co 2018 Genymobile
Genymobile
.UE
Copyright \(co 2018\-2020
Copyright \(co 2018\-2021
.MT rom@rom1v.com
Romain Vimont
.ME

View File

@ -373,7 +373,7 @@ bool
sc_aoa_start(struct sc_aoa *aoa) {
LOGD("Starting AOA thread");
bool ok = sc_thread_create(&aoa->thread, run_aoa_thread, "aoa_thread", aoa);
bool ok = sc_thread_create(&aoa->thread, run_aoa_thread, "scrcpy-aoa", aoa);
if (!ok) {
LOGC("Could not start AOA thread");
return false;

View File

@ -71,6 +71,11 @@ struct sc_shortcut {
const char *text;
};
struct sc_envvar {
const char *name;
const char *text;
};
struct sc_getopt_adapter {
char *optstring;
struct option *longopts;
@ -585,6 +590,21 @@ static const struct sc_shortcut shortcuts[] = {
},
};
static const struct sc_envvar envvars[] = {
{
.name = "ADB",
.text = "Path to adb executable",
},
{
.name = "SCRCPY_ICON_PATH",
.text = "Path to the program icon",
},
{
.name = "SCRCPY_SERVER_PATH",
.text = "Path to the server binary",
}
};
static char *
sc_getopt_adapter_create_optstring(void) {
struct sc_strbuf buf;
@ -678,7 +698,7 @@ sc_getopt_adapter_init(struct sc_getopt_adapter *adapter) {
}
return true;
};
}
static void
sc_getopt_adapter_destroy(struct sc_getopt_adapter *adapter) {
@ -776,7 +796,7 @@ print_shortcuts_intro(unsigned cols) {
return;
}
printf("%s\n", intro);
printf("\n%s\n", intro);
free(intro);
}
@ -804,6 +824,23 @@ print_shortcut(const struct sc_shortcut *shortcut, unsigned cols) {
free(text);
}
static void
print_envvar(const struct sc_envvar *envvar, unsigned cols) {
assert(cols > 8); // sc_str_wrap_lines() requires indent < columns
assert(envvar->name);
assert(envvar->text);
printf("\n %s\n", envvar->name);
char *text = sc_str_wrap_lines(envvar->text, cols, 8);
if (!text) {
printf("<ERROR>\n");
return;
}
printf("%s\n", text);
free(text);
}
void
scrcpy_print_usage(const char *arg0) {
#define SC_TERM_COLS_DEFAULT 80
@ -831,11 +868,17 @@ scrcpy_print_usage(const char *arg0) {
}
// Print shortcuts section
printf("\nShortcuts:\n\n");
printf("\nShortcuts:\n");
print_shortcuts_intro(cols);
for (size_t i = 0; i < ARRAY_LEN(shortcuts); ++i) {
print_shortcut(&shortcuts[i], cols);
}
// Print environment variables section
printf("\nEnvironment variables:\n");
for (size_t i = 0; i < ARRAY_LEN(envvars); ++i) {
print_envvar(&envvars[i], cols);
}
}
static bool
@ -1078,7 +1121,7 @@ parse_log_level(const char *s, enum sc_log_level *log_level) {
}
// item is a list of mod keys separated by '+' (e.g. "lctrl+lalt")
// returns a bitwise-or of SC_MOD_* constants (or 0 on error)
// returns a bitwise-or of SC_SHORTCUT_MOD_* constants (or 0 on error)
static unsigned
parse_shortcut_mods_item(const char *item, size_t len) {
unsigned mod = 0;
@ -1096,17 +1139,17 @@ parse_shortcut_mods_item(const char *item, size_t len) {
((sizeof(literal)-1 == len) && !memcmp(literal, s, len))
if (STREQ("lctrl", item, key_len)) {
mod |= SC_MOD_LCTRL;
mod |= SC_SHORTCUT_MOD_LCTRL;
} else if (STREQ("rctrl", item, key_len)) {
mod |= SC_MOD_RCTRL;
mod |= SC_SHORTCUT_MOD_RCTRL;
} else if (STREQ("lalt", item, key_len)) {
mod |= SC_MOD_LALT;
mod |= SC_SHORTCUT_MOD_LALT;
} else if (STREQ("ralt", item, key_len)) {
mod |= SC_MOD_RALT;
mod |= SC_SHORTCUT_MOD_RALT;
} else if (STREQ("lsuper", item, key_len)) {
mod |= SC_MOD_LSUPER;
mod |= SC_SHORTCUT_MOD_LSUPER;
} else if (STREQ("rsuper", item, key_len)) {
mod |= SC_MOD_RSUPER;
mod |= SC_SHORTCUT_MOD_RSUPER;
} else {
LOGE("Unknown modifier key: %.*s "
"(must be one of: lctrl, rctrl, lalt, ralt, lsuper, rsuper)",

View File

@ -35,15 +35,6 @@
# define SCRCPY_LAVF_HAS_AVFORMATCONTEXT_URL
#endif
#if SDL_VERSION_ATLEAST(2, 0, 5)
// <https://wiki.libsdl.org/SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH>
# define SCRCPY_SDL_HAS_HINT_MOUSE_FOCUS_CLICKTHROUGH
// <https://wiki.libsdl.org/SDL_GetDisplayUsableBounds>
# define SCRCPY_SDL_HAS_GET_DISPLAY_USABLE_BOUNDS
// <https://wiki.libsdl.org/SDL_WindowFlags>
# define SCRCPY_SDL_HAS_WINDOW_ALWAYS_ON_TOP
#endif
#if SDL_VERSION_ATLEAST(2, 0, 6)
// <https://github.com/libsdl-org/SDL/commit/d7a318de563125e5bb465b1000d6bc9576fbc6fc>
# define SCRCPY_SDL_HAS_HINT_TOUCH_MOUSE_EVENTS

View File

@ -41,7 +41,7 @@ static const char *const android_motionevent_action_labels[] = {
"pointer-up",
"hover-move",
"scroll",
"hover-enter"
"hover-enter",
"hover-exit",
"btn-press",
"btn-release",
@ -69,7 +69,7 @@ write_position(uint8_t *buf, const struct sc_position *position) {
buffer_write16be(&buf[10], position->screen_size.height);
}
// write length (2 bytes) + string (non nul-terminated)
// write length (4 bytes) + string (non null-terminated)
static size_t
write_string(const char *utf8, size_t max_len, unsigned char *buf) {
size_t len = sc_str_utf8_truncation_index(utf8, max_len);

View File

@ -14,8 +14,8 @@
#define CONTROL_MSG_MAX_SIZE (1 << 18) // 256k
#define CONTROL_MSG_INJECT_TEXT_MAX_LENGTH 300
// type: 1 byte; paste flag: 1 byte; length: 4 bytes
#define CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH (CONTROL_MSG_MAX_SIZE - 6)
// type: 1 byte; sequence: 8 bytes; paste flag: 1 byte; length: 4 bytes
#define CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH (CONTROL_MSG_MAX_SIZE - 14)
#define POINTER_ID_MOUSE UINT64_C(-1)
#define POINTER_ID_VIRTUAL_FINGER UINT64_C(-2)

View File

@ -110,7 +110,7 @@ controller_start(struct controller *controller) {
LOGD("Starting controller thread");
bool ok = sc_thread_create(&controller->thread, run_controller,
"controller", controller);
"scrcpy-ctl", controller);
if (!ok) {
LOGC("Could not start controller thread");
return false;

View File

@ -46,6 +46,8 @@ decoder_open(struct decoder *decoder, const AVCodec *codec) {
return false;
}
decoder->codec_ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
if (avcodec_open2(decoder->codec_ctx, codec, NULL) < 0) {
LOGE("Could not open codec");
avcodec_free_context(&decoder->codec_ctx);

View File

@ -154,7 +154,7 @@ file_handler_start(struct file_handler *file_handler) {
LOGD("Starting file_handler thread");
bool ok = sc_thread_create(&file_handler->thread, run_file_handler,
"file_handler", file_handler);
"scrcpy-file", file_handler);
if (!ok) {
LOGC("Could not start file_handler thread");
return false;

View File

@ -108,7 +108,7 @@ fps_counter_start(struct fps_counter *counter) {
// same thread, no need to lock
if (!counter->thread_started) {
bool ok = sc_thread_create(&counter->thread, run_fps_counter,
"fps counter", counter);
"scrcpy-fps", counter);
if (!ok) {
LOGE("Could not start FPS counter thread");
return false;

View File

@ -1,8 +1,8 @@
#include "hid_keyboard.h"
#include <assert.h>
#include <SDL2/SDL_events.h>
#include "input_events.h"
#include "util/log.h"
/** Downcast key processor to hid_keyboard */
@ -126,31 +126,105 @@ static const unsigned char keyboard_report_desc[] = {
0xC0
};
/**
* A keyboard HID event is 8 bytes long:
*
* - byte 0: modifiers (1 flag per modifier key, 8 possible modifier keys)
* - byte 1: reserved (always 0)
* - bytes 2 to 7: pressed keys (6 at most)
*
* 7 6 5 4 3 2 1 0
* +---------------+
* byte 0: |. . . . . . . .| modifiers
* +---------------+
* ^ ^ ^ ^ ^ ^ ^ ^
* | | | | | | | `- left Ctrl
* | | | | | | `--- left Shift
* | | | | | `----- left Alt
* | | | | `------- left Gui
* | | | `--------- right Ctrl
* | | `----------- right Shift
* | `------------- right Alt
* `--------------- right Gui
*
* +---------------+
* byte 1: |0 0 0 0 0 0 0 0| reserved
* +---------------+
*
* +---------------+
* bytes 2 to 7: |. . . . . . . .| scancode of 1st key pressed
* +---------------+
* |. . . . . . . .| scancode of 2nd key pressed
* +---------------+
* |. . . . . . . .| scancode of 3rd key pressed
* +---------------+
* |. . . . . . . .| scancode of 4th key pressed
* +---------------+
* |. . . . . . . .| scancode of 5th key pressed
* +---------------+
* |. . . . . . . .| scancode of 6th key pressed
* +---------------+
*
* If there are less than 6 keys pressed, the last items are set to 0.
* For example, if A and W are pressed:
*
* +---------------+
* bytes 2 to 7: |0 0 0 0 0 1 0 0| A is pressed (scancode = 4)
* +---------------+
* |0 0 0 1 1 0 1 0| W is pressed (scancode = 26)
* +---------------+
* |0 0 0 0 0 0 0 0| ^
* +---------------+ | only 2 keys are pressed, the
* |0 0 0 0 0 0 0 0| | remaining items are set to 0
* +---------------+ |
* |0 0 0 0 0 0 0 0| |
* +---------------+ |
* |0 0 0 0 0 0 0 0| v
* +---------------+
*
* Pressing more than 6 keys is not supported. If this happens (typically,
* never in practice), report a "phantom state":
*
* +---------------+
* bytes 2 to 7: |0 0 0 0 0 0 0 1| ^
* +---------------+ |
* |0 0 0 0 0 0 0 1| | more than 6 keys pressed:
* +---------------+ | the list is filled with a special
* |0 0 0 0 0 0 0 1| | rollover error code (0x01)
* +---------------+ |
* |0 0 0 0 0 0 0 1| |
* +---------------+ |
* |0 0 0 0 0 0 0 1| |
* +---------------+ |
* |0 0 0 0 0 0 0 1| v
* +---------------+
*/
static unsigned char
sdl_keymod_to_hid_modifiers(SDL_Keymod mod) {
sdl_keymod_to_hid_modifiers(uint16_t mod) {
unsigned char modifiers = HID_MODIFIER_NONE;
if (mod & KMOD_LCTRL) {
if (mod & SC_MOD_LCTRL) {
modifiers |= HID_MODIFIER_LEFT_CONTROL;
}
if (mod & KMOD_LSHIFT) {
if (mod & SC_MOD_LSHIFT) {
modifiers |= HID_MODIFIER_LEFT_SHIFT;
}
if (mod & KMOD_LALT) {
if (mod & SC_MOD_LALT) {
modifiers |= HID_MODIFIER_LEFT_ALT;
}
if (mod & KMOD_LGUI) {
if (mod & SC_MOD_LGUI) {
modifiers |= HID_MODIFIER_LEFT_GUI;
}
if (mod & KMOD_RCTRL) {
if (mod & SC_MOD_RCTRL) {
modifiers |= HID_MODIFIER_RIGHT_CONTROL;
}
if (mod & KMOD_RSHIFT) {
if (mod & SC_MOD_RSHIFT) {
modifiers |= HID_MODIFIER_RIGHT_SHIFT;
}
if (mod & KMOD_RALT) {
if (mod & SC_MOD_RALT) {
modifiers |= HID_MODIFIER_RIGHT_ALT;
}
if (mod & KMOD_RGUI) {
if (mod & SC_MOD_RGUI) {
modifiers |= HID_MODIFIER_RIGHT_GUI;
}
return modifiers;
@ -174,15 +248,15 @@ sc_hid_keyboard_event_init(struct sc_hid_event *hid_event) {
}
static inline bool
scancode_is_modifier(SDL_Scancode scancode) {
return scancode >= SDL_SCANCODE_LCTRL && scancode <= SDL_SCANCODE_RGUI;
scancode_is_modifier(enum sc_scancode scancode) {
return scancode >= SC_SCANCODE_LCTRL && scancode <= SC_SCANCODE_RGUI;
}
static bool
convert_hid_keyboard_event(struct sc_hid_keyboard *kb,
struct sc_hid_event *hid_event,
const SDL_KeyboardEvent *event) {
SDL_Scancode scancode = event->keysym.scancode;
const struct sc_key_event *event) {
enum sc_scancode scancode = event->scancode;
assert(scancode >= 0);
// SDL also generates events when only modifiers are pressed, we cannot
@ -198,11 +272,11 @@ convert_hid_keyboard_event(struct sc_hid_keyboard *kb,
return false;
}
unsigned char modifiers = sdl_keymod_to_hid_modifiers(event->keysym.mod);
unsigned char modifiers = sdl_keymod_to_hid_modifiers(event->mods_state);
if (scancode < SC_HID_KEYBOARD_KEYS) {
// Pressed is true and released is false
kb->keys[scancode] = (event->type == SDL_KEYDOWN);
kb->keys[scancode] = (event->action == SC_ACTION_DOWN);
LOGV("keys[%02x] = %s", scancode,
kb->keys[scancode] ? "true" : "false");
}
@ -217,7 +291,7 @@ convert_hid_keyboard_event(struct sc_hid_keyboard *kb,
// USB HID protocol says that if keys exceeds report count, a
// phantom state should be reported
if (keys_pressed_count >= HID_KEYBOARD_MAX_KEYS) {
// Pantom state:
// Phantom state:
// - Modifiers
// - Reserved
// - ErrorRollOver * HID_MAX_KEYS
@ -232,17 +306,17 @@ convert_hid_keyboard_event(struct sc_hid_keyboard *kb,
end:
LOGV("hid keyboard: key %-4s scancode=%02x (%u) mod=%02x",
event->type == SDL_KEYDOWN ? "down" : "up", event->keysym.scancode,
event->keysym.scancode, modifiers);
event->action == SC_ACTION_DOWN ? "down" : "up", event->scancode,
event->scancode, modifiers);
return true;
}
static bool
push_mod_lock_state(struct sc_hid_keyboard *kb, uint16_t sdl_mod) {
bool capslock = sdl_mod & KMOD_CAPS;
bool numlock = sdl_mod & KMOD_NUM;
push_mod_lock_state(struct sc_hid_keyboard *kb, uint16_t mods_state) {
bool capslock = mods_state & SC_MOD_CAPS;
bool numlock = mods_state & SC_MOD_NUM;
if (!capslock && !numlock) {
// Nothing to do
return true;
@ -254,8 +328,6 @@ push_mod_lock_state(struct sc_hid_keyboard *kb, uint16_t sdl_mod) {
return false;
}
#define SC_SCANCODE_CAPSLOCK SDL_SCANCODE_CAPSLOCK
#define SC_SCANCODE_NUMLOCK SDL_SCANCODE_NUMLOCKCLEAR
unsigned i = 0;
if (capslock) {
hid_event.buffer[HID_KEYBOARD_INDEX_KEYS + i] = SC_SCANCODE_CAPSLOCK;
@ -279,7 +351,7 @@ push_mod_lock_state(struct sc_hid_keyboard *kb, uint16_t sdl_mod) {
static void
sc_key_processor_process_key(struct sc_key_processor *kp,
const SDL_KeyboardEvent *event,
const struct sc_key_event *event,
uint64_t ack_to_wait) {
if (event->repeat) {
// In USB HID protocol, key repeat is handled by the host (Android), so
@ -295,7 +367,7 @@ sc_key_processor_process_key(struct sc_key_processor *kp,
if (!kb->mod_lock_synchronized) {
// Inject CAPSLOCK and/or NUMLOCK if necessary to synchronize
// keyboard state
if (push_mod_lock_state(kb, event->keysym.mod)) {
if (push_mod_lock_state(kb, event->mods_state)) {
kb->mod_lock_synchronized = true;
}
}
@ -317,7 +389,7 @@ sc_key_processor_process_key(struct sc_key_processor *kp,
static void
sc_key_processor_process_text(struct sc_key_processor *kp,
const SDL_TextInputEvent *event) {
const struct sc_text_event *event) {
(void) kp;
(void) event;

76
app/src/input_events.c Normal file
View File

@ -0,0 +1,76 @@
#include <input_events.h>
static inline uint16_t
sc_mods_state_from_sdl(uint16_t mods_state) {
return mods_state;
}
static inline enum sc_keycode
sc_keycode_from_sdl(SDL_Keycode keycode) {
return (enum sc_keycode) keycode;
}
static inline enum sc_scancode
sc_scancode_from_sdl(SDL_Scancode scancode) {
return (enum sc_scancode) scancode;
}
static inline enum sc_action
sc_action_from_sdl_keyboard_type(uint32_t type) {
assert(type == SDL_KEYDOWN || type == SDL_KEYUP);
if (type == SDL_KEYDOWN) {
return SC_ACTION_DOWN;
}
return SC_ACTION_UP;
}
static inline enum sc_action
sc_action_from_sdl_mousebutton_type(uint32_t type) {
assert(type == SDL_MOUSEBUTTONDOWN || type == SDL_MOUSEBUTTONUP);
if (type == SDL_MOUSEBUTTONDOWN) {
return SC_ACTION_DOWN;
}
return SC_ACTION_UP;
}
static inline enum sc_mouse_button
sc_mouse_button_from_sdl(uint8_t button) {
if (button >= SDL_BUTTON_LEFT && button <= SDL_BUTTON_X2) {
// SC_MOUSE_BUTTON_* constants are initialized from SDL_BUTTON(index)
return SDL_BUTTON(button);
}
return SC_MOUSE_BUTTON_UNKNOWN;
}
static inline uint8_t
sc_mouse_buttons_state_from_sdl(uint32_t buttons_state) {
assert(buttons_state < 0x100); // fits in uint8_t
return buttons_state;
}
void
sc_key_event_from_sdl(struct sc_key_event *event,
const SDL_KeyboardEvent *sdl) {
event->action = sc_action_from_sdl_keyboard_type(sdl->type);
event->keycode = sc_keycode_from_sdl(sdl->keysym.sym);
event->scancode = sc_scancode_from_sdl(sdl->keysym.scancode);
event->repeat = sdl->repeat;
event->mods_state = sc_mods_state_from_sdl(sdl->keysym.mod);
}
void
sc_text_event_from_sdl(struct sc_text_event *event,
const SDL_TextInputEvent *sdl) {
event->text = sdl->text;
}
void
sc_mouse_click_event_from_sdl(struct sc_mouse_click_event *event,
const SDL_MouseButtonEvent *sdl,
const SDL_Window *window,
struct sc_size screen_size) {
event->action = sc_action_from_sdl_mousebutton_type(sdl->type);
event->button = sc_mouse_button_from_sdl(sdl->button);
event->position.screen_size = screen_size;
}

404
app/src/input_events.h Normal file
View File

@ -0,0 +1,404 @@
#ifndef SC_INPUT_EVENTS_H
#define SC_INPUT_EVENTS_H
#include "common.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <SDL2/SDL_events.h>
#include "coords.h"
/* The values are purposely the same as the SDL constants, so that the
* implementation to convert from the SDL version to the scrcpy version is
* straightforward */
enum sc_mod {
SC_MOD_LSHIFT = KMOD_LSHIFT,
SC_MOD_RSHIFT = KMOD_RSHIFT,
SC_MOD_LCTRL = KMOD_LCTRL,
SC_MOD_RCTRL = KMOD_RCTRL,
SC_MOD_LALT = KMOD_LALT,
SC_MOD_RALT = KMOD_RALT,
SC_MOD_LGUI = KMOD_LGUI,
SC_MOD_RGUI = KMOD_RGUI,
SC_MOD_NUM = KMOD_NUM,
SC_MOD_CAPS = KMOD_CAPS,
SC_MOD_SCROLL = KMOD_SCROLL,
};
enum sc_action {
SC_ACTION_DOWN = 1, // key or button pressed
SC_ACTION_UP = 2, // key or button released
};
enum sc_keycode {
SC_KEYCODE_UNKNOWN = SDLK_UNKNOWN,
SC_KEYCODE_RETURN = SDLK_RETURN,
SC_KEYCODE_ESCAPE = SDLK_ESCAPE,
SC_KEYCODE_BACKSPACE = SDLK_BACKSPACE,
SC_KEYCODE_TAB = SDLK_TAB,
SC_KEYCODE_SPACE = SDLK_SPACE,
SC_KEYCODE_EXCLAIM = SDLK_EXCLAIM,
SC_KEYCODE_QUOTEDBL = SDLK_QUOTEDBL,
SC_KEYCODE_HASH = SDLK_HASH,
SC_KEYCODE_PERCENT = SDLK_PERCENT,
SC_KEYCODE_DOLLAR = SDLK_DOLLAR,
SC_KEYCODE_AMPERSAND = SDLK_AMPERSAND,
SC_KEYCODE_QUOTE = SDLK_QUOTE,
SC_KEYCODE_LEFTPAREN = SDLK_LEFTPAREN,
SC_KEYCODE_RIGHTPAREN = SDLK_RIGHTPAREN,
SC_KEYCODE_ASTERISK = SDLK_ASTERISK,
SC_KEYCODE_PLUS = SDLK_PLUS,
SC_KEYCODE_COMMA = SDLK_COMMA,
SC_KEYCODE_MINUS = SDLK_MINUS,
SC_KEYCODE_PERIOD = SDLK_PERIOD,
SC_KEYCODE_SLASH = SDLK_SLASH,
SC_KEYCODE_0 = SDLK_0,
SC_KEYCODE_1 = SDLK_1,
SC_KEYCODE_2 = SDLK_2,
SC_KEYCODE_3 = SDLK_3,
SC_KEYCODE_4 = SDLK_4,
SC_KEYCODE_5 = SDLK_5,
SC_KEYCODE_6 = SDLK_6,
SC_KEYCODE_7 = SDLK_7,
SC_KEYCODE_8 = SDLK_8,
SC_KEYCODE_9 = SDLK_9,
SC_KEYCODE_COLON = SDLK_COLON,
SC_KEYCODE_SEMICOLON = SDLK_SEMICOLON,
SC_KEYCODE_LESS = SDLK_LESS,
SC_KEYCODE_EQUALS = SDLK_EQUALS,
SC_KEYCODE_GREATER = SDLK_GREATER,
SC_KEYCODE_QUESTION = SDLK_QUESTION,
SC_KEYCODE_AT = SDLK_AT,
SC_KEYCODE_LEFTBRACKET = SDLK_LEFTBRACKET,
SC_KEYCODE_BACKSLASH = SDLK_BACKSLASH,
SC_KEYCODE_RIGHTBRACKET = SDLK_RIGHTBRACKET,
SC_KEYCODE_CARET = SDLK_CARET,
SC_KEYCODE_UNDERSCORE = SDLK_UNDERSCORE,
SC_KEYCODE_BACKQUOTE = SDLK_BACKQUOTE,
SC_KEYCODE_a = SDLK_a,
SC_KEYCODE_b = SDLK_b,
SC_KEYCODE_c = SDLK_c,
SC_KEYCODE_d = SDLK_d,
SC_KEYCODE_e = SDLK_e,
SC_KEYCODE_f = SDLK_f,
SC_KEYCODE_g = SDLK_g,
SC_KEYCODE_h = SDLK_h,
SC_KEYCODE_i = SDLK_i,
SC_KEYCODE_j = SDLK_j,
SC_KEYCODE_k = SDLK_k,
SC_KEYCODE_l = SDLK_l,
SC_KEYCODE_m = SDLK_m,
SC_KEYCODE_n = SDLK_n,
SC_KEYCODE_o = SDLK_o,
SC_KEYCODE_p = SDLK_p,
SC_KEYCODE_q = SDLK_q,
SC_KEYCODE_r = SDLK_r,
SC_KEYCODE_s = SDLK_s,
SC_KEYCODE_t = SDLK_t,
SC_KEYCODE_u = SDLK_u,
SC_KEYCODE_v = SDLK_v,
SC_KEYCODE_w = SDLK_w,
SC_KEYCODE_x = SDLK_x,
SC_KEYCODE_y = SDLK_y,
SC_KEYCODE_z = SDLK_z,
SC_KEYCODE_CAPSLOCK = SDLK_CAPSLOCK,
SC_KEYCODE_F1 = SDLK_F1,
SC_KEYCODE_F2 = SDLK_F2,
SC_KEYCODE_F3 = SDLK_F3,
SC_KEYCODE_F4 = SDLK_F4,
SC_KEYCODE_F5 = SDLK_F5,
SC_KEYCODE_F6 = SDLK_F6,
SC_KEYCODE_F7 = SDLK_F7,
SC_KEYCODE_F8 = SDLK_F8,
SC_KEYCODE_F9 = SDLK_F9,
SC_KEYCODE_F10 = SDLK_F10,
SC_KEYCODE_F11 = SDLK_F11,
SC_KEYCODE_F12 = SDLK_F12,
SC_KEYCODE_PRINTSCREEN = SDLK_PRINTSCREEN,
SC_KEYCODE_SCROLLLOCK = SDLK_SCROLLLOCK,
SC_KEYCODE_PAUSE = SDLK_PAUSE,
SC_KEYCODE_INSERT = SDLK_INSERT,
SC_KEYCODE_HOME = SDLK_HOME,
SC_KEYCODE_PAGEUP = SDLK_PAGEUP,
SC_KEYCODE_DELETE = SDLK_DELETE,
SC_KEYCODE_END = SDLK_END,
SC_KEYCODE_PAGEDOWN = SDLK_PAGEDOWN,
SC_KEYCODE_RIGHT = SDLK_RIGHT,
SC_KEYCODE_LEFT = SDLK_LEFT,
SC_KEYCODE_DOWN = SDLK_DOWN,
SC_KEYCODE_UP = SDLK_UP,
SC_KEYCODE_KP_DIVIDE = SDLK_KP_DIVIDE,
SC_KEYCODE_KP_MULTIPLY = SDLK_KP_MULTIPLY,
SC_KEYCODE_KP_MINUS = SDLK_KP_MINUS,
SC_KEYCODE_KP_PLUS = SDLK_KP_PLUS,
SC_KEYCODE_KP_ENTER = SDLK_KP_ENTER,
SC_KEYCODE_KP_1 = SDLK_KP_1,
SC_KEYCODE_KP_2 = SDLK_KP_2,
SC_KEYCODE_KP_3 = SDLK_KP_3,
SC_KEYCODE_KP_4 = SDLK_KP_4,
SC_KEYCODE_KP_5 = SDLK_KP_5,
SC_KEYCODE_KP_6 = SDLK_KP_6,
SC_KEYCODE_KP_7 = SDLK_KP_7,
SC_KEYCODE_KP_8 = SDLK_KP_8,
SC_KEYCODE_KP_9 = SDLK_KP_9,
SC_KEYCODE_KP_0 = SDLK_KP_0,
SC_KEYCODE_KP_PERIOD = SDLK_KP_PERIOD,
SC_KEYCODE_KP_EQUALS = SDLK_KP_EQUALS,
SC_KEYCODE_KP_LEFTPAREN = SDLK_KP_LEFTPAREN,
SC_KEYCODE_KP_RIGHTPAREN = SDLK_KP_RIGHTPAREN,
SC_KEYCODE_LCTRL = SDLK_LCTRL,
SC_KEYCODE_LSHIFT = SDLK_LSHIFT,
SC_KEYCODE_LALT = SDLK_LALT,
SC_KEYCODE_LGUI = SDLK_LGUI,
SC_KEYCODE_RCTRL = SDLK_RCTRL,
SC_KEYCODE_RSHIFT = SDLK_RSHIFT,
SC_KEYCODE_RALT = SDLK_RALT,
SC_KEYCODE_RGUI = SDLK_RGUI,
};
enum sc_scancode {
SC_SCANCODE_UNKNOWN = SDL_SCANCODE_UNKNOWN,
SC_SCANCODE_A = SDL_SCANCODE_A,
SC_SCANCODE_B = SDL_SCANCODE_B,
SC_SCANCODE_C = SDL_SCANCODE_C,
SC_SCANCODE_D = SDL_SCANCODE_D,
SC_SCANCODE_E = SDL_SCANCODE_E,
SC_SCANCODE_F = SDL_SCANCODE_F,
SC_SCANCODE_G = SDL_SCANCODE_G,
SC_SCANCODE_H = SDL_SCANCODE_H,
SC_SCANCODE_I = SDL_SCANCODE_I,
SC_SCANCODE_J = SDL_SCANCODE_J,
SC_SCANCODE_K = SDL_SCANCODE_K,
SC_SCANCODE_L = SDL_SCANCODE_L,
SC_SCANCODE_M = SDL_SCANCODE_M,
SC_SCANCODE_N = SDL_SCANCODE_N,
SC_SCANCODE_O = SDL_SCANCODE_O,
SC_SCANCODE_P = SDL_SCANCODE_P,
SC_SCANCODE_Q = SDL_SCANCODE_Q,
SC_SCANCODE_R = SDL_SCANCODE_R,
SC_SCANCODE_S = SDL_SCANCODE_S,
SC_SCANCODE_T = SDL_SCANCODE_T,
SC_SCANCODE_U = SDL_SCANCODE_U,
SC_SCANCODE_V = SDL_SCANCODE_V,
SC_SCANCODE_W = SDL_SCANCODE_W,
SC_SCANCODE_X = SDL_SCANCODE_X,
SC_SCANCODE_Y = SDL_SCANCODE_Y,
SC_SCANCODE_Z = SDL_SCANCODE_Z,
SC_SCANCODE_1 = SDL_SCANCODE_1,
SC_SCANCODE_2 = SDL_SCANCODE_2,
SC_SCANCODE_3 = SDL_SCANCODE_3,
SC_SCANCODE_4 = SDL_SCANCODE_4,
SC_SCANCODE_5 = SDL_SCANCODE_5,
SC_SCANCODE_6 = SDL_SCANCODE_6,
SC_SCANCODE_7 = SDL_SCANCODE_7,
SC_SCANCODE_8 = SDL_SCANCODE_8,
SC_SCANCODE_9 = SDL_SCANCODE_9,
SC_SCANCODE_0 = SDL_SCANCODE_0,
SC_SCANCODE_RETURN = SDL_SCANCODE_RETURN,
SC_SCANCODE_ESCAPE = SDL_SCANCODE_ESCAPE,
SC_SCANCODE_BACKSPACE = SDL_SCANCODE_BACKSPACE,
SC_SCANCODE_TAB = SDL_SCANCODE_TAB,
SC_SCANCODE_SPACE = SDL_SCANCODE_SPACE,
SC_SCANCODE_MINUS = SDL_SCANCODE_MINUS,
SC_SCANCODE_EQUALS = SDL_SCANCODE_EQUALS,
SC_SCANCODE_LEFTBRACKET = SDL_SCANCODE_LEFTBRACKET,
SC_SCANCODE_RIGHTBRACKET = SDL_SCANCODE_RIGHTBRACKET,
SC_SCANCODE_BACKSLASH = SDL_SCANCODE_BACKSLASH,
SC_SCANCODE_NONUSHASH = SDL_SCANCODE_NONUSHASH,
SC_SCANCODE_SEMICOLON = SDL_SCANCODE_SEMICOLON,
SC_SCANCODE_APOSTROPHE = SDL_SCANCODE_APOSTROPHE,
SC_SCANCODE_GRAVE = SDL_SCANCODE_GRAVE,
SC_SCANCODE_COMMA = SDL_SCANCODE_COMMA,
SC_SCANCODE_PERIOD = SDL_SCANCODE_PERIOD,
SC_SCANCODE_SLASH = SDL_SCANCODE_SLASH,
SC_SCANCODE_CAPSLOCK = SDL_SCANCODE_CAPSLOCK,
SC_SCANCODE_F1 = SDL_SCANCODE_F1,
SC_SCANCODE_F2 = SDL_SCANCODE_F2,
SC_SCANCODE_F3 = SDL_SCANCODE_F3,
SC_SCANCODE_F4 = SDL_SCANCODE_F4,
SC_SCANCODE_F5 = SDL_SCANCODE_F5,
SC_SCANCODE_F6 = SDL_SCANCODE_F6,
SC_SCANCODE_F7 = SDL_SCANCODE_F7,
SC_SCANCODE_F8 = SDL_SCANCODE_F8,
SC_SCANCODE_F9 = SDL_SCANCODE_F9,
SC_SCANCODE_F10 = SDL_SCANCODE_F10,
SC_SCANCODE_F11 = SDL_SCANCODE_F11,
SC_SCANCODE_F12 = SDL_SCANCODE_F12,
SC_SCANCODE_PRINTSCREEN = SDL_SCANCODE_PRINTSCREEN,
SC_SCANCODE_SCROLLLOCK = SDL_SCANCODE_SCROLLLOCK,
SC_SCANCODE_PAUSE = SDL_SCANCODE_PAUSE,
SC_SCANCODE_INSERT = SDL_SCANCODE_INSERT,
SC_SCANCODE_HOME = SDL_SCANCODE_HOME,
SC_SCANCODE_PAGEUP = SDL_SCANCODE_PAGEUP,
SC_SCANCODE_DELETE = SDL_SCANCODE_DELETE,
SC_SCANCODE_END = SDL_SCANCODE_END,
SC_SCANCODE_PAGEDOWN = SDL_SCANCODE_PAGEDOWN,
SC_SCANCODE_RIGHT = SDL_SCANCODE_RIGHT,
SC_SCANCODE_LEFT = SDL_SCANCODE_LEFT,
SC_SCANCODE_DOWN = SDL_SCANCODE_DOWN,
SC_SCANCODE_UP = SDL_SCANCODE_UP,
SC_SCANCODE_NUMLOCK = SDL_SCANCODE_NUMLOCKCLEAR,
SC_SCANCODE_KP_DIVIDE = SDL_SCANCODE_KP_DIVIDE,
SC_SCANCODE_KP_MULTIPLY = SDL_SCANCODE_KP_MULTIPLY,
SC_SCANCODE_KP_MINUS = SDL_SCANCODE_KP_MINUS,
SC_SCANCODE_KP_PLUS = SDL_SCANCODE_KP_PLUS,
SC_SCANCODE_KP_ENTER = SDL_SCANCODE_KP_ENTER,
SC_SCANCODE_KP_1 = SDL_SCANCODE_KP_1,
SC_SCANCODE_KP_2 = SDL_SCANCODE_KP_2,
SC_SCANCODE_KP_3 = SDL_SCANCODE_KP_3,
SC_SCANCODE_KP_4 = SDL_SCANCODE_KP_4,
SC_SCANCODE_KP_5 = SDL_SCANCODE_KP_5,
SC_SCANCODE_KP_6 = SDL_SCANCODE_KP_6,
SC_SCANCODE_KP_7 = SDL_SCANCODE_KP_7,
SC_SCANCODE_KP_8 = SDL_SCANCODE_KP_8,
SC_SCANCODE_KP_9 = SDL_SCANCODE_KP_9,
SC_SCANCODE_KP_0 = SDL_SCANCODE_KP_0,
SC_SCANCODE_KP_PERIOD = SDL_SCANCODE_KP_PERIOD,
SC_SCANCODE_LCTRL = SDL_SCANCODE_LCTRL,
SC_SCANCODE_LSHIFT = SDL_SCANCODE_LSHIFT,
SC_SCANCODE_LALT = SDL_SCANCODE_LALT,
SC_SCANCODE_LGUI = SDL_SCANCODE_LGUI,
SC_SCANCODE_RCTRL = SDL_SCANCODE_RCTRL,
SC_SCANCODE_RSHIFT = SDL_SCANCODE_RSHIFT,
SC_SCANCODE_RALT = SDL_SCANCODE_RALT,
SC_SCANCODE_RGUI = SDL_SCANCODE_RGUI,
};
// On purpose, only use the "mask" values (1, 2, 4, 8, 16) for a single button,
// to avoid unnecessary conversions (and confusion).
enum sc_mouse_button {
SC_MOUSE_BUTTON_UNKNOWN = 0,
SC_MOUSE_BUTTON_LEFT = SDL_BUTTON(SDL_BUTTON_LEFT),
SC_MOUSE_BUTTON_RIGHT = SDL_BUTTON(SDL_BUTTON_RIGHT),
SC_MOUSE_BUTTON_MIDDLE = SDL_BUTTON(SDL_BUTTON_MIDDLE),
SC_MOUSE_BUTTON_X1 = SDL_BUTTON(SDL_BUTTON_X1),
SC_MOUSE_BUTTON_X2 = SDL_BUTTON(SDL_BUTTON_X2),
};
static_assert(sizeof(enum sc_mod) >= sizeof(SDL_Keymod),
"SDL_Keymod must be convertible to sc_mod");
static_assert(sizeof(enum sc_keycode) >= sizeof(SDL_Keycode),
"SDL_Keycode must be convertible to sc_keycode");
static_assert(sizeof(enum sc_scancode) >= sizeof(SDL_Scancode),
"SDL_Scancode must be convertible to sc_scancode");
enum sc_touch_action {
SC_TOUCH_ACTION_MOVE,
SC_TOUCH_ACTION_DOWN,
SC_TOUCH_ACTION_UP,
};
struct sc_key_event {
enum sc_action action;
enum sc_keycode keycode;
enum sc_scancode scancode;
uint16_t mods_state; // bitwise-OR of sc_mod values
bool repeat;
};
struct sc_text_event {
const char *text; // not owned
};
struct sc_mouse_click_event {
struct sc_position position;
enum sc_action action;
enum sc_mouse_button button;
uint8_t buttons_state; // bitwise-OR of sc_mouse_button values
};
struct sc_mouse_scroll_event {
struct sc_position position;
int32_t hscroll;
int32_t vscroll;
};
struct sc_mouse_motion_event {
struct sc_position position;
int32_t xrel;
int32_t yrel;
uint8_t buttons_state; // bitwise-OR of sc_mouse_button values
};
//enum sc_mouse_event_type {
// SC_MOUSE_EVENT_TYPE_CLICK,
// SC_MOUSE_EVENT_TYPE_MOVE,
// SC_MOUSE_EVENT_TYPE_SCROLL,
//};
//
//struct sc_mouse_event {
// enum sc_mouse_event_type type;
// struct sc_position position;
// uint8_t buttons_state; // bitwise-OR of sc_mouse_button values
// union {
// struct {
// enum sc_action action;
// enum sc_mouse_button button;
// } click;
// struct {
// int32_t xrel;
// int32_t yrel;
// } move;
// struct {
// int32_t h;
// int32_t v;
// } scroll;
// };
//};
struct sc_touch_event {
struct sc_position position;
enum sc_touch_action action;
uint64_t pointer_id;
float pressure;
};
//void
//sc_key_event_from_sdl(struct sc_key_event *event, const SDL_KeyboardEvent *sdl);
//
//void
//sc_text_event_from_sdl(struct sc_text_event *event,
// const SDL_TextInputEvent *sdl);
//
//void
//sc_mouse_click_event_from_sdl(struct sc_mouse_click_event *event,
// const SDL_MouseButtonEvent *sdl,
// const SDL_Window *window,
// struct sc_size screen_size);
//
//void
//sc_mouse_wheel_event_from_sdl(struct sc_mouse_wheel_event *event,
// const SDL_MouseWheelEvent *sdl);
//
//void
//sc_mouse_motion_event_from_sdl(struct sc_mouse_motion_event *event,
// const SDL_MouseMotionEvent *sdl);
//
//void
//sc_touch_event_from_sdl(struct sc_touch_event *event,
// const SDL_TouchFingerEvent *sdl);
#endif

View File

@ -3,32 +3,117 @@
#include <assert.h>
#include <SDL2/SDL_keycode.h>
#include "input_events.h"
#include "util/log.h"
static const int ACTION_DOWN = 1;
static const int ACTION_UP = 1 << 1;
static inline uint16_t
sc_mods_state_from_sdl(uint16_t mods_state) {
return mods_state;
}
static inline enum sc_keycode
sc_keycode_from_sdl(SDL_Keycode keycode) {
return (enum sc_keycode) keycode;
}
static inline enum sc_scancode
sc_scancode_from_sdl(SDL_Scancode scancode) {
return (enum sc_scancode) scancode;
}
static inline enum sc_action
sc_action_from_sdl_keyboard_type(uint32_t type) {
assert(type == SDL_KEYDOWN || type == SDL_KEYUP);
if (type == SDL_KEYDOWN) {
return SC_ACTION_DOWN;
}
return SC_ACTION_UP;
}
static inline enum sc_action
sc_action_from_sdl_mousebutton_type(uint32_t type) {
assert(type == SDL_MOUSEBUTTONDOWN || type == SDL_MOUSEBUTTONUP);
if (type == SDL_MOUSEBUTTONDOWN) {
return SC_ACTION_DOWN;
}
return SC_ACTION_UP;
}
static inline enum sc_touch_action
sc_touch_action_from_sdl(uint32_t type) {
assert(type == SDL_FINGERMOTION || type == SDL_FINGERDOWN ||
type == SDL_FINGERUP);
if (type == SDL_FINGERMOTION) {
return SC_TOUCH_ACTION_MOVE;
}
if (type == SDL_FINGERDOWN) {
return SC_TOUCH_ACTION_DOWN;
}
return SC_TOUCH_ACTION_UP;
}
static inline enum sc_mouse_button
sc_mouse_button_from_sdl(uint8_t button) {
if (button >= SDL_BUTTON_LEFT && button <= SDL_BUTTON_X2) {
// SC_MOUSE_BUTTON_* constants are initialized from SDL_BUTTON(index)
return SDL_BUTTON(button);
}
return SC_MOUSE_BUTTON_UNKNOWN;
}
static inline uint8_t
sc_mouse_buttons_state_from_sdl(uint32_t buttons_state) {
assert(buttons_state < 0x100); // fits in uint8_t
return buttons_state;
}
//void
//sc_key_event_from_sdl(struct sc_key_event *event,
// const SDL_KeyboardEvent *sdl) {
// event->action = sc_action_from_sdl_keyboard_type(sdl->type);
// event->keycode = sc_keycode_from_sdl(sdl->keysym.sym);
// event->scancode = sc_scancode_from_sdl(sdl->keysym.scancode);
// event->repeat = sdl->repeat;
// event->mods_state = sc_mods_state_from_sdl(sdl->keysym.mod);
//}
//
//void
//sc_text_event_from_sdl(struct sc_text_event *event,
// const SDL_TextInputEvent *sdl) {
// event->text = sdl->text;
//}
//
//void
//sc_mouse_click_event_from_sdl(struct sc_mouse_click_event *event,
// const SDL_MouseButtonEvent *sdl,
// struct sc_size screen_size) {
// event->action = sc_action_from_sdl_mousebutton_type(sdl->type);
// event->button = sc_mouse_button_from_sdl(sdl->button);
// event->position.screen_size = screen_size;
//}
#define SC_SDL_SHORTCUT_MODS_MASK (KMOD_CTRL | KMOD_ALT | KMOD_GUI)
static inline uint16_t
to_sdl_mod(unsigned mod) {
to_sdl_mod(unsigned shortcut_mod) {
uint16_t sdl_mod = 0;
if (mod & SC_MOD_LCTRL) {
if (shortcut_mod & SC_SHORTCUT_MOD_LCTRL) {
sdl_mod |= KMOD_LCTRL;
}
if (mod & SC_MOD_RCTRL) {
if (shortcut_mod & SC_SHORTCUT_MOD_RCTRL) {
sdl_mod |= KMOD_RCTRL;
}
if (mod & SC_MOD_LALT) {
if (shortcut_mod & SC_SHORTCUT_MOD_LALT) {
sdl_mod |= KMOD_LALT;
}
if (mod & SC_MOD_RALT) {
if (shortcut_mod & SC_SHORTCUT_MOD_RALT) {
sdl_mod |= KMOD_RALT;
}
if (mod & SC_MOD_LSUPER) {
if (shortcut_mod & SC_SHORTCUT_MOD_LSUPER) {
sdl_mod |= KMOD_LGUI;
}
if (mod & SC_MOD_RSUPER) {
if (shortcut_mod & SC_SHORTCUT_MOD_RSUPER) {
sdl_mod |= KMOD_RGUI;
}
return sdl_mod;
@ -97,7 +182,7 @@ send_keycode(struct controller *controller, enum android_keycode keycode,
msg.inject_keycode.metastate = 0;
msg.inject_keycode.repeat = 0;
if (actions & ACTION_DOWN) {
if (actions & SC_ACTION_DOWN) {
msg.inject_keycode.action = AKEY_EVENT_ACTION_DOWN;
if (!controller_push_msg(controller, &msg)) {
LOGW("Could not request 'inject %s (DOWN)'", name);
@ -105,7 +190,7 @@ send_keycode(struct controller *controller, enum android_keycode keycode,
}
}
if (actions & ACTION_UP) {
if (actions & SC_ACTION_UP) {
msg.inject_keycode.action = AKEY_EVENT_ACTION_UP;
if (!controller_push_msg(controller, &msg)) {
LOGW("Could not request 'inject %s (UP)'", name);
@ -155,7 +240,7 @@ press_back_or_turn_screen_on(struct controller *controller, int actions) {
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON;
if (actions & ACTION_DOWN) {
if (actions & SC_ACTION_DOWN) {
msg.back_or_screen_on.action = AKEY_EVENT_ACTION_DOWN;
if (!controller_push_msg(controller, &msg)) {
LOGW("Could not request 'press back or turn screen on'");
@ -163,7 +248,7 @@ press_back_or_turn_screen_on(struct controller *controller, int actions) {
}
}
if (actions & ACTION_UP) {
if (actions & SC_ACTION_UP) {
msg.back_or_screen_on.action = AKEY_EVENT_ACTION_UP;
if (!controller_push_msg(controller, &msg)) {
LOGW("Could not request 'press back or turn screen on'");
@ -334,7 +419,11 @@ input_manager_process_text_input(struct input_manager *im,
return;
}
im->kp->ops->process_text(im->kp, event);
struct sc_text_event evt = {
.text = event->text,
};
im->kp->ops->process_text(im->kp, &evt);
}
static bool
@ -396,7 +485,7 @@ input_manager_process_key(struct input_manager *im,
// The shortcut modifier is pressed
if (smod) {
int action = down ? ACTION_DOWN : ACTION_UP;
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
switch (keycode) {
case SDLK_h:
if (control && !shift && !repeat) {
@ -553,7 +642,15 @@ input_manager_process_key(struct input_manager *im,
}
}
im->kp->ops->process_key(im->kp, event, ack_to_wait);
struct sc_key_event evt = {
.action = sc_action_from_sdl_keyboard_type(event->type),
.keycode = sc_keycode_from_sdl(event->keysym.sym),
.scancode = sc_scancode_from_sdl(event->keysym.scancode),
.repeat = event->repeat,
.mods_state = sc_mods_state_from_sdl(event->keysym.mod),
};
im->kp->ops->process_key(im->kp, &evt, ack_to_wait);
}
static void
@ -572,6 +669,17 @@ input_manager_process_mouse_motion(struct input_manager *im,
return;
}
struct sc_mouse_motion_event evt = {
.position = {
.screen_size = im->screen->frame_size,
.point = screen_convert_window_to_frame_coords(im->screen,
event->x, event->y),
},
.xrel = event->xrel,
.yrel = event->yrel,
.buttons_state = sc_mouse_buttons_state_from_sdl(event->state),
};
im->mp->ops->process_mouse_motion(im->mp, event);
if (im->vfinger_down) {
@ -586,6 +694,24 @@ input_manager_process_mouse_motion(struct input_manager *im,
static void
input_manager_process_touch(struct input_manager *im,
const SDL_TouchFingerEvent *event) {
int dw;
int dh;
SDL_GL_GetDrawableSize(im->screen->window, &dw, &dh);
// SDL touch event coordinates are normalized in the range [0; 1]
int32_t x = event->x * dw;
int32_t y = event->y * dh;
struct sc_touch_event evt = {
.position = {
.screen_size = im->screen->frame_size,
.point = screen_convert_drawable_to_frame_coords(im->screen, x, y),
},
.action = sc_touch_action_from_sdl(event->type),
.pointer_id = event->fingerId,
.pressure = event->pressure,
};
im->mp->ops->process_touch(im->mp, event);
}
@ -601,7 +727,7 @@ input_manager_process_mouse_button(struct input_manager *im,
bool down = event->type == SDL_MOUSEBUTTONDOWN;
if (!im->forward_all_clicks) {
int action = down ? ACTION_DOWN : ACTION_UP;
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
if (control && event->button == SDL_BUTTON_X1) {
action_app_switch(im->controller, action);
@ -646,6 +772,19 @@ input_manager_process_mouse_button(struct input_manager *im,
return;
}
uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL);
struct sc_mouse_click_event evt = {
.position = {
.screen_size = im->screen->frame_size,
.point = screen_convert_window_to_frame_coords(im->screen, event->x,
event->y),
},
.action = sc_action_from_sdl_mousebutton_type(event->type),
.button = sc_mouse_button_from_sdl(event->button),
.buttons_state = sc_mouse_buttons_state_from_sdl(sdl_buttons_state),
};
im->mp->ops->process_mouse_button(im->mp, event);
// Pinch-to-zoom simulation.
@ -677,6 +816,21 @@ input_manager_process_mouse_button(struct input_manager *im,
static void
input_manager_process_mouse_wheel(struct input_manager *im,
const SDL_MouseWheelEvent *event) {
// mouse_x and mouse_y are expressed in pixels relative to the window
int mouse_x;
int mouse_y;
SDL_GetMouseState(&mouse_x, &mouse_y);
struct sc_mouse_scroll_event evt = {
.position = {
.screen_size = im->screen->frame_size,
.point = screen_convert_window_to_frame_coords(im->screen,
mouse_x, mouse_y),
},
.hscroll = event->x,
.vscroll = event->y,
};
im->mp->ops->process_mouse_wheel(im->mp, event);
}

View File

@ -1,11 +1,11 @@
#include "keyboard_inject.h"
#include <assert.h>
#include <SDL2/SDL_events.h>
#include "android/input.h"
#include "control_msg.h"
#include "controller.h"
#include "input_events.h"
#include "util/intmap.h"
#include "util/log.h"
@ -13,10 +13,10 @@
#define DOWNCAST(KP) container_of(KP, struct sc_keyboard_inject, key_processor)
static bool
convert_keycode_action(SDL_EventType from, enum android_keyevent_action *to) {
convert_keycode_action(enum sc_action from, enum android_keyevent_action *to) {
static const struct sc_intmap_entry actions[] = {
{SDL_KEYDOWN, AKEY_EVENT_ACTION_DOWN},
{SDL_KEYUP, AKEY_EVENT_ACTION_UP},
{SC_ACTION_DOWN, AKEY_EVENT_ACTION_DOWN},
{SC_ACTION_UP, AKEY_EVENT_ACTION_UP},
};
const struct sc_intmap_entry *entry = SC_INTMAP_FIND_ENTRY(actions, from);
@ -29,125 +29,125 @@ convert_keycode_action(SDL_EventType from, enum android_keyevent_action *to) {
}
static bool
convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod,
convert_keycode(enum sc_keycode from, enum android_keycode *to, uint16_t mod,
enum sc_key_inject_mode key_inject_mode) {
// Navigation keys and ENTER.
// Used in all modes.
static const struct sc_intmap_entry special_keys[] = {
{SDLK_RETURN, AKEYCODE_ENTER},
{SDLK_KP_ENTER, AKEYCODE_NUMPAD_ENTER},
{SDLK_ESCAPE, AKEYCODE_ESCAPE},
{SDLK_BACKSPACE, AKEYCODE_DEL},
{SDLK_TAB, AKEYCODE_TAB},
{SDLK_PAGEUP, AKEYCODE_PAGE_UP},
{SDLK_DELETE, AKEYCODE_FORWARD_DEL},
{SDLK_HOME, AKEYCODE_MOVE_HOME},
{SDLK_END, AKEYCODE_MOVE_END},
{SDLK_PAGEDOWN, AKEYCODE_PAGE_DOWN},
{SDLK_RIGHT, AKEYCODE_DPAD_RIGHT},
{SDLK_LEFT, AKEYCODE_DPAD_LEFT},
{SDLK_DOWN, AKEYCODE_DPAD_DOWN},
{SDLK_UP, AKEYCODE_DPAD_UP},
{SDLK_LCTRL, AKEYCODE_CTRL_LEFT},
{SDLK_RCTRL, AKEYCODE_CTRL_RIGHT},
{SDLK_LSHIFT, AKEYCODE_SHIFT_LEFT},
{SDLK_RSHIFT, AKEYCODE_SHIFT_RIGHT},
{SC_KEYCODE_RETURN, AKEYCODE_ENTER},
{SC_KEYCODE_KP_ENTER, AKEYCODE_NUMPAD_ENTER},
{SC_KEYCODE_ESCAPE, AKEYCODE_ESCAPE},
{SC_KEYCODE_BACKSPACE, AKEYCODE_DEL},
{SC_KEYCODE_TAB, AKEYCODE_TAB},
{SC_KEYCODE_PAGEUP, AKEYCODE_PAGE_UP},
{SC_KEYCODE_DELETE, AKEYCODE_FORWARD_DEL},
{SC_KEYCODE_HOME, AKEYCODE_MOVE_HOME},
{SC_KEYCODE_END, AKEYCODE_MOVE_END},
{SC_KEYCODE_PAGEDOWN, AKEYCODE_PAGE_DOWN},
{SC_KEYCODE_RIGHT, AKEYCODE_DPAD_RIGHT},
{SC_KEYCODE_LEFT, AKEYCODE_DPAD_LEFT},
{SC_KEYCODE_DOWN, AKEYCODE_DPAD_DOWN},
{SC_KEYCODE_UP, AKEYCODE_DPAD_UP},
{SC_KEYCODE_LCTRL, AKEYCODE_CTRL_LEFT},
{SC_KEYCODE_RCTRL, AKEYCODE_CTRL_RIGHT},
{SC_KEYCODE_LSHIFT, AKEYCODE_SHIFT_LEFT},
{SC_KEYCODE_RSHIFT, AKEYCODE_SHIFT_RIGHT},
};
// Numpad navigation keys.
// Used in all modes, when NumLock and Shift are disabled.
static const struct sc_intmap_entry kp_nav_keys[] = {
{SDLK_KP_0, AKEYCODE_INSERT},
{SDLK_KP_1, AKEYCODE_MOVE_END},
{SDLK_KP_2, AKEYCODE_DPAD_DOWN},
{SDLK_KP_3, AKEYCODE_PAGE_DOWN},
{SDLK_KP_4, AKEYCODE_DPAD_LEFT},
{SDLK_KP_6, AKEYCODE_DPAD_RIGHT},
{SDLK_KP_7, AKEYCODE_MOVE_HOME},
{SDLK_KP_8, AKEYCODE_DPAD_UP},
{SDLK_KP_9, AKEYCODE_PAGE_UP},
{SDLK_KP_PERIOD, AKEYCODE_FORWARD_DEL},
{SC_KEYCODE_KP_0, AKEYCODE_INSERT},
{SC_KEYCODE_KP_1, AKEYCODE_MOVE_END},
{SC_KEYCODE_KP_2, AKEYCODE_DPAD_DOWN},
{SC_KEYCODE_KP_3, AKEYCODE_PAGE_DOWN},
{SC_KEYCODE_KP_4, AKEYCODE_DPAD_LEFT},
{SC_KEYCODE_KP_6, AKEYCODE_DPAD_RIGHT},
{SC_KEYCODE_KP_7, AKEYCODE_MOVE_HOME},
{SC_KEYCODE_KP_8, AKEYCODE_DPAD_UP},
{SC_KEYCODE_KP_9, AKEYCODE_PAGE_UP},
{SC_KEYCODE_KP_PERIOD, AKEYCODE_FORWARD_DEL},
};
// Letters and space.
// Used in non-text mode.
static const struct sc_intmap_entry alphaspace_keys[] = {
{SDLK_a, AKEYCODE_A},
{SDLK_b, AKEYCODE_B},
{SDLK_c, AKEYCODE_C},
{SDLK_d, AKEYCODE_D},
{SDLK_e, AKEYCODE_E},
{SDLK_f, AKEYCODE_F},
{SDLK_g, AKEYCODE_G},
{SDLK_h, AKEYCODE_H},
{SDLK_i, AKEYCODE_I},
{SDLK_j, AKEYCODE_J},
{SDLK_k, AKEYCODE_K},
{SDLK_l, AKEYCODE_L},
{SDLK_m, AKEYCODE_M},
{SDLK_n, AKEYCODE_N},
{SDLK_o, AKEYCODE_O},
{SDLK_p, AKEYCODE_P},
{SDLK_q, AKEYCODE_Q},
{SDLK_r, AKEYCODE_R},
{SDLK_s, AKEYCODE_S},
{SDLK_t, AKEYCODE_T},
{SDLK_u, AKEYCODE_U},
{SDLK_v, AKEYCODE_V},
{SDLK_w, AKEYCODE_W},
{SDLK_x, AKEYCODE_X},
{SDLK_y, AKEYCODE_Y},
{SDLK_z, AKEYCODE_Z},
{SDLK_SPACE, AKEYCODE_SPACE},
{SC_KEYCODE_a, AKEYCODE_A},
{SC_KEYCODE_b, AKEYCODE_B},
{SC_KEYCODE_c, AKEYCODE_C},
{SC_KEYCODE_d, AKEYCODE_D},
{SC_KEYCODE_e, AKEYCODE_E},
{SC_KEYCODE_f, AKEYCODE_F},
{SC_KEYCODE_g, AKEYCODE_G},
{SC_KEYCODE_h, AKEYCODE_H},
{SC_KEYCODE_i, AKEYCODE_I},
{SC_KEYCODE_j, AKEYCODE_J},
{SC_KEYCODE_k, AKEYCODE_K},
{SC_KEYCODE_l, AKEYCODE_L},
{SC_KEYCODE_m, AKEYCODE_M},
{SC_KEYCODE_n, AKEYCODE_N},
{SC_KEYCODE_o, AKEYCODE_O},
{SC_KEYCODE_p, AKEYCODE_P},
{SC_KEYCODE_q, AKEYCODE_Q},
{SC_KEYCODE_r, AKEYCODE_R},
{SC_KEYCODE_s, AKEYCODE_S},
{SC_KEYCODE_t, AKEYCODE_T},
{SC_KEYCODE_u, AKEYCODE_U},
{SC_KEYCODE_v, AKEYCODE_V},
{SC_KEYCODE_w, AKEYCODE_W},
{SC_KEYCODE_x, AKEYCODE_X},
{SC_KEYCODE_y, AKEYCODE_Y},
{SC_KEYCODE_z, AKEYCODE_Z},
{SC_KEYCODE_SPACE, AKEYCODE_SPACE},
};
// Numbers and punctuation keys.
// Used in raw mode only.
static const struct sc_intmap_entry numbers_punct_keys[] = {
{SDLK_HASH, AKEYCODE_POUND},
{SDLK_PERCENT, AKEYCODE_PERIOD},
{SDLK_QUOTE, AKEYCODE_APOSTROPHE},
{SDLK_ASTERISK, AKEYCODE_STAR},
{SDLK_PLUS, AKEYCODE_PLUS},
{SDLK_COMMA, AKEYCODE_COMMA},
{SDLK_MINUS, AKEYCODE_MINUS},
{SDLK_PERIOD, AKEYCODE_PERIOD},
{SDLK_SLASH, AKEYCODE_SLASH},
{SDLK_0, AKEYCODE_0},
{SDLK_1, AKEYCODE_1},
{SDLK_2, AKEYCODE_2},
{SDLK_3, AKEYCODE_3},
{SDLK_4, AKEYCODE_4},
{SDLK_5, AKEYCODE_5},
{SDLK_6, AKEYCODE_6},
{SDLK_7, AKEYCODE_7},
{SDLK_8, AKEYCODE_8},
{SDLK_9, AKEYCODE_9},
{SDLK_SEMICOLON, AKEYCODE_SEMICOLON},
{SDLK_EQUALS, AKEYCODE_EQUALS},
{SDLK_AT, AKEYCODE_AT},
{SDLK_LEFTBRACKET, AKEYCODE_LEFT_BRACKET},
{SDLK_BACKSLASH, AKEYCODE_BACKSLASH},
{SDLK_RIGHTBRACKET, AKEYCODE_RIGHT_BRACKET},
{SDLK_BACKQUOTE, AKEYCODE_GRAVE},
{SDLK_KP_1, AKEYCODE_NUMPAD_1},
{SDLK_KP_2, AKEYCODE_NUMPAD_2},
{SDLK_KP_3, AKEYCODE_NUMPAD_3},
{SDLK_KP_4, AKEYCODE_NUMPAD_4},
{SDLK_KP_5, AKEYCODE_NUMPAD_5},
{SDLK_KP_6, AKEYCODE_NUMPAD_6},
{SDLK_KP_7, AKEYCODE_NUMPAD_7},
{SDLK_KP_8, AKEYCODE_NUMPAD_8},
{SDLK_KP_9, AKEYCODE_NUMPAD_9},
{SDLK_KP_0, AKEYCODE_NUMPAD_0},
{SDLK_KP_DIVIDE, AKEYCODE_NUMPAD_DIVIDE},
{SDLK_KP_MULTIPLY, AKEYCODE_NUMPAD_MULTIPLY},
{SDLK_KP_MINUS, AKEYCODE_NUMPAD_SUBTRACT},
{SDLK_KP_PLUS, AKEYCODE_NUMPAD_ADD},
{SDLK_KP_PERIOD, AKEYCODE_NUMPAD_DOT},
{SDLK_KP_EQUALS, AKEYCODE_NUMPAD_EQUALS},
{SDLK_KP_LEFTPAREN, AKEYCODE_NUMPAD_LEFT_PAREN},
{SDLK_KP_RIGHTPAREN, AKEYCODE_NUMPAD_RIGHT_PAREN},
{SC_KEYCODE_HASH, AKEYCODE_POUND},
{SC_KEYCODE_PERCENT, AKEYCODE_PERIOD},
{SC_KEYCODE_QUOTE, AKEYCODE_APOSTROPHE},
{SC_KEYCODE_ASTERISK, AKEYCODE_STAR},
{SC_KEYCODE_PLUS, AKEYCODE_PLUS},
{SC_KEYCODE_COMMA, AKEYCODE_COMMA},
{SC_KEYCODE_MINUS, AKEYCODE_MINUS},
{SC_KEYCODE_PERIOD, AKEYCODE_PERIOD},
{SC_KEYCODE_SLASH, AKEYCODE_SLASH},
{SC_KEYCODE_0, AKEYCODE_0},
{SC_KEYCODE_1, AKEYCODE_1},
{SC_KEYCODE_2, AKEYCODE_2},
{SC_KEYCODE_3, AKEYCODE_3},
{SC_KEYCODE_4, AKEYCODE_4},
{SC_KEYCODE_5, AKEYCODE_5},
{SC_KEYCODE_6, AKEYCODE_6},
{SC_KEYCODE_7, AKEYCODE_7},
{SC_KEYCODE_8, AKEYCODE_8},
{SC_KEYCODE_9, AKEYCODE_9},
{SC_KEYCODE_SEMICOLON, AKEYCODE_SEMICOLON},
{SC_KEYCODE_EQUALS, AKEYCODE_EQUALS},
{SC_KEYCODE_AT, AKEYCODE_AT},
{SC_KEYCODE_LEFTBRACKET, AKEYCODE_LEFT_BRACKET},
{SC_KEYCODE_BACKSLASH, AKEYCODE_BACKSLASH},
{SC_KEYCODE_RIGHTBRACKET, AKEYCODE_RIGHT_BRACKET},
{SC_KEYCODE_BACKQUOTE, AKEYCODE_GRAVE},
{SC_KEYCODE_KP_1, AKEYCODE_NUMPAD_1},
{SC_KEYCODE_KP_2, AKEYCODE_NUMPAD_2},
{SC_KEYCODE_KP_3, AKEYCODE_NUMPAD_3},
{SC_KEYCODE_KP_4, AKEYCODE_NUMPAD_4},
{SC_KEYCODE_KP_5, AKEYCODE_NUMPAD_5},
{SC_KEYCODE_KP_6, AKEYCODE_NUMPAD_6},
{SC_KEYCODE_KP_7, AKEYCODE_NUMPAD_7},
{SC_KEYCODE_KP_8, AKEYCODE_NUMPAD_8},
{SC_KEYCODE_KP_9, AKEYCODE_NUMPAD_9},
{SC_KEYCODE_KP_0, AKEYCODE_NUMPAD_0},
{SC_KEYCODE_KP_DIVIDE, AKEYCODE_NUMPAD_DIVIDE},
{SC_KEYCODE_KP_MULTIPLY, AKEYCODE_NUMPAD_MULTIPLY},
{SC_KEYCODE_KP_MINUS, AKEYCODE_NUMPAD_SUBTRACT},
{SC_KEYCODE_KP_PLUS, AKEYCODE_NUMPAD_ADD},
{SC_KEYCODE_KP_PERIOD, AKEYCODE_NUMPAD_DOT},
{SC_KEYCODE_KP_EQUALS, AKEYCODE_NUMPAD_EQUALS},
{SC_KEYCODE_KP_LEFTPAREN, AKEYCODE_NUMPAD_LEFT_PAREN},
{SC_KEYCODE_KP_RIGHTPAREN, AKEYCODE_NUMPAD_RIGHT_PAREN},
};
const struct sc_intmap_entry *entry =
@ -157,7 +157,7 @@ convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod,
return true;
}
if (!(mod & (KMOD_NUM | KMOD_SHIFT))) {
if (!(mod & (SC_MOD_NUM | SC_MOD_LSHIFT | SC_MOD_RSHIFT))) {
// Handle Numpad events when Num Lock is disabled
// If SHIFT is pressed, a text event will be sent instead
entry = SC_INTMAP_FIND_ENTRY(kp_nav_keys, from);
@ -167,12 +167,13 @@ convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod,
}
}
if (key_inject_mode == SC_KEY_INJECT_MODE_TEXT && !(mod & KMOD_CTRL)) {
if (key_inject_mode == SC_KEY_INJECT_MODE_TEXT &&
!(mod & (SC_MOD_LCTRL | SC_MOD_RCTRL))) {
// do not forward alpha and space key events (unless Ctrl is pressed)
return false;
}
if (mod & (KMOD_LALT | KMOD_RALT | KMOD_LGUI | KMOD_RGUI)) {
if (mod & (SC_MOD_LALT | SC_MOD_RALT | SC_MOD_LGUI | SC_MOD_RGUI)) {
return false;
}
@ -214,70 +215,66 @@ autocomplete_metastate(enum android_metastate metastate) {
}
static enum android_metastate
convert_meta_state(SDL_Keymod mod) {
convert_meta_state(uint16_t mod) {
enum android_metastate metastate = 0;
if (mod & KMOD_LSHIFT) {
if (mod & SC_MOD_LSHIFT) {
metastate |= AMETA_SHIFT_LEFT_ON;
}
if (mod & KMOD_RSHIFT) {
if (mod & SC_MOD_RSHIFT) {
metastate |= AMETA_SHIFT_RIGHT_ON;
}
if (mod & KMOD_LCTRL) {
if (mod & SC_MOD_LCTRL) {
metastate |= AMETA_CTRL_LEFT_ON;
}
if (mod & KMOD_RCTRL) {
if (mod & SC_MOD_RCTRL) {
metastate |= AMETA_CTRL_RIGHT_ON;
}
if (mod & KMOD_LALT) {
if (mod & SC_MOD_LALT) {
metastate |= AMETA_ALT_LEFT_ON;
}
if (mod & KMOD_RALT) {
if (mod & SC_MOD_RALT) {
metastate |= AMETA_ALT_RIGHT_ON;
}
if (mod & KMOD_LGUI) { // Windows key
if (mod & SC_MOD_LGUI) { // Windows key
metastate |= AMETA_META_LEFT_ON;
}
if (mod & KMOD_RGUI) { // Windows key
if (mod & SC_MOD_RGUI) { // Windows key
metastate |= AMETA_META_RIGHT_ON;
}
if (mod & KMOD_NUM) {
if (mod & SC_MOD_NUM) {
metastate |= AMETA_NUM_LOCK_ON;
}
if (mod & KMOD_CAPS) {
if (mod & SC_MOD_CAPS) {
metastate |= AMETA_CAPS_LOCK_ON;
}
if (mod & KMOD_MODE) { // Alt Gr
// no mapping?
}
// fill the dependent fields
return autocomplete_metastate(metastate);
}
static bool
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to,
convert_input_key(const struct sc_key_event *event, struct control_msg *msg,
enum sc_key_inject_mode key_inject_mode, uint32_t repeat) {
to->type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
msg->type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
if (!convert_keycode_action(from->type, &to->inject_keycode.action)) {
if (!convert_keycode_action(event->action, &msg->inject_keycode.action)) {
return false;
}
uint16_t mod = from->keysym.mod;
if (!convert_keycode(from->keysym.sym, &to->inject_keycode.keycode, mod,
key_inject_mode)) {
if (!convert_keycode(event->keycode, &msg->inject_keycode.keycode,
event->mods_state, key_inject_mode)) {
return false;
}
to->inject_keycode.repeat = repeat;
to->inject_keycode.metastate = convert_meta_state(mod);
msg->inject_keycode.repeat = repeat;
msg->inject_keycode.metastate = convert_meta_state(event->mods_state);
return true;
}
static void
sc_key_processor_process_key(struct sc_key_processor *kp,
const SDL_KeyboardEvent *event,
const struct sc_key_event *event,
uint64_t ack_to_wait) {
// The device clipboard synchronization and the key event messages are
// serialized, there is nothing special to do to ensure that the clipboard
@ -305,7 +302,7 @@ sc_key_processor_process_key(struct sc_key_processor *kp,
static void
sc_key_processor_process_text(struct sc_key_processor *kp,
const SDL_TextInputEvent *event) {
const struct sc_text_event *event) {
struct sc_keyboard_inject *ki = DOWNCAST(kp);
if (ki->key_inject_mode == SC_KEY_INJECT_MODE_RAW) {

View File

@ -22,7 +22,7 @@ const struct scrcpy_options scrcpy_options_default = {
.tunnel_host = 0,
.tunnel_port = 0,
.shortcut_mods = {
.data = {SC_MOD_LALT, SC_MOD_LSUPER},
.data = {SC_SHORTCUT_MOD_LALT, SC_SHORTCUT_MOD_LSUPER},
.count = 2,
},
.max_size = 0,

View File

@ -55,12 +55,12 @@ enum sc_key_inject_mode {
#define SC_MAX_SHORTCUT_MODS 8
enum sc_shortcut_mod {
SC_MOD_LCTRL = 1 << 0,
SC_MOD_RCTRL = 1 << 1,
SC_MOD_LALT = 1 << 2,
SC_MOD_RALT = 1 << 3,
SC_MOD_LSUPER = 1 << 4,
SC_MOD_RSUPER = 1 << 5,
SC_SHORTCUT_MOD_LCTRL = 1 << 0,
SC_SHORTCUT_MOD_RCTRL = 1 << 1,
SC_SHORTCUT_MOD_LALT = 1 << 2,
SC_SHORTCUT_MOD_RALT = 1 << 3,
SC_SHORTCUT_MOD_LSUPER = 1 << 4,
SC_SHORTCUT_MOD_RSUPER = 1 << 5,
};
struct sc_shortcut_mods {

View File

@ -111,8 +111,8 @@ bool
receiver_start(struct receiver *receiver) {
LOGD("Starting receiver thread");
bool ok = sc_thread_create(&receiver->thread, run_receiver, "receiver",
receiver);
bool ok = sc_thread_create(&receiver->thread, run_receiver,
"scrcpy-receiver", receiver);
if (!ok) {
LOGC("Could not start receiver thread");
return false;

View File

@ -287,8 +287,8 @@ recorder_open(struct recorder *recorder, const AVCodec *input_codec) {
}
LOGD("Starting recorder thread");
ok = sc_thread_create(&recorder->thread, run_recorder, "recorder",
recorder);
ok = sc_thread_create(&recorder->thread, run_recorder, "scrcpy-recorder",
recorder);
if (!ok) {
LOGC("Could not start recorder thread");
goto error_avio_close;

View File

@ -94,12 +94,10 @@ sdl_set_hints(const char *render_driver) {
LOGW("Could not enable linear filtering");
}
#ifdef SCRCPY_SDL_HAS_HINT_MOUSE_FOCUS_CLICKTHROUGH
// Handle a click to gain focus as any other click
if (!SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1")) {
LOGW("Could not enable mouse focus clickthrough");
}
#endif
#ifdef SCRCPY_SDL_HAS_HINT_TOUCH_MOUSE_EVENTS
// Disable synthetic mouse events from touch events

View File

@ -64,12 +64,7 @@ set_window_size(struct screen *screen, struct sc_size new_size) {
static bool
get_preferred_display_bounds(struct sc_size *bounds) {
SDL_Rect rect;
#ifdef SCRCPY_SDL_HAS_GET_DISPLAY_USABLE_BOUNDS
# define GET_DISPLAY_BOUNDS(i, r) SDL_GetDisplayUsableBounds((i), (r))
#else
# define GET_DISPLAY_BOUNDS(i, r) SDL_GetDisplayBounds((i), (r))
#endif
if (GET_DISPLAY_BOUNDS(0, &rect)) {
if (SDL_GetDisplayUsableBounds(0, &rect)) {
LOGW("Could not get display usable bounds: %s", SDL_GetError());
return false;
}
@ -95,7 +90,8 @@ is_optimal_size(struct sc_size current_size, struct sc_size content_size) {
// - it keeps the aspect ratio
// - it scales down to make it fit in the display_size
static struct sc_size
get_optimal_size(struct sc_size current_size, struct sc_size content_size) {
get_optimal_size(struct sc_size current_size, struct sc_size content_size,
bool within_display_bounds) {
if (content_size.width == 0 || content_size.height == 0) {
// avoid division by 0
return current_size;
@ -104,10 +100,10 @@ get_optimal_size(struct sc_size current_size, struct sc_size content_size) {
struct sc_size window_size;
struct sc_size display_size;
if (!get_preferred_display_bounds(&display_size)) {
// could not get display bounds, do not constraint the size
window_size.width = current_size.width;
window_size.height = current_size.height;
if (!within_display_bounds ||
!get_preferred_display_bounds(&display_size)) {
// do not constraint the size
window_size = current_size;
} else {
window_size.width = MIN(current_size.width, display_size.width);
window_size.height = MIN(current_size.height, display_size.height);
@ -140,7 +136,7 @@ get_initial_optimal_size(struct sc_size content_size, uint16_t req_width,
uint16_t req_height) {
struct sc_size window_size;
if (!req_width && !req_height) {
window_size = get_optimal_size(content_size, content_size);
window_size = get_optimal_size(content_size, content_size, true);
} else {
if (req_width) {
window_size.width = req_width;
@ -394,12 +390,7 @@ screen_init(struct screen *screen, const struct screen_params *params) {
| SDL_WINDOW_RESIZABLE
| SDL_WINDOW_ALLOW_HIGHDPI;
if (params->always_on_top) {
#ifdef SCRCPY_SDL_HAS_WINDOW_ALWAYS_ON_TOP
window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
#else
LOGW("The 'always on top' flag is not available "
"(compile with SDL >= 2.0.5 to enable it)");
#endif
}
if (params->window_borderless) {
window_flags |= SDL_WINDOW_BORDERLESS;
@ -570,7 +561,7 @@ resize_for_content(struct screen *screen, struct sc_size old_content_size,
.height = (uint32_t) window_size.height * new_content_size.height
/ old_content_size.height,
};
target_size = get_optimal_size(target_size, new_content_size);
target_size = get_optimal_size(target_size, new_content_size, true);
set_window_size(screen, target_size);
}
@ -705,7 +696,7 @@ screen_resize_to_fit(struct screen *screen) {
struct sc_size window_size = get_window_size(screen);
struct sc_size optimal_size =
get_optimal_size(window_size, screen->content_size);
get_optimal_size(window_size, screen->content_size, false);
// Center the window related to the device screen
assert(optimal_size.width <= window_size.width);

View File

@ -232,7 +232,7 @@ execute_server(struct sc_server *server,
ADD_PARAM("power_off_on_close=%s", STRBOOL(params->power_off_on_close));
}
if (!params->clipboard_autosync) {
// By defaut, clipboard_autosync is true
// By default, clipboard_autosync is true
ADD_PARAM("clipboard_autosync=%s", STRBOOL(params->clipboard_autosync));
}
@ -388,6 +388,7 @@ sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) {
assert(tunnel->enabled);
const char *serial = server->params.serial;
bool control = server->params.control;
sc_socket video_socket = SC_SOCKET_NONE;
sc_socket control_socket = SC_SOCKET_NONE;
@ -397,9 +398,12 @@ sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) {
goto fail;
}
control_socket = net_accept_intr(&server->intr, tunnel->server_socket);
if (control_socket == SC_SOCKET_NONE) {
goto fail;
if (control) {
control_socket =
net_accept_intr(&server->intr, tunnel->server_socket);
if (control_socket == SC_SOCKET_NONE) {
goto fail;
}
}
} else {
uint32_t tunnel_host = server->params.tunnel_host;
@ -420,15 +424,18 @@ sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) {
goto fail;
}
// we know that the device is listening, we don't need several attempts
control_socket = net_socket();
if (control_socket == SC_SOCKET_NONE) {
goto fail;
}
bool ok = net_connect_intr(&server->intr, control_socket, tunnel_host,
tunnel_port);
if (!ok) {
goto fail;
if (control) {
// we know that the device is listening, we don't need several
// attempts
control_socket = net_socket();
if (control_socket == SC_SOCKET_NONE) {
goto fail;
}
bool ok = net_connect_intr(&server->intr, control_socket,
tunnel_host, tunnel_port);
if (!ok) {
goto fail;
}
}
}
@ -442,7 +449,7 @@ sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) {
}
assert(video_socket != SC_SOCKET_NONE);
assert(control_socket != SC_SOCKET_NONE);
assert(!control || control_socket != SC_SOCKET_NONE);
server->video_socket = video_socket;
server->control_socket = control_socket;
@ -756,6 +763,17 @@ run_server(void *data) {
}
sc_mutex_unlock(&server->mutex);
// Interrupt sockets to wake up socket blocking calls on the server
assert(server->video_socket != SC_SOCKET_NONE);
net_interrupt(server->video_socket);
net_close(server->video_socket);
if (server->control_socket != SC_SOCKET_NONE) {
// There is no control_socket if --no-control is set
net_interrupt(server->control_socket);
net_close(server->control_socket);
}
// Give some delay for the server to terminate properly
#define WATCHDOG_DELAY SC_TICK_FROM_SEC(1)
sc_tick deadline = sc_tick_now() + WATCHDOG_DELAY;
@ -786,7 +804,8 @@ error_connection_failed:
bool
sc_server_start(struct sc_server *server) {
bool ok = sc_thread_create(&server->thread, run_server, "server", server);
bool ok =
sc_thread_create(&server->thread, run_server, "scrcpy-server", server);
if (!ok) {
LOGE("Could not create server thread");
return false;

View File

@ -284,7 +284,8 @@ bool
stream_start(struct stream *stream) {
LOGD("Starting stream thread");
bool ok = sc_thread_create(&stream->thread, run_stream, "stream", stream);
bool ok =
sc_thread_create(&stream->thread, run_stream, "scrcpy-stream", stream);
if (!ok) {
LOGC("Could not start stream thread");
return false;

View File

@ -119,6 +119,13 @@ sc_process_execute_p(const char *const argv[], sc_pid *pid, unsigned flags,
close(internal[0]);
enum sc_process_result err;
// Somehow SDL masks many signals - undo them for other processes
// https://github.com/libsdl-org/SDL/blob/release-2.0.18/src/thread/pthread/SDL_systhread.c#L167
sigset_t mask;
sigemptyset(&mask);
sigprocmask(SIG_SETMASK, &mask, NULL);
if (fcntl(internal[1], F_SETFD, FD_CLOEXEC) == 0) {
execvp(argv[0], (char *const *) argv);
perror("exec");

View File

@ -30,9 +30,7 @@ sc_process_execute_p(const char *const argv[], HANDLE *handle, unsigned flags,
bool inherit_stderr = !perr && !(flags & SC_PROCESS_NO_STDERR);
// Add 1 per non-NULL pointer
unsigned handle_count = !!pin
+ (pout || inherit_stdout)
+ (perr || inherit_stderr);
unsigned handle_count = !!pin || !!pout || !!perr;
enum sc_process_result ret = SC_PROCESS_ERROR_GENERIC;
@ -81,23 +79,29 @@ sc_process_execute_p(const char *const argv[], HANDLE *handle, unsigned flags,
si.StartupInfo.cb = sizeof(si);
HANDLE handles[3];
si.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
if (inherit_stdout) {
si.StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
}
if (inherit_stderr) {
si.StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
}
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL;
if (handle_count) {
si.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
unsigned i = 0;
if (pin) {
si.StartupInfo.hStdInput = stdin_read_handle;
handles[i++] = si.StartupInfo.hStdInput;
}
if (pout || inherit_stdout) {
si.StartupInfo.hStdOutput = pout ? stdout_write_handle
: GetStdHandle(STD_OUTPUT_HANDLE);
if (pout) {
assert(!inherit_stdout);
si.StartupInfo.hStdOutput = stdout_write_handle;
handles[i++] = si.StartupInfo.hStdOutput;
}
if (perr || inherit_stderr) {
si.StartupInfo.hStdError = perr ? stderr_write_handle
: GetStdHandle(STD_ERROR_HANDLE);
if (perr) {
assert(!inherit_stderr);
si.StartupInfo.hStdError = stderr_write_handle;
handles[i++] = si.StartupInfo.hStdError;
}
@ -146,15 +150,22 @@ sc_process_execute_p(const char *const argv[], HANDLE *handle, unsigned flags,
goto error_free_attribute_list;
}
BOOL bInheritHandles = handle_count > 0;
// DETACHED_PROCESS to disable stdin, stdout and stderr
DWORD dwCreationFlags = handle_count > 0 ? EXTENDED_STARTUPINFO_PRESENT
: DETACHED_PROCESS;
BOOL bInheritHandles = handle_count > 0 || inherit_stdout || inherit_stderr;
DWORD dwCreationFlags = 0;
if (handle_count > 0) {
dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT;
}
if (!inherit_stdout && !inherit_stderr) {
// DETACHED_PROCESS to disable stdin, stdout and stderr
dwCreationFlags |= DETACHED_PROCESS;
}
BOOL ok = CreateProcessW(NULL, wide, NULL, NULL, bInheritHandles,
dwCreationFlags, NULL, NULL, &si.StartupInfo, &pi);
free(wide);
if (!ok) {
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
int err = GetLastError();
LOGE("CreateProcessW() error %d", err);
if (err == ERROR_FILE_NOT_FOUND) {
ret = SC_PROCESS_ERROR_MISSING_BINARY;
}
goto error_free_attribute_list;

View File

@ -6,7 +6,7 @@
#include <assert.h>
#include <stdbool.h>
#include <SDL2/SDL_events.h>
#include "input_events.h"
/**
* Key processor trait.
@ -37,12 +37,12 @@ struct sc_key_processor_ops {
* Ctrl+v on the device.
*/
void
(*process_key)(struct sc_key_processor *kp, const SDL_KeyboardEvent *event,
uint64_t ack_to_wait);
(*process_key)(struct sc_key_processor *kp,
const struct sc_key_event *event, uint64_t ack_to_wait);
void
(*process_text)(struct sc_key_processor *kp,
const SDL_TextInputEvent *event);
const struct sc_text_event *event);
};
#endif

View File

@ -64,7 +64,7 @@ sc_process_observer_init(struct sc_process_observer *observer, sc_pid pid,
observer->listener_userdata = listener_userdata;
observer->terminated = false;
ok = sc_thread_create(&observer->thread, run_observer, "process_observer",
ok = sc_thread_create(&observer->thread, run_observer, "scrcpy-proc",
observer);
if (!ok) {
sc_cond_destroy(&observer->cond_terminated);

View File

@ -240,7 +240,7 @@ sc_str_wrap_lines(const char *input, unsigned columns, unsigned indent) {
APPEND_INDENT();
// The last separator encountered, it must be inserted only conditionnaly,
// The last separator encountered, it must be inserted only conditionally,
// depending on the next token
char pending = 0;

View File

@ -8,6 +8,10 @@
bool
sc_thread_create(sc_thread *thread, sc_thread_fn fn, const char *name,
void *userdata) {
// The thread name length is limited on some systems. Never use a name
// longer than 16 bytes (including the final '\0')
assert(strlen(name) <= 15);
SDL_Thread *sdl_thread = SDL_CreateThread(fn, name, userdata);
if (!sdl_thread) {
LOG_OOM();

View File

@ -1,16 +1,55 @@
#include "tick.h"
#include <SDL2/SDL_timer.h>
#include <assert.h>
#include <time.h>
#ifdef _WIN32
# include <windows.h>
#endif
sc_tick
sc_tick_now(void) {
// SDL_GetTicks() resolution is in milliseconds, but sc_tick are expressed
// in microseconds to store PTS without precision loss.
//
// As an alternative, SDL_GetPerformanceCounter() and
// SDL_GetPerformanceFrequency() could be used, but:
// - the conversions (avoiding overflow) are expansive, since the
// frequency is not known at compile time;
// - in practice, we don't need more precision for now.
return (sc_tick) SDL_GetTicks() * 1000;
#ifndef _WIN32
// Maximum sc_tick precision (microsecond)
struct timespec ts;
int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
if (ret) {
abort();
}
return SC_TICK_FROM_SEC(ts.tv_sec) + SC_TICK_FROM_NS(ts.tv_nsec);
#else
LARGE_INTEGER c;
// On systems that run Windows XP or later, the function will always
// succeed and will thus never return zero.
// <https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter>
// <https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancefrequency>
BOOL ok = QueryPerformanceCounter(&c);
assert(ok);
(void) ok;
LONGLONG counter = c.QuadPart;
static LONGLONG frequency;
if (!frequency) {
// Initialize on first call
LARGE_INTEGER f;
ok = QueryPerformanceFrequency(&f);
assert(ok);
frequency = f.QuadPart;
assert(frequency);
}
if (frequency % SC_TICK_FREQ == 0) {
// Expected case (typically frequency = 10000000, i.e. 100ns precision)
sc_tick div = frequency / SC_TICK_FREQ;
return SC_TICK_FROM_US(counter / div);
}
// Split the division to avoid overflow
sc_tick secs = SC_TICK_FROM_SEC(counter / frequency);
sc_tick subsec = SC_TICK_FREQ * (counter % frequency) / frequency;
return secs + subsec;
#endif
}

View File

@ -10,9 +10,11 @@ typedef int64_t sc_tick;
#define SC_TICK_FREQ 1000000 // microsecond
// To be adapted if SC_TICK_FREQ changes
#define SC_TICK_TO_NS(tick) ((tick) * 1000)
#define SC_TICK_TO_US(tick) (tick)
#define SC_TICK_TO_MS(tick) ((tick) / 1000)
#define SC_TICK_TO_SEC(tick) ((tick) / 1000000)
#define SC_TICK_FROM_NS(ns) ((ns) / 1000)
#define SC_TICK_FROM_US(us) (us)
#define SC_TICK_FROM_MS(ms) ((ms) * 1000)
#define SC_TICK_FROM_SEC(sec) ((sec) * 1000000)

View File

@ -272,7 +272,7 @@ sc_v4l2_sink_open(struct sc_v4l2_sink *vs) {
vs->stopped = false;
LOGD("Starting v4l2 thread");
ok = sc_thread_create(&vs->thread, run_v4l2_sink, "v4l2", vs);
ok = sc_thread_create(&vs->thread, run_v4l2_sink, "scrcpy-v4l2", vs);
if (!ok) {
LOGC("Could not start v4l2 thread");
goto error_av_packet_free;

View File

@ -170,7 +170,7 @@ bool
sc_video_buffer_start(struct sc_video_buffer *vb) {
if (vb->buffering_time) {
bool ok =
sc_thread_create(&vb->b.thread, run_buffering, "buffering", vb);
sc_thread_create(&vb->b.thread, run_buffering, "scrcpy-vbuf", vb);
if (!ok) {
LOGE("Could not start buffering thread");
return false;

View File

@ -129,25 +129,26 @@ static void test_parse_shortcut_mods(void) {
ok = sc_parse_shortcut_mods("lctrl", &mods);
assert(ok);
assert(mods.count == 1);
assert(mods.data[0] == SC_MOD_LCTRL);
assert(mods.data[0] == SC_SHORTCUT_MOD_LCTRL);
ok = sc_parse_shortcut_mods("lctrl+lalt", &mods);
assert(ok);
assert(mods.count == 1);
assert(mods.data[0] == (SC_MOD_LCTRL | SC_MOD_LALT));
assert(mods.data[0] == (SC_SHORTCUT_MOD_LCTRL | SC_SHORTCUT_MOD_LALT));
ok = sc_parse_shortcut_mods("rctrl,lalt", &mods);
assert(ok);
assert(mods.count == 2);
assert(mods.data[0] == SC_MOD_RCTRL);
assert(mods.data[1] == SC_MOD_LALT);
assert(mods.data[0] == SC_SHORTCUT_MOD_RCTRL);
assert(mods.data[1] == SC_SHORTCUT_MOD_LALT);
ok = sc_parse_shortcut_mods("lsuper,rsuper+lalt,lctrl+rctrl+ralt", &mods);
assert(ok);
assert(mods.count == 3);
assert(mods.data[0] == SC_MOD_LSUPER);
assert(mods.data[1] == (SC_MOD_RSUPER | SC_MOD_LALT));
assert(mods.data[2] == (SC_MOD_LCTRL | SC_MOD_RCTRL | SC_MOD_RALT));
assert(mods.data[0] == SC_SHORTCUT_MOD_LSUPER);
assert(mods.data[1] == (SC_SHORTCUT_MOD_RSUPER | SC_SHORTCUT_MOD_LALT));
assert(mods.data[2] == (SC_SHORTCUT_MOD_LCTRL | SC_SHORTCUT_MOD_RCTRL |
SC_SHORTCUT_MOD_RALT));
ok = sc_parse_shortcut_mods("", &mods);
assert(!ok);
@ -169,4 +170,4 @@ int main(int argc, char *argv[]) {
test_options2();
test_parse_shortcut_mods();
return 0;
};
}

View File

@ -54,7 +54,7 @@ static void test_serialize_inject_text_long(void) {
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
char text[CONTROL_MSG_INJECT_TEXT_MAX_LENGTH + 1];
memset(text, 'a', sizeof(text));
memset(text, 'a', CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
text[CONTROL_MSG_INJECT_TEXT_MAX_LENGTH] = '\0';
msg.inject_text.text = text;
@ -250,6 +250,40 @@ static void test_serialize_set_clipboard(void) {
assert(!memcmp(buf, expected, sizeof(expected)));
}
static void test_serialize_set_clipboard_long(void) {
struct control_msg msg = {
.type = CONTROL_MSG_TYPE_SET_CLIPBOARD,
.set_clipboard = {
.sequence = UINT64_C(0x0102030405060708),
.paste = true,
.text = NULL,
},
};
char text[CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH + 1];
memset(text, 'a', CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH);
text[CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH] = '\0';
msg.set_clipboard.text = text;
unsigned char buf[CONTROL_MSG_MAX_SIZE];
size_t size = control_msg_serialize(&msg, buf);
assert(size == CONTROL_MSG_MAX_SIZE);
unsigned char expected[CONTROL_MSG_MAX_SIZE] = {
CONTROL_MSG_TYPE_SET_CLIPBOARD,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // sequence
1, // paste
// text length
CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH >> 24,
(CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH >> 16) & 0xff,
(CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH >> 8) & 0xff,
CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH & 0xff,
};
memset(expected + 14, 'a', CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH);
assert(!memcmp(buf, expected, sizeof(expected)));
}
static void test_serialize_set_screen_power_mode(void) {
struct control_msg msg = {
.type = CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
@ -299,6 +333,7 @@ int main(int argc, char *argv[]) {
test_serialize_collapse_panels();
test_serialize_get_clipboard();
test_serialize_set_clipboard();
test_serialize_set_clipboard_long();
test_serialize_set_screen_power_mode();
test_serialize_rotate_device();
return 0;

34
bump_version Executable file
View File

@ -0,0 +1,34 @@
#!/usr/bin/env bash
#
# This script bump scrcpy version by editing all the necessary files.
#
# Usage:
#
# ./bump_version 1.23.4
#
# Then check the diff manually to confirm that everything is ok.
set -e
if [[ $# != 1 ]]
then
echo "Syntax: $0 <version>" >&2
exit 1
fi
VERSION="$1"
a=( ${VERSION//./ } )
MAJOR="${a[0]:-0}"
MINOR="${a[1]:-0}"
PATCH="${a[2]:-0}"
# If VERSION is 1.23.4, then VERSION_CODE is 12304
VERSION_CODE="$(( $MAJOR * 10000 + $MINOR * 100 + "$PATCH" ))"
echo "$VERSION: major=$MAJOR minor=$MINOR patch=$PATCH [versionCode=$VERSION_CODE]"
sed -i "s/^\(\s*version: \)'[^']*'/\1'$VERSION'/" meson.build
sed -i "s/^\(\s*versionCode \).*/\1$VERSION_CODE/;s/^\(\s*versionName \).*/\1\"$VERSION\"/" server/build.gradle
sed -i "s/^\(SCRCPY_VERSION_NAME=\).*/\1$VERSION/" server/build_without_gradle.sh
sed -i "s/^\(\s*VALUE \"ProductVersion\", \)\"[^\"]*\"/\1\"$VERSION\"/" app/scrcpy-windows.rc
echo done

View File

@ -7,6 +7,7 @@ cpp = 'i686-w64-mingw32-g++'
ar = 'i686-w64-mingw32-ar'
strip = 'i686-w64-mingw32-strip'
pkgconfig = 'i686-w64-mingw32-pkg-config'
windres = 'i686-w64-mingw32-windres'
[host_machine]
system = 'windows'
@ -17,4 +18,4 @@ endian = 'little'
[properties]
prebuilt_ffmpeg_shared = 'ffmpeg-4.3.1-win32-shared'
prebuilt_ffmpeg_dev = 'ffmpeg-4.3.1-win32-dev'
prebuilt_sdl2 = 'SDL2-2.0.16/i686-w64-mingw32'
prebuilt_sdl2 = 'SDL2-2.0.18/i686-w64-mingw32'

View File

@ -7,6 +7,7 @@ cpp = 'x86_64-w64-mingw32-g++'
ar = 'x86_64-w64-mingw32-ar'
strip = 'x86_64-w64-mingw32-strip'
pkgconfig = 'x86_64-w64-mingw32-pkg-config'
windres = 'x86_64-w64-mingw32-windres'
[host_machine]
system = 'windows'
@ -17,4 +18,4 @@ endian = 'little'
[properties]
prebuilt_ffmpeg_shared = 'ffmpeg-4.3.1-win64-shared'
prebuilt_ffmpeg_dev = 'ffmpeg-4.3.1-win64-dev'
prebuilt_sdl2 = 'SDL2-2.0.16/x86_64-w64-mingw32'
prebuilt_sdl2 = 'SDL2-2.0.18/x86_64-w64-mingw32'

BIN
data/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -2,8 +2,8 @@
set -e
BUILDDIR=build-auto
PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v1.20/scrcpy-server-v1.20
PREBUILT_SERVER_SHA256=b20aee4951f99b060c4a44000ba94de973f9604758ef62beb253b371aad3df34
PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v1.21/scrcpy-server-v1.21
PREBUILT_SERVER_SHA256=dbcccab523ee26796e55ea33652649e4b7af498edae9aa75e4d4d7869c0ab848
echo "[scrcpy] Downloading prebuilt server..."
wget "$PREBUILT_SERVER_URL" -O scrcpy-server

View File

@ -1,6 +1,5 @@
option('compile_app', type: 'boolean', value: true, description: 'Build the client')
option('compile_server', type: 'boolean', value: true, description: 'Build the server')
option('crossbuild_windows', type: 'boolean', value: false, description: 'Build for Windows from Linux')
option('prebuilt_server', type: 'string', description: 'Path of the prebuilt server')
option('portable', type: 'boolean', value: false, description: 'Use scrcpy-server from the same directory as the scrcpy executable')
option('server_debugger', type: 'boolean', value: false, description: 'Run a server debugger and wait for a client to be attached')

View File

@ -30,9 +30,9 @@ prepare-ffmpeg-dev-win64:
ffmpeg-4.3.1-win64-dev
prepare-sdl2:
@./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.16-mingw.tar.gz \
2bfe48628aa9635c12eac7d421907e291525de1d0b04b3bca4a5bd6e6c881a6f \
SDL2-2.0.16
@./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.18-mingw.tar.gz \
bbad7c6947f6ca3e05292f065852ed8b62f319fc5533047e7708769c4dbae394 \
SDL2-2.0.18
prepare-adb:
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r31.0.3-windows.zip \

View File

@ -70,7 +70,6 @@ build-win32: prepare-deps-win32
meson "$(WIN32_BUILD_DIR)" \
--cross-file cross_win32.txt \
--buildtype release --strip -Db_lto=true \
-Dcrossbuild_windows=true \
-Dcompile_server=false \
-Dportable=true )
ninja -C "$(WIN32_BUILD_DIR)"
@ -83,7 +82,6 @@ build-win64: prepare-deps-win64
meson "$(WIN64_BUILD_DIR)" \
--cross-file cross_win64.txt \
--buildtype release --strip -Db_lto=true \
-Dcrossbuild_windows=true \
-Dcompile_server=false \
-Dportable=true )
ninja -C "$(WIN64_BUILD_DIR)"
@ -103,7 +101,7 @@ dist-win32: build-server build-win32
cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/SDL2-2.0.16/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/SDL2-2.0.18/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
dist-win64: build-server build-win64
mkdir -p "$(DIST)/$(WIN64_TARGET_DIR)"
@ -120,7 +118,7 @@ dist-win64: build-server build-win64
cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/SDL2-2.0.16/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/SDL2-2.0.18/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
zip-win32: dist-win32
cd "$(DIST)/$(WIN32_TARGET_DIR)"; \

View File

@ -14,8 +14,7 @@ set -e
SCRCPY_DEBUG=false
SCRCPY_VERSION_NAME=1.21
PLATFORM_VERSION=31
PLATFORM=${ANDROID_PLATFORM:-$PLATFORM_VERSION}
PLATFORM=${ANDROID_PLATFORM:-31}
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-31.0.0}
BUILD_DIR="$(realpath ${BUILD_DIR:-build_manual})"
@ -57,7 +56,7 @@ javac -bootclasspath "$ANDROID_JAR" -cp "$CLASSES_DIR" -d "$CLASSES_DIR" \
echo "Dexing..."
cd "$CLASSES_DIR"
if [[ $PLATFORM_VERSION -lt 31 ]]
if [[ $PLATFORM -lt 31 ]]
then
# use dx
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/dx" --dex \

View File

@ -30,8 +30,13 @@ public final class DesktopConnection implements Closeable {
private DesktopConnection(LocalSocket videoSocket, LocalSocket controlSocket) throws IOException {
this.videoSocket = videoSocket;
this.controlSocket = controlSocket;
controlInputStream = controlSocket.getInputStream();
controlOutputStream = controlSocket.getOutputStream();
if (controlSocket != null) {
controlInputStream = controlSocket.getInputStream();
controlOutputStream = controlSocket.getOutputStream();
} else {
controlInputStream = null;
controlOutputStream = null;
}
videoFd = videoSocket.getFileDescriptor();
}
@ -41,31 +46,35 @@ public final class DesktopConnection implements Closeable {
return localSocket;
}
public static DesktopConnection open(Device device, boolean tunnelForward) throws IOException {
public static DesktopConnection open(Device device, boolean tunnelForward, boolean control) throws IOException {
LocalSocket videoSocket;
LocalSocket controlSocket;
LocalSocket controlSocket = null;
if (tunnelForward) {
LocalServerSocket localServerSocket = new LocalServerSocket(SOCKET_NAME);
try {
videoSocket = localServerSocket.accept();
// send one byte so the client may read() to detect a connection error
videoSocket.getOutputStream().write(0);
try {
controlSocket = localServerSocket.accept();
} catch (IOException | RuntimeException e) {
videoSocket.close();
throw e;
if (control) {
try {
controlSocket = localServerSocket.accept();
} catch (IOException | RuntimeException e) {
videoSocket.close();
throw e;
}
}
} finally {
localServerSocket.close();
}
} else {
videoSocket = connect(SOCKET_NAME);
try {
controlSocket = connect(SOCKET_NAME);
} catch (IOException | RuntimeException e) {
videoSocket.close();
throw e;
if (control) {
try {
controlSocket = connect(SOCKET_NAME);
} catch (IOException | RuntimeException e) {
videoSocket.close();
throw e;
}
}
}
@ -79,9 +88,11 @@ public final class DesktopConnection implements Closeable {
videoSocket.shutdownInput();
videoSocket.shutdownOutput();
videoSocket.close();
controlSocket.shutdownInput();
controlSocket.shutdownOutput();
controlSocket.close();
if (controlSocket != null) {
controlSocket.shutdownInput();
controlSocket.shutdownOutput();
controlSocket.close();
}
}
private void send(String deviceName, int width, int height) throws IOException {

View File

@ -66,14 +66,15 @@ public final class Server {
Thread initThread = startInitThread(options);
boolean tunnelForward = options.isTunnelForward();
boolean control = options.getControl();
try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward)) {
try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward, control)) {
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), codecOptions,
options.getEncoderName());
Thread controllerThread = null;
Thread deviceMessageSenderThread = null;
if (options.getControl()) {
if (control) {
final Controller controller = new Controller(device, connection, options.getClipboardAutosync());
// asynchronous