Compare commits

...

23 Commits

Author SHA1 Message Date
59772324ff buttons 4 & 5 2021-12-31 11:33:04 +01:00
57574f3d3b notouch 2021-12-31 11:23:12 +01:00
058da31c8a vscroll 2021-12-31 11:23:12 +01:00
2412717168 wip 2021-12-31 11:23:12 +01:00
cc072c65d7 wip 2021-12-31 11:23:12 +01:00
28915784ce Add relative mouse motion in event
This will allow the mouse processor to handle relative motion easily.
2021-12-31 11:23:12 +01:00
9066461abc relative-mouse-mode 2021-12-31 11:23:12 +01:00
0e66c22bb4 hid_mouse 2021-12-31 11:23:12 +01:00
97599c5db3 Add CLAMP() macro 2021-12-31 11:23:12 +01:00
54180fde50 relative_mode 2021-12-31 11:23:12 +01:00
923a892654 Pass buttons state in scroll events
A scroll event might be produced when a mouse button is pressed (for
example when scrolling while selecting a text). For consistency, pass
the actual buttons state (instead of 0).

In practice, it seems that this use case does not work properly with
Android event injection, but it will work with HID mouse.
2021-12-31 11:19:52 +01:00
850348ab60 Make some mouse processors ops optional
Do not force all mouse processors implementations to implement scroll
events or touch events.
2021-12-31 10:49:54 +01:00
1b8cbcbc65 Make process_text() optional
Not all key processors support text injection (HID keyboard does not
support it).

Instead of providing a dummy op function, set it to NULL and check on
the caller side before calling it.
2021-12-31 10:49:54 +01:00
7a7ec97e9d Apply buttons mask if not --forward-all-clicks
If --forward-all-clicks is not set, then only left clicks are forwarded.
For consistency, also mask the buttons state in other events.
2021-12-31 10:49:54 +01:00
9469db094a Reorder mouse processor ops
Group the mouse events callbacks before the touch event callback.
2021-12-31 10:49:54 +01:00
9ed983845a Simplify mouse injection implementation
The static functions are now so simple they become unnecessary: the
control message may be initialized directly instead.
2021-12-31 10:49:54 +01:00
196acfab77 Make some event conversions infallible
When the implementation handles all possible input values, it may never
fail.
2021-12-31 10:49:54 +01:00
a79f4ea901 Use scrcpy input events for mouse processors
Pass scrcpy input events instead of SDL input events to mouse
processors.

These events represent exactly what mouse processors need, abstracted
from any visual orientation and scaling applied on the SDL window.

This makes the mouse processors independent of the "screen" instance,
and the implementation source code independent of the SDL API.
2021-12-31 10:49:54 +01:00
d2cdf2f507 Use scrcpy input events for key processors
Pass scrcpy input events instead of SDL input events to key processors.

This makes the source code of key processors independent of the SDL API.
2021-12-31 10:49:54 +01:00
8aa92bdaa8 Use common sc_action in input manager
Now that the scrcpy input events API exposes a sc_action enum, use the
same from the input manager.
2021-12-31 10:49:54 +01:00
21f94132d5 Add intermediate input events layer
This aims to make the key/mouse processors independent of the "screen",
instead of processing SDL events themselves.

In particular, these scrcpy events are not impacted by any UI window
scaling or rotation (contrary to SDL events).
2021-12-31 10:49:54 +01:00
6310b99411 Rename SC_MOD_* to SC_SHORTCUT_MOD_*
This will avoid conflicts with new SC_MOD_* constants.
2021-12-31 10:49:54 +01:00
cfd3b9534b Remove actions bitset
The input manager exposed functions taking an "actions" parameter,
containing a bitmask-OR of ACTION_UP and ACTION_DOWN.

But they are never called with both actions simultaneously anymore, so
simplify.

Refs 964b6d2243
Refs d0739911a3
2021-12-31 10:49:54 +01:00
25 changed files with 1247 additions and 456 deletions

View File

@ -77,6 +77,7 @@ if aoa_hid_support
src += [ src += [
'src/aoa_hid.c', 'src/aoa_hid.c',
'src/hid_keyboard.c', 'src/hid_keyboard.c',
'src/hid_mouse.c',
] ]
endif endif

View File

