mirror of
https://github.com/Genymobile/scrcpy
synced 2024-12-18 05:14:35 +00:00
Extract keyboard HID handling
Split the keyboard implementation using AOA and the code handling HID events, so that HID events can be reused for another protocol (UHID).
This commit is contained in:
parent
ec307a9d0e
commit
112420f99d
@ -87,8 +87,9 @@ endif
|
||||
usb_support = get_option('usb')
|
||||
if usb_support
|
||||
src += [
|
||||
'src/hid/hid_keyboard.c',
|
||||
'src/usb/aoa_hid.c',
|
||||
'src/usb/hid_keyboard.c',
|
||||
'src/usb/keyboard_aoa.c',
|
||||
'src/usb/hid_mouse.c',
|
||||
'src/usb/scrcpy_otg.c',
|
||||
'src/usb/screen_otg.c',
|
||||
|
@ -1,40 +1,34 @@
|
||||
#include "hid_keyboard.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "input_events.h"
|
||||
#include "util/log.h"
|
||||
|
||||
/** Downcast key processor to hid_keyboard */
|
||||
#define DOWNCAST(KP) container_of(KP, struct sc_hid_keyboard, key_processor)
|
||||
#define SC_HID_MOD_NONE 0x00
|
||||
#define SC_HID_MOD_LEFT_CONTROL (1 << 0)
|
||||
#define SC_HID_MOD_LEFT_SHIFT (1 << 1)
|
||||
#define SC_HID_MOD_LEFT_ALT (1 << 2)
|
||||
#define SC_HID_MOD_LEFT_GUI (1 << 3)
|
||||
#define SC_HID_MOD_RIGHT_CONTROL (1 << 4)
|
||||
#define SC_HID_MOD_RIGHT_SHIFT (1 << 5)
|
||||
#define SC_HID_MOD_RIGHT_ALT (1 << 6)
|
||||
#define SC_HID_MOD_RIGHT_GUI (1 << 7)
|
||||
|
||||
#define HID_KEYBOARD_ACCESSORY_ID 1
|
||||
|
||||
#define HID_MODIFIER_NONE 0x00
|
||||
#define HID_MODIFIER_LEFT_CONTROL (1 << 0)
|
||||
#define HID_MODIFIER_LEFT_SHIFT (1 << 1)
|
||||
#define HID_MODIFIER_LEFT_ALT (1 << 2)
|
||||
#define HID_MODIFIER_LEFT_GUI (1 << 3)
|
||||
#define HID_MODIFIER_RIGHT_CONTROL (1 << 4)
|
||||
#define HID_MODIFIER_RIGHT_SHIFT (1 << 5)
|
||||
#define HID_MODIFIER_RIGHT_ALT (1 << 6)
|
||||
#define HID_MODIFIER_RIGHT_GUI (1 << 7)
|
||||
|
||||
#define HID_KEYBOARD_INDEX_MODIFIER 0
|
||||
#define HID_KEYBOARD_INDEX_KEYS 2
|
||||
#define SC_HID_KEYBOARD_INDEX_MODS 0
|
||||
#define SC_HID_KEYBOARD_INDEX_KEYS 2
|
||||
|
||||
// USB HID protocol says 6 keys in an event is the requirement for BIOS
|
||||
// keyboard support, though OS could support more keys via modifying the report
|
||||
// desc. 6 should be enough for scrcpy.
|
||||
#define HID_KEYBOARD_MAX_KEYS 6
|
||||
#define HID_KEYBOARD_EVENT_SIZE \
|
||||
(HID_KEYBOARD_INDEX_KEYS + HID_KEYBOARD_MAX_KEYS)
|
||||
#define SC_HID_KEYBOARD_MAX_KEYS 6
|
||||
#define SC_HID_KEYBOARD_EVENT_SIZE \
|
||||
(SC_HID_KEYBOARD_INDEX_KEYS + SC_HID_KEYBOARD_MAX_KEYS)
|
||||
|
||||
#define HID_RESERVED 0x00
|
||||
#define HID_ERROR_ROLL_OVER 0x01
|
||||
#define SC_HID_RESERVED 0x00
|
||||
#define SC_HID_ERROR_ROLL_OVER 0x01
|
||||
|
||||
/**
|
||||
* For HID over AOAv2, only report descriptor is needed.
|
||||
* For HID, only report descriptor is needed.
|
||||
*
|
||||
* The specification is available here:
|
||||
* <https://www.usb.org/sites/default/files/hid1_11.pdf>
|
||||
@ -53,7 +47,7 @@
|
||||
*
|
||||
* (change vid:pid' to your device's vendor ID and product ID).
|
||||
*/
|
||||
static const unsigned char keyboard_report_desc[] = {
|
||||
const uint8_t SC_HID_KEYBOARD_REPORT_DESC[] = {
|
||||
// Usage Page (Generic Desktop)
|
||||
0x05, 0x01,
|
||||
// Usage (Keyboard)
|
||||
@ -119,7 +113,7 @@ static const unsigned char keyboard_report_desc[] = {
|
||||
// Report Size (8)
|
||||
0x75, 0x08,
|
||||
// Report Count (6)
|
||||
0x95, HID_KEYBOARD_MAX_KEYS,
|
||||
0x95, SC_HID_KEYBOARD_MAX_KEYS,
|
||||
// Input (Data, Array): Keys
|
||||
0x81, 0x00,
|
||||
|
||||
@ -127,6 +121,9 @@ static const unsigned char keyboard_report_desc[] = {
|
||||
0xC0
|
||||
};
|
||||
|
||||
const size_t SC_HID_KEYBOARD_REPORT_DESC_LEN =
|
||||
sizeof(SC_HID_KEYBOARD_REPORT_DESC);
|
||||
|
||||
/**
|
||||
* A keyboard HID event is 8 bytes long:
|
||||
*
|
||||
@ -201,45 +198,50 @@ static const unsigned char keyboard_report_desc[] = {
|
||||
* +---------------+
|
||||
*/
|
||||
|
||||
static unsigned char
|
||||
sdl_keymod_to_hid_modifiers(uint16_t mod) {
|
||||
unsigned char modifiers = HID_MODIFIER_NONE;
|
||||
if (mod & SC_MOD_LCTRL) {
|
||||
modifiers |= HID_MODIFIER_LEFT_CONTROL;
|
||||
}
|
||||
if (mod & SC_MOD_LSHIFT) {
|
||||
modifiers |= HID_MODIFIER_LEFT_SHIFT;
|
||||
}
|
||||
if (mod & SC_MOD_LALT) {
|
||||
modifiers |= HID_MODIFIER_LEFT_ALT;
|
||||
}
|
||||
if (mod & SC_MOD_LGUI) {
|
||||
modifiers |= HID_MODIFIER_LEFT_GUI;
|
||||
}
|
||||
if (mod & SC_MOD_RCTRL) {
|
||||
modifiers |= HID_MODIFIER_RIGHT_CONTROL;
|
||||
}
|
||||
if (mod & SC_MOD_RSHIFT) {
|
||||
modifiers |= HID_MODIFIER_RIGHT_SHIFT;
|
||||
}
|
||||
if (mod & SC_MOD_RALT) {
|
||||
modifiers |= HID_MODIFIER_RIGHT_ALT;
|
||||
}
|
||||
if (mod & SC_MOD_RGUI) {
|
||||
modifiers |= HID_MODIFIER_RIGHT_GUI;
|
||||
}
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
static void
|
||||
sc_hid_keyboard_event_init(struct sc_hid_event *hid_event) {
|
||||
hid_event->size = HID_KEYBOARD_EVENT_SIZE;
|
||||
hid_event->size = SC_HID_KEYBOARD_EVENT_SIZE;
|
||||
|
||||
uint8_t *data = hid_event->data;
|
||||
|
||||
data[HID_KEYBOARD_INDEX_MODIFIER] = HID_MODIFIER_NONE;
|
||||
data[1] = HID_RESERVED;
|
||||
memset(&data[HID_KEYBOARD_INDEX_KEYS], 0, HID_KEYBOARD_MAX_KEYS);
|
||||
data[SC_HID_KEYBOARD_INDEX_MODS] = SC_HID_MOD_NONE;
|
||||
data[1] = SC_HID_RESERVED;
|
||||
memset(&data[SC_HID_KEYBOARD_INDEX_KEYS], 0, SC_HID_KEYBOARD_MAX_KEYS);
|
||||
}
|
||||
|
||||
static uint16_t
|
||||
sc_hid_mod_from_sdl_keymod(uint16_t mod) {
|
||||
uint16_t mods = SC_HID_MOD_NONE;
|
||||
if (mod & SC_MOD_LCTRL) {
|
||||
mods |= SC_HID_MOD_LEFT_CONTROL;
|
||||
}
|
||||
if (mod & SC_MOD_LSHIFT) {
|
||||
mods |= SC_HID_MOD_LEFT_SHIFT;
|
||||
}
|
||||
if (mod & SC_MOD_LALT) {
|
||||
mods |= SC_HID_MOD_LEFT_ALT;
|
||||
}
|
||||
if (mod & SC_MOD_LGUI) {
|
||||
mods |= SC_HID_MOD_LEFT_GUI;
|
||||
}
|
||||
if (mod & SC_MOD_RCTRL) {
|
||||
mods |= SC_HID_MOD_RIGHT_CONTROL;
|
||||
}
|
||||
if (mod & SC_MOD_RSHIFT) {
|
||||
mods |= SC_HID_MOD_RIGHT_SHIFT;
|
||||
}
|
||||
if (mod & SC_MOD_RALT) {
|
||||
mods |= SC_HID_MOD_RIGHT_ALT;
|
||||
}
|
||||
if (mod & SC_MOD_RGUI) {
|
||||
mods |= SC_HID_MOD_RIGHT_GUI;
|
||||
}
|
||||
return mods;
|
||||
}
|
||||
|
||||
void
|
||||
sc_hid_keyboard_init(struct sc_hid_keyboard *hid) {
|
||||
memset(hid->keys, false, SC_HID_KEYBOARD_KEYS);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
@ -247,10 +249,10 @@ scancode_is_modifier(enum sc_scancode scancode) {
|
||||
return scancode >= SC_SCANCODE_LCTRL && scancode <= SC_SCANCODE_RGUI;
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_hid_keyboard_event(struct sc_hid_keyboard *kb,
|
||||
struct sc_hid_event *hid_event,
|
||||
const struct sc_key_event *event) {
|
||||
bool
|
||||
sc_hid_keyboard_event_from_key(struct sc_hid_keyboard *hid,
|
||||
struct sc_hid_event *hid_event,
|
||||
const struct sc_key_event *event) {
|
||||
enum sc_scancode scancode = event->scancode;
|
||||
assert(scancode >= 0);
|
||||
|
||||
@ -264,30 +266,31 @@ convert_hid_keyboard_event(struct sc_hid_keyboard *kb,
|
||||
|
||||
sc_hid_keyboard_event_init(hid_event);
|
||||
|
||||
unsigned char modifiers = sdl_keymod_to_hid_modifiers(event->mods_state);
|
||||
uint16_t mods = sc_hid_mod_from_sdl_keymod(event->mods_state);
|
||||
|
||||
if (scancode < SC_HID_KEYBOARD_KEYS) {
|
||||
// Pressed is true and released is false
|
||||
kb->keys[scancode] = (event->action == SC_ACTION_DOWN);
|
||||
hid->keys[scancode] = (event->action == SC_ACTION_DOWN);
|
||||
LOGV("keys[%02x] = %s", scancode,
|
||||
kb->keys[scancode] ? "true" : "false");
|
||||
hid->keys[scancode] ? "true" : "false");
|
||||
}
|
||||
|
||||
hid_event->data[HID_KEYBOARD_INDEX_MODIFIER] = modifiers;
|
||||
hid_event->data[SC_HID_KEYBOARD_INDEX_MODS] = mods;
|
||||
|
||||
unsigned char *keys_data = &hid_event->data[HID_KEYBOARD_INDEX_KEYS];
|
||||
uint8_t *keys_data = &hid_event->data[SC_HID_KEYBOARD_INDEX_KEYS];
|
||||
// Re-calculate pressed keys every time
|
||||
int keys_pressed_count = 0;
|
||||
for (int i = 0; i < SC_HID_KEYBOARD_KEYS; ++i) {
|
||||
if (kb->keys[i]) {
|
||||
if (hid->keys[i]) {
|
||||
// USB HID protocol says that if keys exceeds report count, a
|
||||
// phantom state should be reported
|
||||
if (keys_pressed_count >= HID_KEYBOARD_MAX_KEYS) {
|
||||
if (keys_pressed_count >= SC_HID_KEYBOARD_MAX_KEYS) {
|
||||
// Phantom state:
|
||||
// - Modifiers
|
||||
// - Reserved
|
||||
// - ErrorRollOver * HID_MAX_KEYS
|
||||
memset(keys_data, HID_ERROR_ROLL_OVER, HID_KEYBOARD_MAX_KEYS);
|
||||
memset(keys_data, SC_HID_ERROR_ROLL_OVER,
|
||||
SC_HID_KEYBOARD_MAX_KEYS);
|
||||
goto end;
|
||||
}
|
||||
|
||||
@ -299,120 +302,30 @@ convert_hid_keyboard_event(struct sc_hid_keyboard *kb,
|
||||
end:
|
||||
LOGV("hid keyboard: key %-4s scancode=%02x (%u) mod=%02x",
|
||||
event->action == SC_ACTION_DOWN ? "down" : "up", event->scancode,
|
||||
event->scancode, modifiers);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
push_mod_lock_state(struct sc_hid_keyboard *kb, uint16_t mods_state) {
|
||||
bool capslock = mods_state & SC_MOD_CAPS;
|
||||
bool numlock = mods_state & SC_MOD_NUM;
|
||||
if (!capslock && !numlock) {
|
||||
// Nothing to do
|
||||
return true;
|
||||
}
|
||||
|
||||
struct sc_hid_event hid_event;
|
||||
sc_hid_keyboard_event_init(&hid_event);
|
||||
|
||||
unsigned i = 0;
|
||||
if (capslock) {
|
||||
hid_event.data[HID_KEYBOARD_INDEX_KEYS + i] = SC_SCANCODE_CAPSLOCK;
|
||||
++i;
|
||||
}
|
||||
if (numlock) {
|
||||
hid_event.data[HID_KEYBOARD_INDEX_KEYS + i] = SC_SCANCODE_NUMLOCK;
|
||||
++i;
|
||||
}
|
||||
|
||||
if (!sc_aoa_push_hid_event(kb->aoa, HID_KEYBOARD_ACCESSORY_ID,
|
||||
&hid_event)) {
|
||||
LOGW("Could not request HID event (mod lock state)");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGD("HID keyboard state synchronized");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
sc_key_processor_process_key(struct sc_key_processor *kp,
|
||||
const struct sc_key_event *event,
|
||||
uint64_t ack_to_wait) {
|
||||
if (event->repeat) {
|
||||
// In USB HID protocol, key repeat is handled by the host (Android), so
|
||||
// just ignore key repeat here.
|
||||
return;
|
||||
}
|
||||
|
||||
struct sc_hid_keyboard *kb = DOWNCAST(kp);
|
||||
|
||||
struct sc_hid_event hid_event;
|
||||
// Not all keys are supported, just ignore unsupported keys
|
||||
if (convert_hid_keyboard_event(kb, &hid_event, event)) {
|
||||
if (!kb->mod_lock_synchronized) {
|
||||
// Inject CAPSLOCK and/or NUMLOCK if necessary to synchronize
|
||||
// keyboard state
|
||||
if (push_mod_lock_state(kb, event->mods_state)) {
|
||||
kb->mod_lock_synchronized = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If ack_to_wait is != SC_SEQUENCE_INVALID, then Ctrl+v is pressed, so
|
||||
// clipboard synchronization has been requested. Wait until clipboard
|
||||
// synchronization is acknowledged by the server, otherwise it could
|
||||
// paste the old clipboard content.
|
||||
|
||||
if (!sc_aoa_push_hid_event_with_ack_to_wait(kb->aoa,
|
||||
HID_KEYBOARD_ACCESSORY_ID,
|
||||
&hid_event,
|
||||
ack_to_wait)) {
|
||||
LOGW("Could not request HID event (key)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
sc_hid_keyboard_init(struct sc_hid_keyboard *kb, struct sc_aoa *aoa) {
|
||||
kb->aoa = aoa;
|
||||
|
||||
bool ok = sc_aoa_setup_hid(aoa, HID_KEYBOARD_ACCESSORY_ID,
|
||||
keyboard_report_desc,
|
||||
ARRAY_LEN(keyboard_report_desc));
|
||||
if (!ok) {
|
||||
LOGW("Register HID keyboard failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset all states
|
||||
memset(kb->keys, false, SC_HID_KEYBOARD_KEYS);
|
||||
|
||||
kb->mod_lock_synchronized = false;
|
||||
|
||||
static const struct sc_key_processor_ops ops = {
|
||||
.process_key = sc_key_processor_process_key,
|
||||
// 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
|
||||
// events are sent over AOA, so it must wait for clipboard synchronization
|
||||
// to be acknowledged by the device before injecting Ctrl+v.
|
||||
kb->key_processor.async_paste = true;
|
||||
kb->key_processor.ops = &ops;
|
||||
event->scancode, mods);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
sc_hid_keyboard_destroy(struct sc_hid_keyboard *kb) {
|
||||
// Unregister HID keyboard so the soft keyboard shows again on Android
|
||||
bool ok = sc_aoa_unregister_hid(kb->aoa, HID_KEYBOARD_ACCESSORY_ID);
|
||||
if (!ok) {
|
||||
LOGW("Could not unregister HID keyboard");
|
||||
sc_hid_keyboard_event_from_mods(struct sc_hid_event *event,
|
||||
uint16_t mods_state) {
|
||||
bool capslock = mods_state & SC_MOD_CAPS;
|
||||
bool numlock = mods_state & SC_MOD_NUM;
|
||||
if (!capslock && !numlock) {
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
sc_hid_keyboard_event_init(event);
|
||||
|
||||
unsigned i = 0;
|
||||
if (capslock) {
|
||||
event->data[SC_HID_KEYBOARD_INDEX_KEYS + i] = SC_SCANCODE_CAPSLOCK;
|
||||
++i;
|
||||
}
|
||||
if (numlock) {
|
||||
event->data[SC_HID_KEYBOARD_INDEX_KEYS + i] = SC_SCANCODE_NUMLOCK;
|
||||
++i;
|
||||
}
|
||||
}
|
@ -5,8 +5,8 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "aoa_hid.h"
|
||||
#include "trait/key_processor.h"
|
||||
#include "hid/hid_event.h"
|
||||
#include "input_events.h"
|
||||
|
||||
// See "SDL2/SDL_scancode.h".
|
||||
// Maybe SDL_Keycode is used by most people, but SDL_Scancode is taken from USB
|
||||
@ -14,6 +14,9 @@
|
||||
// 0x65 is Application, typically AT-101 Keyboard ends here.
|
||||
#define SC_HID_KEYBOARD_KEYS 0x66
|
||||
|
||||
extern const uint8_t SC_HID_KEYBOARD_REPORT_DESC[];
|
||||
extern const size_t SC_HID_KEYBOARD_REPORT_DESC_LEN;
|
||||
|
||||
/**
|
||||
* HID keyboard events are sequence-based, every time keyboard state changes
|
||||
* it sends an array of currently pressed keys, the host is responsible for
|
||||
@ -27,18 +30,19 @@
|
||||
* phantom state.
|
||||
*/
|
||||
struct sc_hid_keyboard {
|
||||
struct sc_key_processor key_processor; // key processor trait
|
||||
|
||||
struct sc_aoa *aoa;
|
||||
bool keys[SC_HID_KEYBOARD_KEYS];
|
||||
|
||||
bool mod_lock_synchronized;
|
||||
};
|
||||
|
||||
void
|
||||
sc_hid_keyboard_init(struct sc_hid_keyboard *hid);
|
||||
|
||||
bool
|
||||
sc_hid_keyboard_init(struct sc_hid_keyboard *kb, struct sc_aoa *aoa);
|
||||
sc_hid_keyboard_event_from_key(struct sc_hid_keyboard *hid,
|
||||
struct sc_hid_event *hid_event,
|
||||
const struct sc_key_event *event);
|
||||
|
||||
void
|
||||
sc_hid_keyboard_destroy(struct sc_hid_keyboard *kb);
|
||||
sc_hid_keyboard_event_from_mods(struct sc_hid_event *event,
|
||||
uint16_t mods_state);
|
||||
|
||||
#endif
|
@ -27,7 +27,7 @@
|
||||
#include "server.h"
|
||||
#ifdef HAVE_USB
|
||||
# include "usb/aoa_hid.h"
|
||||
# include "usb/hid_keyboard.h"
|
||||
# include "usb/keyboard_aoa.h"
|
||||
# include "usb/hid_mouse.h"
|
||||
# include "usb/usb.h"
|
||||
#endif
|
||||
@ -65,7 +65,7 @@ struct scrcpy {
|
||||
union {
|
||||
struct sc_keyboard_inject keyboard_inject;
|
||||
#ifdef HAVE_USB
|
||||
struct sc_hid_keyboard keyboard_hid;
|
||||
struct sc_keyboard_aoa keyboard_aoa;
|
||||
#endif
|
||||
};
|
||||
union {
|
||||
@ -330,7 +330,7 @@ scrcpy(struct scrcpy_options *options) {
|
||||
bool audio_demuxer_started = false;
|
||||
#ifdef HAVE_USB
|
||||
bool aoa_hid_initialized = false;
|
||||
bool hid_keyboard_initialized = false;
|
||||
bool keyboard_aoa_initialized = false;
|
||||
bool hid_mouse_initialized = false;
|
||||
#endif
|
||||
bool controller_initialized = false;
|
||||
@ -543,11 +543,11 @@ scrcpy(struct scrcpy_options *options) {
|
||||
|
||||
if (options->control) {
|
||||
#ifdef HAVE_USB
|
||||
bool use_aoa_keyboard =
|
||||
bool use_keyboard_aoa =
|
||||
options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AOA;
|
||||
bool use_aoa_mouse =
|
||||
options->mouse_input_mode == SC_MOUSE_INPUT_MODE_AOA;
|
||||
if (use_aoa_keyboard || use_aoa_mouse) {
|
||||
if (use_keyboard_aoa || use_aoa_mouse) {
|
||||
bool ok = sc_acksync_init(&s->acksync);
|
||||
if (!ok) {
|
||||
goto end;
|
||||
@ -590,10 +590,10 @@ scrcpy(struct scrcpy_options *options) {
|
||||
goto aoa_hid_end;
|
||||
}
|
||||
|
||||
if (use_aoa_keyboard) {
|
||||
if (sc_hid_keyboard_init(&s->keyboard_hid, &s->aoa)) {
|
||||
hid_keyboard_initialized = true;
|
||||
kp = &s->keyboard_hid.key_processor;
|
||||
if (use_keyboard_aoa) {
|
||||
if (sc_keyboard_aoa_init(&s->keyboard_aoa, &s->aoa)) {
|
||||
keyboard_aoa_initialized = true;
|
||||
kp = &s->keyboard_aoa.key_processor;
|
||||
} else {
|
||||
LOGE("Could not initialize HID keyboard");
|
||||
}
|
||||
@ -608,7 +608,7 @@ scrcpy(struct scrcpy_options *options) {
|
||||
}
|
||||
}
|
||||
|
||||
bool need_aoa = hid_keyboard_initialized || hid_mouse_initialized;
|
||||
bool need_aoa = keyboard_aoa_initialized || hid_mouse_initialized;
|
||||
|
||||
if (!need_aoa || !sc_aoa_start(&s->aoa)) {
|
||||
sc_acksync_destroy(&s->acksync);
|
||||
@ -624,9 +624,9 @@ scrcpy(struct scrcpy_options *options) {
|
||||
|
||||
aoa_hid_end:
|
||||
if (!aoa_hid_initialized) {
|
||||
if (hid_keyboard_initialized) {
|
||||
sc_hid_keyboard_destroy(&s->keyboard_hid);
|
||||
hid_keyboard_initialized = false;
|
||||
if (keyboard_aoa_initialized) {
|
||||
sc_keyboard_aoa_destroy(&s->keyboard_aoa);
|
||||
keyboard_aoa_initialized = false;
|
||||
}
|
||||
if (hid_mouse_initialized) {
|
||||
sc_hid_mouse_destroy(&s->mouse_hid);
|
||||
@ -634,7 +634,7 @@ aoa_hid_end:
|
||||
}
|
||||
}
|
||||
|
||||
if (use_aoa_keyboard && !hid_keyboard_initialized) {
|
||||
if (use_keyboard_aoa && !keyboard_aoa_initialized) {
|
||||
LOGE("Fallback to --keyboard=sdk (--keyboard=aoa ignored)");
|
||||
options->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_SDK;
|
||||
}
|
||||
@ -813,8 +813,8 @@ end:
|
||||
// end-of-stream
|
||||
#ifdef HAVE_USB
|
||||
if (aoa_hid_initialized) {
|
||||
if (hid_keyboard_initialized) {
|
||||
sc_hid_keyboard_destroy(&s->keyboard_hid);
|
||||
if (keyboard_aoa_initialized) {
|
||||
sc_keyboard_aoa_destroy(&s->keyboard_aoa);
|
||||
}
|
||||
if (hid_mouse_initialized) {
|
||||
sc_hid_mouse_destroy(&s->mouse_hid);
|
||||
|
106
app/src/usb/keyboard_aoa.c
Normal file
106
app/src/usb/keyboard_aoa.c
Normal file
@ -0,0 +1,106 @@
|
||||
#include "keyboard_aoa.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "input_events.h"
|
||||
#include "util/log.h"
|
||||
|
||||
/** Downcast key processor to keyboard_aoa */
|
||||
#define DOWNCAST(KP) container_of(KP, struct sc_keyboard_aoa, key_processor)
|
||||
|
||||
#define HID_KEYBOARD_ACCESSORY_ID 1
|
||||
|
||||
static bool
|
||||
push_mod_lock_state(struct sc_keyboard_aoa *kb, uint16_t mods_state) {
|
||||
struct sc_hid_event hid_event;
|
||||
sc_hid_keyboard_event_from_mods(&hid_event, mods_state);
|
||||
|
||||
if (!sc_aoa_push_hid_event(kb->aoa, HID_KEYBOARD_ACCESSORY_ID,
|
||||
&hid_event)) {
|
||||
LOGW("Could not request HID event (mod lock state)");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGD("HID keyboard state synchronized");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
sc_key_processor_process_key(struct sc_key_processor *kp,
|
||||
const struct sc_key_event *event,
|
||||
uint64_t ack_to_wait) {
|
||||
if (event->repeat) {
|
||||
// In USB HID protocol, key repeat is handled by the host (Android), so
|
||||
// just ignore key repeat here.
|
||||
return;
|
||||
}
|
||||
|
||||
struct sc_keyboard_aoa *kb = DOWNCAST(kp);
|
||||
|
||||
struct sc_hid_event hid_event;
|
||||
|
||||
// Not all keys are supported, just ignore unsupported keys
|
||||
if (sc_hid_keyboard_event_from_key(&kb->hid, &hid_event, event)) {
|
||||
if (!kb->mod_lock_synchronized) {
|
||||
// Inject CAPSLOCK and/or NUMLOCK if necessary to synchronize
|
||||
// keyboard state
|
||||
if (push_mod_lock_state(kb, event->mods_state)) {
|
||||
kb->mod_lock_synchronized = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If ack_to_wait is != SC_SEQUENCE_INVALID, then Ctrl+v is pressed, so
|
||||
// clipboard synchronization has been requested. Wait until clipboard
|
||||
// synchronization is acknowledged by the server, otherwise it could
|
||||
// paste the old clipboard content.
|
||||
|
||||
if (!sc_aoa_push_hid_event_with_ack_to_wait(kb->aoa,
|
||||
HID_KEYBOARD_ACCESSORY_ID,
|
||||
&hid_event,
|
||||
ack_to_wait)) {
|
||||
LOGW("Could not request HID event (key)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
sc_keyboard_aoa_init(struct sc_keyboard_aoa *kb, struct sc_aoa *aoa) {
|
||||
kb->aoa = aoa;
|
||||
|
||||
bool ok = sc_aoa_setup_hid(aoa, HID_KEYBOARD_ACCESSORY_ID,
|
||||
SC_HID_KEYBOARD_REPORT_DESC,
|
||||
SC_HID_KEYBOARD_REPORT_DESC_LEN);
|
||||
if (!ok) {
|
||||
LOGW("Register HID keyboard failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
sc_hid_keyboard_init(&kb->hid);
|
||||
|
||||
kb->mod_lock_synchronized = false;
|
||||
|
||||
static const struct sc_key_processor_ops ops = {
|
||||
.process_key = sc_key_processor_process_key,
|
||||
// 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
|
||||
// events are sent over AOA, so it must wait for clipboard synchronization
|
||||
// to be acknowledged by the device before injecting Ctrl+v.
|
||||
kb->key_processor.async_paste = true;
|
||||
kb->key_processor.ops = &ops;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
sc_keyboard_aoa_destroy(struct sc_keyboard_aoa *kb) {
|
||||
// Unregister HID keyboard so the soft keyboard shows again on Android
|
||||
bool ok = sc_aoa_unregister_hid(kb->aoa, HID_KEYBOARD_ACCESSORY_ID);
|
||||
if (!ok) {
|
||||
LOGW("Could not unregister HID keyboard");
|
||||
}
|
||||
}
|
27
app/src/usb/keyboard_aoa.h
Normal file
27
app/src/usb/keyboard_aoa.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef SC_KEYBOARD_AOA_H
|
||||
#define SC_KEYBOARD_AOA_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "aoa_hid.h"
|
||||
#include "hid/hid_keyboard.h"
|
||||
#include "trait/key_processor.h"
|
||||
|
||||
struct sc_keyboard_aoa {
|
||||
struct sc_key_processor key_processor; // key processor trait
|
||||
|
||||
struct sc_hid_keyboard hid;
|
||||
struct sc_aoa *aoa;
|
||||
|
||||
bool mod_lock_synchronized;
|
||||
};
|
||||
|
||||
bool
|
||||
sc_keyboard_aoa_init(struct sc_keyboard_aoa *kb, struct sc_aoa *aoa);
|
||||
|
||||
void
|
||||
sc_keyboard_aoa_destroy(struct sc_keyboard_aoa *kb);
|
||||
|
||||
#endif
|
@ -10,7 +10,7 @@
|
||||
struct scrcpy_otg {
|
||||
struct sc_usb usb;
|
||||
struct sc_aoa aoa;
|
||||
struct sc_hid_keyboard keyboard;
|
||||
struct sc_keyboard_aoa keyboard;
|
||||
struct sc_hid_mouse mouse;
|
||||
|
||||
struct sc_screen_otg screen_otg;
|
||||
@ -73,7 +73,7 @@ scrcpy_otg(struct scrcpy_options *options) {
|
||||
|
||||
enum scrcpy_exit_code ret = SCRCPY_EXIT_FAILURE;
|
||||
|
||||
struct sc_hid_keyboard *keyboard = NULL;
|
||||
struct sc_keyboard_aoa *keyboard = NULL;
|
||||
struct sc_hid_mouse *mouse = NULL;
|
||||
bool usb_device_initialized = false;
|
||||
bool usb_connected = false;
|
||||
@ -134,7 +134,7 @@ scrcpy_otg(struct scrcpy_options *options) {
|
||||
}
|
||||
|
||||
if (enable_keyboard) {
|
||||
ok = sc_hid_keyboard_init(&s->keyboard, &s->aoa);
|
||||
ok = sc_keyboard_aoa_init(&s->keyboard, &s->aoa);
|
||||
if (!ok) {
|
||||
goto end;
|
||||
}
|
||||
@ -194,7 +194,7 @@ end:
|
||||
sc_hid_mouse_destroy(&s->mouse);
|
||||
}
|
||||
if (keyboard) {
|
||||
sc_hid_keyboard_destroy(&s->keyboard);
|
||||
sc_keyboard_aoa_destroy(&s->keyboard);
|
||||
}
|
||||
|
||||
if (aoa_initialized) {
|
||||
|
@ -6,11 +6,11 @@
|
||||
#include <stdbool.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "hid_keyboard.h"
|
||||
#include "keyboard_aoa.h"
|
||||
#include "hid_mouse.h"
|
||||
|
||||
struct sc_screen_otg {
|
||||
struct sc_hid_keyboard *keyboard;
|
||||
struct sc_keyboard_aoa *keyboard;
|
||||
struct sc_hid_mouse *mouse;
|
||||
|
||||
SDL_Window *window;
|
||||
@ -22,7 +22,7 @@ struct sc_screen_otg {
|
||||
};
|
||||
|
||||
struct sc_screen_otg_params {
|
||||
struct sc_hid_keyboard *keyboard;
|
||||
struct sc_keyboard_aoa *keyboard;
|
||||
struct sc_hid_mouse *mouse;
|
||||
|
||||
const char *window_title;
|
||||
|
Loading…
Reference in New Issue
Block a user