2018-08-15 23:01:54 +08:00
|
|
|
#include "input_manager.h"
|
2018-02-09 01:14:50 +08:00
|
|
|
|
2019-11-28 04:11:40 +08:00
|
|
|
#include <assert.h>
|
2020-07-17 06:00:42 +08:00
|
|
|
#include <SDL2/SDL_keycode.h>
|
2019-09-30 04:36:56 +08:00
|
|
|
|
2021-12-29 22:56:59 +08:00
|
|
|
#include "input_events.h"
|
2021-12-31 23:32:07 +08:00
|
|
|
#include "screen.h"
|
2019-11-24 18:53:00 +08:00
|
|
|
#include "util/log.h"
|
2018-02-09 01:14:50 +08:00
|
|
|
|
2021-12-29 23:14:34 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-12-29 23:24:20 +08:00
|
|
|
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
|
2021-12-30 06:52:08 +08:00
|
|
|
sc_mouse_buttons_state_from_sdl(uint32_t buttons_state,
|
|
|
|
bool forward_all_clicks) {
|
2021-12-29 23:24:20 +08:00
|
|
|
assert(buttons_state < 0x100); // fits in uint8_t
|
2021-12-30 06:52:08 +08:00
|
|
|
|
|
|
|
uint8_t mask = SC_MOUSE_BUTTON_LEFT;
|
|
|
|
if (forward_all_clicks) {
|
|
|
|
mask |= SC_MOUSE_BUTTON_RIGHT
|
|
|
|
| SC_MOUSE_BUTTON_MIDDLE
|
|
|
|
| SC_MOUSE_BUTTON_X1
|
|
|
|
| SC_MOUSE_BUTTON_X2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return buttons_state & mask;
|
2021-12-29 23:24:20 +08:00
|
|
|
}
|
|
|
|
|
2020-07-17 06:00:42 +08:00
|
|
|
#define SC_SDL_SHORTCUT_MODS_MASK (KMOD_CTRL | KMOD_ALT | KMOD_GUI)
|
|
|
|
|
|
|
|
static inline uint16_t
|
2021-12-28 22:24:15 +08:00
|
|
|
to_sdl_mod(unsigned shortcut_mod) {
|
2020-07-17 06:00:42 +08:00
|
|
|
uint16_t sdl_mod = 0;
|
2021-12-28 22:24:15 +08:00
|
|
|
if (shortcut_mod & SC_SHORTCUT_MOD_LCTRL) {
|
2020-07-17 06:00:42 +08:00
|
|
|
sdl_mod |= KMOD_LCTRL;
|
|
|
|
}
|
2021-12-28 22:24:15 +08:00
|
|
|
if (shortcut_mod & SC_SHORTCUT_MOD_RCTRL) {
|
2020-07-17 06:00:42 +08:00
|
|
|
sdl_mod |= KMOD_RCTRL;
|
|
|
|
}
|
2021-12-28 22:24:15 +08:00
|
|
|
if (shortcut_mod & SC_SHORTCUT_MOD_LALT) {
|
2020-07-17 06:00:42 +08:00
|
|
|
sdl_mod |= KMOD_LALT;
|
|
|
|
}
|
2021-12-28 22:24:15 +08:00
|
|
|
if (shortcut_mod & SC_SHORTCUT_MOD_RALT) {
|
2020-07-17 06:00:42 +08:00
|
|
|
sdl_mod |= KMOD_RALT;
|
|
|
|
}
|
2021-12-28 22:24:15 +08:00
|
|
|
if (shortcut_mod & SC_SHORTCUT_MOD_LSUPER) {
|
2020-07-17 06:00:42 +08:00
|
|
|
sdl_mod |= KMOD_LGUI;
|
|
|
|
}
|
2021-12-28 22:24:15 +08:00
|
|
|
if (shortcut_mod & SC_SHORTCUT_MOD_RSUPER) {
|
2020-07-17 06:00:42 +08:00
|
|
|
sdl_mod |= KMOD_RGUI;
|
|
|
|
}
|
|
|
|
return sdl_mod;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2022-01-15 05:17:30 +08:00
|
|
|
is_shortcut_mod(struct sc_input_manager *im, uint16_t sdl_mod) {
|
2020-07-17 06:00:42 +08:00
|
|
|
// keep only the relevant modifier keys
|
|
|
|
sdl_mod &= SC_SDL_SHORTCUT_MODS_MASK;
|
|
|
|
|
|
|
|
assert(im->sdl_shortcut_mods.count);
|
|
|
|
assert(im->sdl_shortcut_mods.count < SC_MAX_SHORTCUT_MODS);
|
|
|
|
for (unsigned i = 0; i < im->sdl_shortcut_mods.count; ++i) {
|
|
|
|
if (im->sdl_shortcut_mods.data[i] == sdl_mod) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_input_manager_init(struct sc_input_manager *im,
|
|
|
|
const struct sc_input_manager_params *params) {
|
2022-01-23 19:08:55 +08:00
|
|
|
assert(!params->controller || (params->kp && params->kp->ops));
|
|
|
|
assert(!params->controller || (params->mp && params->mp->ops));
|
2021-12-31 23:15:41 +08:00
|
|
|
|
|
|
|
im->controller = params->controller;
|
2022-01-22 02:26:36 +08:00
|
|
|
im->fp = params->fp;
|
2021-12-31 23:15:41 +08:00
|
|
|
im->screen = params->screen;
|
|
|
|
im->kp = params->kp;
|
|
|
|
im->mp = params->mp;
|
|
|
|
|
|
|
|
im->forward_all_clicks = params->forward_all_clicks;
|
|
|
|
im->legacy_paste = params->legacy_paste;
|
|
|
|
im->clipboard_autosync = params->clipboard_autosync;
|
|
|
|
|
|
|
|
const struct sc_shortcut_mods *shortcut_mods = params->shortcut_mods;
|
2020-07-17 06:00:42 +08:00
|
|
|
assert(shortcut_mods->count);
|
|
|
|
assert(shortcut_mods->count < SC_MAX_SHORTCUT_MODS);
|
|
|
|
for (unsigned i = 0; i < shortcut_mods->count; ++i) {
|
|
|
|
uint16_t sdl_mod = to_sdl_mod(shortcut_mods->data[i]);
|
|
|
|
assert(sdl_mod);
|
|
|
|
im->sdl_shortcut_mods.data[i] = sdl_mod;
|
|
|
|
}
|
|
|
|
im->sdl_shortcut_mods.count = shortcut_mods->count;
|
2020-08-09 22:04:02 +08:00
|
|
|
|
|
|
|
im->vfinger_down = false;
|
2021-04-17 20:15:31 +08:00
|
|
|
|
|
|
|
im->last_keycode = SDLK_UNKNOWN;
|
|
|
|
im->last_mod = 0;
|
|
|
|
im->key_repeat = 0;
|
Wait SET_CLIPBOARD ack before Ctrl+v via HID
To allow seamless copy-paste, on Ctrl+v, a SET_CLIPBOARD request is
performed before injecting Ctrl+v.
But when HID keyboard is enabled, the Ctrl+v injection is not sent on
the same channel as the clipboard request, so they are not serialized,
and may occur in any order. If Ctrl+v happens to be injected before the
new clipboard content is set, then the old content is pasted instead,
which is incorrect.
To minimize the probability of occurrence of the wrong order, a delay of
2 milliseconds was added before injecting Ctrl+v. Then 5ms. But even
with 5ms, the wrong behavior sometimes happens.
To handle it properly, add an acknowledgement mechanism, so that Ctrl+v
is injected over AOA only after the SET_CLIPBOARD request has been
performed and acknowledged by the server.
Refs e4163321f00bb3830c6049bdb6c1515e7cc668a0
Refs 45b0f8123a52f5c73a5860d616f4ceba2766ca6a
PR #2814 <https://github.com/Genymobile/scrcpy/pull/2814>
2021-11-22 00:40:11 +08:00
|
|
|
|
|
|
|
im->next_sequence = 1; // 0 is reserved for SC_SEQUENCE_INVALID
|
2020-07-17 06:00:42 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static void
|
2022-01-15 05:17:30 +08:00
|
|
|
send_keycode(struct sc_controller *controller, enum android_keycode keycode,
|
2021-12-29 08:34:54 +08:00
|
|
|
enum sc_action action, const char *name) {
|
2018-02-09 01:14:50 +08:00
|
|
|
// send DOWN event
|
2022-01-15 05:17:30 +08:00
|
|
|
struct sc_control_msg msg;
|
2022-01-27 04:31:30 +08:00
|
|
|
msg.type = SC_CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
2021-12-29 08:34:54 +08:00
|
|
|
msg.inject_keycode.action = action == SC_ACTION_DOWN
|
|
|
|
? AKEY_EVENT_ACTION_DOWN
|
|
|
|
: AKEY_EVENT_ACTION_UP;
|
2019-05-31 20:55:11 +08:00
|
|
|
msg.inject_keycode.keycode = keycode;
|
|
|
|
msg.inject_keycode.metastate = 0;
|
2020-08-07 15:21:13 +08:00
|
|
|
msg.inject_keycode.repeat = 0;
|
2018-02-09 01:14:50 +08:00
|
|
|
|
2022-01-15 05:17:30 +08:00
|
|
|
if (!sc_controller_push_msg(controller, &msg)) {
|
2021-12-29 08:34:54 +08:00
|
|
|
LOGW("Could not request 'inject %s'", name);
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static inline void
|
2022-01-15 05:17:30 +08:00
|
|
|
action_home(struct sc_controller *controller, enum sc_action action) {
|
2021-12-29 08:34:54 +08:00
|
|
|
send_keycode(controller, AKEYCODE_HOME, action, "HOME");
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static inline void
|
2022-01-15 05:17:30 +08:00
|
|
|
action_back(struct sc_controller *controller, enum sc_action action) {
|
2021-12-29 08:34:54 +08:00
|
|
|
send_keycode(controller, AKEYCODE_BACK, action, "BACK");
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static inline void
|
2022-01-15 05:17:30 +08:00
|
|
|
action_app_switch(struct sc_controller *controller, enum sc_action action) {
|
2021-12-29 08:34:54 +08:00
|
|
|
send_keycode(controller, AKEYCODE_APP_SWITCH, action, "APP_SWITCH");
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static inline void
|
2022-01-15 05:17:30 +08:00
|
|
|
action_power(struct sc_controller *controller, enum sc_action action) {
|
2021-12-29 08:34:54 +08:00
|
|
|
send_keycode(controller, AKEYCODE_POWER, action, "POWER");
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static inline void
|
2022-01-15 05:17:30 +08:00
|
|
|
action_volume_up(struct sc_controller *controller, enum sc_action action) {
|
2021-12-29 08:34:54 +08:00
|
|
|
send_keycode(controller, AKEYCODE_VOLUME_UP, action, "VOLUME_UP");
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static inline void
|
2022-01-15 05:17:30 +08:00
|
|
|
action_volume_down(struct sc_controller *controller, enum sc_action action) {
|
2021-12-29 08:34:54 +08:00
|
|
|
send_keycode(controller, AKEYCODE_VOLUME_DOWN, action, "VOLUME_DOWN");
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static inline void
|
2022-01-15 05:17:30 +08:00
|
|
|
action_menu(struct sc_controller *controller, enum sc_action action) {
|
2021-12-29 08:34:54 +08:00
|
|
|
send_keycode(controller, AKEYCODE_MENU, action, "MENU");
|
2018-03-21 09:04:40 +08:00
|
|
|
}
|
|
|
|
|
2018-03-10 07:11:52 +08:00
|
|
|
// turn the screen on if it was off, press BACK otherwise
|
2021-04-17 00:37:50 +08:00
|
|
|
// If the screen is off, it is turned on only on ACTION_DOWN
|
2019-03-03 03:09:56 +08:00
|
|
|
static void
|
2022-01-15 05:17:30 +08:00
|
|
|
press_back_or_turn_screen_on(struct sc_controller *controller,
|
2021-12-29 08:34:54 +08:00
|
|
|
enum sc_action action) {
|
2022-01-15 05:17:30 +08:00
|
|
|
struct sc_control_msg msg;
|
2022-01-27 04:31:30 +08:00
|
|
|
msg.type = SC_CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON;
|
2021-12-29 08:34:54 +08:00
|
|
|
msg.back_or_screen_on.action = action == SC_ACTION_DOWN
|
|
|
|
? AKEY_EVENT_ACTION_DOWN
|
|
|
|
: AKEY_EVENT_ACTION_UP;
|
2018-03-07 21:46:31 +08:00
|
|
|
|
2022-01-15 05:17:30 +08:00
|
|
|
if (!sc_controller_push_msg(controller, &msg)) {
|
2021-12-29 08:34:54 +08:00
|
|
|
LOGW("Could not request 'press back or turn screen on'");
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static void
|
2022-01-15 05:17:30 +08:00
|
|
|
expand_notification_panel(struct sc_controller *controller) {
|
2022-01-15 05:17:30 +08:00
|
|
|
struct sc_control_msg msg;
|
2022-01-27 04:31:30 +08:00
|
|
|
msg.type = SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL;
|
2019-02-27 03:35:37 +08:00
|
|
|
|
2022-01-15 05:17:30 +08:00
|
|
|
if (!sc_controller_push_msg(controller, &msg)) {
|
2019-06-24 02:49:38 +08:00
|
|
|
LOGW("Could not request 'expand notification panel'");
|
2019-02-27 03:35:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-17 20:32:18 +08:00
|
|
|
static void
|
2022-01-15 05:17:30 +08:00
|
|
|
expand_settings_panel(struct sc_controller *controller) {
|
2022-01-15 05:17:30 +08:00
|
|
|
struct sc_control_msg msg;
|
2022-01-27 04:31:30 +08:00
|
|
|
msg.type = SC_CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL;
|
2021-04-17 20:32:18 +08:00
|
|
|
|
2022-01-15 05:17:30 +08:00
|
|
|
if (!sc_controller_push_msg(controller, &msg)) {
|
2021-04-17 20:32:18 +08:00
|
|
|
LOGW("Could not request 'expand settings panel'");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static void
|
2022-01-15 05:17:30 +08:00
|
|
|
collapse_panels(struct sc_controller *controller) {
|
2022-01-15 05:17:30 +08:00
|
|
|
struct sc_control_msg msg;
|
2022-01-27 04:31:30 +08:00
|
|
|
msg.type = SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS;
|
2019-02-27 03:35:37 +08:00
|
|
|
|
2022-01-15 05:17:30 +08:00
|
|
|
if (!sc_controller_push_msg(controller, &msg)) {
|
2019-06-24 02:49:38 +08:00
|
|
|
LOGW("Could not request 'collapse notification panel'");
|
2019-02-27 03:35:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-29 16:30:57 +08:00
|
|
|
static bool
|
2022-01-15 05:17:30 +08:00
|
|
|
get_device_clipboard(struct sc_controller *controller,
|
2022-01-27 04:31:30 +08:00
|
|
|
enum sc_copy_key copy_key) {
|
2022-01-15 05:17:30 +08:00
|
|
|
struct sc_control_msg msg;
|
2022-01-27 04:31:30 +08:00
|
|
|
msg.type = SC_CONTROL_MSG_TYPE_GET_CLIPBOARD;
|
2021-11-29 16:30:57 +08:00
|
|
|
msg.get_clipboard.copy_key = copy_key;
|
|
|
|
|
2022-01-15 05:17:30 +08:00
|
|
|
if (!sc_controller_push_msg(controller, &msg)) {
|
2021-11-29 16:30:57 +08:00
|
|
|
LOGW("Could not request 'get device clipboard'");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-11-21 00:44:00 +08:00
|
|
|
static bool
|
2022-01-15 05:17:30 +08:00
|
|
|
set_device_clipboard(struct sc_controller *controller, bool paste,
|
Wait SET_CLIPBOARD ack before Ctrl+v via HID
To allow seamless copy-paste, on Ctrl+v, a SET_CLIPBOARD request is
performed before injecting Ctrl+v.
But when HID keyboard is enabled, the Ctrl+v injection is not sent on
the same channel as the clipboard request, so they are not serialized,
and may occur in any order. If Ctrl+v happens to be injected before the
new clipboard content is set, then the old content is pasted instead,
which is incorrect.
To minimize the probability of occurrence of the wrong order, a delay of
2 milliseconds was added before injecting Ctrl+v. Then 5ms. But even
with 5ms, the wrong behavior sometimes happens.
To handle it properly, add an acknowledgement mechanism, so that Ctrl+v
is injected over AOA only after the SET_CLIPBOARD request has been
performed and acknowledged by the server.
Refs e4163321f00bb3830c6049bdb6c1515e7cc668a0
Refs 45b0f8123a52f5c73a5860d616f4ceba2766ca6a
PR #2814 <https://github.com/Genymobile/scrcpy/pull/2814>
2021-11-22 00:40:11 +08:00
|
|
|
uint64_t sequence) {
|
2019-05-31 02:25:23 +08:00
|
|
|
char *text = SDL_GetClipboardText();
|
|
|
|
if (!text) {
|
2019-06-24 02:49:38 +08:00
|
|
|
LOGW("Could not get clipboard text: %s", SDL_GetError());
|
2021-11-21 00:44:00 +08:00
|
|
|
return false;
|
2019-05-31 02:25:23 +08:00
|
|
|
}
|
|
|
|
|
2021-01-24 22:14:53 +08:00
|
|
|
char *text_dup = strdup(text);
|
|
|
|
SDL_free(text);
|
|
|
|
if (!text_dup) {
|
|
|
|
LOGW("Could not strdup input text");
|
2021-11-21 00:44:00 +08:00
|
|
|
return false;
|
2021-01-24 22:14:53 +08:00
|
|
|
}
|
|
|
|
|
2022-01-15 05:17:30 +08:00
|
|
|
struct sc_control_msg msg;
|
2022-01-27 04:31:30 +08:00
|
|
|
msg.type = SC_CONTROL_MSG_TYPE_SET_CLIPBOARD;
|
Wait SET_CLIPBOARD ack before Ctrl+v via HID
To allow seamless copy-paste, on Ctrl+v, a SET_CLIPBOARD request is
performed before injecting Ctrl+v.
But when HID keyboard is enabled, the Ctrl+v injection is not sent on
the same channel as the clipboard request, so they are not serialized,
and may occur in any order. If Ctrl+v happens to be injected before the
new clipboard content is set, then the old content is pasted instead,
which is incorrect.
To minimize the probability of occurrence of the wrong order, a delay of
2 milliseconds was added before injecting Ctrl+v. Then 5ms. But even
with 5ms, the wrong behavior sometimes happens.
To handle it properly, add an acknowledgement mechanism, so that Ctrl+v
is injected over AOA only after the SET_CLIPBOARD request has been
performed and acknowledged by the server.
Refs e4163321f00bb3830c6049bdb6c1515e7cc668a0
Refs 45b0f8123a52f5c73a5860d616f4ceba2766ca6a
PR #2814 <https://github.com/Genymobile/scrcpy/pull/2814>
2021-11-22 00:40:11 +08:00
|
|
|
msg.set_clipboard.sequence = sequence;
|
2021-01-24 22:14:53 +08:00
|
|
|
msg.set_clipboard.text = text_dup;
|
2020-05-26 02:58:24 +08:00
|
|
|
msg.set_clipboard.paste = paste;
|
2019-05-31 02:25:23 +08:00
|
|
|
|
2022-01-15 05:17:30 +08:00
|
|
|
if (!sc_controller_push_msg(controller, &msg)) {
|
2021-01-24 22:14:53 +08:00
|
|
|
free(text_dup);
|
2019-06-24 02:49:38 +08:00
|
|
|
LOGW("Could not request 'set device clipboard'");
|
2021-11-21 00:44:00 +08:00
|
|
|
return false;
|
2019-05-31 02:25:23 +08:00
|
|
|
}
|
2021-11-21 00:44:00 +08:00
|
|
|
|
|
|
|
return true;
|
2019-05-31 02:25:23 +08:00
|
|
|
}
|
|
|
|
|
2019-03-16 03:23:30 +08:00
|
|
|
static void
|
2022-01-15 05:17:30 +08:00
|
|
|
set_screen_power_mode(struct sc_controller *controller,
|
2022-01-27 04:31:30 +08:00
|
|
|
enum sc_screen_power_mode mode) {
|
2022-01-15 05:17:30 +08:00
|
|
|
struct sc_control_msg msg;
|
2022-01-27 04:31:30 +08:00
|
|
|
msg.type = SC_CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE;
|
2019-03-16 03:23:30 +08:00
|
|
|
msg.set_screen_power_mode.mode = mode;
|
|
|
|
|
2022-01-15 05:17:30 +08:00
|
|
|
if (!sc_controller_push_msg(controller, &msg)) {
|
2019-06-24 02:49:38 +08:00
|
|
|
LOGW("Could not request 'set screen power mode'");
|
2019-03-16 03:23:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static void
|
2019-06-07 22:55:19 +08:00
|
|
|
switch_fps_counter_state(struct fps_counter *fps_counter) {
|
|
|
|
// the started state can only be written from the current thread, so there
|
|
|
|
// is no ToCToU issue
|
|
|
|
if (fps_counter_is_started(fps_counter)) {
|
|
|
|
fps_counter_stop(fps_counter);
|
2018-02-15 19:24:16 +08:00
|
|
|
LOGI("FPS counter stopped");
|
|
|
|
} else {
|
2019-06-07 22:55:19 +08:00
|
|
|
if (fps_counter_start(fps_counter)) {
|
|
|
|
LOGI("FPS counter started");
|
|
|
|
} else {
|
|
|
|
LOGE("FPS counter starting failed");
|
|
|
|
}
|
2018-02-15 19:24:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static void
|
2022-01-15 05:17:30 +08:00
|
|
|
clipboard_paste(struct sc_controller *controller) {
|
2018-03-07 22:29:33 +08:00
|
|
|
char *text = SDL_GetClipboardText();
|
|
|
|
if (!text) {
|
2019-06-24 02:49:38 +08:00
|
|
|
LOGW("Could not get clipboard text: %s", SDL_GetError());
|
2018-03-07 22:29:33 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!*text) {
|
|
|
|
// empty text
|
|
|
|
SDL_free(text);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-24 22:14:53 +08:00
|
|
|
char *text_dup = strdup(text);
|
|
|
|
SDL_free(text);
|
|
|
|
if (!text_dup) {
|
|
|
|
LOGW("Could not strdup input text");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-15 05:17:30 +08:00
|
|
|
struct sc_control_msg msg;
|
2022-01-27 04:31:30 +08:00
|
|
|
msg.type = SC_CONTROL_MSG_TYPE_INJECT_TEXT;
|
2021-01-24 22:14:53 +08:00
|
|
|
msg.inject_text.text = text_dup;
|
2022-01-15 05:17:30 +08:00
|
|
|
if (!sc_controller_push_msg(controller, &msg)) {
|
2021-01-24 22:14:53 +08:00
|
|
|
free(text_dup);
|
2019-06-24 02:49:38 +08:00
|
|
|
LOGW("Could not request 'paste clipboard'");
|
2018-03-07 22:29:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-05 02:55:28 +08:00
|
|
|
static void
|
2022-01-15 05:17:30 +08:00
|
|
|
rotate_device(struct sc_controller *controller) {
|
2022-01-15 05:17:30 +08:00
|
|
|
struct sc_control_msg msg;
|
2022-01-27 04:31:30 +08:00
|
|
|
msg.type = SC_CONTROL_MSG_TYPE_ROTATE_DEVICE;
|
2019-12-05 02:55:28 +08:00
|
|
|
|
2022-01-15 05:17:30 +08:00
|
|
|
if (!sc_controller_push_msg(controller, &msg)) {
|
2019-12-05 02:55:28 +08:00
|
|
|
LOGW("Could not request device rotation");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-08 05:03:23 +08:00
|
|
|
static void
|
2022-01-15 05:17:30 +08:00
|
|
|
rotate_client_left(struct sc_screen *screen) {
|
2020-04-08 05:03:23 +08:00
|
|
|
unsigned new_rotation = (screen->rotation + 1) % 4;
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_screen_set_rotation(screen, new_rotation);
|
2020-04-08 05:03:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2022-01-15 05:17:30 +08:00
|
|
|
rotate_client_right(struct sc_screen *screen) {
|
2020-04-08 05:03:23 +08:00
|
|
|
unsigned new_rotation = (screen->rotation + 3) % 4;
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_screen_set_rotation(screen, new_rotation);
|
2020-04-08 05:03:23 +08:00
|
|
|
}
|
|
|
|
|
2021-02-16 01:53:23 +08:00
|
|
|
static void
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_input_manager_process_text_input(struct sc_input_manager *im,
|
|
|
|
const SDL_TextInputEvent *event) {
|
2021-12-30 22:03:39 +08:00
|
|
|
if (!im->kp->ops->process_text) {
|
|
|
|
// The key processor does not support text input
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-17 07:23:08 +08:00
|
|
|
if (is_shortcut_mod(im, SDL_GetModState())) {
|
|
|
|
// A shortcut must never generate text events
|
|
|
|
return;
|
|
|
|
}
|
2019-11-08 02:01:35 +08:00
|
|
|
|
2021-12-29 23:14:34 +08:00
|
|
|
struct sc_text_event evt = {
|
|
|
|
.text = event->text,
|
|
|
|
};
|
|
|
|
|
|
|
|
im->kp->ops->process_text(im->kp, &evt);
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2020-08-09 22:04:02 +08:00
|
|
|
static bool
|
2022-01-15 05:17:30 +08:00
|
|
|
simulate_virtual_finger(struct sc_input_manager *im,
|
2020-08-09 22:04:02 +08:00
|
|
|
enum android_motionevent_action action,
|
2021-10-30 21:20:39 +08:00
|
|
|
struct sc_point point) {
|
2020-08-09 22:04:02 +08:00
|
|
|
bool up = action == AMOTION_EVENT_ACTION_UP;
|
|
|
|
|
2022-01-15 05:17:30 +08:00
|
|
|
struct sc_control_msg msg;
|
2022-01-27 04:31:30 +08:00
|
|
|
msg.type = SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
2020-08-09 22:04:02 +08:00
|
|
|
msg.inject_touch_event.action = action;
|
|
|
|
msg.inject_touch_event.position.screen_size = im->screen->frame_size;
|
|
|
|
msg.inject_touch_event.position.point = point;
|
|
|
|
msg.inject_touch_event.pointer_id = POINTER_ID_VIRTUAL_FINGER;
|
|
|
|
msg.inject_touch_event.pressure = up ? 0.0f : 1.0f;
|
|
|
|
msg.inject_touch_event.buttons = 0;
|
|
|
|
|
2022-01-15 05:17:30 +08:00
|
|
|
if (!sc_controller_push_msg(im->controller, &msg)) {
|
2020-08-09 22:04:02 +08:00
|
|
|
LOGW("Could not request 'inject virtual finger event'");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-10-30 21:20:39 +08:00
|
|
|
static struct sc_point
|
|
|
|
inverse_point(struct sc_point point, struct sc_size size) {
|
2020-08-09 22:04:02 +08:00
|
|
|
point.x = size.width - point.x;
|
|
|
|
point.y = size.height - point.y;
|
|
|
|
return point;
|
|
|
|
}
|
|
|
|
|
2021-02-16 01:53:23 +08:00
|
|
|
static void
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_input_manager_process_key(struct sc_input_manager *im,
|
|
|
|
const SDL_KeyboardEvent *event) {
|
2022-01-23 19:08:55 +08:00
|
|
|
// controller is NULL if --no-control is requested
|
2022-01-15 05:17:30 +08:00
|
|
|
struct sc_controller *controller = im->controller;
|
2019-06-01 05:25:41 +08:00
|
|
|
|
2020-07-17 06:00:42 +08:00
|
|
|
SDL_Keycode keycode = event->keysym.sym;
|
2021-04-17 20:15:31 +08:00
|
|
|
uint16_t mod = event->keysym.mod;
|
2020-07-17 06:00:42 +08:00
|
|
|
bool down = event->type == SDL_KEYDOWN;
|
|
|
|
bool ctrl = event->keysym.mod & KMOD_CTRL;
|
|
|
|
bool shift = event->keysym.mod & KMOD_SHIFT;
|
|
|
|
bool repeat = event->repeat;
|
|
|
|
|
2021-04-17 20:15:31 +08:00
|
|
|
bool smod = is_shortcut_mod(im, mod);
|
|
|
|
|
|
|
|
if (down && !repeat) {
|
|
|
|
if (keycode == im->last_keycode && mod == im->last_mod) {
|
|
|
|
++im->key_repeat;
|
|
|
|
} else {
|
|
|
|
im->key_repeat = 0;
|
|
|
|
im->last_keycode = keycode;
|
|
|
|
im->last_mod = mod;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-17 06:00:42 +08:00
|
|
|
// The shortcut modifier is pressed
|
|
|
|
if (smod) {
|
2021-12-29 08:34:54 +08:00
|
|
|
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
|
2018-02-09 01:14:50 +08:00
|
|
|
switch (keycode) {
|
|
|
|
case SDLK_h:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (controller && !shift && !repeat) {
|
2019-06-01 05:25:41 +08:00
|
|
|
action_home(controller, action);
|
2018-06-25 03:48:34 +08:00
|
|
|
}
|
2018-02-09 01:14:50 +08:00
|
|
|
return;
|
|
|
|
case SDLK_b: // fall-through
|
|
|
|
case SDLK_BACKSPACE:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (controller && !shift && !repeat) {
|
2019-06-01 05:25:41 +08:00
|
|
|
action_back(controller, action);
|
2018-06-25 03:48:34 +08:00
|
|
|
}
|
2018-02-09 01:14:50 +08:00
|
|
|
return;
|
2018-03-23 17:10:24 +08:00
|
|
|
case SDLK_s:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (controller && !shift && !repeat) {
|
2019-06-01 05:25:41 +08:00
|
|
|
action_app_switch(controller, action);
|
2018-06-25 03:48:34 +08:00
|
|
|
}
|
2018-02-09 01:14:50 +08:00
|
|
|
return;
|
2018-03-23 17:10:24 +08:00
|
|
|
case SDLK_m:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (controller && !shift && !repeat) {
|
2019-06-01 05:25:41 +08:00
|
|
|
action_menu(controller, action);
|
2018-06-25 03:48:34 +08:00
|
|
|
}
|
2018-03-21 09:04:40 +08:00
|
|
|
return;
|
2018-02-09 01:14:50 +08:00
|
|
|
case SDLK_p:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (controller && !shift && !repeat) {
|
2019-06-01 05:25:41 +08:00
|
|
|
action_power(controller, action);
|
2018-06-25 03:48:34 +08:00
|
|
|
}
|
2018-02-09 01:14:50 +08:00
|
|
|
return;
|
2019-03-16 03:23:30 +08:00
|
|
|
case SDLK_o:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (controller && !repeat && down) {
|
2022-01-27 04:31:30 +08:00
|
|
|
enum sc_screen_power_mode mode = shift
|
|
|
|
? SC_SCREEN_POWER_MODE_NORMAL
|
|
|
|
: SC_SCREEN_POWER_MODE_OFF;
|
2020-05-28 00:18:39 +08:00
|
|
|
set_screen_power_mode(controller, mode);
|
2019-03-16 03:23:30 +08:00
|
|
|
}
|
|
|
|
return;
|
2018-04-03 20:20:33 +08:00
|
|
|
case SDLK_DOWN:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (controller && !shift) {
|
2018-10-25 01:03:07 +08:00
|
|
|
// forward repeated events
|
2019-06-01 05:25:41 +08:00
|
|
|
action_volume_down(controller, action);
|
2018-10-25 01:03:07 +08:00
|
|
|
}
|
2018-04-03 20:20:33 +08:00
|
|
|
return;
|
|
|
|
case SDLK_UP:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (controller && !shift) {
|
2018-10-25 01:03:07 +08:00
|
|
|
// forward repeated events
|
2019-06-01 05:25:41 +08:00
|
|
|
action_volume_up(controller, action);
|
2018-10-25 01:03:07 +08:00
|
|
|
}
|
2018-04-03 20:20:33 +08:00
|
|
|
return;
|
2020-04-08 05:03:23 +08:00
|
|
|
case SDLK_LEFT:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (!shift && !repeat && down) {
|
2020-04-08 05:03:23 +08:00
|
|
|
rotate_client_left(im->screen);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case SDLK_RIGHT:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (!shift && !repeat && down) {
|
2020-04-08 05:03:23 +08:00
|
|
|
rotate_client_right(im->screen);
|
|
|
|
}
|
|
|
|
return;
|
2020-07-17 06:00:42 +08:00
|
|
|
case SDLK_c:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (controller && !shift && !repeat && down) {
|
2022-01-27 04:31:30 +08:00
|
|
|
get_device_clipboard(controller, SC_COPY_KEY_COPY);
|
2020-07-17 06:00:42 +08:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
case SDLK_x:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (controller && !shift && !repeat && down) {
|
2022-01-27 04:31:30 +08:00
|
|
|
get_device_clipboard(controller, SC_COPY_KEY_CUT);
|
2020-07-17 06:00:42 +08:00
|
|
|
}
|
|
|
|
return;
|
2018-03-07 22:29:33 +08:00
|
|
|
case SDLK_v:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (controller && !repeat && down) {
|
2020-10-07 03:30:10 +08:00
|
|
|
if (shift || im->legacy_paste) {
|
2019-05-31 02:25:23 +08:00
|
|
|
// inject the text as input events
|
2019-06-01 05:25:41 +08:00
|
|
|
clipboard_paste(controller);
|
2020-07-17 06:00:42 +08:00
|
|
|
} else {
|
Wait SET_CLIPBOARD ack before Ctrl+v via HID
To allow seamless copy-paste, on Ctrl+v, a SET_CLIPBOARD request is
performed before injecting Ctrl+v.
But when HID keyboard is enabled, the Ctrl+v injection is not sent on
the same channel as the clipboard request, so they are not serialized,
and may occur in any order. If Ctrl+v happens to be injected before the
new clipboard content is set, then the old content is pasted instead,
which is incorrect.
To minimize the probability of occurrence of the wrong order, a delay of
2 milliseconds was added before injecting Ctrl+v. Then 5ms. But even
with 5ms, the wrong behavior sometimes happens.
To handle it properly, add an acknowledgement mechanism, so that Ctrl+v
is injected over AOA only after the SET_CLIPBOARD request has been
performed and acknowledged by the server.
Refs e4163321f00bb3830c6049bdb6c1515e7cc668a0
Refs 45b0f8123a52f5c73a5860d616f4ceba2766ca6a
PR #2814 <https://github.com/Genymobile/scrcpy/pull/2814>
2021-11-22 00:40:11 +08:00
|
|
|
// store the text in the device clipboard and paste,
|
|
|
|
// without requesting an acknowledgment
|
|
|
|
set_device_clipboard(controller, true,
|
|
|
|
SC_SEQUENCE_INVALID);
|
2019-05-31 02:25:23 +08:00
|
|
|
}
|
2018-06-25 03:09:23 +08:00
|
|
|
}
|
2018-03-07 22:29:33 +08:00
|
|
|
return;
|
2018-02-09 01:14:50 +08:00
|
|
|
case SDLK_f:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (!shift && !repeat && down) {
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_screen_switch_fullscreen(im->screen);
|
2018-06-25 03:09:23 +08:00
|
|
|
}
|
2018-02-09 01:14:50 +08:00
|
|
|
return;
|
2020-07-17 06:00:42 +08:00
|
|
|
case SDLK_w:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (!shift && !repeat && down) {
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_screen_resize_to_fit(im->screen);
|
2018-06-25 03:09:23 +08:00
|
|
|
}
|
2018-02-09 01:14:50 +08:00
|
|
|
return;
|
|
|
|
case SDLK_g:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (!shift && !repeat && down) {
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_screen_resize_to_pixel_perfect(im->screen);
|
2018-06-25 03:09:23 +08:00
|
|
|
}
|
2018-02-09 01:14:50 +08:00
|
|
|
return;
|
2018-02-15 19:24:16 +08:00
|
|
|
case SDLK_i:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (!shift && !repeat && down) {
|
2021-05-17 00:26:20 +08:00
|
|
|
switch_fps_counter_state(&im->screen->fps_counter);
|
2018-06-25 03:09:23 +08:00
|
|
|
}
|
2019-02-27 03:35:37 +08:00
|
|
|
return;
|
|
|
|
case SDLK_n:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (controller && !repeat && down) {
|
2019-02-27 03:35:37 +08:00
|
|
|
if (shift) {
|
2021-04-17 20:26:54 +08:00
|
|
|
collapse_panels(controller);
|
2021-04-21 00:31:39 +08:00
|
|
|
} else if (im->key_repeat == 0) {
|
2019-06-01 05:25:41 +08:00
|
|
|
expand_notification_panel(controller);
|
2021-04-21 00:31:39 +08:00
|
|
|
} else {
|
|
|
|
expand_settings_panel(controller);
|
2019-02-27 03:35:37 +08:00
|
|
|
}
|
|
|
|
}
|
2018-02-15 19:24:16 +08:00
|
|
|
return;
|
2019-12-05 02:55:28 +08:00
|
|
|
case SDLK_r:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (controller && !shift && !repeat && down) {
|
2019-12-05 02:55:28 +08:00
|
|
|
rotate_device(controller);
|
|
|
|
}
|
|
|
|
return;
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-23 19:08:55 +08:00
|
|
|
if (!controller) {
|
2019-03-03 05:40:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
Wait SET_CLIPBOARD ack before Ctrl+v via HID
To allow seamless copy-paste, on Ctrl+v, a SET_CLIPBOARD request is
performed before injecting Ctrl+v.
But when HID keyboard is enabled, the Ctrl+v injection is not sent on
the same channel as the clipboard request, so they are not serialized,
and may occur in any order. If Ctrl+v happens to be injected before the
new clipboard content is set, then the old content is pasted instead,
which is incorrect.
To minimize the probability of occurrence of the wrong order, a delay of
2 milliseconds was added before injecting Ctrl+v. Then 5ms. But even
with 5ms, the wrong behavior sometimes happens.
To handle it properly, add an acknowledgement mechanism, so that Ctrl+v
is injected over AOA only after the SET_CLIPBOARD request has been
performed and acknowledged by the server.
Refs e4163321f00bb3830c6049bdb6c1515e7cc668a0
Refs 45b0f8123a52f5c73a5860d616f4ceba2766ca6a
PR #2814 <https://github.com/Genymobile/scrcpy/pull/2814>
2021-11-22 00:40:11 +08:00
|
|
|
uint64_t ack_to_wait = SC_SEQUENCE_INVALID;
|
2021-11-22 00:24:34 +08:00
|
|
|
bool is_ctrl_v = ctrl && !shift && keycode == SDLK_v && down && !repeat;
|
2021-11-22 15:49:10 +08:00
|
|
|
if (im->clipboard_autosync && is_ctrl_v) {
|
2020-10-07 03:30:10 +08:00
|
|
|
if (im->legacy_paste) {
|
|
|
|
// inject the text as input events
|
|
|
|
clipboard_paste(controller);
|
|
|
|
return;
|
|
|
|
}
|
Wait SET_CLIPBOARD ack before Ctrl+v via HID
To allow seamless copy-paste, on Ctrl+v, a SET_CLIPBOARD request is
performed before injecting Ctrl+v.
But when HID keyboard is enabled, the Ctrl+v injection is not sent on
the same channel as the clipboard request, so they are not serialized,
and may occur in any order. If Ctrl+v happens to be injected before the
new clipboard content is set, then the old content is pasted instead,
which is incorrect.
To minimize the probability of occurrence of the wrong order, a delay of
2 milliseconds was added before injecting Ctrl+v. Then 5ms. But even
with 5ms, the wrong behavior sometimes happens.
To handle it properly, add an acknowledgement mechanism, so that Ctrl+v
is injected over AOA only after the SET_CLIPBOARD request has been
performed and acknowledged by the server.
Refs e4163321f00bb3830c6049bdb6c1515e7cc668a0
Refs 45b0f8123a52f5c73a5860d616f4ceba2766ca6a
PR #2814 <https://github.com/Genymobile/scrcpy/pull/2814>
2021-11-22 00:40:11 +08:00
|
|
|
|
|
|
|
// Request an acknowledgement only if necessary
|
|
|
|
uint64_t sequence = im->kp->async_paste ? im->next_sequence
|
|
|
|
: SC_SEQUENCE_INVALID;
|
|
|
|
|
2020-07-17 06:00:42 +08:00
|
|
|
// Synchronize the computer clipboard to the device clipboard before
|
|
|
|
// sending Ctrl+v, to allow seamless copy-paste.
|
Wait SET_CLIPBOARD ack before Ctrl+v via HID
To allow seamless copy-paste, on Ctrl+v, a SET_CLIPBOARD request is
performed before injecting Ctrl+v.
But when HID keyboard is enabled, the Ctrl+v injection is not sent on
the same channel as the clipboard request, so they are not serialized,
and may occur in any order. If Ctrl+v happens to be injected before the
new clipboard content is set, then the old content is pasted instead,
which is incorrect.
To minimize the probability of occurrence of the wrong order, a delay of
2 milliseconds was added before injecting Ctrl+v. Then 5ms. But even
with 5ms, the wrong behavior sometimes happens.
To handle it properly, add an acknowledgement mechanism, so that Ctrl+v
is injected over AOA only after the SET_CLIPBOARD request has been
performed and acknowledged by the server.
Refs e4163321f00bb3830c6049bdb6c1515e7cc668a0
Refs 45b0f8123a52f5c73a5860d616f4ceba2766ca6a
PR #2814 <https://github.com/Genymobile/scrcpy/pull/2814>
2021-11-22 00:40:11 +08:00
|
|
|
bool ok = set_device_clipboard(controller, false, sequence);
|
2021-11-21 00:44:00 +08:00
|
|
|
if (!ok) {
|
|
|
|
LOGW("Clipboard could not be synchronized, Ctrl+v not injected");
|
|
|
|
return;
|
|
|
|
}
|
Wait SET_CLIPBOARD ack before Ctrl+v via HID
To allow seamless copy-paste, on Ctrl+v, a SET_CLIPBOARD request is
performed before injecting Ctrl+v.
But when HID keyboard is enabled, the Ctrl+v injection is not sent on
the same channel as the clipboard request, so they are not serialized,
and may occur in any order. If Ctrl+v happens to be injected before the
new clipboard content is set, then the old content is pasted instead,
which is incorrect.
To minimize the probability of occurrence of the wrong order, a delay of
2 milliseconds was added before injecting Ctrl+v. Then 5ms. But even
with 5ms, the wrong behavior sometimes happens.
To handle it properly, add an acknowledgement mechanism, so that Ctrl+v
is injected over AOA only after the SET_CLIPBOARD request has been
performed and acknowledged by the server.
Refs e4163321f00bb3830c6049bdb6c1515e7cc668a0
Refs 45b0f8123a52f5c73a5860d616f4ceba2766ca6a
PR #2814 <https://github.com/Genymobile/scrcpy/pull/2814>
2021-11-22 00:40:11 +08:00
|
|
|
|
|
|
|
if (im->kp->async_paste) {
|
|
|
|
// The key processor must wait for this ack before injecting Ctrl+v
|
|
|
|
ack_to_wait = sequence;
|
|
|
|
// Increment only when the request succeeded
|
|
|
|
++im->next_sequence;
|
|
|
|
}
|
2020-07-17 06:00:42 +08:00
|
|
|
}
|
|
|
|
|
2021-12-29 23:14:34 +08:00
|
|
|
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),
|
|
|
|
};
|
|
|
|
|
2021-12-30 22:03:39 +08:00
|
|
|
assert(im->kp->ops->process_key);
|
2021-12-29 23:14:34 +08:00
|
|
|
im->kp->ops->process_key(im->kp, &evt, ack_to_wait);
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2021-02-16 01:53:23 +08:00
|
|
|
static void
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
|
|
|
|
const SDL_MouseMotionEvent *event) {
|
2021-12-30 06:52:08 +08:00
|
|
|
|
2019-09-23 03:33:16 +08:00
|
|
|
if (event->which == SDL_TOUCH_MOUSEID) {
|
|
|
|
// simulated from touch events, so it's a duplicate
|
|
|
|
return;
|
|
|
|
}
|
2020-08-09 22:04:02 +08:00
|
|
|
|
2021-12-29 23:24:20 +08:00
|
|
|
struct sc_mouse_motion_event evt = {
|
|
|
|
.position = {
|
|
|
|
.screen_size = im->screen->frame_size,
|
2022-01-15 05:17:30 +08:00
|
|
|
.point = sc_screen_convert_window_to_frame_coords(im->screen,
|
|
|
|
event->x,
|
|
|
|
event->y),
|
2021-12-29 23:24:20 +08:00
|
|
|
},
|
2021-12-30 07:05:30 +08:00
|
|
|
.xrel = event->xrel,
|
|
|
|
.yrel = event->yrel,
|
2021-12-30 06:52:08 +08:00
|
|
|
.buttons_state =
|
|
|
|
sc_mouse_buttons_state_from_sdl(event->state,
|
|
|
|
im->forward_all_clicks),
|
2021-12-29 23:24:20 +08:00
|
|
|
};
|
|
|
|
|
2021-12-30 22:08:42 +08:00
|
|
|
assert(im->mp->ops->process_mouse_motion);
|
2021-12-29 23:24:20 +08:00
|
|
|
im->mp->ops->process_mouse_motion(im->mp, &evt);
|
2020-08-09 22:04:02 +08:00
|
|
|
|
2021-12-30 22:46:00 +08:00
|
|
|
// vfinger must never be used in relative mode
|
|
|
|
assert(!im->mp->relative_mode || !im->vfinger_down);
|
|
|
|
|
2020-08-09 22:04:02 +08:00
|
|
|
if (im->vfinger_down) {
|
2021-12-30 22:46:00 +08:00
|
|
|
assert(!im->mp->relative_mode); // assert one more time
|
2021-10-30 21:20:39 +08:00
|
|
|
struct sc_point mouse =
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_screen_convert_window_to_frame_coords(im->screen, event->x,
|
|
|
|
event->y);
|
2021-10-30 21:20:39 +08:00
|
|
|
struct sc_point vfinger = inverse_point(mouse, im->screen->frame_size);
|
2020-08-09 22:04:02 +08:00
|
|
|
simulate_virtual_finger(im, AMOTION_EVENT_ACTION_MOVE, vfinger);
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-16 01:53:23 +08:00
|
|
|
static void
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_input_manager_process_touch(struct sc_input_manager *im,
|
|
|
|
const SDL_TouchFingerEvent *event) {
|
2021-12-30 22:08:42 +08:00
|
|
|
if (!im->mp->ops->process_touch) {
|
|
|
|
// The mouse processor does not support touch events
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-29 23:24:20 +08:00
|
|
|
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,
|
2022-01-15 05:17:30 +08:00
|
|
|
.point =
|
|
|
|
sc_screen_convert_drawable_to_frame_coords(im->screen, x, y),
|
2021-12-29 23:24:20 +08:00
|
|
|
},
|
|
|
|
.action = sc_touch_action_from_sdl(event->type),
|
|
|
|
.pointer_id = event->fingerId,
|
|
|
|
.pressure = event->pressure,
|
|
|
|
};
|
|
|
|
|
|
|
|
im->mp->ops->process_touch(im->mp, &evt);
|
2019-10-20 18:49:47 +08:00
|
|
|
}
|
|
|
|
|
2021-02-16 01:53:23 +08:00
|
|
|
static void
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
|
|
|
const SDL_MouseButtonEvent *event) {
|
2022-01-23 19:08:55 +08:00
|
|
|
struct sc_controller *controller = im->controller;
|
2020-08-02 21:45:31 +08:00
|
|
|
|
2019-09-23 03:33:16 +08:00
|
|
|
if (event->which == SDL_TOUCH_MOUSEID) {
|
|
|
|
// simulated from touch events, so it's a duplicate
|
|
|
|
return;
|
|
|
|
}
|
2020-08-09 22:04:02 +08:00
|
|
|
|
|
|
|
bool down = event->type == SDL_MOUSEBUTTONDOWN;
|
2021-04-16 23:53:37 +08:00
|
|
|
if (!im->forward_all_clicks) {
|
2022-01-23 19:11:53 +08:00
|
|
|
if (controller) {
|
|
|
|
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
|
2021-04-16 23:58:22 +08:00
|
|
|
|
2022-01-23 19:11:53 +08:00
|
|
|
if (event->button == SDL_BUTTON_X1) {
|
|
|
|
action_app_switch(controller, action);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (event->button == SDL_BUTTON_X2 && down) {
|
|
|
|
if (event->clicks < 2) {
|
|
|
|
expand_notification_panel(controller);
|
|
|
|
} else {
|
|
|
|
expand_settings_panel(controller);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (event->button == SDL_BUTTON_RIGHT) {
|
|
|
|
press_back_or_turn_screen_on(controller, action);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (event->button == SDL_BUTTON_MIDDLE) {
|
|
|
|
action_home(controller, action);
|
|
|
|
return;
|
2021-04-17 20:56:53 +08:00
|
|
|
}
|
2018-03-10 07:40:55 +08:00
|
|
|
}
|
2020-04-18 00:44:24 +08:00
|
|
|
|
2018-03-13 15:32:48 +08:00
|
|
|
// double-click on black borders resize to fit the device screen
|
2018-11-27 15:54:31 +08:00
|
|
|
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
|
2020-04-18 00:44:24 +08:00
|
|
|
int32_t x = event->x;
|
|
|
|
int32_t y = event->y;
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_screen_hidpi_scale_coords(im->screen, &x, &y);
|
2020-04-18 00:44:24 +08:00
|
|
|
SDL_Rect *r = &im->screen->rect;
|
|
|
|
bool outside = x < r->x || x >= r->x + r->w
|
|
|
|
|| y < r->y || y >= r->y + r->h;
|
2018-11-27 15:54:31 +08:00
|
|
|
if (outside) {
|
2021-04-16 23:53:37 +08:00
|
|
|
if (down) {
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_screen_resize_to_fit(im->screen);
|
2021-04-16 23:53:37 +08:00
|
|
|
}
|
2019-01-18 20:21:25 +08:00
|
|
|
return;
|
2018-11-27 15:54:31 +08:00
|
|
|
}
|
2018-03-13 15:32:48 +08:00
|
|
|
}
|
2018-11-19 04:20:21 +08:00
|
|
|
// otherwise, send the click event to the device
|
2018-08-11 21:06:38 +08:00
|
|
|
}
|
2018-11-19 04:20:21 +08:00
|
|
|
|
2022-01-23 19:08:55 +08:00
|
|
|
if (!controller) {
|
2019-03-03 05:40:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-29 23:24:20 +08:00
|
|
|
uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL);
|
|
|
|
|
|
|
|
struct sc_mouse_click_event evt = {
|
|
|
|
.position = {
|
|
|
|
.screen_size = im->screen->frame_size,
|
2022-01-15 05:17:30 +08:00
|
|
|
.point = sc_screen_convert_window_to_frame_coords(im->screen,
|
|
|
|
event->x,
|
|
|
|
event->y),
|
2021-12-29 23:24:20 +08:00
|
|
|
},
|
|
|
|
.action = sc_action_from_sdl_mousebutton_type(event->type),
|
|
|
|
.button = sc_mouse_button_from_sdl(event->button),
|
2021-12-30 06:52:08 +08:00
|
|
|
.buttons_state =
|
|
|
|
sc_mouse_buttons_state_from_sdl(sdl_buttons_state,
|
|
|
|
im->forward_all_clicks),
|
2021-12-29 23:24:20 +08:00
|
|
|
};
|
|
|
|
|
2021-12-30 22:08:42 +08:00
|
|
|
assert(im->mp->ops->process_mouse_click);
|
2021-12-29 23:24:20 +08:00
|
|
|
im->mp->ops->process_mouse_click(im->mp, &evt);
|
2020-08-09 22:04:02 +08:00
|
|
|
|
2021-12-30 22:46:00 +08:00
|
|
|
if (im->mp->relative_mode) {
|
|
|
|
assert(!im->vfinger_down); // vfinger must not be used in relative mode
|
|
|
|
// No pinch-to-zoom simulation
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-09 22:04:02 +08:00
|
|
|
// Pinch-to-zoom simulation.
|
|
|
|
//
|
|
|
|
// If Ctrl is hold when the left-click button is pressed, then
|
|
|
|
// pinch-to-zoom mode is enabled: on every mouse event until the left-click
|
|
|
|
// button is released, an additional "virtual finger" event is generated,
|
|
|
|
// having a position inverted through the center of the screen.
|
|
|
|
//
|
|
|
|
// In other words, the center of the rotation/scaling is the center of the
|
|
|
|
// screen.
|
|
|
|
#define CTRL_PRESSED (SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL))
|
2022-01-02 06:59:20 +08:00
|
|
|
if (event->button == SDL_BUTTON_LEFT &&
|
|
|
|
((down && !im->vfinger_down && CTRL_PRESSED) ||
|
|
|
|
(!down && im->vfinger_down))) {
|
2021-10-30 21:20:39 +08:00
|
|
|
struct sc_point mouse =
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_screen_convert_window_to_frame_coords(im->screen, event->x,
|
|
|
|
event->y);
|
2021-10-30 21:20:39 +08:00
|
|
|
struct sc_point vfinger = inverse_point(mouse, im->screen->frame_size);
|
2020-08-09 22:04:02 +08:00
|
|
|
enum android_motionevent_action action = down
|
|
|
|
? AMOTION_EVENT_ACTION_DOWN
|
|
|
|
: AMOTION_EVENT_ACTION_UP;
|
|
|
|
if (!simulate_virtual_finger(im, action, vfinger)) {
|
|
|
|
return;
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
2020-08-09 22:04:02 +08:00
|
|
|
im->vfinger_down = down;
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-16 01:53:23 +08:00
|
|
|
static void
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
|
|
|
|
const SDL_MouseWheelEvent *event) {
|
2021-12-30 22:08:42 +08:00
|
|
|
if (!im->mp->ops->process_mouse_scroll) {
|
|
|
|
// The mouse processor does not support scroll events
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-29 23:24:20 +08:00
|
|
|
// mouse_x and mouse_y are expressed in pixels relative to the window
|
|
|
|
int mouse_x;
|
|
|
|
int mouse_y;
|
2021-12-31 17:38:05 +08:00
|
|
|
uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y);
|
2021-12-29 23:24:20 +08:00
|
|
|
|
|
|
|
struct sc_mouse_scroll_event evt = {
|
|
|
|
.position = {
|
|
|
|
.screen_size = im->screen->frame_size,
|
2022-01-15 05:17:30 +08:00
|
|
|
.point = sc_screen_convert_window_to_frame_coords(im->screen,
|
|
|
|
mouse_x, mouse_y),
|
2021-12-29 23:24:20 +08:00
|
|
|
},
|
|
|
|
.hscroll = event->x,
|
|
|
|
.vscroll = event->y,
|
2021-12-31 17:38:05 +08:00
|
|
|
.buttons_state =
|
|
|
|
sc_mouse_buttons_state_from_sdl(buttons, im->forward_all_clicks),
|
2021-12-29 23:24:20 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
im->mp->ops->process_mouse_scroll(im->mp, &evt);
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
2021-02-16 01:53:23 +08:00
|
|
|
|
2022-01-22 02:26:36 +08:00
|
|
|
static bool
|
|
|
|
is_apk(const char *file) {
|
|
|
|
const char *ext = strrchr(file, '.');
|
|
|
|
return ext && !strcmp(ext, ".apk");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sc_input_manager_process_file(struct sc_input_manager *im,
|
|
|
|
const SDL_DropEvent *event) {
|
|
|
|
char *file = strdup(event->file);
|
|
|
|
SDL_free(event->file);
|
|
|
|
if (!file) {
|
|
|
|
LOG_OOM();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum sc_file_pusher_action action;
|
|
|
|
if (is_apk(file)) {
|
|
|
|
action = SC_FILE_PUSHER_ACTION_INSTALL_APK;
|
|
|
|
} else {
|
|
|
|
action = SC_FILE_PUSHER_ACTION_PUSH_FILE;
|
|
|
|
}
|
|
|
|
bool ok = sc_file_pusher_request(im->fp, action, file);
|
|
|
|
if (!ok) {
|
|
|
|
free(file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-22 02:16:32 +08:00
|
|
|
void
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_input_manager_handle_event(struct sc_input_manager *im, SDL_Event *event) {
|
2022-01-23 19:08:55 +08:00
|
|
|
bool control = im->controller;
|
2021-02-16 01:53:23 +08:00
|
|
|
switch (event->type) {
|
|
|
|
case SDL_TEXTINPUT:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (!control) {
|
2022-01-22 02:16:32 +08:00
|
|
|
break;
|
2021-02-16 01:53:23 +08:00
|
|
|
}
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_input_manager_process_text_input(im, &event->text);
|
2022-01-22 02:16:32 +08:00
|
|
|
break;
|
2021-02-16 01:53:23 +08:00
|
|
|
case SDL_KEYDOWN:
|
|
|
|
case SDL_KEYUP:
|
|
|
|
// some key events do not interact with the device, so process the
|
|
|
|
// event even if control is disabled
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_input_manager_process_key(im, &event->key);
|
2022-01-22 02:16:32 +08:00
|
|
|
break;
|
2021-02-16 01:53:23 +08:00
|
|
|
case SDL_MOUSEMOTION:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (!control) {
|
2021-02-16 01:53:23 +08:00
|
|
|
break;
|
|
|
|
}
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_input_manager_process_mouse_motion(im, &event->motion);
|
2022-01-22 02:16:32 +08:00
|
|
|
break;
|
2021-02-16 01:53:23 +08:00
|
|
|
case SDL_MOUSEWHEEL:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (!control) {
|
2021-02-16 01:53:23 +08:00
|
|
|
break;
|
|
|
|
}
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_input_manager_process_mouse_wheel(im, &event->wheel);
|
2022-01-22 02:16:32 +08:00
|
|
|
break;
|
2021-02-16 01:53:23 +08:00
|
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
|
|
case SDL_MOUSEBUTTONUP:
|
|
|
|
// some mouse events do not interact with the device, so process
|
|
|
|
// the event even if control is disabled
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_input_manager_process_mouse_button(im, &event->button);
|
2022-01-22 02:16:32 +08:00
|
|
|
break;
|
2021-02-16 01:53:23 +08:00
|
|
|
case SDL_FINGERMOTION:
|
|
|
|
case SDL_FINGERDOWN:
|
|
|
|
case SDL_FINGERUP:
|
2022-01-23 19:08:55 +08:00
|
|
|
if (!control) {
|
2022-01-23 19:02:35 +08:00
|
|
|
break;
|
|
|
|
}
|
2022-01-15 05:17:30 +08:00
|
|
|
sc_input_manager_process_touch(im, &event->tfinger);
|
2022-01-22 02:16:32 +08:00
|
|
|
break;
|
2022-01-22 02:26:36 +08:00
|
|
|
case SDL_DROPFILE: {
|
2022-01-23 19:08:55 +08:00
|
|
|
if (!control) {
|
2022-01-22 02:26:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
sc_input_manager_process_file(im, &event->drop);
|
|
|
|
}
|
2021-02-16 01:53:23 +08:00
|
|
|
}
|
|
|
|
}
|