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
|
|
|
|
2019-11-24 18:53:00 +08:00
|
|
|
#include "util/log.h"
|
2018-02-09 01:14:50 +08:00
|
|
|
|
2018-06-25 03:09:23 +08:00
|
|
|
static const int ACTION_DOWN = 1;
|
|
|
|
static const int ACTION_UP = 1 << 1;
|
|
|
|
|
2020-07-17 06:00:42 +08:00
|
|
|
#define SC_SDL_SHORTCUT_MODS_MASK (KMOD_CTRL | KMOD_ALT | KMOD_GUI)
|
|
|
|
|
|
|
|
static inline uint16_t
|
|
|
|
to_sdl_mod(unsigned mod) {
|
|
|
|
uint16_t sdl_mod = 0;
|
|
|
|
if (mod & SC_MOD_LCTRL) {
|
|
|
|
sdl_mod |= KMOD_LCTRL;
|
|
|
|
}
|
|
|
|
if (mod & SC_MOD_RCTRL) {
|
|
|
|
sdl_mod |= KMOD_RCTRL;
|
|
|
|
}
|
|
|
|
if (mod & SC_MOD_LALT) {
|
|
|
|
sdl_mod |= KMOD_LALT;
|
|
|
|
}
|
|
|
|
if (mod & SC_MOD_RALT) {
|
|
|
|
sdl_mod |= KMOD_RALT;
|
|
|
|
}
|
|
|
|
if (mod & SC_MOD_LSUPER) {
|
|
|
|
sdl_mod |= KMOD_LGUI;
|
|
|
|
}
|
|
|
|
if (mod & SC_MOD_RSUPER) {
|
|
|
|
sdl_mod |= KMOD_RGUI;
|
|
|
|
}
|
|
|
|
return sdl_mod;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
is_shortcut_mod(struct input_manager *im, uint16_t sdl_mod) {
|
|
|
|
// 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
|
2021-05-17 14:46:38 +08:00
|
|
|
input_manager_init(struct input_manager *im, struct controller *controller,
|
2021-10-03 23:11:20 +08:00
|
|
|
struct screen *screen, struct sc_key_processor *kp,
|
2021-10-03 23:44:14 +08:00
|
|
|
struct sc_mouse_processor *mp,
|
2021-05-17 14:46:38 +08:00
|
|
|
const struct scrcpy_options *options) {
|
2021-10-03 23:11:20 +08:00
|
|
|
assert(!options->control || (kp && kp->ops));
|
2021-10-03 23:44:14 +08:00
|
|
|
assert(!options->control || (mp && mp->ops));
|
2021-10-03 23:11:20 +08:00
|
|
|
|
2021-05-17 14:46:38 +08:00
|
|
|
im->controller = controller;
|
|
|
|
im->screen = screen;
|
2021-10-03 23:11:20 +08:00
|
|
|
im->kp = kp;
|
2021-10-03 23:44:14 +08:00
|
|
|
im->mp = mp;
|
2021-05-17 14:46:38 +08:00
|
|
|
|
2020-08-02 21:45:31 +08:00
|
|
|
im->control = options->control;
|
2020-10-06 02:45:53 +08:00
|
|
|
im->forward_all_clicks = options->forward_all_clicks;
|
2020-10-07 03:30:10 +08:00
|
|
|
im->legacy_paste = options->legacy_paste;
|
2021-11-22 15:49:10 +08:00
|
|
|
im->clipboard_autosync = options->clipboard_autosync;
|
2020-07-17 06:00:42 +08:00
|
|
|
|
2020-08-02 21:45:31 +08:00
|
|
|
const struct sc_shortcut_mods *shortcut_mods = &options->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
|
|
|
|
send_keycode(struct controller *controller, enum android_keycode keycode,
|
|
|
|
int actions, const char *name) {
|
2018-02-09 01:14:50 +08:00
|
|
|
// send DOWN event
|
2019-05-31 20:55:11 +08:00
|
|
|
struct control_msg msg;
|
|
|
|
msg.type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
|
|
|
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
|
|
|
|
2018-06-25 03:09:23 +08:00
|
|
|
if (actions & ACTION_DOWN) {
|
2019-05-31 20:55:11 +08:00
|
|
|
msg.inject_keycode.action = AKEY_EVENT_ACTION_DOWN;
|
|
|
|
if (!controller_push_msg(controller, &msg)) {
|
2019-06-24 02:49:38 +08:00
|
|
|
LOGW("Could not request 'inject %s (DOWN)'", name);
|
2018-06-25 03:09:23 +08:00
|
|
|
return;
|
|
|
|
}
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2018-06-25 03:09:23 +08:00
|
|
|
if (actions & ACTION_UP) {
|
2019-05-31 20:55:11 +08:00
|
|
|
msg.inject_keycode.action = AKEY_EVENT_ACTION_UP;
|
|
|
|
if (!controller_push_msg(controller, &msg)) {
|
2019-06-24 02:49:38 +08:00
|
|
|
LOGW("Could not request 'inject %s (UP)'", name);
|
2018-06-25 03:09:23 +08:00
|
|
|
}
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static inline void
|
|
|
|
action_home(struct controller *controller, int actions) {
|
2018-06-25 03:09:23 +08:00
|
|
|
send_keycode(controller, AKEYCODE_HOME, actions, "HOME");
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static inline void
|
|
|
|
action_back(struct controller *controller, int actions) {
|
2018-06-25 03:09:23 +08:00
|
|
|
send_keycode(controller, AKEYCODE_BACK, actions, "BACK");
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static inline void
|
|
|
|
action_app_switch(struct controller *controller, int actions) {
|
2018-06-25 03:09:23 +08:00
|
|
|
send_keycode(controller, AKEYCODE_APP_SWITCH, actions, "APP_SWITCH");
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static inline void
|
|
|
|
action_power(struct controller *controller, int actions) {
|
2018-06-25 03:09:23 +08:00
|
|
|
send_keycode(controller, AKEYCODE_POWER, actions, "POWER");
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static inline void
|
|
|
|
action_volume_up(struct controller *controller, int actions) {
|
2018-06-25 03:09:23 +08:00
|
|
|
send_keycode(controller, AKEYCODE_VOLUME_UP, actions, "VOLUME_UP");
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static inline void
|
|
|
|
action_volume_down(struct controller *controller, int actions) {
|
2018-06-25 03:09:23 +08:00
|
|
|
send_keycode(controller, AKEYCODE_VOLUME_DOWN, actions, "VOLUME_DOWN");
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static inline void
|
|
|
|
action_menu(struct controller *controller, int actions) {
|
2018-06-25 03:09:23 +08:00
|
|
|
send_keycode(controller, AKEYCODE_MENU, actions, "MENU");
|
2018-03-21 09:04:40 +08:00
|
|
|
}
|
|
|
|
|
2020-07-17 06:00:42 +08:00
|
|
|
static inline void
|
|
|
|
action_copy(struct controller *controller, int actions) {
|
|
|
|
send_keycode(controller, AKEYCODE_COPY, actions, "COPY");
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
action_cut(struct controller *controller, int actions) {
|
|
|
|
send_keycode(controller, AKEYCODE_CUT, actions, "CUT");
|
|
|
|
}
|
|
|
|
|
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
|
2021-04-17 00:37:50 +08:00
|
|
|
press_back_or_turn_screen_on(struct controller *controller, int actions) {
|
2019-05-31 20:55:11 +08:00
|
|
|
struct control_msg msg;
|
|
|
|
msg.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON;
|
2018-03-07 21:46:31 +08:00
|
|
|
|
2021-04-17 00:37:50 +08:00
|
|
|
if (actions & ACTION_DOWN) {
|
|
|
|
msg.back_or_screen_on.action = AKEY_EVENT_ACTION_DOWN;
|
|
|
|
if (!controller_push_msg(controller, &msg)) {
|
|
|
|
LOGW("Could not request 'press back or turn screen on'");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (actions & ACTION_UP) {
|
|
|
|
msg.back_or_screen_on.action = AKEY_EVENT_ACTION_UP;
|
|
|
|
if (!controller_push_msg(controller, &msg)) {
|
|
|
|
LOGW("Could not request 'press back or turn screen on'");
|
|
|
|
}
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static void
|
|
|
|
expand_notification_panel(struct controller *controller) {
|
2019-05-31 20:55:11 +08:00
|
|
|
struct control_msg msg;
|
|
|
|
msg.type = CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL;
|
2019-02-27 03:35:37 +08:00
|
|
|
|
2019-05-31 20:55:11 +08:00
|
|
|
if (!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
|
|
|
|
expand_settings_panel(struct controller *controller) {
|
|
|
|
struct control_msg msg;
|
|
|
|
msg.type = CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL;
|
|
|
|
|
|
|
|
if (!controller_push_msg(controller, &msg)) {
|
|
|
|
LOGW("Could not request 'expand settings panel'");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static void
|
2021-04-17 20:26:54 +08:00
|
|
|
collapse_panels(struct controller *controller) {
|
2019-05-31 20:55:11 +08:00
|
|
|
struct control_msg msg;
|
2021-04-17 20:26:54 +08:00
|
|
|
msg.type = CONTROL_MSG_TYPE_COLLAPSE_PANELS;
|
2019-02-27 03:35:37 +08:00
|
|
|
|
2019-05-31 20:55:11 +08:00
|
|
|
if (!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-21 00:44:00 +08:00
|
|
|
static bool
|
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
|
|
|
set_device_clipboard(struct controller *controller, bool paste,
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-05-31 20:55:11 +08:00
|
|
|
struct control_msg msg;
|
|
|
|
msg.type = 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
|
|
|
|
2019-05-31 20:55:11 +08:00
|
|
|
if (!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
|
|
|
|
set_screen_power_mode(struct controller *controller,
|
|
|
|
enum screen_power_mode mode) {
|
|
|
|
struct control_msg msg;
|
|
|
|
msg.type = CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE;
|
|
|
|
msg.set_screen_power_mode.mode = mode;
|
|
|
|
|
|
|
|
if (!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
|
|
|
|
clipboard_paste(struct 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;
|
|
|
|
}
|
|
|
|
|
2019-05-31 20:55:11 +08:00
|
|
|
struct control_msg msg;
|
|
|
|
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
|
2021-01-24 22:14:53 +08:00
|
|
|
msg.inject_text.text = text_dup;
|
2019-05-31 20:55:11 +08:00
|
|
|
if (!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
|
|
|
|
rotate_device(struct controller *controller) {
|
|
|
|
struct control_msg msg;
|
|
|
|
msg.type = CONTROL_MSG_TYPE_ROTATE_DEVICE;
|
|
|
|
|
|
|
|
if (!controller_push_msg(controller, &msg)) {
|
|
|
|
LOGW("Could not request device rotation");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-08 05:03:23 +08:00
|
|
|
static void
|
|
|
|
rotate_client_left(struct screen *screen) {
|
|
|
|
unsigned new_rotation = (screen->rotation + 1) % 4;
|
|
|
|
screen_set_rotation(screen, new_rotation);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
rotate_client_right(struct screen *screen) {
|
|
|
|
unsigned new_rotation = (screen->rotation + 3) % 4;
|
|
|
|
screen_set_rotation(screen, new_rotation);
|
|
|
|
}
|
|
|
|
|
2021-02-16 01:53:23 +08:00
|
|
|
static void
|
2019-10-20 17:51:54 +08:00
|
|
|
input_manager_process_text_input(struct input_manager *im,
|
2019-03-03 03:09:56 +08:00
|
|
|
const SDL_TextInputEvent *event) {
|
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-10-03 23:11:20 +08:00
|
|
|
im->kp->ops->process_text(im->kp, event);
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2020-08-09 22:04:02 +08:00
|
|
|
static bool
|
|
|
|
simulate_virtual_finger(struct input_manager *im,
|
|
|
|
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;
|
|
|
|
|
|
|
|
struct control_msg msg;
|
|
|
|
msg.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (!controller_push_msg(im->controller, &msg)) {
|
|
|
|
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
|
2019-10-20 17:51:54 +08:00
|
|
|
input_manager_process_key(struct input_manager *im,
|
2020-08-02 21:45:31 +08:00
|
|
|
const SDL_KeyboardEvent *event) {
|
2019-06-01 05:25:41 +08:00
|
|
|
// control: indicates the state of the command-line option --no-control
|
2020-08-02 21:45:31 +08:00
|
|
|
bool control = im->control;
|
2018-10-25 00:51:02 +08:00
|
|
|
|
2019-10-20 17:51:54 +08:00
|
|
|
struct 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) {
|
2019-06-01 05:25:41 +08:00
|
|
|
int action = down ? ACTION_DOWN : ACTION_UP;
|
2018-02-09 01:14:50 +08:00
|
|
|
switch (keycode) {
|
|
|
|
case SDLK_h:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (control && !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:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (control && !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:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (control && !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:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (control && !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:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (control && !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:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (control && !repeat && down) {
|
2020-05-28 00:18:39 +08:00
|
|
|
enum screen_power_mode mode = shift
|
|
|
|
? SCREEN_POWER_MODE_NORMAL
|
|
|
|
: SCREEN_POWER_MODE_OFF;
|
|
|
|
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:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (control && !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:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (control && !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:
|
|
|
|
if (control && !shift && !repeat) {
|
|
|
|
action_copy(controller, action);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case SDLK_x:
|
|
|
|
if (control && !shift && !repeat) {
|
|
|
|
action_cut(controller, action);
|
|
|
|
}
|
|
|
|
return;
|
2018-03-07 22:29:33 +08:00
|
|
|
case SDLK_v:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (control && !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) {
|
2019-10-20 17:51:54 +08:00
|
|
|
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) {
|
2019-10-20 17:51:54 +08:00
|
|
|
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) {
|
2019-10-20 17:51:54 +08:00
|
|
|
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:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (control && !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:
|
2020-07-17 06:00:42 +08:00
|
|
|
if (control && !shift && !repeat && down) {
|
2019-12-05 02:55:28 +08:00
|
|
|
rotate_device(controller);
|
|
|
|
}
|
|
|
|
return;
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-03 05:40:51 +08:00
|
|
|
if (!control) {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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->kp->ops->process_key(im->kp, event, ack_to_wait);
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
|
|
|
|
2021-02-16 01:53:23 +08:00
|
|
|
static void
|
2019-10-20 17:51:54 +08:00
|
|
|
input_manager_process_mouse_motion(struct input_manager *im,
|
2019-03-03 03:09:56 +08:00
|
|
|
const SDL_MouseMotionEvent *event) {
|
2021-06-20 06:42:55 +08:00
|
|
|
uint32_t mask = SDL_BUTTON_LMASK;
|
|
|
|
if (im->forward_all_clicks) {
|
|
|
|
mask |= SDL_BUTTON_MMASK | SDL_BUTTON_RMASK;
|
|
|
|
}
|
|
|
|
if (!(event->state & mask)) {
|
|
|
|
// do not send motion events when no click is pressed
|
2018-02-09 01:14:50 +08:00
|
|
|
return;
|
|
|
|
}
|
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-10-03 23:44:14 +08:00
|
|
|
im->mp->ops->process_mouse_motion(im->mp, event);
|
2020-08-09 22:04:02 +08:00
|
|
|
|
|
|
|
if (im->vfinger_down) {
|
2021-10-30 21:20:39 +08:00
|
|
|
struct sc_point mouse =
|
2021-10-03 23:44:14 +08:00
|
|
|
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
|
2019-10-20 17:51:54 +08:00
|
|
|
input_manager_process_touch(struct input_manager *im,
|
2019-09-23 03:30:05 +08:00
|
|
|
const SDL_TouchFingerEvent *event) {
|
2021-10-03 23:44:14 +08:00
|
|
|
im->mp->ops->process_touch(im->mp, event);
|
2019-10-20 18:49:47 +08:00
|
|
|
}
|
|
|
|
|
2021-02-16 01:53:23 +08:00
|
|
|
static void
|
2019-10-20 17:51:54 +08:00
|
|
|
input_manager_process_mouse_button(struct input_manager *im,
|
2020-08-02 21:45:31 +08:00
|
|
|
const SDL_MouseButtonEvent *event) {
|
|
|
|
bool control = im->control;
|
|
|
|
|
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) {
|
2021-04-16 23:58:22 +08:00
|
|
|
int action = down ? ACTION_DOWN : ACTION_UP;
|
|
|
|
|
2021-04-12 17:15:33 +08:00
|
|
|
if (control && event->button == SDL_BUTTON_X1) {
|
|
|
|
action_app_switch(im->controller, action);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (control && event->button == SDL_BUTTON_X2 && down) {
|
2021-04-17 20:56:53 +08:00
|
|
|
if (event->clicks < 2) {
|
|
|
|
expand_notification_panel(im->controller);
|
|
|
|
} else {
|
|
|
|
expand_settings_panel(im->controller);
|
|
|
|
}
|
2021-04-12 17:15:33 +08:00
|
|
|
return;
|
|
|
|
}
|
2019-03-03 05:40:51 +08:00
|
|
|
if (control && event->button == SDL_BUTTON_RIGHT) {
|
2021-04-17 00:37:50 +08:00
|
|
|
press_back_or_turn_screen_on(im->controller, action);
|
2018-03-10 07:40:55 +08:00
|
|
|
return;
|
|
|
|
}
|
2019-03-03 05:40:51 +08:00
|
|
|
if (control && event->button == SDL_BUTTON_MIDDLE) {
|
2021-04-16 23:58:22 +08:00
|
|
|
action_home(im->controller, action);
|
2018-03-10 07:40:55 +08:00
|
|
|
return;
|
|
|
|
}
|
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;
|
|
|
|
screen_hidpi_scale_coords(im->screen, &x, &y);
|
|
|
|
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) {
|
|
|
|
screen_resize_to_fit(im->screen);
|
|
|
|
}
|
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
|
|
|
|
2019-03-03 05:40:51 +08:00
|
|
|
if (!control) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-10-03 23:44:14 +08:00
|
|
|
im->mp->ops->process_mouse_button(im->mp, event);
|
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))
|
|
|
|
if ((down && !im->vfinger_down && CTRL_PRESSED)
|
|
|
|
|| (!down && im->vfinger_down)) {
|
2021-10-30 21:20:39 +08:00
|
|
|
struct sc_point mouse =
|
2021-10-03 23:44:14 +08:00
|
|
|
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
|
2019-10-20 17:51:54 +08:00
|
|
|
input_manager_process_mouse_wheel(struct input_manager *im,
|
2019-03-03 03:09:56 +08:00
|
|
|
const SDL_MouseWheelEvent *event) {
|
2021-10-03 23:44:14 +08:00
|
|
|
im->mp->ops->process_mouse_wheel(im->mp, event);
|
2018-02-09 01:14:50 +08:00
|
|
|
}
|
2021-02-16 01:53:23 +08:00
|
|
|
|
|
|
|
bool
|
|
|
|
input_manager_handle_event(struct input_manager *im, SDL_Event *event) {
|
|
|
|
switch (event->type) {
|
|
|
|
case SDL_TEXTINPUT:
|
|
|
|
if (!im->control) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
input_manager_process_text_input(im, &event->text);
|
|
|
|
return true;
|
|
|
|
case SDL_KEYDOWN:
|
|
|
|
case SDL_KEYUP:
|
|
|
|
// some key events do not interact with the device, so process the
|
|
|
|
// event even if control is disabled
|
|
|
|
input_manager_process_key(im, &event->key);
|
|
|
|
return true;
|
|
|
|
case SDL_MOUSEMOTION:
|
|
|
|
if (!im->control) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
input_manager_process_mouse_motion(im, &event->motion);
|
|
|
|
return true;
|
|
|
|
case SDL_MOUSEWHEEL:
|
|
|
|
if (!im->control) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
input_manager_process_mouse_wheel(im, &event->wheel);
|
|
|
|
return true;
|
|
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
|
|
case SDL_MOUSEBUTTONUP:
|
|
|
|
// some mouse events do not interact with the device, so process
|
|
|
|
// the event even if control is disabled
|
|
|
|
input_manager_process_mouse_button(im, &event->button);
|
|
|
|
return true;
|
|
|
|
case SDL_FINGERMOTION:
|
|
|
|
case SDL_FINGERDOWN:
|
|
|
|
case SDL_FINGERUP:
|
|
|
|
input_manager_process_touch(im, &event->tfinger);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|