@ -1121,7 +1121,7 @@ parse_log_level(const char *s, enum sc_log_level *log_level) {
} }
// item is a list of mod keys separated by '+' (e.g. "lctrl+lalt") // item is a list of mod keys separated by '+' (e.g. "lctrl+lalt")
// returns a bitwise-or of SC_MOD_* constants (or 0 on error) // returns a bitwise-or of SC_SHORTCUT_MOD_* constants (or 0 on error)
static unsigned static unsigned
parse_shortcut_mods_item(const char *item, size_t len) { parse_shortcut_mods_item(const char *item, size_t len) {
unsigned mod = 0; unsigned mod = 0;
@ -1139,17 +1139,17 @@ parse_shortcut_mods_item(const char *item, size_t len) {
((sizeof(literal)-1 == len) && !memcmp(literal, s, len)) ((sizeof(literal)-1 == len) && !memcmp(literal, s, len))
if (STREQ("lctrl", item, key_len)) { if (STREQ("lctrl", item, key_len)) {
mod |= SC_MOD_LCTRL; mod |= SC_SHORTCUT_MOD_LCTRL;
} else if (STREQ("rctrl", item, key_len)) { } else if (STREQ("rctrl", item, key_len)) {
mod |= SC_MOD_RCTRL; mod |= SC_SHORTCUT_MOD_RCTRL;
} else if (STREQ("lalt", item, key_len)) { } else if (STREQ("lalt", item, key_len)) {
mod |= SC_MOD_LALT; mod |= SC_SHORTCUT_MOD_LALT;
} else if (STREQ("ralt", item, key_len)) { } else if (STREQ("ralt", item, key_len)) {
mod |= SC_MOD_RALT; mod |= SC_SHORTCUT_MOD_RALT;
} else if (STREQ("lsuper", item, key_len)) { } else if (STREQ("lsuper", item, key_len)) {
mod |= SC_MOD_LSUPER; mod |= SC_SHORTCUT_MOD_LSUPER;
} else if (STREQ("rsuper", item, key_len)) { } else if (STREQ("rsuper", item, key_len)) {
mod |= SC_MOD_RSUPER; mod |= SC_SHORTCUT_MOD_RSUPER;
} else { } else {
LOGE("Unknown modifier key: %.*s " LOGE("Unknown modifier key: %.*s "
"(must be one of: lctrl, rctrl, lalt, ralt, lsuper, rsuper)", "(must be one of: lctrl, rctrl, lalt, ralt, lsuper, rsuper)",

View File

@ -7,6 +7,7 @@
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0])) #define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
#define MIN(X,Y) (X) < (Y) ? (X) : (Y) #define MIN(X,Y) (X) < (Y) ? (X) : (Y)
#define MAX(X,Y) (X) > (Y) ? (X) : (Y) #define MAX(X,Y) (X) > (Y) ? (X) : (Y)
#define CLAMP(V,X,Y) MIN( MAX((V),(X)), (Y) )
#define container_of(ptr, type, member) \ #define container_of(ptr, type, member) \
((type *) (((char *) (ptr)) - offsetof(type, member))) ((type *) (((char *) (ptr)) - offsetof(type, member)))

View File

@ -119,7 +119,8 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
(uint32_t) msg->inject_scroll_event.hscroll); (uint32_t) msg->inject_scroll_event.hscroll);
buffer_write32be(&buf[17], buffer_write32be(&buf[17],
(uint32_t) msg->inject_scroll_event.vscroll); (uint32_t) msg->inject_scroll_event.vscroll);
return 21; buffer_write32be(&buf[21], msg->inject_scroll_event.buttons);
return 25;
case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON: case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
buf[1] = msg->inject_keycode.action; buf[1] = msg->inject_keycode.action;
return 2; return 2;
@ -192,11 +193,12 @@ control_msg_log(const struct control_msg *msg) {
} }
case CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT: case CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
LOG_CMSG("scroll position=%" PRIi32 ",%" PRIi32 " hscroll=%" PRIi32 LOG_CMSG("scroll position=%" PRIi32 ",%" PRIi32 " hscroll=%" PRIi32
" vscroll=%" PRIi32, " vscroll=%" PRIi32 " buttons=%06lx",
msg->inject_scroll_event.position.point.x, msg->inject_scroll_event.position.point.x,
msg->inject_scroll_event.position.point.y, msg->inject_scroll_event.position.point.y,
msg->inject_scroll_event.hscroll, msg->inject_scroll_event.hscroll,
msg->inject_scroll_event.vscroll); msg->inject_scroll_event.vscroll,
(long) msg->inject_scroll_event.buttons);
break; break;
case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON: case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
LOG_CMSG("back-or-screen-on %s", LOG_CMSG("back-or-screen-on %s",

View File

@ -70,6 +70,7 @@ struct control_msg {
struct sc_position position; struct sc_position position;
int32_t hscroll; int32_t hscroll;
int32_t vscroll; int32_t vscroll;
enum android_motionevent_buttons buttons;
} inject_scroll_event; } inject_scroll_event;
struct { struct {
enum android_keyevent_action action; // action for the BACK key enum android_keyevent_action action; // action for the BACK key

View File

@ -1,8 +1,8 @@
#include "hid_keyboard.h" #include "hid_keyboard.h"
#include <assert.h> #include <assert.h>
#include <SDL2/SDL_events.h>
#include "input_events.h"
#include "util/log.h" #include "util/log.h"
/** Downcast key processor to hid_keyboard */ /** Downcast key processor to hid_keyboard */
@ -201,30 +201,30 @@ static const unsigned char keyboard_report_desc[] = {
*/ */
static unsigned char static unsigned char
sdl_keymod_to_hid_modifiers(SDL_Keymod mod) { sdl_keymod_to_hid_modifiers(uint16_t mod) {
unsigned char modifiers = HID_MODIFIER_NONE; unsigned char modifiers = HID_MODIFIER_NONE;
if (mod & KMOD_LCTRL) { if (mod & SC_MOD_LCTRL) {
modifiers |= HID_MODIFIER_LEFT_CONTROL; modifiers |= HID_MODIFIER_LEFT_CONTROL;
} }
if (mod & KMOD_LSHIFT) { if (mod & SC_MOD_LSHIFT) {
modifiers |= HID_MODIFIER_LEFT_SHIFT; modifiers |= HID_MODIFIER_LEFT_SHIFT;
} }
if (mod & KMOD_LALT) { if (mod & SC_MOD_LALT) {
modifiers |= HID_MODIFIER_LEFT_ALT; modifiers |= HID_MODIFIER_LEFT_ALT;
} }
if (mod & KMOD_LGUI) { if (mod & SC_MOD_LGUI) {
modifiers |= HID_MODIFIER_LEFT_GUI; modifiers |= HID_MODIFIER_LEFT_GUI;
} }
if (mod & KMOD_RCTRL) { if (mod & SC_MOD_RCTRL) {
modifiers |= HID_MODIFIER_RIGHT_CONTROL; modifiers |= HID_MODIFIER_RIGHT_CONTROL;
} }
if (mod & KMOD_RSHIFT) { if (mod & SC_MOD_RSHIFT) {
modifiers |= HID_MODIFIER_RIGHT_SHIFT; modifiers |= HID_MODIFIER_RIGHT_SHIFT;
} }
if (mod & KMOD_RALT) { if (mod & SC_MOD_RALT) {
modifiers |= HID_MODIFIER_RIGHT_ALT; modifiers |= HID_MODIFIER_RIGHT_ALT;
} }
if (mod & KMOD_RGUI) { if (mod & SC_MOD_RGUI) {
modifiers |= HID_MODIFIER_RIGHT_GUI; modifiers |= HID_MODIFIER_RIGHT_GUI;
} }
return modifiers; return modifiers;
@ -248,15 +248,15 @@ sc_hid_keyboard_event_init(struct sc_hid_event *hid_event) {
} }
static inline bool static inline bool
scancode_is_modifier(SDL_Scancode scancode) { scancode_is_modifier(enum sc_scancode scancode) {
return scancode >= SDL_SCANCODE_LCTRL && scancode <= SDL_SCANCODE_RGUI; return scancode >= SC_SCANCODE_LCTRL && scancode <= SC_SCANCODE_RGUI;
} }
static bool static bool
convert_hid_keyboard_event(struct sc_hid_keyboard *kb, convert_hid_keyboard_event(struct sc_hid_keyboard *kb,
struct sc_hid_event *hid_event, struct sc_hid_event *hid_event,
const SDL_KeyboardEvent *event) { const struct sc_key_event *event) {
SDL_Scancode scancode = event->keysym.scancode; enum sc_scancode scancode = event->scancode;
assert(scancode >= 0); assert(scancode >= 0);
// SDL also generates events when only modifiers are pressed, we cannot // SDL also generates events when only modifiers are pressed, we cannot
@ -272,11 +272,11 @@ convert_hid_keyboard_event(struct sc_hid_keyboard *kb,
return false; return false;
} }
unsigned char modifiers = sdl_keymod_to_hid_modifiers(event->keysym.mod); unsigned char modifiers = sdl_keymod_to_hid_modifiers(event->mods_state);
if (scancode < SC_HID_KEYBOARD_KEYS) { if (scancode < SC_HID_KEYBOARD_KEYS) {
// Pressed is true and released is false // Pressed is true and released is false
kb->keys[scancode] = (event->type == SDL_KEYDOWN); kb->keys[scancode] = (event->action == SC_ACTION_DOWN);
LOGV("keys[%02x] = %s", scancode, LOGV("keys[%02x] = %s", scancode,
kb->keys[scancode] ? "true" : "false"); kb->keys[scancode] ? "true" : "false");
} }
@ -306,17 +306,17 @@ convert_hid_keyboard_event(struct sc_hid_keyboard *kb,
end: end:
LOGV("hid keyboard: key %-4s scancode=%02x (%u) mod=%02x", LOGV("hid keyboard: key %-4s scancode=%02x (%u) mod=%02x",
event->type == SDL_KEYDOWN ? "down" : "up", event->keysym.scancode, event->action == SC_ACTION_DOWN ? "down" : "up", event->scancode,
event->keysym.scancode, modifiers); event->scancode, modifiers);
return true; return true;
} }
static bool static bool
push_mod_lock_state(struct sc_hid_keyboard *kb, uint16_t sdl_mod) { push_mod_lock_state(struct sc_hid_keyboard *kb, uint16_t mods_state) {
bool capslock = sdl_mod & KMOD_CAPS; bool capslock = mods_state & SC_MOD_CAPS;
bool numlock = sdl_mod & KMOD_NUM; bool numlock = mods_state & SC_MOD_NUM;
if (!capslock && !numlock) { if (!capslock && !numlock) {
// Nothing to do // Nothing to do
return true; return true;
@ -328,8 +328,6 @@ push_mod_lock_state(struct sc_hid_keyboard *kb, uint16_t sdl_mod) {
return false; return false;
} }
#define SC_SCANCODE_CAPSLOCK SDL_SCANCODE_CAPSLOCK
#define SC_SCANCODE_NUMLOCK SDL_SCANCODE_NUMLOCKCLEAR
unsigned i = 0; unsigned i = 0;
if (capslock) { if (capslock) {
hid_event.buffer[HID_KEYBOARD_INDEX_KEYS + i] = SC_SCANCODE_CAPSLOCK; hid_event.buffer[HID_KEYBOARD_INDEX_KEYS + i] = SC_SCANCODE_CAPSLOCK;
@ -353,7 +351,7 @@ push_mod_lock_state(struct sc_hid_keyboard *kb, uint16_t sdl_mod) {
static void static void
sc_key_processor_process_key(struct sc_key_processor *kp, sc_key_processor_process_key(struct sc_key_processor *kp,
const SDL_KeyboardEvent *event, const struct sc_key_event *event,
uint64_t ack_to_wait) { uint64_t ack_to_wait) {
if (event->repeat) { if (event->repeat) {
// In USB HID protocol, key repeat is handled by the host (Android), so // In USB HID protocol, key repeat is handled by the host (Android), so
@ -369,7 +367,7 @@ sc_key_processor_process_key(struct sc_key_processor *kp,
if (!kb->mod_lock_synchronized) { if (!kb->mod_lock_synchronized) {
// Inject CAPSLOCK and/or NUMLOCK if necessary to synchronize // Inject CAPSLOCK and/or NUMLOCK if necessary to synchronize
// keyboard state // keyboard state
if (push_mod_lock_state(kb, event->keysym.mod)) { if (push_mod_lock_state(kb, event->mods_state)) {
kb->mod_lock_synchronized = true; kb->mod_lock_synchronized = true;
} }
} }
@ -389,15 +387,6 @@ sc_key_processor_process_key(struct sc_key_processor *kp,
} }
} }
static void
sc_key_processor_process_text(struct sc_key_processor *kp,
const SDL_TextInputEvent *event) {
(void) kp;
(void) event;
// Never forward text input via HID (all the keys are injected separately)
}
bool bool
sc_hid_keyboard_init(struct sc_hid_keyboard *kb, struct sc_aoa *aoa) { sc_hid_keyboard_init(struct sc_hid_keyboard *kb, struct sc_aoa *aoa) {
kb->aoa = aoa; kb->aoa = aoa;
@ -417,7 +406,9 @@ sc_hid_keyboard_init(struct sc_hid_keyboard *kb, struct sc_aoa *aoa) {
static const struct sc_key_processor_ops ops = { static const struct sc_key_processor_ops ops = {
.process_key = sc_key_processor_process_key, .process_key = sc_key_processor_process_key,
.process_text = sc_key_processor_process_text, // Never forward text input via HID (all the keys are injected
// separately)
.process_text = NULL,
}; };
// Clipboard synchronization is requested over the control socket, while HID // Clipboard synchronization is requested over the control socket, while HID

265
app/src/hid_mouse.c Normal file
View File

@ -0,0 +1,265 @@
#include "hid_mouse.h"
#include <assert.h>
#include "input_events.h"
#include "util/log.h"
/** Downcast mouse processor to hid_mouse */
#define DOWNCAST(MP) container_of(MP, struct sc_hid_mouse, mouse_processor)
#define HID_MOUSE_ACCESSORY_ID 2
// 1 byte for buttons + padding, 1 byte for X position, 1 byte for Y position
#define HID_MOUSE_EVENT_SIZE 4
/**
* Mouse descriptor from the specification:
* <https://www.usb.org/sites/default/files/hid1_11.pdf>
*
* Appendix E (p71): §E.10 Report Descriptor (Mouse)
*
* The usage tags (like Wheel) are listed in "HID Usage Tables":
* <https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf>
* §4 Generic Desktop Page (0x01) (p26)
*/
static const unsigned char mouse_report_desc[] = {
// Usage Page (Generic Desktop)
0x05, 0x01,
// Usage (Mouse)
0x09, 0x02,
// Collection (Application)
0xA1, 0x01,
// Usage (Pointer)
0x09, 0x01,
// Collection (Physical)
0xA1, 0x00,
// Usage Page (Buttons)
0x05, 0x09,
// Usage Minimum (1)
0x19, 0x01,
// Usage Maximum (5)
0x29, 0x05,
// Logical Minimum (0)
0x15, 0x00,
// Logical Maximum (1)
0x25, 0x01,
// Report Count (5)
0x95, 0x05,
// Report Size (1)
0x75, 0x01,
// Input (Data, Variable, Absolute): 5 buttons bits
0x81, 0x02,
// Report Count (1)
0x95, 0x01,
// Report Size (3)
0x75, 0x03,
// Input (Constant): 3 bits padding
0x81, 0x01,
// Usage Page (Generic Desktop)
0x05, 0x01,
// Usage (X)
0x09, 0x30,
// Usage (Y)
0x09, 0x31,
// Usage (Wheel)
0x09, 0x38,
// Local Minimum (-127)
0x15, 0x81,
// Local Maximum (127)
0x25, 0x7F,
// Report Size (8)
0x75, 0x08,
// Report Count (3)
0x95, 0x03,
// Input (Data, Variable, Relative): 3 position bytes (X, Y, Wheel)
0x81, 0x06,
// End Collection
0xC0,
// End Collection
0xC0,
};
/**
* A mouse HID event is 3 bytes long:
*
* - byte 0: buttons state
* - byte 1: relative x motion (signed byte from -127 to 127)
* - byte 2: relative y motion (signed byte from -127 to 127)
*
* 7 6 5 4 3 2 1 0
* +---------------+
* byte 0: |0 0 0 . . . . .| buttons state
* +---------------+
* ^ ^ ^ ^ ^
* | | | | `- left button
* | | | `--- right button
* | | `----- middle button
* | `------- button 4
* `--------- button 5
*
* +---------------+
* byte 1: |. . . . . . . .| relative x motion
* +---------------+
* byte 2: |. . . . . . . .| relative y motion
* +---------------+
* byte 3: |. . . . . . . .| wheel motion (-1, 0 or 1)
* +---------------+
*
* As an example, here is the report for a motion of (x=5, y=-4) with left
* button pressed:
*
* +---------------+
* |0 0 0 0 0 0 0 1| left button pressed
* +---------------+
* |0 0 0 0 0 1 0 1| horizontal motion (x = 5)
* +---------------+
* |1 1 1 1 1 1 0 0| relative y motion (y = -4)
* +---------------+
* |0 0 0 0 0 0 0 0| wheel motion
* +---------------+
*/
static bool
sc_hid_mouse_event_init(struct sc_hid_event *hid_event) {
unsigned char *buffer = calloc(1, HID_MOUSE_EVENT_SIZE);
if (!buffer) {
LOG_OOM();
return false;
}
sc_hid_event_init(hid_event, HID_MOUSE_ACCESSORY_ID, buffer,
HID_MOUSE_EVENT_SIZE);
return true;
}
static unsigned char
buttons_state_to_hid_buttons(uint8_t buttons_state) {
unsigned char c = 0;
if (buttons_state & SC_MOUSE_BUTTON_LEFT) {
c |= 1 << 0;
}
if (buttons_state & SC_MOUSE_BUTTON_RIGHT) {
c |= 1 << 1;
}
if (buttons_state & SC_MOUSE_BUTTON_MIDDLE) {
c |= 1 << 2;
}
if (buttons_state & SC_MOUSE_BUTTON_X1) {
c |= 1 << 3;
}
if (buttons_state & SC_MOUSE_BUTTON_X2) {
c |= 1 << 4;
}
return c;
}
static void
sc_mouse_processor_process_mouse_motion(struct sc_mouse_processor *mp,
const struct sc_mouse_motion_event *event) {
struct sc_hid_mouse *mouse = DOWNCAST(mp);
struct sc_hid_event hid_event;
if (!sc_hid_mouse_event_init(&hid_event)) {
return;
}
unsigned char *buffer = hid_event.buffer;
buffer[0] = buttons_state_to_hid_buttons(event->buttons_state);
buffer[1] = CLAMP(event->xrel, -127, 127);
buffer[2] = CLAMP(event->yrel, -127, 127);
buffer[3] = 0; // wheel coordinates only used for scrolling
if (!sc_aoa_push_hid_event(mouse->aoa, &hid_event)) {
sc_hid_event_destroy(&hid_event);
LOGW("Could request HID event");
}
}
static void
sc_mouse_processor_process_mouse_click(struct sc_mouse_processor *mp,
const struct sc_mouse_click_event *event) {
struct sc_hid_mouse *mouse = DOWNCAST(mp);
struct sc_hid_event hid_event;
if (!sc_hid_mouse_event_init(&hid_event)) {
return;
}
unsigned char *buffer = hid_event.buffer;
buffer[0] = buttons_state_to_hid_buttons(event->buttons_state);
buffer[1] = 0; // no x motion
buffer[2] = 0; // no y motion
buffer[3] = 0; // wheel coordinates only used for scrolling
if (!sc_aoa_push_hid_event(mouse->aoa, &hid_event)) {
sc_hid_event_destroy(&hid_event);
LOGW("Could request HID event");
}
}
static void
sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp,
const struct sc_mouse_scroll_event *event) {
struct sc_hid_mouse *mouse = DOWNCAST(mp);
struct sc_hid_event hid_event;
if (!sc_hid_mouse_event_init(&hid_event)) {
return;
}
unsigned char *buffer = hid_event.buffer;
buffer[0] = 0; // buttons state irrelevant (and unknown)
buffer[1] = 0; // no x motion
buffer[2] = 0; // no y motion
// In practice, vscroll is always -1, 0 or 1, but in theory other values
// are possible
buffer[3] = CLAMP(event->vscroll, -127, 127);
// Horizontal scrolling ignored
if (!sc_aoa_push_hid_event(mouse->aoa, &hid_event)) {
sc_hid_event_destroy(&hid_event);
LOGW("Could request HID event");
}
}
bool
sc_hid_mouse_init(struct sc_hid_mouse *mouse, struct sc_aoa *aoa) {
mouse->aoa = aoa;
bool ok = sc_aoa_setup_hid(aoa, HID_MOUSE_ACCESSORY_ID, mouse_report_desc,
ARRAY_LEN(mouse_report_desc));
if (!ok) {
LOGW("Register HID mouse failed");
return false;
}
static const struct sc_mouse_processor_ops ops = {
.process_mouse_motion = sc_mouse_processor_process_mouse_motion,
.process_mouse_click = sc_mouse_processor_process_mouse_click,
.process_mouse_scroll = sc_mouse_processor_process_mouse_scroll,
// Touch events not supported (coordinates are not relative)
.process_touch = NULL,
};
mouse->mouse_processor.ops = &ops;
return true;
}
void
sc_hid_mouse_destroy(struct sc_hid_mouse *mouse) {
bool ok = sc_aoa_unregister_hid(mouse->aoa, HID_MOUSE_ACCESSORY_ID);
if (!ok) {
LOGW("Could not unregister HID");
}
}

23
app/src/hid_mouse.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef HID_MOUSE_H
#define HID_MOUSE_H
#include "common.h"
#include <stdbool.h>
#include "aoa_hid.h"
#include "trait/mouse_processor.h"
struct sc_hid_mouse {
struct sc_mouse_processor mouse_processor; // mouse processor trait
struct sc_aoa *aoa;
};
bool
sc_hid_mouse_init(struct sc_hid_mouse *mouse, struct sc_aoa *aoa);
void
sc_hid_mouse_destroy(struct sc_hid_mouse *mouse);
#endif

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

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

View File

@ -3,32 +3,102 @@
#include <assert.h> #include <assert.h>
#include <SDL2/SDL_keycode.h> #include <SDL2/SDL_keycode.h>
#include "input_events.h"
#include "util/log.h" #include "util/log.h"
static const int ACTION_DOWN = 1; static inline uint16_t
static const int ACTION_UP = 1 << 1; sc_mods_state_from_sdl(uint16_t mods_state) {
return mods_state;
}
static inline enum sc_keycode
sc_keycode_from_sdl(SDL_Keycode keycode) {
return (enum sc_keycode) keycode;
}
static inline enum sc_scancode
sc_scancode_from_sdl(SDL_Scancode scancode) {
return (enum sc_scancode) scancode;
}
static inline enum sc_action
sc_action_from_sdl_keyboard_type(uint32_t type) {
assert(type == SDL_KEYDOWN || type == SDL_KEYUP);
if (type == SDL_KEYDOWN) {
return SC_ACTION_DOWN;
}
return SC_ACTION_UP;
}
static inline enum sc_action
sc_action_from_sdl_mousebutton_type(uint32_t type) {
assert(type == SDL_MOUSEBUTTONDOWN || type == SDL_MOUSEBUTTONUP);
if (type == SDL_MOUSEBUTTONDOWN) {
return SC_ACTION_DOWN;
}
return SC_ACTION_UP;
}
static inline enum sc_touch_action
sc_touch_action_from_sdl(uint32_t type) {
assert(type == SDL_FINGERMOTION || type == SDL_FINGERDOWN ||
type == SDL_FINGERUP);
if (type == SDL_FINGERMOTION) {
return SC_TOUCH_ACTION_MOVE;
}
if (type == SDL_FINGERDOWN) {
return SC_TOUCH_ACTION_DOWN;
}
return SC_TOUCH_ACTION_UP;
}
static inline enum sc_mouse_button
sc_mouse_button_from_sdl(uint8_t button) {
if (button >= SDL_BUTTON_LEFT && button <= SDL_BUTTON_X2) {
// SC_MOUSE_BUTTON_* constants are initialized from SDL_BUTTON(index)
return SDL_BUTTON(button);
}
return SC_MOUSE_BUTTON_UNKNOWN;
}
static inline uint8_t
sc_mouse_buttons_state_from_sdl(uint32_t buttons_state,
bool forward_all_clicks) {
assert(buttons_state < 0x100); // fits in uint8_t
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;
}
#define SC_SDL_SHORTCUT_MODS_MASK (KMOD_CTRL | KMOD_ALT | KMOD_GUI) #define SC_SDL_SHORTCUT_MODS_MASK (KMOD_CTRL | KMOD_ALT | KMOD_GUI)
static inline uint16_t static inline uint16_t
to_sdl_mod(unsigned mod) { to_sdl_mod(unsigned shortcut_mod) {
uint16_t sdl_mod = 0; uint16_t sdl_mod = 0;
if (mod & SC_MOD_LCTRL) { if (shortcut_mod & SC_SHORTCUT_MOD_LCTRL) {
sdl_mod |= KMOD_LCTRL; sdl_mod |= KMOD_LCTRL;
} }
if (mod & SC_MOD_RCTRL) { if (shortcut_mod & SC_SHORTCUT_MOD_RCTRL) {
sdl_mod |= KMOD_RCTRL; sdl_mod |= KMOD_RCTRL;
} }
if (mod & SC_MOD_LALT) { if (shortcut_mod & SC_SHORTCUT_MOD_LALT) {
sdl_mod |= KMOD_LALT; sdl_mod |= KMOD_LALT;
} }
if (mod & SC_MOD_RALT) { if (shortcut_mod & SC_SHORTCUT_MOD_RALT) {
sdl_mod |= KMOD_RALT; sdl_mod |= KMOD_RALT;
} }
if (mod & SC_MOD_LSUPER) { if (shortcut_mod & SC_SHORTCUT_MOD_LSUPER) {
sdl_mod |= KMOD_LGUI; sdl_mod |= KMOD_LGUI;
} }
if (mod & SC_MOD_RSUPER) { if (shortcut_mod & SC_SHORTCUT_MOD_RSUPER) {
sdl_mod |= KMOD_RGUI; sdl_mod |= KMOD_RGUI;
} }
return sdl_mod; return sdl_mod;
@ -89,85 +159,72 @@ input_manager_init(struct input_manager *im, struct controller *controller,
static void static void
send_keycode(struct controller *controller, enum android_keycode keycode, send_keycode(struct controller *controller, enum android_keycode keycode,
int actions, const char *name) { enum sc_action action, const char *name) {
// send DOWN event // send DOWN event
struct control_msg msg; struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_INJECT_KEYCODE; msg.type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
msg.inject_keycode.action = action == SC_ACTION_DOWN
? AKEY_EVENT_ACTION_DOWN
: AKEY_EVENT_ACTION_UP;
msg.inject_keycode.keycode = keycode; msg.inject_keycode.keycode = keycode;
msg.inject_keycode.metastate = 0; msg.inject_keycode.metastate = 0;
msg.inject_keycode.repeat = 0; msg.inject_keycode.repeat = 0;
if (actions & ACTION_DOWN) { if (!controller_push_msg(controller, &msg)) {
msg.inject_keycode.action = AKEY_EVENT_ACTION_DOWN; LOGW("Could not request 'inject %s'", name);
if (!controller_push_msg(controller, &msg)) { return;
LOGW("Could not request 'inject %s (DOWN)'", name);
return;
}
}
if (actions & ACTION_UP) {
msg.inject_keycode.action = AKEY_EVENT_ACTION_UP;
if (!controller_push_msg(controller, &msg)) {
LOGW("Could not request 'inject %s (UP)'", name);
}
} }
} }
static inline void static inline void
action_home(struct controller *controller, int actions) { action_home(struct controller *controller, enum sc_action action) {
send_keycode(controller, AKEYCODE_HOME, actions, "HOME"); send_keycode(controller, AKEYCODE_HOME, action, "HOME");
} }
static inline void static inline void
action_back(struct controller *controller, int actions) { action_back(struct controller *controller, enum sc_action action) {
send_keycode(controller, AKEYCODE_BACK, actions, "BACK"); send_keycode(controller, AKEYCODE_BACK, action, "BACK");
} }
static inline void static inline void
action_app_switch(struct controller *controller, int actions) { action_app_switch(struct controller *controller, enum sc_action action) {
send_keycode(controller, AKEYCODE_APP_SWITCH, actions, "APP_SWITCH"); send_keycode(controller, AKEYCODE_APP_SWITCH, action, "APP_SWITCH");
} }
static inline void static inline void
action_power(struct controller *controller, int actions) { action_power(struct controller *controller, enum sc_action action) {
send_keycode(controller, AKEYCODE_POWER, actions, "POWER"); send_keycode(controller, AKEYCODE_POWER, action, "POWER");
} }
static inline void static inline void
action_volume_up(struct controller *controller, int actions) { action_volume_up(struct controller *controller, enum sc_action action) {
send_keycode(controller, AKEYCODE_VOLUME_UP, actions, "VOLUME_UP"); send_keycode(controller, AKEYCODE_VOLUME_UP, action, "VOLUME_UP");
} }
static inline void static inline void
action_volume_down(struct controller *controller, int actions) { action_volume_down(struct controller *controller, enum sc_action action) {
send_keycode(controller, AKEYCODE_VOLUME_DOWN, actions, "VOLUME_DOWN"); send_keycode(controller, AKEYCODE_VOLUME_DOWN, action, "VOLUME_DOWN");
} }
static inline void static inline void
action_menu(struct controller *controller, int actions) { action_menu(struct controller *controller, enum sc_action action) {
send_keycode(controller, AKEYCODE_MENU, actions, "MENU"); send_keycode(controller, AKEYCODE_MENU, action, "MENU");
} }
// turn the screen on if it was off, press BACK otherwise // turn the screen on if it was off, press BACK otherwise
// If the screen is off, it is turned on only on ACTION_DOWN // If the screen is off, it is turned on only on ACTION_DOWN
static void static void
press_back_or_turn_screen_on(struct controller *controller, int actions) { press_back_or_turn_screen_on(struct controller *controller,
enum sc_action action) {
struct control_msg msg; struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON; msg.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON;
msg.back_or_screen_on.action = action == SC_ACTION_DOWN
? AKEY_EVENT_ACTION_DOWN
: AKEY_EVENT_ACTION_UP;
if (actions & ACTION_DOWN) { if (!controller_push_msg(controller, &msg)) {
msg.back_or_screen_on.action = AKEY_EVENT_ACTION_DOWN; LOGW("Could not request 'press back or turn screen on'");
if (!controller_push_msg(controller, &msg)) { return;
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'");
}
} }
} }
@ -329,12 +386,21 @@ rotate_client_right(struct screen *screen) {
static void static void
input_manager_process_text_input(struct input_manager *im, input_manager_process_text_input(struct input_manager *im,
const SDL_TextInputEvent *event) { const SDL_TextInputEvent *event) {
if (!im->kp->ops->process_text) {
// The key processor does not support text input
return;
}
if (is_shortcut_mod(im, SDL_GetModState())) { if (is_shortcut_mod(im, SDL_GetModState())) {
// A shortcut must never generate text events // A shortcut must never generate text events
return; return;
} }
im->kp->ops->process_text(im->kp, event); struct sc_text_event evt = {
.text = event->text,
};
im->kp->ops->process_text(im->kp, &evt);
} }
static bool static bool
@ -396,7 +462,7 @@ input_manager_process_key(struct input_manager *im,
// The shortcut modifier is pressed // The shortcut modifier is pressed
if (smod) { if (smod) {
int action = down ? ACTION_DOWN : ACTION_UP; enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
switch (keycode) { switch (keycode) {
case SDLK_h: case SDLK_h:
if (control && !shift && !repeat) { if (control && !shift && !repeat) {
@ -553,26 +619,50 @@ input_manager_process_key(struct input_manager *im,
} }
} }
im->kp->ops->process_key(im->kp, event, ack_to_wait); struct sc_key_event evt = {
.action = sc_action_from_sdl_keyboard_type(event->type),
.keycode = sc_keycode_from_sdl(event->keysym.sym),
.scancode = sc_scancode_from_sdl(event->keysym.scancode),
.repeat = event->repeat,
.mods_state = sc_mods_state_from_sdl(event->keysym.mod),
};
assert(im->kp->ops->process_key);
im->kp->ops->process_key(im->kp, &evt, ack_to_wait);
} }
static void static void
input_manager_process_mouse_motion(struct input_manager *im, input_manager_process_mouse_motion(struct input_manager *im,
const SDL_MouseMotionEvent *event) { const SDL_MouseMotionEvent *event) {
uint32_t mask = SDL_BUTTON_LMASK; uint32_t mask = SDL_BUTTON_LMASK;
if (im->forward_all_clicks) { if (im->forward_all_clicks) {
mask |= SDL_BUTTON_MMASK | SDL_BUTTON_RMASK; mask |= SDL_BUTTON_MMASK | SDL_BUTTON_RMASK;
} }
if (!(event->state & mask)) { //if (!(event->state & mask)) {
// do not send motion events when no click is pressed // // do not send motion events when no click is pressed
return; // return;
} //}
if (event->which == SDL_TOUCH_MOUSEID) { if (event->which == SDL_TOUCH_MOUSEID) {
// simulated from touch events, so it's a duplicate // simulated from touch events, so it's a duplicate
return; return;
} }
im->mp->ops->process_mouse_motion(im->mp, event); struct sc_mouse_motion_event evt = {
.position = {
.screen_size = im->screen->frame_size,
.point = screen_convert_window_to_frame_coords(im->screen,
event->x, event->y),
},
.xrel = event->xrel,
.yrel = event->yrel,
.buttons_state =
sc_mouse_buttons_state_from_sdl(event->state,
im->forward_all_clicks),
};
assert(im->mp->ops->process_mouse_motion);
im->mp->ops->process_mouse_motion(im->mp, &evt);
if (im->vfinger_down) { if (im->vfinger_down) {
struct sc_point mouse = struct sc_point mouse =
@ -586,7 +676,30 @@ input_manager_process_mouse_motion(struct input_manager *im,
static void static void
input_manager_process_touch(struct input_manager *im, input_manager_process_touch(struct input_manager *im,
const SDL_TouchFingerEvent *event) { const SDL_TouchFingerEvent *event) {
im->mp->ops->process_touch(im->mp, event); if (!im->mp->ops->process_touch) {
// The mouse processor does not support touch events
return;
}
int dw;
int dh;
SDL_GL_GetDrawableSize(im->screen->window, &dw, &dh);
// SDL touch event coordinates are normalized in the range [0; 1]
int32_t x = event->x * dw;
int32_t y = event->y * dh;
struct sc_touch_event evt = {
.position = {
.screen_size = im->screen->frame_size,
.point = screen_convert_drawable_to_frame_coords(im->screen, x, y),
},
.action = sc_touch_action_from_sdl(event->type),
.pointer_id = event->fingerId,
.pressure = event->pressure,
};
im->mp->ops->process_touch(im->mp, &evt);
} }
static void static void
@ -601,7 +714,7 @@ input_manager_process_mouse_button(struct input_manager *im,
bool down = event->type == SDL_MOUSEBUTTONDOWN; bool down = event->type == SDL_MOUSEBUTTONDOWN;
if (!im->forward_all_clicks) { if (!im->forward_all_clicks) {
int action = down ? ACTION_DOWN : ACTION_UP; enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
if (control && event->button == SDL_BUTTON_X1) { if (control && event->button == SDL_BUTTON_X1) {
action_app_switch(im->controller, action); action_app_switch(im->controller, action);
@ -646,7 +759,29 @@ input_manager_process_mouse_button(struct input_manager *im,
return; return;
} }
im->mp->ops->process_mouse_button(im->mp, event); uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL);
struct sc_mouse_click_event evt = {
.position = {
.screen_size = im->screen->frame_size,
.point = screen_convert_window_to_frame_coords(im->screen, event->x,
event->y),
},
.action = sc_action_from_sdl_mousebutton_type(event->type),
.button = sc_mouse_button_from_sdl(event->button),
.buttons_state =
sc_mouse_buttons_state_from_sdl(sdl_buttons_state,
im->forward_all_clicks),
};
assert(im->mp->ops->process_mouse_click);
im->mp->ops->process_mouse_click(im->mp, &evt);
if (im->mp->relative_mode) {
assert(!im->vfinger_down); // vfinger must not be used in relative mode
// No pinch-to-zoom simulation
return;
}
// Pinch-to-zoom simulation. // Pinch-to-zoom simulation.
// //
@ -677,7 +812,29 @@ input_manager_process_mouse_button(struct input_manager *im,
static void static void
input_manager_process_mouse_wheel(struct input_manager *im, input_manager_process_mouse_wheel(struct input_manager *im,
const SDL_MouseWheelEvent *event) { const SDL_MouseWheelEvent *event) {
im->mp->ops->process_mouse_wheel(im->mp, event); if (!im->mp->ops->process_mouse_scroll) {
// The mouse processor does not support scroll events
return;
}
// mouse_x and mouse_y are expressed in pixels relative to the window
int mouse_x;
int mouse_y;
uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y);
struct sc_mouse_scroll_event evt = {
.position = {
.screen_size = im->screen->frame_size,
.point = screen_convert_window_to_frame_coords(im->screen,
mouse_x, mouse_y),
},
.hscroll = event->x,
.vscroll = event->y,
.buttons_state =
sc_mouse_buttons_state_from_sdl(buttons, im->forward_all_clicks),
};
im->mp->ops->process_mouse_scroll(im->mp, &evt);
} }
bool bool

View File

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

View File

@ -1,11 +1,11 @@
#include "mouse_inject.h" #include "mouse_inject.h"
#include <assert.h> #include <assert.h>
#include <SDL2/SDL_events.h>
#include "android/input.h" #include "android/input.h"
#include "control_msg.h" #include "control_msg.h"
#include "controller.h" #include "controller.h"
#include "input_events.h"
#include "util/intmap.h" #include "util/intmap.h"
#include "util/log.h" #include "util/log.h"
@ -15,210 +15,142 @@
static enum android_motionevent_buttons static enum android_motionevent_buttons
convert_mouse_buttons(uint32_t state) { convert_mouse_buttons(uint32_t state) {
enum android_motionevent_buttons buttons = 0; enum android_motionevent_buttons buttons = 0;
if (state & SDL_BUTTON_LMASK) { if (state & SC_MOUSE_BUTTON_LEFT) {
buttons |= AMOTION_EVENT_BUTTON_PRIMARY; buttons |= AMOTION_EVENT_BUTTON_PRIMARY;
} }
if (state & SDL_BUTTON_RMASK) { if (state & SC_MOUSE_BUTTON_RIGHT) {
buttons |= AMOTION_EVENT_BUTTON_SECONDARY; buttons |= AMOTION_EVENT_BUTTON_SECONDARY;
} }
if (state & SDL_BUTTON_MMASK) { if (state & SC_MOUSE_BUTTON_MIDDLE) {
buttons |= AMOTION_EVENT_BUTTON_TERTIARY; buttons |= AMOTION_EVENT_BUTTON_TERTIARY;
} }
if (state & SDL_BUTTON_X1MASK) { if (state & SC_MOUSE_BUTTON_X1) {
buttons |= AMOTION_EVENT_BUTTON_BACK; buttons |= AMOTION_EVENT_BUTTON_BACK;
} }
if (state & SDL_BUTTON_X2MASK) { if (state & SC_MOUSE_BUTTON_X2) {
buttons |= AMOTION_EVENT_BUTTON_FORWARD; buttons |= AMOTION_EVENT_BUTTON_FORWARD;
} }
return buttons; return buttons;
} }
static bool static enum android_motionevent_action
convert_mouse_action(SDL_EventType from, enum android_motionevent_action *to) { convert_mouse_action(enum sc_action action) {
static const struct sc_intmap_entry actions[] = { if (action == SC_ACTION_DOWN) {
{SDL_MOUSEBUTTONDOWN, AMOTION_EVENT_ACTION_DOWN}, return AMOTION_EVENT_ACTION_DOWN;
{SDL_MOUSEBUTTONUP, AMOTION_EVENT_ACTION_UP},
};
const struct sc_intmap_entry *entry = SC_INTMAP_FIND_ENTRY(actions, from);
if (entry) {
*to = entry->value;
return true;
} }
assert(action == SC_ACTION_UP);
return false; return AMOTION_EVENT_ACTION_UP;
} }
static bool static enum android_motionevent_action
convert_touch_action(SDL_EventType from, enum android_motionevent_action *to) { convert_touch_action(enum sc_touch_action action) {
static const struct sc_intmap_entry actions[] = { switch (action) {
{SDL_FINGERMOTION, AMOTION_EVENT_ACTION_MOVE}, case SC_TOUCH_ACTION_MOVE:
{SDL_FINGERDOWN, AMOTION_EVENT_ACTION_DOWN}, return AMOTION_EVENT_ACTION_MOVE;
{SDL_FINGERUP, AMOTION_EVENT_ACTION_UP}, case SC_TOUCH_ACTION_DOWN:
}; return AMOTION_EVENT_ACTION_DOWN;
default:
const struct sc_intmap_entry *entry = SC_INTMAP_FIND_ENTRY(actions, from); assert(action == SC_TOUCH_ACTION_UP);
if (entry) { return AMOTION_EVENT_ACTION_UP;
*to = entry->value;
return true;
} }
return false;
}
static bool
convert_mouse_motion(const SDL_MouseMotionEvent *from, struct screen *screen,
struct control_msg *to) {
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
to->inject_touch_event.action = AMOTION_EVENT_ACTION_MOVE;
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
to->inject_touch_event.position.screen_size = screen->frame_size;
to->inject_touch_event.position.point =
screen_convert_window_to_frame_coords(screen, from->x, from->y);
to->inject_touch_event.pressure = 1.f;
to->inject_touch_event.buttons = convert_mouse_buttons(from->state);
return true;
}
static bool
convert_touch(const SDL_TouchFingerEvent *from, struct screen *screen,
struct control_msg *to) {
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
if (!convert_touch_action(from->type, &to->inject_touch_event.action)) {
return false;
}
to->inject_touch_event.pointer_id = from->fingerId;
to->inject_touch_event.position.screen_size = screen->frame_size;
int dw;
int dh;
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
// SDL touch event coordinates are normalized in the range [0; 1]
int32_t x = from->x * dw;
int32_t y = from->y * dh;
to->inject_touch_event.position.point =
screen_convert_drawable_to_frame_coords(screen, x, y);
to->inject_touch_event.pressure = from->pressure;
to->inject_touch_event.buttons = 0;
return true;
}
static bool
convert_mouse_button(const SDL_MouseButtonEvent *from, struct screen *screen,
struct control_msg *to) {
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
if (!convert_mouse_action(from->type, &to->inject_touch_event.action)) {
return false;
}
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
to->inject_touch_event.position.screen_size = screen->frame_size;
to->inject_touch_event.position.point =
screen_convert_window_to_frame_coords(screen, from->x, from->y);
to->inject_touch_event.pressure =
from->type == SDL_MOUSEBUTTONDOWN ? 1.f : 0.f;
to->inject_touch_event.buttons =
convert_mouse_buttons(SDL_BUTTON(from->button));
return true;
}
static bool
convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct screen *screen,
struct control_msg *to) {
// mouse_x and mouse_y are expressed in pixels relative to the window
int mouse_x;
int mouse_y;
SDL_GetMouseState(&mouse_x, &mouse_y);
struct sc_position position = {
.screen_size = screen->frame_size,
.point = screen_convert_window_to_frame_coords(screen,
mouse_x, mouse_y),
};
to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT;
to->inject_scroll_event.position = position;
to->inject_scroll_event.hscroll = from->x;
to->inject_scroll_event.vscroll = from->y;
return true;
} }
static void static void
sc_mouse_processor_process_mouse_motion(struct sc_mouse_processor *mp, sc_mouse_processor_process_mouse_motion(struct sc_mouse_processor *mp,
const SDL_MouseMotionEvent *event) { const struct sc_mouse_motion_event *event) {
struct sc_mouse_inject *mi = DOWNCAST(mp); struct sc_mouse_inject *mi = DOWNCAST(mp);
struct control_msg msg; struct control_msg msg = {
if (!convert_mouse_motion(event, mi->screen, &msg)) { .type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
return; .inject_touch_event = {
} .action = AMOTION_EVENT_ACTION_MOVE,
.pointer_id = POINTER_ID_MOUSE,
.position = event->position,
.pressure = 1.f,
.buttons = convert_mouse_buttons(event->buttons_state),
},
};
if (!controller_push_msg(mi->controller, &msg)) { if (!controller_push_msg(mi->controller, &msg)) {
LOGW("Could not request 'inject mouse motion event'"); LOGW("Could not request 'inject mouse motion event'");
} }
} }
static void
sc_mouse_processor_process_mouse_click(struct sc_mouse_processor *mp,
const struct sc_mouse_click_event *event) {
struct sc_mouse_inject *mi = DOWNCAST(mp);
struct control_msg msg = {
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
.inject_touch_event = {
.action = convert_mouse_action(event->action),
.pointer_id = POINTER_ID_MOUSE,
.position = event->position,
.pressure = event->action == SC_ACTION_DOWN ? 1.f : 0.f,
.buttons = convert_mouse_buttons(event->buttons_state),
},
};
if (!controller_push_msg(mi->controller, &msg)) {
LOGW("Could not request 'inject mouse click event'");
}
}
static void
sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp,
const struct sc_mouse_scroll_event *event) {
struct sc_mouse_inject *mi = DOWNCAST(mp);
struct control_msg msg = {
.type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
.inject_scroll_event = {
.position = event->position,
.hscroll = event->hscroll,
.vscroll = event->vscroll,
.buttons = convert_mouse_buttons(event->buttons_state),
},
};
if (!controller_push_msg(mi->controller, &msg)) {
LOGW("Could not request 'inject mouse scroll event'");
}
}
static void static void
sc_mouse_processor_process_touch(struct sc_mouse_processor *mp, sc_mouse_processor_process_touch(struct sc_mouse_processor *mp,
const SDL_TouchFingerEvent *event) { const struct sc_touch_event *event) {
struct sc_mouse_inject *mi = DOWNCAST(mp); struct sc_mouse_inject *mi = DOWNCAST(mp);
struct control_msg msg; struct control_msg msg = {
if (convert_touch(event, mi->screen, &msg)) { .type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
if (!controller_push_msg(mi->controller, &msg)) { .inject_touch_event = {
LOGW("Could not request 'inject touch event'"); .action = convert_touch_action(event->action),
} .pointer_id = event->pointer_id,
} .position = event->position,
} .pressure = event->pressure,
.buttons = 0,
},
};
static void if (!controller_push_msg(mi->controller, &msg)) {
sc_mouse_processor_process_mouse_button(struct sc_mouse_processor *mp, LOGW("Could not request 'inject touch event'");
const SDL_MouseButtonEvent *event) {
struct sc_mouse_inject *mi = DOWNCAST(mp);
struct control_msg msg;
if (convert_mouse_button(event, mi->screen, &msg)) {
if (!controller_push_msg(mi->controller, &msg)) {
LOGW("Could not request 'inject mouse button event'");
}
}
}
static void
sc_mouse_processor_process_mouse_wheel(struct sc_mouse_processor *mp,
const SDL_MouseWheelEvent *event) {
struct sc_mouse_inject *mi = DOWNCAST(mp);
struct control_msg msg;
if (convert_mouse_wheel(event, mi->screen, &msg)) {
if (!controller_push_msg(mi->controller, &msg)) {
LOGW("Could not request 'inject mouse wheel event'");
}
} }
} }
void void
sc_mouse_inject_init(struct sc_mouse_inject *mi, struct controller *controller, sc_mouse_inject_init(struct sc_mouse_inject *mi,
struct screen *screen) { struct controller *controller) {
mi->controller = controller; mi->controller = controller;
mi->screen = screen;
static const struct sc_mouse_processor_ops ops = { static const struct sc_mouse_processor_ops ops = {
.process_mouse_motion = sc_mouse_processor_process_mouse_motion, .process_mouse_motion = sc_mouse_processor_process_mouse_motion,
.process_mouse_click = sc_mouse_processor_process_mouse_click,
.process_mouse_scroll = sc_mouse_processor_process_mouse_scroll,
.process_touch = sc_mouse_processor_process_touch, .process_touch = sc_mouse_processor_process_touch,
.process_mouse_button = sc_mouse_processor_process_mouse_button,
.process_mouse_wheel = sc_mouse_processor_process_mouse_wheel,
}; };
mi->mouse_processor.ops = &ops; mi->mouse_processor.ops = &ops;
mi->mouse_processor.relative_mode = false;
} }

View File

@ -13,11 +13,9 @@ struct sc_mouse_inject {
struct sc_mouse_processor mouse_processor; // mouse processor trait struct sc_mouse_processor mouse_processor; // mouse processor trait
struct controller *controller; struct controller *controller;
struct screen *screen;
}; };
void void
sc_mouse_inject_init(struct sc_mouse_inject *mi, struct controller *controller, sc_mouse_inject_init(struct sc_mouse_inject *mi, struct controller *controller);
struct screen *screen);
#endif #endif

View File

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

View File

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

View File

@ -20,6 +20,7 @@
#include "input_manager.h" #include "input_manager.h"
#ifdef HAVE_AOA_HID #ifdef HAVE_AOA_HID
# include "hid_keyboard.h" # include "hid_keyboard.h"
# include "hid_mouse.h"
#endif #endif
#include "keyboard_inject.h" #include "keyboard_inject.h"
#include "mouse_inject.h" #include "mouse_inject.h"
@ -56,7 +57,12 @@ struct scrcpy {
struct sc_hid_keyboard keyboard_hid; struct sc_hid_keyboard keyboard_hid;
#endif #endif
}; };
struct sc_mouse_inject mouse_inject; union {
struct sc_mouse_inject mouse_inject;
#ifdef HAVE_AOA_HID
struct sc_hid_mouse mouse_hid;
#endif
};
struct input_manager input_manager; struct input_manager input_manager;
}; };
@ -581,8 +587,9 @@ aoa_hid_end:
kp = &s->keyboard_inject.key_processor; kp = &s->keyboard_inject.key_processor;
} }
sc_mouse_inject_init(&s->mouse_inject, &s->controller, &s->screen); //sc_mouse_inject_init(&s->mouse_inject, &s->controller);
mp = &s->mouse_inject.mouse_processor; sc_hid_mouse_init(&s->mouse_hid, &s->aoa);
mp = &s->mouse_hid.mouse_processor;
} }
input_manager_init(&s->input_manager, &s->controller, &s->screen, kp, mp, input_manager_init(&s->input_manager, &s->controller, &s->screen, kp, mp,

View File

@ -485,6 +485,10 @@ screen_init(struct screen *screen, const struct screen_params *params) {
SDL_AddEventWatch(event_watcher, screen); SDL_AddEventWatch(event_watcher, screen);
#endif #endif
if (SDL_SetRelativeMouseMode(true)) {
LOGE("Could not set relative mouse mode: %s", SDL_GetError());
}
static const struct sc_frame_sink_ops ops = { static const struct sc_frame_sink_ops ops = {
.open = screen_frame_sink_open, .open = screen_frame_sink_open,
.close = screen_frame_sink_close, .close = screen_frame_sink_close,

View File

@ -6,7 +6,7 @@
#include <assert.h> #include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <SDL2/SDL_events.h> #include "input_events.h"
/** /**
* Key processor trait. * Key processor trait.
@ -29,20 +29,27 @@ struct sc_key_processor {
struct sc_key_processor_ops { struct sc_key_processor_ops {
/** /**
* Process the keyboard event * Process a keyboard event
* *
* The `sequence` number (if different from `SC_SEQUENCE_INVALID`) indicates * The `sequence` number (if different from `SC_SEQUENCE_INVALID`) indicates
* the acknowledgement number to wait for before injecting this event. * the acknowledgement number to wait for before injecting this event.
* This allows to ensure that the device clipboard is set before injecting * This allows to ensure that the device clipboard is set before injecting
* Ctrl+v on the device. * Ctrl+v on the device.
*
* This function is mandatory.
*/ */
void void
(*process_key)(struct sc_key_processor *kp, const SDL_KeyboardEvent *event, (*process_key)(struct sc_key_processor *kp,
uint64_t ack_to_wait); const struct sc_key_event *event, uint64_t ack_to_wait);
/**
* Process an input text
*
* This function is optional.
*/
void void
(*process_text)(struct sc_key_processor *kp, (*process_text)(struct sc_key_processor *kp,
const SDL_TextInputEvent *event); const struct sc_text_event *event);
}; };
#endif #endif

View File

@ -6,7 +6,7 @@
#include <assert.h> #include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <SDL2/SDL_events.h> #include "input_events.h"
/** /**
* Mouse processor trait. * Mouse processor trait.
@ -16,24 +16,51 @@
*/ */
struct sc_mouse_processor { struct sc_mouse_processor {
const struct sc_mouse_processor_ops *ops; const struct sc_mouse_processor_ops *ops;
/**
* If set, the mouse processor works in relative mode (the absolute
* position is irrelevant). In particular, it indicates that the mouse
* pointer must be "captured" by the UI.
*/
bool relative_mode;
}; };
struct sc_mouse_processor_ops { struct sc_mouse_processor_ops {
/**
* Process a mouse motion event
*
* This function is mandatory.
*/
void void
(*process_mouse_motion)(struct sc_mouse_processor *mp, (*process_mouse_motion)(struct sc_mouse_processor *mp,
const SDL_MouseMotionEvent *event); const struct sc_mouse_motion_event *event);
/**
* Process a mouse click event
*
* This function is mandatory.
*/
void
(*process_mouse_click)(struct sc_mouse_processor *mp,
const struct sc_mouse_click_event *event);
/**
* Process a mouse scroll event
*
* This function is optional.
*/
void
(*process_mouse_scroll)(struct sc_mouse_processor *mp,
const struct sc_mouse_scroll_event *event);
/**
* Process a touch event
*
* This function is optional.
*/
void void
(*process_touch)(struct sc_mouse_processor *mp, (*process_touch)(struct sc_mouse_processor *mp,
const SDL_TouchFingerEvent *event); const struct sc_touch_event *event);
void
(*process_mouse_button)(struct sc_mouse_processor *mp,
const SDL_MouseButtonEvent *event);
void
(*process_mouse_wheel)(struct sc_mouse_processor *mp,
const SDL_MouseWheelEvent *event);
}; };
#endif #endif

View File

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

View File

@ -126,12 +126,13 @@ static void test_serialize_inject_scroll_event(void) {
}, },
.hscroll = 1, .hscroll = 1,
.vscroll = -1, .vscroll = -1,
.buttons = 1,
}, },
}; };
unsigned char buf[CONTROL_MSG_MAX_SIZE]; unsigned char buf[CONTROL_MSG_MAX_SIZE];
size_t size = control_msg_serialize(&msg, buf); size_t size = control_msg_serialize(&msg, buf);
assert(size == 21); assert(size == 25);
const unsigned char expected[] = { const unsigned char expected[] = {
CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT, CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
@ -139,6 +140,7 @@ static void test_serialize_inject_scroll_event(void) {
0x04, 0x38, 0x07, 0x80, // 1080 1920 0x04, 0x38, 0x07, 0x80, // 1080 1920
0x00, 0x00, 0x00, 0x01, // 1 0x00, 0x00, 0x00, 0x01, // 1
0xFF, 0xFF, 0xFF, 0xFF, // -1 0xFF, 0xFF, 0xFF, 0xFF, // -1
0x00, 0x00, 0x00, 0x01, // 1
}; };
assert(!memcmp(buf, expected, sizeof(expected))); assert(!memcmp(buf, expected, sizeof(expected)));
} }

View File

@ -71,12 +71,13 @@ public final class ControlMessage {
return msg; return msg;
} }
public static ControlMessage createInjectScrollEvent(Position position, int hScroll, int vScroll) { public static ControlMessage createInjectScrollEvent(Position position, int hScroll, int vScroll, int buttons) {
ControlMessage msg = new ControlMessage(); ControlMessage msg = new ControlMessage();
msg.type = TYPE_INJECT_SCROLL_EVENT; msg.type = TYPE_INJECT_SCROLL_EVENT;
msg.position = position; msg.position = position;
msg.hScroll = hScroll; msg.hScroll = hScroll;
msg.vScroll = vScroll; msg.vScroll = vScroll;
msg.buttons = buttons;
return msg; return msg;
} }

View File

@ -10,7 +10,7 @@ public class ControlMessageReader {
static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 13; static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 13;
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 = 24;
static final int BACK_OR_SCREEN_ON_LENGTH = 1; static final int BACK_OR_SCREEN_ON_LENGTH = 1;
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_LENGTH = 1; static final int GET_CLIPBOARD_LENGTH = 1;
@ -154,7 +154,8 @@ public class ControlMessageReader {
Position position = readPosition(buffer); Position position = readPosition(buffer);
int hScroll = buffer.getInt(); int hScroll = buffer.getInt();
int vScroll = buffer.getInt(); int vScroll = buffer.getInt();
return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll); int buttons = buffer.getInt();
return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll, buttons);
} }
private ControlMessage parseBackOrScreenOnEvent() { private ControlMessage parseBackOrScreenOnEvent() {

View File

@ -98,7 +98,7 @@ public class Controller {
break; break;
case ControlMessage.TYPE_INJECT_SCROLL_EVENT: case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
if (device.supportsInputEvents()) { if (device.supportsInputEvents()) {
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll()); injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll(), msg.getButtons());
} }
break; break;
case ControlMessage.TYPE_BACK_OR_SCREEN_ON: case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
@ -221,7 +221,7 @@ public class Controller {
return device.injectEvent(event, Device.INJECT_MODE_ASYNC); return device.injectEvent(event, Device.INJECT_MODE_ASYNC);
} }
private boolean injectScroll(Position position, int hScroll, int vScroll) { private boolean injectScroll(Position position, int hScroll, int vScroll, int buttons) {
long now = SystemClock.uptimeMillis(); long now = SystemClock.uptimeMillis();
Point point = device.getPhysicalPoint(position); Point point = device.getPhysicalPoint(position);
if (point == null) { if (point == null) {
@ -239,7 +239,7 @@ public class Controller {
coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll); coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll);
MotionEvent event = MotionEvent MotionEvent event = MotionEvent
.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, 0, 1f, 1f, DEFAULT_DEVICE_ID, 0, .obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0,
InputDevice.SOURCE_MOUSE, 0); InputDevice.SOURCE_MOUSE, 0);
return device.injectEvent(event, Device.INJECT_MODE_ASYNC); return device.injectEvent(event, Device.INJECT_MODE_ASYNC);
} }

View File

@ -128,6 +128,7 @@ public class ControlMessageReaderTest {
dos.writeShort(1920); dos.writeShort(1920);
dos.writeInt(1); dos.writeInt(1);
dos.writeInt(-1); dos.writeInt(-1);
dos.writeInt(1);
byte[] packet = bos.toByteArray(); byte[] packet = bos.toByteArray();
@ -144,6 +145,7 @@ public class ControlMessageReaderTest {
Assert.assertEquals(1920, event.getPosition().getScreenSize().getHeight()); Assert.assertEquals(1920, event.getPosition().getScreenSize().getHeight());
Assert.assertEquals(1, event.getHScroll()); Assert.assertEquals(1, event.getHScroll());
Assert.assertEquals(-1, event.getVScroll()); Assert.assertEquals(-1, event.getVScroll());
Assert.assertEquals(1, event.getButtons());
} }
@Test @Test