Compare commits

...

26 Commits

Author SHA1 Message Date
9a7e8da12c Handle repeating keycodes
Initialize "repeat" count on key events.

PR #1519 <https://github.com/Genymobile/scrcpy/pull/1519>
Refs #1013 <https://github.com/Genymobile/scrcpy/pull/1013>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-06-19 18:24:07 +02:00
dc7b60e619 Add option for disabling screensaver
PR #1502 <https://github.com/Genymobile/scrcpy/pull/1502>
Fixes #1370 <https://github.com/Genymobile/scrcpy/issues/1370>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-06-18 21:35:28 +02:00
1e4ee547b5 Make message buffer static
Now that the message max size is 256k, do not put the buffer on the
stack.
2020-06-11 23:11:20 +02:00
488d22d4e2 Increase clipboard size from 4k to 256k
Beyond 256k, SDL_GetClipboardText() returns an empty string on my
computer.

Fixes #1117 <https://github.com/Genymobile/scrcpy/issues/1117>
2020-06-11 23:11:20 +02:00
00d292b2f5 Fix receiver on partial device messages
If a single device message was read in several chunks from the control
socket, the communication was broken.
2020-06-11 23:11:20 +02:00
245999aec4 Serialize text size on 4 bytes
This will allow to send text having a size greater than 65535 bytes.
2020-06-11 23:11:17 +02:00
d91c5dcfd5 Rename MSG_SERIALIZED_MAX_SIZE to MSG_MAX_SIZE
For simplicity and consistency with the server part.
2020-06-11 23:08:04 +02:00
d202d7b205 Add unit test for big clipboard device message
Test clipboard synchronization from the device to the computer.
2020-06-11 23:06:02 +02:00
08c0c64af6 Rename test names from "event" to "msg"
The meson test names had not been changed when "event" had been renamed
to "message".

Ref: 28980bbc90
2020-06-11 23:06:02 +02:00
a00a8763d6 Avoid additional copy on Java text parsing
Directly pass the buffer range to the String constructor.
2020-06-11 23:06:02 +02:00
8f314c74b0 Reorganize message size constants
Make the max clipboard text length depend on the max message size.
2020-06-11 22:58:54 +02:00
6e1069a822 Configure log level for application only
Do not expose internal SDL logs to users.

Fixes #1441 <https://github.com/Genymobile/scrcpy/issues/1441>
2020-06-02 18:27:23 +02:00
c4323df976 Fix incorrect log return value
The function must return a SDL_LogPriority, but returned an enum
sc_log_level.

(It was harmless because this specific return should never happen, as
asserted.)
2020-06-02 18:27:14 +02:00
8ff07e0c88 Git-ignore release directories 2020-06-02 18:25:22 +02:00
e4efd75766 Avoid repetition for some shortcuts
Keeping the key pressed generate "repeat" events. It does not make sense
to repeat the event for rotation or turn screen off.
2020-05-29 22:02:41 +02:00
0e4a6f462b Mention stay awake limitation
The "stay awake" feature only works when the device is plugged in.

Refs #1445 <https://github.com/Genymobile/scrcpy/issues/1445>
2020-05-28 23:07:28 +02:00
8b73c90427 Mention how to turn the screen on in README
Now that Ctrl+Shift+o has been reactivated, mention it in the "turn
screen off" section.
2020-05-27 19:32:02 +02:00
ef91ab2841 Update links to v1.14 in README and BUILD 2020-05-27 19:31:12 +02:00
44fa4a090e Bump version to 1.14 2020-05-27 18:26:46 +02:00
dcde578a50 Reactivate "turn device screen on" feature
This reverts commit 8c8649cfcd.

I cannot reproduce the issue with Ctrl+Shift+o on any device, so in
practice it works, it's too bad to remove the feature for a random bug
on some Android versions on some devices.
2020-05-27 18:26:46 +02:00
93a5c5149d Push clipboard text only if not null
In practice, it does not change anything (it just avoids a spurious
wake-up), but semantically, it makes no sense to call
pushClipboardText() with a null value.
2020-05-27 18:26:39 +02:00
2ca8318b9d Improve manpage formatting
Add a new line to avoid unwanted text justification
2020-05-27 12:39:42 +02:00
d499ee53c9 Initialize a default log level
Clean up has been broken by 3df63c579d.

The verbosity was correctly initialized for the Server process, but not
for the CleanUp process.

To avoid the problem, initialize a default log level.
2020-05-27 12:05:29 +02:00
8f619f337b Upgrade platform-tools (30.0.0) for Windows
Include the latest version of adb in Windows releases.
2020-05-26 19:21:05 +02:00
fc1dec0270 Paste on "set clipboard" if possible
Ctrl+Shift+v synchronizes the computer clipboard to the Android device
clipboard. This feature had been added to provide a way to copy UTF-8
text from the computer to the device.

To make such a paste more straightforward, if the device runs at least
Android 7, also send a PASTE keycode to paste immediately.

<https://developer.android.com/reference/android/view/KeyEvent.html#KEYCODE_PASTE>

Fixes #786 <https://github.com/Genymobile/scrcpy/issues/786>
2020-05-25 20:59:21 +02:00
274b591d18 Fix union typo
The "set clipboard" event used the wrong union type to store its text.

In practice, it worked because both are at the same offset.
2020-05-25 18:41:05 +02:00
30 changed files with 302 additions and 144 deletions

3
.gitignore vendored
View File

@ -1,5 +1,8 @@
build/
/dist/
/build-*/
/build_*/
/release-*/
.idea/
.gradle/
/x/

View File

