Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
8da10eb4af | ||
|
b330ba448b | ||
|
a98b74df3d | ||
|
567c5a7da7 | ||
|
9badd2bdf0 | ||
|
31cee2c49f | ||
|
51d969b20c | ||
|
149de2f788 | ||
|
26cadf1022 | ||
|
f650b0195f |
53
README.md
53
README.md
@ -560,33 +560,36 @@ Also see [issue #14].
|
|||||||
|
|
||||||
## Shortcuts
|
## Shortcuts
|
||||||
|
|
||||||
| Action | Shortcut | Shortcut (macOS)
|
_`Meta` is typically the `Windows` key on the keyboard, or `Cmd` on macOS._
|
||||||
| ------------------------------------------- |:----------------------------- |:-----------------------------
|
|
||||||
| Switch fullscreen mode | `Ctrl`+`f` | `Cmd`+`f`
|
| Action | Shortcut
|
||||||
| Rotate display left | `Ctrl`+`←` _(left)_ | `Cmd`+`←` _(left)_
|
| ---------------------------------------------------- |:-----------------------------
|
||||||
| Rotate display right | `Ctrl`+`→` _(right)_ | `Cmd`+`→` _(right)_
|
| Switch fullscreen mode | `Meta`+`f`
|
||||||
| Resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` | `Cmd`+`g`
|
| Rotate display left | `Meta`+`←` _(left)_
|
||||||
| Resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_ | `Cmd`+`x` \| _Double-click¹_
|
| Rotate display right | `Meta`+`→` _(right)_
|
||||||
| Click on `HOME` | `Ctrl`+`h` \| _Middle-click_ | `Ctrl`+`h` \| _Middle-click_
|
| Resize window to 1:1 (pixel-perfect) | `Meta`+`g`
|
||||||
| Click on `BACK` | `Ctrl`+`b` \| _Right-click²_ | `Cmd`+`b` \| _Right-click²_
|
| Resize window to remove black borders | `Meta`+`w` \| _Double-click¹_
|
||||||
| Click on `APP_SWITCH` | `Ctrl`+`s` | `Cmd`+`s`
|
| Click on `HOME` | `Meta`+`h` \| _Middle-click_
|
||||||
| Click on `MENU` | `Ctrl`+`m` | `Ctrl`+`m`
|
| Click on `BACK` | `Meta`+`b` \| _Right-click²_
|
||||||
| Click on `VOLUME_UP` | `Ctrl`+`↑` _(up)_ | `Cmd`+`↑` _(up)_
|
| Click on `APP_SWITCH` | `Meta`+`s`
|
||||||
| Click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_ | `Cmd`+`↓` _(down)_
|
| Click on `MENU` | `Meta`+`m`
|
||||||
| Click on `POWER` | `Ctrl`+`p` | `Cmd`+`p`
|
| Click on `VOLUME_UP` | `Meta`+`↑` _(up)_
|
||||||
| Power on | _Right-click²_ | _Right-click²_
|
| Click on `VOLUME_DOWN` | `Meta`+`↓` _(down)_
|
||||||
| Turn device screen off (keep mirroring) | `Ctrl`+`o` | `Cmd`+`o`
|
| Click on `POWER` | `Meta`+`p`
|
||||||
| Turn device screen on | `Ctrl`+`Shift`+`o` | `Cmd`+`Shift`+`o`
|
| Power on | _Right-click²_
|
||||||
| Rotate device screen | `Ctrl`+`r` | `Cmd`+`r`
|
| Turn device screen off (keep mirroring) | `Meta`+`o`
|
||||||
| Expand notification panel | `Ctrl`+`n` | `Cmd`+`n`
|
| Turn device screen on | `Meta`+`Shift`+`o`
|
||||||
| Collapse notification panel | `Ctrl`+`Shift`+`n` | `Cmd`+`Shift`+`n`
|
| Rotate device screen | `Meta`+`r`
|
||||||
| Copy device clipboard to computer | `Ctrl`+`c` | `Cmd`+`c`
|
| Expand notification panel | `Meta`+`n`
|
||||||
| Paste computer clipboard to device | `Ctrl`+`v` | `Cmd`+`v`
|
| Collapse notification panel | `Meta`+`Shift`+`n`
|
||||||
| Copy computer clipboard to device and paste | `Ctrl`+`Shift`+`v` | `Cmd`+`Shift`+`v`
|
| Press COPY³, then Copy device clipboard to computer | `Meta`+`c`
|
||||||
| Enable/disable FPS counter (on stdout) | `Ctrl`+`i` | `Cmd`+`i`
|
| Press CUT³ | `Meta`+`x`
|
||||||
|
| Paste computer clipboard to device | `Meta`+`v`
|
||||||
|
| Enable/disable FPS counter (on stdout) | `Meta`+`i`
|
||||||
|
|
||||||
_¹Double-click on black borders to remove them._
|
_¹Double-click on black borders to remove them._
|
||||||
_²Right-click turns the screen on if it was off, presses BACK otherwise._
|
_²Right-click turns the screen on if it was off, presses BACK otherwise._
|
||||||
|
_³Only if the device runs Android >= 7._
|
||||||
|
|
||||||
|
|
||||||
## Custom paths
|
## Custom paths
|
||||||
|
50
app/scrcpy.1
50
app/scrcpy.1
@ -204,51 +204,51 @@ Default is 0 (automatic).\n
|
|||||||
.SH SHORTCUTS
|
.SH SHORTCUTS
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+f
|
.B Meta+f
|
||||||
Switch fullscreen mode
|
Switch fullscreen mode
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+Left
|
.B Meta+Left
|
||||||
Rotate display left
|
Rotate display left
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+Right
|
.B Meta+Right
|
||||||
Rotate display right
|
Rotate display right
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+g
|
.B Meta+g
|
||||||
Resize window to 1:1 (pixel\-perfect)
|
Resize window to 1:1 (pixel\-perfect)
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+x, Double\-click on black borders
|
.B Meta+w, Double\-click on black borders
|
||||||
Resize window to remove black borders
|
Resize window to remove black borders
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+h, Home, Middle\-click
|
.B Meta+h, Home, Middle\-click
|
||||||
Click on HOME
|
Click on HOME
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+b, Ctrl+Backspace, Right\-click (when screen is on)
|
.B Meta+b, Meta+Backspace, Right\-click (when screen is on)
|
||||||
Click on BACK
|
Click on BACK
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+s
|
.B Meta+s
|
||||||
Click on APP_SWITCH
|
Click on APP_SWITCH
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+m
|
.B Meta+m
|
||||||
Click on MENU
|
Click on MENU
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+Up
|
.B Meta+Up
|
||||||
Click on VOLUME_UP
|
Click on VOLUME_UP
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+Down
|
.B Meta+Down
|
||||||
Click on VOLUME_DOWN
|
Click on VOLUME_DOWN
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+p
|
.B Meta+p
|
||||||
Click on POWER (turn screen on/off)
|
Click on POWER (turn screen on/off)
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
@ -256,39 +256,39 @@ Click on POWER (turn screen on/off)
|
|||||||
Turn screen on
|
Turn screen on
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+o
|
.B Meta+o
|
||||||
Turn device screen off (keep mirroring)
|
Turn device screen off (keep mirroring)
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+Shift+o
|
.B Meta+Shift+o
|
||||||
Turn device screen on
|
Turn device screen on
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+r
|
.B Meta+r
|
||||||
Rotate device screen
|
Rotate device screen
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+n
|
.B Meta+n
|
||||||
Expand notification panel
|
Expand notification panel
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+Shift+n
|
.B Meta+Shift+n
|
||||||
Collapse notification panel
|
Collapse notification panel
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+c
|
.B Meta+c
|
||||||
Copy device clipboard to computer
|
Press COPY (Android >= 7), then copy device clipboard to computer
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+v
|
.B Meta+x
|
||||||
|
Press CUT (Android >= 7)
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B Meta+v
|
||||||
Paste computer clipboard to device
|
Paste computer clipboard to device
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+Shift+v
|
.B Meta+i
|
||||||
Copy computer clipboard to device (and paste if the device runs Android >= 7)
|
|
||||||
|
|
||||||
.TP
|
|
||||||
.B Ctrl+i
|
|
||||||
Enable/disable FPS counter (print frames/second in logs)
|
Enable/disable FPS counter (print frames/second in logs)
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
|
@ -13,9 +13,9 @@
|
|||||||
void
|
void
|
||||||
scrcpy_print_usage(const char *arg0) {
|
scrcpy_print_usage(const char *arg0) {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
# define CTRL_OR_CMD "Cmd"
|
# define MOD "Cmd"
|
||||||
#else
|
#else
|
||||||
# define CTRL_OR_CMD "Ctrl"
|
# define MOD "Meta"
|
||||||
#endif
|
#endif
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Usage: %s [options]\n"
|
"Usage: %s [options]\n"
|
||||||
@ -186,19 +186,19 @@ scrcpy_print_usage(const char *arg0) {
|
|||||||
"\n"
|
"\n"
|
||||||
"Shortcuts:\n"
|
"Shortcuts:\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+f\n"
|
" " MOD "+f\n"
|
||||||
" Switch fullscreen mode\n"
|
" Switch fullscreen mode\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+Left\n"
|
" " MOD "+Left\n"
|
||||||
" Rotate display left\n"
|
" Rotate display left\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+Right\n"
|
" " MOD "+Right\n"
|
||||||
" Rotate display right\n"
|
" Rotate display right\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+g\n"
|
" " MOD "+g\n"
|
||||||
" Resize window to 1:1 (pixel-perfect)\n"
|
" Resize window to 1:1 (pixel-perfect)\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+x\n"
|
" " MOD "+w\n"
|
||||||
" Double-click on black borders\n"
|
" Double-click on black borders\n"
|
||||||
" Resize window to remove black borders\n"
|
" Resize window to remove black borders\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -206,55 +206,55 @@ scrcpy_print_usage(const char *arg0) {
|
|||||||
" Middle-click\n"
|
" Middle-click\n"
|
||||||
" Click on HOME\n"
|
" Click on HOME\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+b\n"
|
" " MOD "+b\n"
|
||||||
" " CTRL_OR_CMD "+Backspace\n"
|
" " MOD "+Backspace\n"
|
||||||
" Right-click (when screen is on)\n"
|
" Right-click (when screen is on)\n"
|
||||||
" Click on BACK\n"
|
" Click on BACK\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+s\n"
|
" " MOD "+s\n"
|
||||||
" Click on APP_SWITCH\n"
|
" Click on APP_SWITCH\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+m\n"
|
" Ctrl+m\n"
|
||||||
" Click on MENU\n"
|
" Click on MENU\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+Up\n"
|
" " MOD "+Up\n"
|
||||||
" Click on VOLUME_UP\n"
|
" Click on VOLUME_UP\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+Down\n"
|
" " MOD "+Down\n"
|
||||||
" Click on VOLUME_DOWN\n"
|
" Click on VOLUME_DOWN\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+p\n"
|
" " MOD "+p\n"
|
||||||
" Click on POWER (turn screen on/off)\n"
|
" Click on POWER (turn screen on/off)\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Right-click (when screen is off)\n"
|
" Right-click (when screen is off)\n"
|
||||||
" Power on\n"
|
" Power on\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+o\n"
|
" " MOD "+o\n"
|
||||||
" Turn device screen off (keep mirroring)\n"
|
" Turn device screen off (keep mirroring)\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+Shift+o\n"
|
" " MOD "+Shift+o\n"
|
||||||
" Turn device screen on\n"
|
" Turn device screen on\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+r\n"
|
" " MOD "+r\n"
|
||||||
" Rotate device screen\n"
|
" Rotate device screen\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+n\n"
|
" " MOD "+n\n"
|
||||||
" Expand notification panel\n"
|
" Expand notification panel\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+Shift+n\n"
|
" " MOD "+Shift+n\n"
|
||||||
" Collapse notification panel\n"
|
" Collapse notification panel\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+c\n"
|
" " MOD "+c\n"
|
||||||
" Copy device clipboard to computer\n"
|
" Press COPY (Android >= 7), then copy device clipboard to\n"
|
||||||
|
" computer\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+v\n"
|
" " MOD "+x\n"
|
||||||
|
" Press CUT (Android >= 7)\n"
|
||||||
|
"\n"
|
||||||
|
" " MOD "+v\n"
|
||||||
" Paste computer clipboard to device\n"
|
" Paste computer clipboard to device\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+Shift+v\n"
|
" " MOD "+i\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"
|
" Enable/disable FPS counter (print frames/second in logs)\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Drag & drop APK file\n"
|
" Drag & drop APK file\n"
|
||||||
@ -265,6 +265,7 @@ scrcpy_print_usage(const char *arg0) {
|
|||||||
DEFAULT_LOCK_VIDEO_ORIENTATION, DEFAULT_LOCK_VIDEO_ORIENTATION >= 0 ? "" : " (unlocked)",
|
DEFAULT_LOCK_VIDEO_ORIENTATION, DEFAULT_LOCK_VIDEO_ORIENTATION >= 0 ? "" : " (unlocked)",
|
||||||
DEFAULT_MAX_SIZE, DEFAULT_MAX_SIZE ? "" : " (unlimited)",
|
DEFAULT_MAX_SIZE, DEFAULT_MAX_SIZE ? "" : " (unlimited)",
|
||||||
DEFAULT_LOCAL_PORT_RANGE_FIRST, DEFAULT_LOCAL_PORT_RANGE_LAST);
|
DEFAULT_LOCAL_PORT_RANGE_FIRST, DEFAULT_LOCAL_PORT_RANGE_LAST);
|
||||||
|
#undef MOD
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
@ -66,6 +66,9 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
|||||||
buffer_write32be(&buf[17],
|
buffer_write32be(&buf[17],
|
||||||
(uint32_t) msg->inject_scroll_event.vscroll);
|
(uint32_t) msg->inject_scroll_event.vscroll);
|
||||||
return 21;
|
return 21;
|
||||||
|
case CONTROL_MSG_TYPE_GET_CLIPBOARD:
|
||||||
|
buf[1] = msg->get_clipboard.copy;
|
||||||
|
return 2;
|
||||||
case CONTROL_MSG_TYPE_SET_CLIPBOARD: {
|
case CONTROL_MSG_TYPE_SET_CLIPBOARD: {
|
||||||
buf[1] = !!msg->set_clipboard.paste;
|
buf[1] = !!msg->set_clipboard.paste;
|
||||||
size_t len = write_string(msg->set_clipboard.text,
|
size_t len = write_string(msg->set_clipboard.text,
|
||||||
@ -79,7 +82,6 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
|||||||
case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
|
case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
|
||||||
case CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
|
case CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||||
case CONTROL_MSG_TYPE_COLLAPSE_NOTIFICATION_PANEL:
|
case CONTROL_MSG_TYPE_COLLAPSE_NOTIFICATION_PANEL:
|
||||||
case CONTROL_MSG_TYPE_GET_CLIPBOARD:
|
|
||||||
case CONTROL_MSG_TYPE_ROTATE_DEVICE:
|
case CONTROL_MSG_TYPE_ROTATE_DEVICE:
|
||||||
// no additional data
|
// no additional data
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -60,6 +60,9 @@ struct control_msg {
|
|||||||
int32_t hscroll;
|
int32_t hscroll;
|
||||||
int32_t vscroll;
|
int32_t vscroll;
|
||||||
} inject_scroll_event;
|
} inject_scroll_event;
|
||||||
|
struct {
|
||||||
|
bool copy;
|
||||||
|
} get_clipboard;
|
||||||
struct {
|
struct {
|
||||||
char *text; // owned, to be freed by SDL_free()
|
char *text; // owned, to be freed by SDL_free()
|
||||||
bool paste;
|
bool paste;
|
||||||
|
@ -92,6 +92,10 @@ convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod,
|
|||||||
MAP(SDLK_LEFT, AKEYCODE_DPAD_LEFT);
|
MAP(SDLK_LEFT, AKEYCODE_DPAD_LEFT);
|
||||||
MAP(SDLK_DOWN, AKEYCODE_DPAD_DOWN);
|
MAP(SDLK_DOWN, AKEYCODE_DPAD_DOWN);
|
||||||
MAP(SDLK_UP, AKEYCODE_DPAD_UP);
|
MAP(SDLK_UP, AKEYCODE_DPAD_UP);
|
||||||
|
MAP(SDLK_LCTRL, AKEYCODE_CTRL_LEFT);
|
||||||
|
MAP(SDLK_RCTRL, AKEYCODE_CTRL_RIGHT);
|
||||||
|
MAP(SDLK_LSHIFT, AKEYCODE_SHIFT_LEFT);
|
||||||
|
MAP(SDLK_RSHIFT, AKEYCODE_SHIFT_RIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(mod & (KMOD_NUM | KMOD_SHIFT))) {
|
if (!(mod & (KMOD_NUM | KMOD_SHIFT))) {
|
||||||
|
@ -70,6 +70,11 @@ action_menu(struct controller *controller, int actions) {
|
|||||||
send_keycode(controller, AKEYCODE_MENU, actions, "MENU");
|
send_keycode(controller, AKEYCODE_MENU, actions, "MENU");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
action_cut(struct controller *controller, int actions) {
|
||||||
|
send_keycode(controller, AKEYCODE_CUT, actions, "CUT");
|
||||||
|
}
|
||||||
|
|
||||||
// turn the screen on if it was off, press BACK otherwise
|
// turn the screen on if it was off, press BACK otherwise
|
||||||
static void
|
static void
|
||||||
press_back_or_turn_screen_on(struct controller *controller) {
|
press_back_or_turn_screen_on(struct controller *controller) {
|
||||||
@ -102,9 +107,10 @@ collapse_notification_panel(struct controller *controller) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
request_device_clipboard(struct controller *controller) {
|
request_device_clipboard(struct controller *controller, bool copy) {
|
||||||
struct control_msg msg;
|
struct control_msg msg;
|
||||||
msg.type = CONTROL_MSG_TYPE_GET_CLIPBOARD;
|
msg.type = CONTROL_MSG_TYPE_GET_CLIPBOARD;
|
||||||
|
msg.get_clipboard.copy = copy;
|
||||||
|
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!controller_push_msg(controller, &msg)) {
|
||||||
LOGW("Could not request device clipboard");
|
LOGW("Could not request device clipboard");
|
||||||
@ -262,66 +268,56 @@ input_manager_process_key(struct input_manager *im,
|
|||||||
bool ctrl = event->keysym.mod & (KMOD_LCTRL | KMOD_RCTRL);
|
bool ctrl = event->keysym.mod & (KMOD_LCTRL | KMOD_RCTRL);
|
||||||
bool alt = event->keysym.mod & (KMOD_LALT | KMOD_RALT);
|
bool alt = event->keysym.mod & (KMOD_LALT | KMOD_RALT);
|
||||||
bool meta = event->keysym.mod & (KMOD_LGUI | KMOD_RGUI);
|
bool meta = event->keysym.mod & (KMOD_LGUI | KMOD_RGUI);
|
||||||
|
bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT);
|
||||||
// use Cmd on macOS, Ctrl on other platforms
|
|
||||||
#ifdef __APPLE__
|
|
||||||
bool cmd = !ctrl && meta;
|
|
||||||
#else
|
|
||||||
if (meta) {
|
|
||||||
// no shortcuts involve Meta on platforms other than macOS, and it must
|
|
||||||
// not be forwarded to the device
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bool cmd = ctrl; // && !meta, already guaranteed
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (alt) {
|
if (alt) {
|
||||||
// no shortcuts involve Alt, and it must not be forwarded to the device
|
// No shortcuts involve Alt, and it is not forwarded to the device
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct controller *controller = im->controller;
|
struct controller *controller = im->controller;
|
||||||
|
|
||||||
// capture all Ctrl events
|
SDL_Keycode keycode = event->keysym.sym;
|
||||||
if (ctrl || cmd) {
|
bool down = event->type == SDL_KEYDOWN;
|
||||||
SDL_Keycode keycode = event->keysym.sym;
|
|
||||||
bool down = event->type == SDL_KEYDOWN;
|
// Capture all Meta events
|
||||||
|
if (meta) {
|
||||||
|
if (ctrl) {
|
||||||
|
// No shortcuts involve Ctrl+Meta
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int action = down ? ACTION_DOWN : ACTION_UP;
|
int action = down ? ACTION_DOWN : ACTION_UP;
|
||||||
bool repeat = event->repeat;
|
bool repeat = event->repeat;
|
||||||
bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT);
|
|
||||||
switch (keycode) {
|
switch (keycode) {
|
||||||
case SDLK_h:
|
case SDLK_h:
|
||||||
// Ctrl+h on all platform, since Cmd+h is already captured by
|
if (control && !shift && !repeat) {
|
||||||
// the system on macOS to hide the window
|
|
||||||
if (control && ctrl && !meta && !shift && !repeat) {
|
|
||||||
action_home(controller, action);
|
action_home(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_b: // fall-through
|
case SDLK_b: // fall-through
|
||||||
case SDLK_BACKSPACE:
|
case SDLK_BACKSPACE:
|
||||||
if (control && cmd && !shift && !repeat) {
|
if (control && !shift && !repeat) {
|
||||||
action_back(controller, action);
|
action_back(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_s:
|
case SDLK_s:
|
||||||
if (control && cmd && !shift && !repeat) {
|
if (control && !shift && !repeat) {
|
||||||
action_app_switch(controller, action);
|
action_app_switch(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_m:
|
case SDLK_m:
|
||||||
// Ctrl+m on all platform, since Cmd+m is already captured by
|
if (control && !shift && !repeat) {
|
||||||
// the system on macOS to minimize the window
|
|
||||||
if (control && ctrl && !meta && !shift && !repeat) {
|
|
||||||
action_menu(controller, action);
|
action_menu(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_p:
|
case SDLK_p:
|
||||||
if (control && cmd && !shift && !repeat) {
|
if (control && !shift && !repeat) {
|
||||||
action_power(controller, action);
|
action_power(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_o:
|
case SDLK_o:
|
||||||
if (control && cmd && !repeat && down) {
|
if (control && !repeat && down) {
|
||||||
enum screen_power_mode mode = shift
|
enum screen_power_mode mode = shift
|
||||||
? SCREEN_POWER_MODE_NORMAL
|
? SCREEN_POWER_MODE_NORMAL
|
||||||
: SCREEN_POWER_MODE_OFF;
|
: SCREEN_POWER_MODE_OFF;
|
||||||
@ -329,67 +325,71 @@ input_manager_process_key(struct input_manager *im,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_DOWN:
|
case SDLK_DOWN:
|
||||||
if (control && cmd && !shift) {
|
if (control && !shift) {
|
||||||
// forward repeated events
|
// forward repeated events
|
||||||
action_volume_down(controller, action);
|
action_volume_down(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_UP:
|
case SDLK_UP:
|
||||||
if (control && cmd && !shift) {
|
if (control && !shift) {
|
||||||
// forward repeated events
|
// forward repeated events
|
||||||
action_volume_up(controller, action);
|
action_volume_up(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_LEFT:
|
case SDLK_LEFT:
|
||||||
if (cmd && !shift && !repeat && down) {
|
if (!shift && !repeat && down) {
|
||||||
rotate_client_left(im->screen);
|
rotate_client_left(im->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_RIGHT:
|
case SDLK_RIGHT:
|
||||||
if (cmd && !shift && !repeat && down) {
|
if (!shift && !repeat && down) {
|
||||||
rotate_client_right(im->screen);
|
rotate_client_right(im->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_c:
|
case SDLK_c:
|
||||||
if (control && cmd && !shift && !repeat && down) {
|
if (control && !shift && !repeat && down) {
|
||||||
request_device_clipboard(controller);
|
// Press COPY, then get the clipboard content
|
||||||
}
|
request_device_clipboard(controller, true);
|
||||||
return;
|
|
||||||
case SDLK_v:
|
|
||||||
if (control && cmd && !repeat && down) {
|
|
||||||
if (shift) {
|
|
||||||
// store the text in the device clipboard and paste
|
|
||||||
set_device_clipboard(controller, true);
|
|
||||||
} else {
|
|
||||||
// inject the text as input events
|
|
||||||
clipboard_paste(controller);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
case SDLK_f:
|
|
||||||
if (!shift && cmd && !repeat && down) {
|
|
||||||
screen_switch_fullscreen(im->screen);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_x:
|
case SDLK_x:
|
||||||
if (!shift && cmd && !repeat && down) {
|
if (control && !shift && !repeat && down) {
|
||||||
|
// For convenience (especially on macOS), bind Meta+x to
|
||||||
|
// CUT (even if it is already accessible by pressing Ctrl+x
|
||||||
|
// on the device)
|
||||||
|
action_cut(controller, action);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case SDLK_v:
|
||||||
|
if (control && !shift && !repeat && down) {
|
||||||
|
// Inject the text as input events
|
||||||
|
clipboard_paste(controller);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case SDLK_f:
|
||||||
|
if (!shift && !repeat && down) {
|
||||||
|
screen_switch_fullscreen(im->screen);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case SDLK_w:
|
||||||
|
if (!shift && !repeat && down) {
|
||||||
screen_resize_to_fit(im->screen);
|
screen_resize_to_fit(im->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_g:
|
case SDLK_g:
|
||||||
if (!shift && cmd && !repeat && down) {
|
if (!shift && !repeat && down) {
|
||||||
screen_resize_to_pixel_perfect(im->screen);
|
screen_resize_to_pixel_perfect(im->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_i:
|
case SDLK_i:
|
||||||
if (!shift && cmd && !repeat && down) {
|
if (!shift && !repeat && down) {
|
||||||
struct fps_counter *fps_counter =
|
struct fps_counter *fps_counter =
|
||||||
im->video_buffer->fps_counter;
|
im->video_buffer->fps_counter;
|
||||||
switch_fps_counter_state(fps_counter);
|
switch_fps_counter_state(fps_counter);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_n:
|
case SDLK_n:
|
||||||
if (control && cmd && !repeat && down) {
|
if (control && !repeat && down) {
|
||||||
if (shift) {
|
if (shift) {
|
||||||
collapse_notification_panel(controller);
|
collapse_notification_panel(controller);
|
||||||
} else {
|
} else {
|
||||||
@ -398,7 +398,7 @@ input_manager_process_key(struct input_manager *im,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_r:
|
case SDLK_r:
|
||||||
if (control && cmd && !shift && !repeat && down) {
|
if (control && !shift && !repeat && down) {
|
||||||
rotate_device(controller);
|
rotate_device(controller);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -411,6 +411,14 @@ input_manager_process_key(struct input_manager *im,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(!meta);
|
||||||
|
|
||||||
|
if (ctrl && !shift && keycode == SDLK_v && down) {
|
||||||
|
// Synchronize the computer clipboard to the device clipboard before
|
||||||
|
// sending Ctrl+v
|
||||||
|
set_device_clipboard(controller, false);
|
||||||
|
}
|
||||||
|
|
||||||
struct control_msg msg;
|
struct control_msg msg;
|
||||||
if (convert_input_key(event, &msg, im->prefer_text)) {
|
if (convert_input_key(event, &msg, im->prefer_text)) {
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!controller_push_msg(controller, &msg)) {
|
||||||
|
@ -185,14 +185,18 @@ static void test_serialize_collapse_notification_panel(void) {
|
|||||||
static void test_serialize_get_clipboard(void) {
|
static void test_serialize_get_clipboard(void) {
|
||||||
struct control_msg msg = {
|
struct control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_GET_CLIPBOARD,
|
.type = CONTROL_MSG_TYPE_GET_CLIPBOARD,
|
||||||
|
.get_clipboard = {
|
||||||
|
.copy = true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
|
||||||
int size = control_msg_serialize(&msg, buf);
|
int size = control_msg_serialize(&msg, buf);
|
||||||
assert(size == 1);
|
assert(size == 2);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
CONTROL_MSG_TYPE_GET_CLIPBOARD,
|
CONTROL_MSG_TYPE_GET_CLIPBOARD,
|
||||||
|
1, // copy
|
||||||
};
|
};
|
||||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,6 @@ public final class ControlMessage {
|
|||||||
public static final int TYPE_SET_SCREEN_POWER_MODE = 9;
|
public static final int TYPE_SET_SCREEN_POWER_MODE = 9;
|
||||||
public static final int TYPE_ROTATE_DEVICE = 10;
|
public static final int TYPE_ROTATE_DEVICE = 10;
|
||||||
|
|
||||||
public static final int FLAGS_PASTE = 1;
|
|
||||||
|
|
||||||
private int type;
|
private int type;
|
||||||
private String text;
|
private String text;
|
||||||
private int metaState; // KeyEvent.META_*
|
private int metaState; // KeyEvent.META_*
|
||||||
@ -30,7 +28,7 @@ public final class ControlMessage {
|
|||||||
private Position position;
|
private Position position;
|
||||||
private int hScroll;
|
private int hScroll;
|
||||||
private int vScroll;
|
private int vScroll;
|
||||||
private int flags;
|
private boolean pressCopyOrPaste;
|
||||||
|
|
||||||
private ControlMessage() {
|
private ControlMessage() {
|
||||||
}
|
}
|
||||||
@ -71,13 +69,18 @@ public final class ControlMessage {
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ControlMessage createGetClipboard(boolean copy) {
|
||||||
|
ControlMessage msg = new ControlMessage();
|
||||||
|
msg.type = TYPE_GET_CLIPBOARD;
|
||||||
|
msg.pressCopyOrPaste = copy;
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
public static ControlMessage createSetClipboard(String text, boolean paste) {
|
public static ControlMessage createSetClipboard(String text, boolean paste) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage msg = new ControlMessage();
|
||||||
msg.type = TYPE_SET_CLIPBOARD;
|
msg.type = TYPE_SET_CLIPBOARD;
|
||||||
msg.text = text;
|
msg.text = text;
|
||||||
if (paste) {
|
msg.pressCopyOrPaste = paste;
|
||||||
msg.flags = FLAGS_PASTE;
|
|
||||||
}
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +144,7 @@ public final class ControlMessage {
|
|||||||
return vScroll;
|
return vScroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFlags() {
|
public boolean getPressCopyOrPaste() {
|
||||||
return flags;
|
return pressCopyOrPaste;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ public class ControlMessageReader {
|
|||||||
static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 27;
|
static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 27;
|
||||||
static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
|
static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
|
||||||
static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
||||||
|
static final int GET_CLIPBOARD_PAYLOAD_LENGTH = 1;
|
||||||
static final int SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH = 1;
|
static final int SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH = 1;
|
||||||
|
|
||||||
public static final int CLIPBOARD_TEXT_MAX_LENGTH = 4092; // 4096 - 1 (type) - 1 (parse flag) - 2 (length)
|
public static final int CLIPBOARD_TEXT_MAX_LENGTH = 4092; // 4096 - 1 (type) - 1 (parse flag) - 2 (length)
|
||||||
@ -67,6 +68,9 @@ public class ControlMessageReader {
|
|||||||
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
||||||
msg = parseInjectScrollEvent();
|
msg = parseInjectScrollEvent();
|
||||||
break;
|
break;
|
||||||
|
case ControlMessage.TYPE_GET_CLIPBOARD:
|
||||||
|
msg = parseGetClipboard();
|
||||||
|
break;
|
||||||
case ControlMessage.TYPE_SET_CLIPBOARD:
|
case ControlMessage.TYPE_SET_CLIPBOARD:
|
||||||
msg = parseSetClipboard();
|
msg = parseSetClipboard();
|
||||||
break;
|
break;
|
||||||
@ -76,7 +80,6 @@ public class ControlMessageReader {
|
|||||||
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
|
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
|
||||||
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
|
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||||
case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL:
|
case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL:
|
||||||
case ControlMessage.TYPE_GET_CLIPBOARD:
|
|
||||||
case ControlMessage.TYPE_ROTATE_DEVICE:
|
case ControlMessage.TYPE_ROTATE_DEVICE:
|
||||||
msg = ControlMessage.createEmpty(type);
|
msg = ControlMessage.createEmpty(type);
|
||||||
break;
|
break;
|
||||||
@ -148,6 +151,14 @@ public class ControlMessageReader {
|
|||||||
return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll);
|
return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ControlMessage parseGetClipboard() {
|
||||||
|
if (buffer.remaining() < GET_CLIPBOARD_PAYLOAD_LENGTH) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
boolean copy = buffer.get() != 0;
|
||||||
|
return ControlMessage.createGetClipboard(copy);
|
||||||
|
}
|
||||||
|
|
||||||
private ControlMessage parseSetClipboard() {
|
private ControlMessage parseSetClipboard() {
|
||||||
if (buffer.remaining() < SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH) {
|
if (buffer.remaining() < SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -104,14 +104,10 @@ public class Controller {
|
|||||||
device.collapsePanels();
|
device.collapsePanels();
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_GET_CLIPBOARD:
|
case ControlMessage.TYPE_GET_CLIPBOARD:
|
||||||
String clipboardText = device.getClipboardText();
|
getClipboard(msg.getPressCopyOrPaste());
|
||||||
if (clipboardText != null) {
|
|
||||||
sender.pushClipboardText(clipboardText);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_SET_CLIPBOARD:
|
case ControlMessage.TYPE_SET_CLIPBOARD:
|
||||||
boolean paste = (msg.getFlags() & ControlMessage.FLAGS_PASTE) != 0;
|
setClipboard(msg.getText(), msg.getPressCopyOrPaste());
|
||||||
setClipboard(msg.getText(), paste);
|
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
||||||
if (device.supportsInputEvents()) {
|
if (device.supportsInputEvents()) {
|
||||||
@ -229,6 +225,23 @@ public class Controller {
|
|||||||
return device.injectKeycode(keycode);
|
return device.injectKeycode(keycode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean getClipboard(boolean copy) {
|
||||||
|
// On Android >= 7, also press the COPY key if requested
|
||||||
|
if (copy && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && device.supportsInputEvents()) {
|
||||||
|
// If there is something to copy, the clipboard will be automatically sent to the computer clipboard via the ClipboardListener
|
||||||
|
return device.injectKeycode(KeyEvent.KEYCODE_COPY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can't press COPY, so only synchronize the current clipboard
|
||||||
|
String clipboardText = device.getClipboardText();
|
||||||
|
if (clipboardText != null) {
|
||||||
|
sender.pushClipboardText(clipboardText);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean setClipboard(String text, boolean paste) {
|
private boolean setClipboard(String text, boolean paste) {
|
||||||
boolean ok = device.setClipboardText(text);
|
boolean ok = device.setClipboardText(text);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
|
@ -200,6 +200,7 @@ public class ControlMessageReaderTest {
|
|||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
DataOutputStream dos = new DataOutputStream(bos);
|
DataOutputStream dos = new DataOutputStream(bos);
|
||||||
dos.writeByte(ControlMessage.TYPE_GET_CLIPBOARD);
|
dos.writeByte(ControlMessage.TYPE_GET_CLIPBOARD);
|
||||||
|
dos.writeByte(1); // copy
|
||||||
|
|
||||||
byte[] packet = bos.toByteArray();
|
byte[] packet = bos.toByteArray();
|
||||||
|
|
||||||
@ -207,6 +208,7 @@ public class ControlMessageReaderTest {
|
|||||||
ControlMessage event = reader.next();
|
ControlMessage event = reader.next();
|
||||||
|
|
||||||
Assert.assertEquals(ControlMessage.TYPE_GET_CLIPBOARD, event.getType());
|
Assert.assertEquals(ControlMessage.TYPE_GET_CLIPBOARD, event.getType());
|
||||||
|
Assert.assertTrue(event.getPressCopyOrPaste());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -228,9 +230,7 @@ public class ControlMessageReaderTest {
|
|||||||
|
|
||||||
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
|
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
|
||||||
Assert.assertEquals("testé", event.getText());
|
Assert.assertEquals("testé", event.getText());
|
||||||
|
Assert.assertTrue(event.getPressCopyOrPaste());
|
||||||
boolean parse = (event.getFlags() & ControlMessage.FLAGS_PASTE) != 0;
|
|
||||||
Assert.assertTrue(parse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -256,9 +256,7 @@ public class ControlMessageReaderTest {
|
|||||||
|
|
||||||
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
|
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
|
||||||
Assert.assertEquals(text, event.getText());
|
Assert.assertEquals(text, event.getText());
|
||||||
|
Assert.assertTrue(event.getPressCopyOrPaste());
|
||||||
boolean parse = (event.getFlags() & ControlMessage.FLAGS_PASTE) != 0;
|
|
||||||
Assert.assertTrue(parse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
x
Reference in New Issue
Block a user