@ -249,10 +249,10 @@ You can then [run](README.md#run) _scrcpy_.
## Prebuilt server
- [`scrcpy-server-v1.13`][direct-scrcpy-server]
_(SHA-256: 5fee64ca1ccdc2f38550f31f5353c66de3de30c2e929a964e30fa2d005d5f885)_
- [`scrcpy-server-v1.14`][direct-scrcpy-server]
_(SHA-256: 1d1b18a2b80e956771fd63b99b414d2d028713a8f12ddfa5a369709ad4295620)_
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.13/scrcpy-server-v1.13
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.14/scrcpy-server-v1.14
Download the prebuilt server somewhere, and specify its path during the Meson
configuration:

View File

@ -1,4 +1,4 @@
# scrcpy (v1.13)
# scrcpy (v1.14)
This application provides display and control of Android devices connected on
USB (or [over TCP/IP][article-tcpip]). It does not require any _root_ access.
@ -69,10 +69,10 @@ hard).
For Windows, for simplicity, a prebuilt archive with all the dependencies
(including `adb`) is available:
- [`scrcpy-win64-v1.13.zip`][direct-win64]
_(SHA-256: 806aafc00d4db01513193addaa24f47858893ba5efe75770bfef6ae1ea987d27)_
- [`scrcpy-win64-v1.14.zip`][direct-win64]
_(SHA-256: 2be9139e46e29cf2f5f695848bb2b75a543b8f38be1133257dc5068252abc25f)_
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.13/scrcpy-win64-v1.13.zip
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.14/scrcpy-win64-v1.14.zip
It is also available in [Chocolatey]:
@ -417,7 +417,7 @@ The secondary display may only be controlled if the device runs at least Android
#### Stay awake
To prevent the device to sleep after some delay:
To prevent the device to sleep after some delay when the device is plugged in:
```bash
scrcpy --stay-awake
@ -439,7 +439,7 @@ scrcpy -S
Or by pressing `Ctrl`+`o` at any time.
To turn it back on, press `POWER` (or `Ctrl`+`p`).
To turn it back on, press `Ctrl`+`Shift`+`o` (or `POWER`, `Ctrl`+`p`).
It can be useful to also prevent the device to sleep:
@ -479,6 +479,17 @@ scrcpy -t
Note that it only shows _physical_ touches (with the finger on the device).
#### Disable screensaver
By default, scrcpy does not prevent the screensaver to run on the computer.
To disable it:
```bash
scrcpy --disable-screensaver
```
### Input control
#### Rotate device screen
@ -494,7 +505,8 @@ It is possible to synchronize clipboards between the computer and the device, in
both directions:
- `Ctrl`+`c` copies the device clipboard to the computer clipboard;
- `Ctrl`+`Shift`+`v` copies the computer clipboard to the device clipboard;
- `Ctrl`+`Shift`+`v` copies the computer clipboard to the device clipboard (and
pastes if the device runs Android >= 7);
- `Ctrl`+`v` _pastes_ the computer clipboard as a sequence of text events (but
breaks non-ASCII characters).
@ -559,29 +571,30 @@ Also see [issue #14].
## Shortcuts
| Action | Shortcut | Shortcut (macOS)
| -------------------------------------- |:----------------------------- |:-----------------------------
| Switch fullscreen mode | `Ctrl`+`f` | `Cmd`+`f`
| Rotate display left | `Ctrl`+`←` _(left)_ | `Cmd`+`←` _(left)_
| Rotate display right | `Ctrl`+`→` _(right)_ | `Cmd`+`→` _(right)_
| Resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` | `Cmd`+`g`
| Resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_ | `Cmd`+`x` \| _Double-click¹_
| Click on `HOME` | `Ctrl`+`h` \| _Middle-click_ | `Ctrl`+`h` \| _Middle-click_
| Click on `BACK` | `Ctrl`+`b` \| _Right-click²_ | `Cmd`+`b` \| _Right-click²_
| Click on `APP_SWITCH` | `Ctrl`+`s` | `Cmd`+`s`
| Click on `MENU` | `Ctrl`+`m` | `Ctrl`+`m`
| Click on `VOLUME_UP` | `Ctrl`+`↑` _(up)_ | `Cmd`+`↑` _(up)_
| Click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_ | `Cmd`+`↓` _(down)_
| Click on `POWER` | `Ctrl`+`p` | `Cmd`+`p`
| Power on | _Right-click²_ | _Right-click²_
| Turn device screen off (keep mirroring)| `Ctrl`+`o` | `Cmd`+`o`
| Rotate device screen | `Ctrl`+`r` | `Cmd`+`r`
| Expand notification panel | `Ctrl`+`n` | `Cmd`+`n`
| Collapse notification panel | `Ctrl`+`Shift`+`n` | `Cmd`+`Shift`+`n`
| Copy device clipboard to computer | `Ctrl`+`c` | `Cmd`+`c`
| Paste computer clipboard to device | `Ctrl`+`v` | `Cmd`+`v`
| Copy computer clipboard to device | `Ctrl`+`Shift`+`v` | `Cmd`+`Shift`+`v`
| Enable/disable FPS counter (on stdout) | `Ctrl`+`i` | `Cmd`+`i`
| Action | Shortcut | Shortcut (macOS)
| ------------------------------------------- |:----------------------------- |:-----------------------------
| Switch fullscreen mode | `Ctrl`+`f` | `Cmd`+`f`
| Rotate display left | `Ctrl`+`←` _(left)_ | `Cmd`+`←` _(left)_
| Rotate display right | `Ctrl`+`→` _(right)_ | `Cmd`+`→` _(right)_
| Resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` | `Cmd`+`g`
| Resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_ | `Cmd`+`x` \| _Double-click¹_
| Click on `HOME` | `Ctrl`+`h` \| _Middle-click_ | `Ctrl`+`h` \| _Middle-click_
| Click on `BACK` | `Ctrl`+`b` \| _Right-click²_ | `Cmd`+`b` \| _Right-click²_
| Click on `APP_SWITCH` | `Ctrl`+`s` | `Cmd`+`s`
| Click on `MENU` | `Ctrl`+`m` | `Ctrl`+`m`
| Click on `VOLUME_UP` | `Ctrl`+`↑` _(up)_ | `Cmd`+`↑` _(up)_
| Click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_ | `Cmd`+`↓` _(down)_
| Click on `POWER` | `Ctrl`+`p` | `Cmd`+`p`
| Power on | _Right-click²_ | _Right-click²_
| Turn device screen off (keep mirroring) | `Ctrl`+`o` | `Cmd`+`o`
| Turn device screen on | `Ctrl`+`Shift`+`o` | `Cmd`+`Shift`+`o`
| Rotate device screen | `Ctrl`+`r` | `Cmd`+`r`
| Expand notification panel | `Ctrl`+`n` | `Cmd`+`n`
| Collapse notification panel | `Ctrl`+`Shift`+`n` | `Cmd`+`Shift`+`n`
| Copy device clipboard to computer | `Ctrl`+`c` | `Cmd`+`c`
| Paste computer clipboard to device | `Ctrl`+`v` | `Cmd`+`v`
| Copy computer clipboard to device and paste | `Ctrl`+`Shift`+`v` | `Cmd`+`Shift`+`v`
| Enable/disable FPS counter (on stdout) | `Ctrl`+`i` | `Cmd`+`i`
_¹Double-click on black borders to remove them._
_²Right-click turns the screen on if it was off, presses BACK otherwise._

View File

@ -164,12 +164,12 @@ if get_option('buildtype') == 'debug'
'src/cli.c',
'src/util/str_util.c',
]],
['test_control_event_serialize', [
['test_control_msg_serialize', [
'tests/test_control_msg_serialize.c',
'src/control_msg.c',
'src/util/str_util.c',
]],
['test_device_event_deserialize', [
['test_device_msg_deserialize', [
'tests/test_device_msg_deserialize.c',
'src/device_msg.c',
]],

View File

@ -43,6 +43,10 @@ The values are expressed in the device natural orientation (typically, portrait
.B \-\-max\-size
value is computed on the cropped size.
.TP
.BI "\-\-disable-screensaver"
Disable screensaver while scrcpy is running.
.TP
.BI "\-\-display " id
Specify the display id to mirror.
@ -129,6 +133,7 @@ Force recording format (either mp4 or mkv).
Request SDL to use the given render driver (this is just a hint).
Supported names are currently "direct3d", "opengl", "opengles2", "opengles", "metal" and "software".
.UR https://wiki.libsdl.org/SDL_HINT_RENDER_DRIVER
.UE
@ -166,7 +171,7 @@ Default is "info" for release builds, "debug" for debug builds.
.TP
.B \-w, \-\-stay-awake
Keep the device on while scrcpy is running.
Keep the device on while scrcpy is running, when the device is plugged in.
.TP
.B \-\-window\-borderless
@ -258,6 +263,10 @@ Turn screen on
.B Ctrl+o
Turn device screen off (keep mirroring)
.TP
.B Ctrl+Shift+o
Turn device screen on
.TP
.B Ctrl+r
Rotate device screen
@ -280,7 +289,7 @@ Paste computer clipboard to device
.TP
.B Ctrl+Shift+v
Copy computer clipboard to device
Copy computer clipboard to device (and paste if the device runs Android >= 7)
.TP
.B Ctrl+i

View File

@ -45,6 +45,9 @@ scrcpy_print_usage(const char *arg0) {
" (typically, portrait for a phone, landscape for a tablet).\n"
" Any --max-size value is computed on the cropped size.\n"
"\n"
" --disable-screensaver\n"
" Disable screensaver while scrcpy is running.\n"
"\n"
" --display id\n"
" Specify the display id to mirror.\n"
"\n"
@ -159,7 +162,8 @@ scrcpy_print_usage(const char *arg0) {
#endif
"\n"
" -w, --stay-awake\n"
" Keep the device on while scrcpy is running.\n"
" Keep the device on while scrcpy is running, when the device\n"
" is plugged in.\n"
"\n"
" --window-borderless\n"
" Disable window decorations (display borderless window).\n"
@ -231,6 +235,9 @@ scrcpy_print_usage(const char *arg0) {
" " CTRL_OR_CMD "+o\n"
" Turn device screen off (keep mirroring)\n"
"\n"
" " CTRL_OR_CMD "+Shift+o\n"
" Turn device screen on\n"
"\n"
" " CTRL_OR_CMD "+r\n"
" Rotate device screen\n"
"\n"
@ -247,7 +254,8 @@ scrcpy_print_usage(const char *arg0) {
" Paste computer clipboard to device\n"
"\n"
" " CTRL_OR_CMD "+Shift+v\n"
" Copy computer clipboard to device\n"
" Copy computer clipboard to device (and paste if the device\n"
" runs Android >= 7)\n"
"\n"
" " CTRL_OR_CMD "+i\n"
" Enable/disable FPS counter (print frames/second in logs)\n"
@ -521,6 +529,7 @@ guess_record_format(const char *filename) {
#define OPT_NO_MIPMAPS 1017
#define OPT_CODEC_OPTIONS 1018
#define OPT_FORCE_ADB_FORWARD 1019
#define OPT_DISABLE_SCREENSAVER 1020
bool
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
@ -529,6 +538,8 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
{"bit-rate", required_argument, NULL, 'b'},
{"codec-options", required_argument, NULL, OPT_CODEC_OPTIONS},
{"crop", required_argument, NULL, OPT_CROP},
{"disable-screensaver", no_argument, NULL,
OPT_DISABLE_SCREENSAVER},
{"display", required_argument, NULL, OPT_DISPLAY_ID},
{"force-adb-forward", no_argument, NULL,
OPT_FORCE_ADB_FORWARD},
@ -711,6 +722,9 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
case OPT_FORCE_ADB_FORWARD:
opts->force_adb_forward = true;
break;
case OPT_DISABLE_SCREENSAVER:
opts->disable_screensaver = true;
break;
default:
// getopt prints the error message on stderr
return false;

View File

@ -20,9 +20,9 @@ write_position(uint8_t *buf, const struct position *position) {
static size_t
write_string(const char *utf8, size_t max_len, unsigned char *buf) {
size_t len = utf8_truncation_index(utf8, max_len);
buffer_write16be(buf, (uint16_t) len);
memcpy(&buf[2], utf8, len);
return 2 + len;
buffer_write32be(buf, len);
memcpy(&buf[4], utf8, len);
return 4 + len;
}
static uint16_t
@ -42,8 +42,9 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
case CONTROL_MSG_TYPE_INJECT_KEYCODE:
buf[1] = msg->inject_keycode.action;
buffer_write32be(&buf[2], msg->inject_keycode.keycode);
buffer_write32be(&buf[6], msg->inject_keycode.metastate);
return 10;
buffer_write32be(&buf[6], msg->inject_keycode.repeat);
buffer_write32be(&buf[10], msg->inject_keycode.metastate);
return 14;
case CONTROL_MSG_TYPE_INJECT_TEXT: {
size_t len =
write_string(msg->inject_text.text,
@ -67,10 +68,11 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
(uint32_t) msg->inject_scroll_event.vscroll);
return 21;
case CONTROL_MSG_TYPE_SET_CLIPBOARD: {
size_t len = write_string(msg->inject_text.text,
buf[1] = !!msg->set_clipboard.paste;
size_t len = write_string(msg->set_clipboard.text,
CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH,
&buf[1]);
return 1 + len;
&buf[2]);
return 2 + len;
}
case CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE:
buf[1] = msg->set_screen_power_mode.mode;

View File

@ -10,10 +10,11 @@
#include "android/keycodes.h"
#include "common.h"
#define CONTROL_MSG_MAX_SIZE (1 << 18) // 256k
#define CONTROL_MSG_INJECT_TEXT_MAX_LENGTH 300
#define CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH 4093
#define CONTROL_MSG_SERIALIZED_MAX_SIZE \
(3 + CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH)
// type: 1 byte; paste flag: 1 byte; length: 4 bytes
#define CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH (CONTROL_MSG_MAX_SIZE - 6)
#define POINTER_ID_MOUSE UINT64_C(-1);
@ -43,6 +44,7 @@ struct control_msg {
struct {
enum android_keyevent_action action;
enum android_keycode keycode;
uint32_t repeat;
enum android_metastate metastate;
} inject_keycode;
struct {
@ -62,6 +64,7 @@ struct control_msg {
} inject_scroll_event;
struct {
char *text; // owned, to be freed by SDL_free()
bool paste;
} set_clipboard;
struct {
enum screen_power_mode mode;
@ -69,7 +72,7 @@ struct control_msg {
};
};
// buf size must be at least CONTROL_MSG_SERIALIZED_MAX_SIZE
// buf size must be at least CONTROL_MSG_MAX_SIZE
// return the number of bytes written
size_t
control_msg_serialize(const struct control_msg *msg, unsigned char *buf);

View File

@ -60,7 +60,7 @@ controller_push_msg(struct controller *controller,
static bool
process_msg(struct controller *controller,
const struct control_msg *msg) {
unsigned char serialized_msg[CONTROL_MSG_SERIALIZED_MAX_SIZE];
static unsigned char serialized_msg[CONTROL_MSG_MAX_SIZE];
int length = control_msg_serialize(msg, serialized_msg);
if (!length) {
return false;

View File

@ -9,7 +9,7 @@
ssize_t
device_msg_deserialize(const unsigned char *buf, size_t len,
struct device_msg *msg) {
if (len < 3) {
if (len < 5) {
// at least type + empty string length
return 0; // not available
}
@ -17,8 +17,8 @@ device_msg_deserialize(const unsigned char *buf, size_t len,
msg->type = buf[0];
switch (msg->type) {
case DEVICE_MSG_TYPE_CLIPBOARD: {
uint16_t clipboard_len = buffer_read16be(&buf[1]);
if (clipboard_len > len - 3) {
size_t clipboard_len = buffer_read32be(&buf[1]);
if (clipboard_len > len - 5) {
return 0; // not available
}
char *text = SDL_malloc(clipboard_len + 1);
@ -27,12 +27,12 @@ device_msg_deserialize(const unsigned char *buf, size_t len,
return -1;
}
if (clipboard_len) {
memcpy(text, &buf[3], clipboard_len);
memcpy(text, &buf[5], clipboard_len);
}
text[clipboard_len] = '\0';
msg->clipboard.text = text;
return 3 + clipboard_len;
return 5 + clipboard_len;
}
default:
LOGW("Unknown device message type: %d", (int) msg->type);

View File

@ -7,8 +7,9 @@
#include "config.h"
#define DEVICE_MSG_TEXT_MAX_LENGTH 4093
#define DEVICE_MSG_SERIALIZED_MAX_SIZE (3 + DEVICE_MSG_TEXT_MAX_LENGTH)
#define DEVICE_MSG_MAX_SIZE (1 << 18) // 256k
// type: 1 byte; length: 4 bytes
#define DEVICE_MSG_TEXT_MAX_LENGTH (DEVICE_MSG_MAX_SIZE - 5)
enum device_msg_type {
DEVICE_MSG_TYPE_CLIPBOARD,

View File

@ -112,7 +112,7 @@ request_device_clipboard(struct controller *controller) {
}
static void
set_device_clipboard(struct controller *controller) {
set_device_clipboard(struct controller *controller, bool paste) {
char *text = SDL_GetClipboardText();
if (!text) {
LOGW("Could not get clipboard text: %s", SDL_GetError());
@ -127,6 +127,7 @@ set_device_clipboard(struct controller *controller) {
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_SET_CLIPBOARD;
msg.set_clipboard.text = text;
msg.set_clipboard.paste = paste;
if (!controller_push_msg(controller, &msg)) {
SDL_free(text);
@ -233,7 +234,7 @@ input_manager_process_text_input(struct input_manager *im,
static bool
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to,
bool prefer_text) {
bool prefer_text, uint32_t repeat) {
to->type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
if (!convert_keycode_action(from->type, &to->inject_keycode.action)) {
@ -246,6 +247,7 @@ convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to,
return false;
}
to->inject_keycode.repeat = repeat;
to->inject_keycode.metastate = convert_meta_state(mod);
return true;
@ -320,8 +322,11 @@ input_manager_process_key(struct input_manager *im,
}
return;
case SDLK_o:
if (control && cmd && !shift && down) {
set_screen_power_mode(controller, SCREEN_POWER_MODE_OFF);
if (control && cmd && !repeat && down) {
enum screen_power_mode mode = shift
? SCREEN_POWER_MODE_NORMAL
: SCREEN_POWER_MODE_OFF;
set_screen_power_mode(controller, mode);
}
return;
case SDLK_DOWN:
@ -337,12 +342,12 @@ input_manager_process_key(struct input_manager *im,
}
return;
case SDLK_LEFT:
if (cmd && !shift && down) {
if (cmd && !shift && !repeat && down) {
rotate_client_left(im->screen);
}
return;
case SDLK_RIGHT:
if (cmd && !shift && down) {
if (cmd && !shift && !repeat && down) {
rotate_client_right(im->screen);
}
return;
@ -354,8 +359,8 @@ input_manager_process_key(struct input_manager *im,
case SDLK_v:
if (control && cmd && !repeat && down) {
if (shift) {
// store the text in the device clipboard
set_device_clipboard(controller);
// store the text in the device clipboard and paste
set_device_clipboard(controller, true);
} else {
// inject the text as input events
clipboard_paste(controller);
@ -407,8 +412,14 @@ input_manager_process_key(struct input_manager *im,
return;
}
if (event->repeat) {
++im->repeat;
} else {
im->repeat = 0;
}
struct control_msg msg;
if (convert_input_key(event, &msg, im->prefer_text)) {
if (convert_input_key(event, &msg, im->prefer_text, im->repeat)) {
if (!controller_push_msg(controller, &msg)) {
LOGW("Could not request 'inject keycode'");
}

View File

@ -14,6 +14,11 @@ struct input_manager {
struct controller *controller;
struct video_buffer *video_buffer;
struct screen *screen;
// SDL reports repeated events as a boolean, but Android expects the actual
// number of repetitions. This variable keeps track of the count.
unsigned repeat;
bool prefer_text;
};

View File

@ -42,7 +42,7 @@ convert_log_level_to_sdl(enum sc_log_level level) {
return SDL_LOG_PRIORITY_ERROR;
default:
assert(!"unexpected log level");
return SC_LOG_LEVEL_INFO;
return SDL_LOG_PRIORITY_INFO;
}
}
@ -71,7 +71,7 @@ main(int argc, char *argv[]) {
}
SDL_LogPriority sdl_log = convert_log_level_to_sdl(args.opts.log_level);
SDL_LogSetAllPriority(sdl_log);
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, sdl_log);
if (args.help) {
scrcpy_print_usage(argv[0]);

View File

@ -60,28 +60,29 @@ static int
run_receiver(void *data) {
struct receiver *receiver = data;
unsigned char buf[DEVICE_MSG_SERIALIZED_MAX_SIZE];
static unsigned char buf[DEVICE_MSG_MAX_SIZE];
size_t head = 0;
for (;;) {
assert(head < DEVICE_MSG_SERIALIZED_MAX_SIZE);
ssize_t r = net_recv(receiver->control_socket, buf,
DEVICE_MSG_SERIALIZED_MAX_SIZE - head);
assert(head < DEVICE_MSG_MAX_SIZE);
ssize_t r = net_recv(receiver->control_socket, buf + head,
DEVICE_MSG_MAX_SIZE - head);
if (r <= 0) {
LOGD("Receiver stopped");
break;
}
ssize_t consumed = process_msgs(buf, r);
head += r;
ssize_t consumed = process_msgs(buf, head);
if (consumed == -1) {
// an error occurred
break;
}
if (consumed) {
head -= consumed;
// shift the remaining data in the buffer
memmove(buf, &buf[consumed], r - consumed);
head = r - consumed;
memmove(buf, &buf[consumed], head);
}
}

View File

@ -46,6 +46,7 @@ static struct input_manager input_manager = {
.controller = &controller,
.video_buffer = &video_buffer,
.screen = &screen,
.repeat = 0,
.prefer_text = false, // initialized later
};
@ -63,7 +64,8 @@ BOOL WINAPI windows_ctrl_handler(DWORD ctrl_type) {
// init SDL and set appropriate hints
static bool
sdl_init_and_configure(bool display, const char *render_driver) {
sdl_init_and_configure(bool display, const char *render_driver,
bool disable_screensaver) {
uint32_t flags = display ? SDL_INIT_VIDEO : SDL_INIT_EVENTS;
if (SDL_Init(flags)) {
LOGC("Could not initialize SDL: %s", SDL_GetError());
@ -112,8 +114,13 @@ sdl_init_and_configure(bool display, const char *render_driver) {
LOGW("Could not disable minimize on focus loss");
}
// Do not disable the screensaver when scrcpy is running
SDL_EnableScreenSaver();
if (disable_screensaver) {
LOGD("Screensaver disabled");
SDL_DisableScreenSaver();
} else {
LOGD("Screensaver enabled");
SDL_EnableScreenSaver();
}
return true;
}
@ -321,7 +328,8 @@ scrcpy(const struct scrcpy_options *options) {
bool controller_initialized = false;
bool controller_started = false;
if (!sdl_init_and_configure(options->display, options->render_driver)) {
if (!sdl_init_and_configure(options->display, options->render_driver,
options->disable_screensaver)) {
goto end;
}

View File

@ -43,6 +43,7 @@ struct scrcpy_options {
bool mipmaps;
bool stay_awake;
bool force_adb_forward;
bool disable_screensaver;
};
#define SCRCPY_OPTIONS_DEFAULT { \
@ -81,6 +82,7 @@ struct scrcpy_options {
.mipmaps = true, \
.stay_awake = false, \
.force_adb_forward = false, \
.disable_screensaver = false, \
}
bool

View File

@ -9,18 +9,20 @@ static void test_serialize_inject_keycode(void) {
.inject_keycode = {
.action = AKEY_EVENT_ACTION_UP,
.keycode = AKEYCODE_ENTER,
.repeat = 5,
.metastate = AMETA_SHIFT_ON | AMETA_SHIFT_LEFT_ON,
},
};
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
assert(size == 10);
assert(size == 14);
const unsigned char expected[] = {
CONTROL_MSG_TYPE_INJECT_KEYCODE,
0x01, // AKEY_EVENT_ACTION_UP
0x00, 0x00, 0x00, 0x42, // AKEYCODE_ENTER
0x00, 0x00, 0x00, 0X05, // repeat
0x00, 0x00, 0x00, 0x41, // AMETA_SHIFT_ON | AMETA_SHIFT_LEFT_ON
};
assert(!memcmp(buf, expected, sizeof(expected)));
@ -34,13 +36,13 @@ static void test_serialize_inject_text(void) {
},
};
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
assert(size == 16);
assert(size == 18);
const unsigned char expected[] = {
CONTROL_MSG_TYPE_INJECT_TEXT,
0x00, 0x0d, // text length
0x00, 0x00, 0x00, 0x0d, // text length
'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', // text
};
assert(!memcmp(buf, expected, sizeof(expected)));
@ -54,15 +56,17 @@ static void test_serialize_inject_text_long(void) {
text[CONTROL_MSG_INJECT_TEXT_MAX_LENGTH] = '\0';
msg.inject_text.text = text;
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
assert(size == 3 + CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
assert(size == 5 + CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
unsigned char expected[3 + CONTROL_MSG_INJECT_TEXT_MAX_LENGTH];
unsigned char expected[5 + CONTROL_MSG_INJECT_TEXT_MAX_LENGTH];
expected[0] = CONTROL_MSG_TYPE_INJECT_TEXT;
expected[1] = 0x01;
expected[2] = 0x2c; // text length (16 bits)
memset(&expected[3], 'a', CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
expected[1] = 0x00;
expected[2] = 0x00;
expected[3] = 0x01;
expected[4] = 0x2c; // text length (32 bits)
memset(&expected[5], 'a', CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
assert(!memcmp(buf, expected, sizeof(expected)));
}
@ -88,7 +92,7 @@ static void test_serialize_inject_touch_event(void) {
},
};
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
assert(size == 28);
@ -123,7 +127,7 @@ static void test_serialize_inject_scroll_event(void) {
},
};
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
assert(size == 21);
@ -142,7 +146,7 @@ static void test_serialize_back_or_screen_on(void) {
.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
};
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
assert(size == 1);
@ -157,7 +161,7 @@ static void test_serialize_expand_notification_panel(void) {
.type = CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL,
};
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
assert(size == 1);
@ -172,7 +176,7 @@ static void test_serialize_collapse_notification_panel(void) {
.type = CONTROL_MSG_TYPE_COLLAPSE_NOTIFICATION_PANEL,
};
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
assert(size == 1);
@ -187,7 +191,7 @@ static void test_serialize_get_clipboard(void) {
.type = CONTROL_MSG_TYPE_GET_CLIPBOARD,
};
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
assert(size == 1);
@ -200,18 +204,20 @@ static void test_serialize_get_clipboard(void) {
static void test_serialize_set_clipboard(void) {
struct control_msg msg = {
.type = CONTROL_MSG_TYPE_SET_CLIPBOARD,
.inject_text = {
.set_clipboard = {
.paste = true,
.text = "hello, world!",
},
};
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
assert(size == 16);
assert(size == 19);
const unsigned char expected[] = {
CONTROL_MSG_TYPE_SET_CLIPBOARD,
0x00, 0x0d, // text length
1, // paste
0x00, 0x00, 0x00, 0x0d, // text length
'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', // text
};
assert(!memcmp(buf, expected, sizeof(expected)));
@ -225,7 +231,7 @@ static void test_serialize_set_screen_power_mode(void) {
},
};
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
assert(size == 2);
@ -241,7 +247,7 @@ static void test_serialize_rotate_device(void) {
.type = CONTROL_MSG_TYPE_ROTATE_DEVICE,
};
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
assert(size == 1);

View File

@ -4,16 +4,17 @@
#include "device_msg.h"
#include <stdio.h>
static void test_deserialize_clipboard(void) {
const unsigned char input[] = {
DEVICE_MSG_TYPE_CLIPBOARD,
0x00, 0x03, // text length
0x00, 0x00, 0x00, 0x03, // text length
0x41, 0x42, 0x43, // "ABC"
};
struct device_msg msg;
ssize_t r = device_msg_deserialize(input, sizeof(input), &msg);
assert(r == 6);
assert(r == 8);
assert(msg.type == DEVICE_MSG_TYPE_CLIPBOARD);
assert(msg.clipboard.text);
@ -22,7 +23,30 @@ static void test_deserialize_clipboard(void) {
device_msg_destroy(&msg);
}
static void test_deserialize_clipboard_big(void) {
unsigned char input[DEVICE_MSG_MAX_SIZE];
input[0] = DEVICE_MSG_TYPE_CLIPBOARD;
input[1] = (DEVICE_MSG_TEXT_MAX_LENGTH & 0xff000000u) >> 24;
input[2] = (DEVICE_MSG_TEXT_MAX_LENGTH & 0x00ff0000u) >> 16;
input[3] = (DEVICE_MSG_TEXT_MAX_LENGTH & 0x0000ff00u) >> 8;
input[4] = DEVICE_MSG_TEXT_MAX_LENGTH & 0x000000ffu;
memset(input + 5, 'a', DEVICE_MSG_TEXT_MAX_LENGTH);
struct device_msg msg;
ssize_t r = device_msg_deserialize(input, sizeof(input), &msg);
assert(r == DEVICE_MSG_MAX_SIZE);
assert(msg.type == DEVICE_MSG_TYPE_CLIPBOARD);
assert(msg.clipboard.text);
assert(strlen(msg.clipboard.text) == DEVICE_MSG_TEXT_MAX_LENGTH);
assert(msg.clipboard.text[0] == 'a');
device_msg_destroy(&msg);
}
int main(void) {
test_deserialize_clipboard();
test_deserialize_clipboard_big();
return 0;
}

View File

@ -1,5 +1,5 @@
project('scrcpy', 'c',
version: '1.13',
version: '1.14',
meson_version: '>= 0.48',
default_options: [
'c_std=c11',

View File

@ -35,6 +35,6 @@ prepare-sdl2:
SDL2-2.0.12
prepare-adb:
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r29.0.5-windows.zip \
2df06160056ec9a84c7334af2a1e42740befbb1a2e34370e7af544a2cc78152c \
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r30.0.0-windows.zip \
854305f9a702f5ea2c3de73edde402bd26afa0ee944c9b0c4380420f5a862e0d \
platform-tools

View File

@ -6,8 +6,8 @@ android {
applicationId "com.genymobile.scrcpy"
minSdkVersion 21
targetSdkVersion 29
versionCode 15
versionName "1.13"
versionCode 16
versionName "1.14"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {

View File

@ -12,7 +12,7 @@
set -e
SCRCPY_DEBUG=false
SCRCPY_VERSION_NAME=1.13
SCRCPY_VERSION_NAME=1.14
PLATFORM=${ANDROID_PLATFORM:-29}
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-29.0.2}

View File

@ -17,6 +17,8 @@ public final class ControlMessage {
public static final int TYPE_SET_SCREEN_POWER_MODE = 9;
public static final int TYPE_ROTATE_DEVICE = 10;
public static final int FLAGS_PASTE = 1;
private int type;
private String text;
private int metaState; // KeyEvent.META_*
@ -28,15 +30,18 @@ public final class ControlMessage {
private Position position;
private int hScroll;
private int vScroll;
private int flags;
private int repeat;
private ControlMessage() {
}
public static ControlMessage createInjectKeycode(int action, int keycode, int metaState) {
public static ControlMessage createInjectKeycode(int action, int keycode, int repeat, int metaState) {
ControlMessage msg = new ControlMessage();
msg.type = TYPE_INJECT_KEYCODE;
msg.action = action;
msg.keycode = keycode;
msg.repeat = repeat;
msg.metaState = metaState;
return msg;
}
@ -68,10 +73,13 @@ public final class ControlMessage {
return msg;
}
public static ControlMessage createSetClipboard(String text) {
public static ControlMessage createSetClipboard(String text, boolean paste) {
ControlMessage msg = new ControlMessage();
msg.type = TYPE_SET_CLIPBOARD;
msg.text = text;
if (paste) {
msg.flags = FLAGS_PASTE;
}
return msg;
}
@ -134,4 +142,12 @@ public final class ControlMessage {
public int getVScroll() {
return vScroll;
}
public int getFlags() {
return flags;
}
public int getRepeat() {
return repeat;
}
}

View File

@ -8,19 +8,19 @@ import java.nio.charset.StandardCharsets;
public class ControlMessageReader {
static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 9;
static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 13;
static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 27;
static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
static final int SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH = 1;
public static final int CLIPBOARD_TEXT_MAX_LENGTH = 4093;
private static final int MESSAGE_MAX_SIZE = 1 << 18; // 256k
public static final int CLIPBOARD_TEXT_MAX_LENGTH = MESSAGE_MAX_SIZE - 6; // type: 1 byte; paste flag: 1 byte; length: 4 bytes
public static final int INJECT_TEXT_MAX_LENGTH = 300;
private static final int RAW_BUFFER_SIZE = 4096;
private final byte[] rawBuffer = new byte[RAW_BUFFER_SIZE];
private final byte[] rawBuffer = new byte[MESSAGE_MAX_SIZE];
private final ByteBuffer buffer = ByteBuffer.wrap(rawBuffer);
private final byte[] textBuffer = new byte[CLIPBOARD_TEXT_MAX_LENGTH];
public ControlMessageReader() {
// invariant: the buffer is always in "get" mode
@ -98,20 +98,23 @@ public class ControlMessageReader {
}
int action = toUnsigned(buffer.get());
int keycode = buffer.getInt();
int repeat = buffer.getInt();
int metaState = buffer.getInt();
return ControlMessage.createInjectKeycode(action, keycode, metaState);
return ControlMessage.createInjectKeycode(action, keycode, repeat, metaState);
}
private String parseString() {
if (buffer.remaining() < 2) {
if (buffer.remaining() < 4) {
return null;
}
int len = toUnsigned(buffer.getShort());
int len = buffer.getInt();
if (buffer.remaining() < len) {
return null;
}
buffer.get(textBuffer, 0, len);
return new String(textBuffer, 0, len, StandardCharsets.UTF_8);
int position = buffer.position();
// Move the buffer position to consume the text
buffer.position(position + len);
return new String(rawBuffer, position, len, StandardCharsets.UTF_8);
}
private ControlMessage parseInjectText() {
@ -148,11 +151,15 @@ public class ControlMessageReader {
}
private ControlMessage parseSetClipboard() {
if (buffer.remaining() < SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH) {
return null;
}
boolean parse = buffer.get() != 0;
String text = parseString();
if (text == null) {
return null;
}
return ControlMessage.createSetClipboard(text);
return ControlMessage.createSetClipboard(text, parse);
}
private ControlMessage parseSetScreenPowerMode() {

View File

@ -1,5 +1,6 @@
package com.genymobile.scrcpy;
import android.os.Build;
import android.os.SystemClock;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
@ -73,7 +74,7 @@ public class Controller {
switch (msg.getType()) {
case ControlMessage.TYPE_INJECT_KEYCODE:
if (device.supportsInputEvents()) {
injectKeycode(msg.getAction(), msg.getKeycode(), msg.getMetaState());
injectKeycode(msg.getAction(), msg.getKeycode(), msg.getRepeat(), msg.getMetaState());
}
break;
case ControlMessage.TYPE_INJECT_TEXT:
@ -104,13 +105,13 @@ public class Controller {
break;
case ControlMessage.TYPE_GET_CLIPBOARD:
String clipboardText = device.getClipboardText();
sender.pushClipboardText(clipboardText);
if (clipboardText != null) {
sender.pushClipboardText(clipboardText);
}
break;
case ControlMessage.TYPE_SET_CLIPBOARD:
boolean setClipboardOk = device.setClipboardText(msg.getText());
if (setClipboardOk) {
Ln.i("Device clipboard set");
}
boolean paste = (msg.getFlags() & ControlMessage.FLAGS_PASTE) != 0;
setClipboard(msg.getText(), paste);
break;
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
if (device.supportsInputEvents()) {
@ -129,8 +130,8 @@ public class Controller {
}
}
private boolean injectKeycode(int action, int keycode, int metaState) {
return device.injectKeyEvent(action, keycode, 0, metaState);
private boolean injectKeycode(int action, int keycode, int repeat, int metaState) {
return device.injectKeyEvent(action, keycode, repeat, metaState);
}
private boolean injectChar(char c) {
@ -227,4 +228,18 @@ public class Controller {
int keycode = device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_POWER;
return device.injectKeycode(keycode);
}
private boolean setClipboard(String text, boolean paste) {
boolean ok = device.setClipboardText(text);
if (ok) {
Ln.i("Device clipboard set");
}
// On Android >= 7, also press the PASTE key if requested
if (paste && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && device.supportsInputEvents()) {
device.injectKeycode(KeyEvent.KEYCODE_PASTE);
}
return ok;
}
}

View File

@ -7,10 +7,10 @@ import java.nio.charset.StandardCharsets;
public class DeviceMessageWriter {
public static final int CLIPBOARD_TEXT_MAX_LENGTH = 4093;
private static final int MAX_EVENT_SIZE = CLIPBOARD_TEXT_MAX_LENGTH + 3;
private static final int MESSAGE_MAX_SIZE = 1 << 18; // 256k
public static final int CLIPBOARD_TEXT_MAX_LENGTH = MESSAGE_MAX_SIZE - 5; // type: 1 byte; length: 4 bytes
private final byte[] rawBuffer = new byte[MAX_EVENT_SIZE];
private final byte[] rawBuffer = new byte[MESSAGE_MAX_SIZE];
private final ByteBuffer buffer = ByteBuffer.wrap(rawBuffer);
public void writeTo(DeviceMessage msg, OutputStream output) throws IOException {
@ -21,7 +21,7 @@ public class DeviceMessageWriter {
String text = msg.getText();
byte[] raw = text.getBytes(StandardCharsets.UTF_8);
int len = StringUtils.getUtf8TruncationIndex(raw, CLIPBOARD_TEXT_MAX_LENGTH);
buffer.putShort((short) len);
buffer.putInt(len);
buffer.put(raw, 0, len);
output.write(rawBuffer, 0, buffer.position());
break;

View File

@ -15,7 +15,7 @@ public final class Ln {
DEBUG, INFO, WARN, ERROR
}
private static Level threshold;
private static Level threshold = Level.INFO;
private Ln() {
// not instantiable

View File

@ -25,6 +25,7 @@ public class ControlMessageReaderTest {
dos.writeByte(ControlMessage.TYPE_INJECT_KEYCODE);
dos.writeByte(KeyEvent.ACTION_UP);
dos.writeInt(KeyEvent.KEYCODE_ENTER);
dos.writeInt(5); // repeat
dos.writeInt(KeyEvent.META_CTRL_ON);
byte[] packet = bos.toByteArray();
@ -37,6 +38,7 @@ public class ControlMessageReaderTest {
Assert.assertEquals(ControlMessage.TYPE_INJECT_KEYCODE, event.getType());
Assert.assertEquals(KeyEvent.ACTION_UP, event.getAction());
Assert.assertEquals(KeyEvent.KEYCODE_ENTER, event.getKeycode());
Assert.assertEquals(5, event.getRepeat());
Assert.assertEquals(KeyEvent.META_CTRL_ON, event.getMetaState());
}
@ -48,7 +50,7 @@ public class ControlMessageReaderTest {
DataOutputStream dos = new DataOutputStream(bos);
dos.writeByte(ControlMessage.TYPE_INJECT_TEXT);
byte[] text = "testé".getBytes(StandardCharsets.UTF_8);
dos.writeShort(text.length);
dos.writeInt(text.length);
dos.write(text);
byte[] packet = bos.toByteArray();
@ -68,7 +70,7 @@ public class ControlMessageReaderTest {
dos.writeByte(ControlMessage.TYPE_INJECT_TEXT);
byte[] text = new byte[ControlMessageReader.INJECT_TEXT_MAX_LENGTH];
Arrays.fill(text, (byte) 'a');
dos.writeShort(text.length);
dos.writeInt(text.length);
dos.write(text);
byte[] packet = bos.toByteArray();
@ -216,8 +218,9 @@ public class ControlMessageReaderTest {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeByte(ControlMessage.TYPE_SET_CLIPBOARD);
dos.writeByte(1); // paste
byte[] text = "testé".getBytes(StandardCharsets.UTF_8);
dos.writeShort(text.length);
dos.writeInt(text.length);
dos.write(text);
byte[] packet = bos.toByteArray();
@ -227,6 +230,9 @@ public class ControlMessageReaderTest {
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
Assert.assertEquals("testé", event.getText());
boolean parse = (event.getFlags() & ControlMessage.FLAGS_PASTE) != 0;
Assert.assertTrue(parse);
}
@Test
@ -238,10 +244,11 @@ public class ControlMessageReaderTest {
dos.writeByte(ControlMessage.TYPE_SET_CLIPBOARD);
byte[] rawText = new byte[ControlMessageReader.CLIPBOARD_TEXT_MAX_LENGTH];
dos.writeByte(1); // paste
Arrays.fill(rawText, (byte) 'a');
String text = new String(rawText, 0, rawText.length);
dos.writeShort(rawText.length);
dos.writeInt(rawText.length);
dos.write(rawText);
byte[] packet = bos.toByteArray();
@ -251,6 +258,9 @@ public class ControlMessageReaderTest {
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
Assert.assertEquals(text, event.getText());
boolean parse = (event.getFlags() & ControlMessage.FLAGS_PASTE) != 0;
Assert.assertTrue(parse);
}
@Test
@ -300,11 +310,13 @@ public class ControlMessageReaderTest {
dos.writeByte(ControlMessage.TYPE_INJECT_KEYCODE);
dos.writeByte(KeyEvent.ACTION_UP);
dos.writeInt(KeyEvent.KEYCODE_ENTER);
dos.writeInt(0); // repeat
dos.writeInt(KeyEvent.META_CTRL_ON);
dos.writeByte(ControlMessage.TYPE_INJECT_KEYCODE);
dos.writeByte(MotionEvent.ACTION_DOWN);
dos.writeInt(MotionEvent.BUTTON_PRIMARY);
dos.writeInt(1); // repeat
dos.writeInt(KeyEvent.META_CTRL_ON);
byte[] packet = bos.toByteArray();
@ -314,12 +326,14 @@ public class ControlMessageReaderTest {
Assert.assertEquals(ControlMessage.TYPE_INJECT_KEYCODE, event.getType());
Assert.assertEquals(KeyEvent.ACTION_UP, event.getAction());
Assert.assertEquals(KeyEvent.KEYCODE_ENTER, event.getKeycode());
Assert.assertEquals(0, event.getRepeat());
Assert.assertEquals(KeyEvent.META_CTRL_ON, event.getMetaState());
event = reader.next();
Assert.assertEquals(ControlMessage.TYPE_INJECT_KEYCODE, event.getType());
Assert.assertEquals(MotionEvent.ACTION_DOWN, event.getAction());
Assert.assertEquals(MotionEvent.BUTTON_PRIMARY, event.getKeycode());
Assert.assertEquals(1, event.getRepeat());
Assert.assertEquals(KeyEvent.META_CTRL_ON, event.getMetaState());
}
@ -333,6 +347,7 @@ public class ControlMessageReaderTest {
dos.writeByte(ControlMessage.TYPE_INJECT_KEYCODE);
dos.writeByte(KeyEvent.ACTION_UP);
dos.writeInt(KeyEvent.KEYCODE_ENTER);
dos.writeInt(4); // repeat
dos.writeInt(KeyEvent.META_CTRL_ON);
dos.writeByte(ControlMessage.TYPE_INJECT_KEYCODE);
@ -345,6 +360,7 @@ public class ControlMessageReaderTest {
Assert.assertEquals(ControlMessage.TYPE_INJECT_KEYCODE, event.getType());
Assert.assertEquals(KeyEvent.ACTION_UP, event.getAction());
Assert.assertEquals(KeyEvent.KEYCODE_ENTER, event.getKeycode());
Assert.assertEquals(4, event.getRepeat());
Assert.assertEquals(KeyEvent.META_CTRL_ON, event.getMetaState());
event = reader.next();
@ -352,6 +368,7 @@ public class ControlMessageReaderTest {
bos.reset();
dos.writeInt(MotionEvent.BUTTON_PRIMARY);
dos.writeInt(5); // repeat
dos.writeInt(KeyEvent.META_CTRL_ON);
packet = bos.toByteArray();
reader.readFrom(new ByteArrayInputStream(packet));
@ -361,6 +378,7 @@ public class ControlMessageReaderTest {
Assert.assertEquals(ControlMessage.TYPE_INJECT_KEYCODE, event.getType());
Assert.assertEquals(MotionEvent.ACTION_DOWN, event.getAction());
Assert.assertEquals(MotionEvent.BUTTON_PRIMARY, event.getKeycode());
Assert.assertEquals(5, event.getRepeat());
Assert.assertEquals(KeyEvent.META_CTRL_ON, event.getMetaState());
}
}

View File

@ -19,7 +19,7 @@ public class DeviceMessageWriterTest {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeByte(DeviceMessage.TYPE_CLIPBOARD);
dos.writeShort(data.length);
dos.writeInt(data.length);
dos.write(data);
byte[] expected = bos.toByteArray();