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).

PR #4473 <https://github.com/Genymobile/scrcpy/pull/4473>
uhid.38
Romain Vimont 4 months ago
parent f2d6203156
commit 91485e2863

@ -31,6 +31,7 @@ src = [
'src/screen.c', 'src/screen.c',
'src/server.c', 'src/server.c',
'src/version.c', 'src/version.c',
'src/hid/hid_keyboard.c',
'src/trait/frame_source.c', 'src/trait/frame_source.c',
'src/trait/packet_source.c', 'src/trait/packet_source.c',
'src/util/acksync.c', 'src/util/acksync.c',
@ -88,7 +89,7 @@ usb_support = get_option('usb')
if usb_support if usb_support
src += [ src += [
'src/usb/aoa_hid.c', 'src/usb/aoa_hid.c',
'src/usb/hid_keyboard.c', 'src/usb/keyboard_aoa.c',
'src/usb/hid_mouse.c', 'src/usb/hid_mouse.c',
'src/usb/scrcpy_otg.c', 'src/usb/scrcpy_otg.c',
'src/usb/screen_otg.c', 'src/usb/screen_otg.c',

@ -1,40 +1,34 @@
#include "hid_keyboard.h" #include "hid_keyboard.h"
#include <assert.h> #include <string.h>
#include "input_events.h"
#include "util/log.h" #include "util/log.h"
/** Downcast key processor to hid_keyboard */ #define SC_HID_MOD_NONE 0x00
#define DOWNCAST(KP) container_of(KP, struct sc_hid_keyboard, key_processor) #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 SC_HID_KEYBOARD_INDEX_MODS 0
#define SC_HID_KEYBOARD_INDEX_KEYS 2
#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
// USB HID protocol says 6 keys in an event is the requirement for BIOS // 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 // keyboard support, though OS could support more keys via modifying the report
// desc. 6 should be enough for scrcpy. // desc. 6 should be enough for scrcpy.
#define HID_KEYBOARD_MAX_KEYS 6 #define SC_HID_KEYBOARD_MAX_KEYS 6
#define HID_KEYBOARD_EVENT_SIZE \ #define SC_HID_KEYBOARD_EVENT_SIZE \
(HID_KEYBOARD_INDEX_KEYS + HID_KEYBOARD_MAX_KEYS) (SC_HID_KEYBOARD_INDEX_KEYS + SC_HID_KEYBOARD_MAX_KEYS)
#define HID_RESERVED 0x00 #define SC_HID_RESERVED 0x00
#define HID_ERROR_ROLL_OVER 0x01 #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: * The specification is available here:
* <https://www.usb.org/sites/default/files/hid1_11.pdf> * <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). * (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) // Usage Page (Generic Desktop)
0x05, 0x01, 0x05, 0x01,
// Usage (Keyboard) // Usage (Keyboard)
@ -119,7 +113,7 @@ static const unsigned char keyboard_report_desc[] = {
// Report Size (8) // Report Size (8)
0x75, 0x08, 0x75, 0x08,
// Report Count (6) // Report Count (6)
0x95, HID_KEYBOARD_MAX_KEYS, 0x95, SC_HID_KEYBOARD_MAX_KEYS,
// Input (Data, Array): Keys // Input (Data, Array): Keys
0x81, 0x00, 0x81, 0x00,
@ -127,6 +121,9 @@ static const unsigned char keyboard_report_desc[] = {
0xC0 0xC0
}; };
const size_t SC_HID_KEYBOARD_REPORT_DESC_LEN =
sizeof(SC_HID_KEYBOARD_REPORT_DESC);
/** /**
* A keyboard HID event is 8 bytes long: * A keyboard HID event is 8 bytes long:
* *
@ -201,45 +198,50 @@ static const unsigned char keyboard_report_desc[] = {
* +---------------+ * +---------------+
*/ */
static unsigned char static void
sdl_keymod_to_hid_modifiers(uint16_t mod) { sc_hid_keyboard_event_init(struct sc_hid_event *hid_event) {
unsigned char modifiers = HID_MODIFIER_NONE; hid_event->size = SC_HID_KEYBOARD_EVENT_SIZE;
uint8_t *data = hid_event->data;
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) { if (mod & SC_MOD_LCTRL) {
modifiers |= HID_MODIFIER_LEFT_CONTROL; mods |= SC_HID_MOD_LEFT_CONTROL;
} }
if (mod & SC_MOD_LSHIFT) { if (mod & SC_MOD_LSHIFT) {
modifiers |= HID_MODIFIER_LEFT_SHIFT; mods |= SC_HID_MOD_LEFT_SHIFT;
} }
if (mod & SC_MOD_LALT) { if (mod & SC_MOD_LALT) {
modifiers |= HID_MODIFIER_LEFT_ALT; mods |= SC_HID_MOD_LEFT_ALT;
} }
if (mod & SC_MOD_LGUI) { if (mod & SC_MOD_LGUI) {
modifiers |= HID_MODIFIER_LEFT_GUI; mods |= SC_HID_MOD_LEFT_GUI;
} }
if (mod & SC_MOD_RCTRL) { if (mod & SC_MOD_RCTRL) {
modifiers |= HID_MODIFIER_RIGHT_CONTROL; mods |= SC_HID_MOD_RIGHT_CONTROL;
} }
if (mod & SC_MOD_RSHIFT) { if (mod & SC_MOD_RSHIFT) {
modifiers |= HID_MODIFIER_RIGHT_SHIFT; mods |= SC_HID_MOD_RIGHT_SHIFT;
} }
if (mod & SC_MOD_RALT) { if (mod & SC_MOD_RALT) {
modifiers |= HID_MODIFIER_RIGHT_ALT; mods |= SC_HID_MOD_RIGHT_ALT;
} }
if (mod & SC_MOD_RGUI) { if (mod & SC_MOD_RGUI) {
modifiers |= HID_MODIFIER_RIGHT_GUI; mods |= SC_HID_MOD_RIGHT_GUI;
} }
return modifiers; return mods;
} }
static void void
sc_hid_keyboard_event_init(struct sc_hid_event *hid_event) { sc_hid_keyboard_init(struct sc_hid_keyboard *hid) {
hid_event->size = HID_KEYBOARD_EVENT_SIZE; memset(hid->keys, false, SC_HID_KEYBOARD_KEYS);
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);
} }
static inline bool static inline bool
@ -247,10 +249,10 @@ scancode_is_modifier(enum sc_scancode scancode) {
return scancode >= SC_SCANCODE_LCTRL && scancode <= SC_SCANCODE_RGUI; return scancode >= SC_SCANCODE_LCTRL && scancode <= SC_SCANCODE_RGUI;
} }
static bool bool
convert_hid_keyboard_event(struct sc_hid_keyboard *kb, sc_hid_keyboard_event_from_key(struct sc_hid_keyboard *hid,
struct sc_hid_event *hid_event, struct sc_hid_event *hid_event,
const struct sc_key_event *event) { const struct sc_key_event *event) {
enum sc_scancode scancode = event->scancode; enum sc_scancode scancode = event->scancode;
assert(scancode >= 0); assert(scancode >= 0);
@ -264,30 +266,31 @@ convert_hid_keyboard_event(struct sc_hid_keyboard *kb,
sc_hid_keyboard_event_init(hid_event); 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) { if (scancode < SC_HID_KEYBOARD_KEYS) {
// Pressed is true and released is false // 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, 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 // Re-calculate pressed keys every time
int keys_pressed_count = 0; int keys_pressed_count = 0;
for (int i = 0; i < SC_HID_KEYBOARD_KEYS; ++i) { 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 // USB HID protocol says that if keys exceeds report count, a
// phantom state should be reported // phantom state should be reported
if (keys_pressed_count >= HID_KEYBOARD_MAX_KEYS) { if (keys_pressed_count >= SC_HID_KEYBOARD_MAX_KEYS) {
// Phantom state: // Phantom state:
// - Modifiers // - Modifiers
// - Reserved // - Reserved
// - ErrorRollOver * HID_MAX_KEYS // - 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; goto end;
} }
@ -299,120 +302,32 @@ 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->action == SC_ACTION_DOWN ? "down" : "up", event->scancode, event->action == SC_ACTION_DOWN ? "down" : "up", event->scancode,
event->scancode, modifiers); event->scancode, mods);
return true; return true;
} }
bool
static bool sc_hid_keyboard_event_from_mods(struct sc_hid_event *event,
push_mod_lock_state(struct sc_hid_keyboard *kb, uint16_t mods_state) { uint16_t mods_state) {
bool capslock = mods_state & SC_MOD_CAPS; bool capslock = mods_state & SC_MOD_CAPS;
bool numlock = mods_state & SC_MOD_NUM; bool numlock = mods_state & SC_MOD_NUM;
if (!capslock && !numlock) { if (!capslock && !numlock) {
// Nothing to do // Nothing to do
return true; return false;
} }
struct sc_hid_event hid_event; sc_hid_keyboard_event_init(event);
sc_hid_keyboard_event_init(&hid_event);
unsigned i = 0; unsigned i = 0;
if (capslock) { if (capslock) {
hid_event.data[HID_KEYBOARD_INDEX_KEYS + i] = SC_SCANCODE_CAPSLOCK; event->data[SC_HID_KEYBOARD_INDEX_KEYS + i] = SC_SCANCODE_CAPSLOCK;
++i; ++i;
} }
if (numlock) { if (numlock) {
hid_event.data[HID_KEYBOARD_INDEX_KEYS + i] = SC_SCANCODE_NUMLOCK; event->data[SC_HID_KEYBOARD_INDEX_KEYS + i] = SC_SCANCODE_NUMLOCK;
++i; ++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; 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;
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");
}
}

@ -5,8 +5,8 @@
#include <stdbool.h> #include <stdbool.h>
#include "aoa_hid.h" #include "hid/hid_event.h"
#include "trait/key_processor.h" #include "input_events.h"
// See "SDL2/SDL_scancode.h". // See "SDL2/SDL_scancode.h".
// Maybe SDL_Keycode is used by most people, but SDL_Scancode is taken from USB // 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. // 0x65 is Application, typically AT-101 Keyboard ends here.
#define SC_HID_KEYBOARD_KEYS 0x66 #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 * HID keyboard events are sequence-based, every time keyboard state changes
* it sends an array of currently pressed keys, the host is responsible for * it sends an array of currently pressed keys, the host is responsible for
@ -27,18 +30,19 @@
* phantom state. * phantom state.
*/ */
struct sc_hid_keyboard { struct sc_hid_keyboard {
struct sc_key_processor key_processor; // key processor trait
struct sc_aoa *aoa;
bool keys[SC_HID_KEYBOARD_KEYS]; bool keys[SC_HID_KEYBOARD_KEYS];
bool mod_lock_synchronized;
}; };
void
sc_hid_keyboard_init(struct sc_hid_keyboard *hid);
bool 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 bool
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 #endif

@ -27,7 +27,7 @@
#include "server.h" #include "server.h"
#ifdef HAVE_USB #ifdef HAVE_USB
# include "usb/aoa_hid.h" # include "usb/aoa_hid.h"
# include "usb/hid_keyboard.h" # include "usb/keyboard_aoa.h"
# include "usb/hid_mouse.h" # include "usb/hid_mouse.h"
# include "usb/usb.h" # include "usb/usb.h"
#endif #endif
@ -65,7 +65,7 @@ struct scrcpy {
union { union {
struct sc_keyboard_inject keyboard_inject; struct sc_keyboard_inject keyboard_inject;
#ifdef HAVE_USB #ifdef HAVE_USB
struct sc_hid_keyboard keyboard_hid; struct sc_keyboard_aoa keyboard_aoa;
#endif #endif
}; };
union { union {
@ -330,7 +330,7 @@ scrcpy(struct scrcpy_options *options) {
bool audio_demuxer_started = false; bool audio_demuxer_started = false;
#ifdef HAVE_USB #ifdef HAVE_USB
bool aoa_hid_initialized = false; bool aoa_hid_initialized = false;
bool hid_keyboard_initialized = false; bool keyboard_aoa_initialized = false;
bool hid_mouse_initialized = false; bool hid_mouse_initialized = false;
#endif #endif
bool controller_initialized = false; bool controller_initialized = false;
@ -543,11 +543,11 @@ scrcpy(struct scrcpy_options *options) {
if (options->control) { if (options->control) {
#ifdef HAVE_USB #ifdef HAVE_USB
bool use_aoa_keyboard = bool use_keyboard_aoa =
options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AOA; options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AOA;
bool use_aoa_mouse = bool use_aoa_mouse =
options->mouse_input_mode == SC_MOUSE_INPUT_MODE_AOA; 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); bool ok = sc_acksync_init(&s->acksync);
if (!ok) { if (!ok) {
goto end; goto end;
@ -590,10 +590,10 @@ scrcpy(struct scrcpy_options *options) {
goto aoa_hid_end; goto aoa_hid_end;
} }
if (use_aoa_keyboard) { if (use_keyboard_aoa) {
if (sc_hid_keyboard_init(&s->keyboard_hid, &s->aoa)) { if (sc_keyboard_aoa_init(&s->keyboard_aoa, &s->aoa)) {
hid_keyboard_initialized = true; keyboard_aoa_initialized = true;
kp = &s->keyboard_hid.key_processor; kp = &s->keyboard_aoa.key_processor;
} else { } else {
LOGE("Could not initialize HID keyboard"); 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)) { if (!need_aoa || !sc_aoa_start(&s->aoa)) {
sc_acksync_destroy(&s->acksync); sc_acksync_destroy(&s->acksync);
@ -624,9 +624,9 @@ scrcpy(struct scrcpy_options *options) {
aoa_hid_end: aoa_hid_end:
if (!aoa_hid_initialized) { if (!aoa_hid_initialized) {
if (hid_keyboard_initialized) { if (keyboard_aoa_initialized) {
sc_hid_keyboard_destroy(&s->keyboard_hid); sc_keyboard_aoa_destroy(&s->keyboard_aoa);
hid_keyboard_initialized = false; keyboard_aoa_initialized = false;
} }
if (hid_mouse_initialized) { if (hid_mouse_initialized) {
sc_hid_mouse_destroy(&s->mouse_hid); 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)"); LOGE("Fallback to --keyboard=sdk (--keyboard=aoa ignored)");
options->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_SDK; options->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_SDK;
} }
@ -813,8 +813,8 @@ end:
// end-of-stream // end-of-stream
#ifdef HAVE_USB #ifdef HAVE_USB
if (aoa_hid_initialized) { if (aoa_hid_initialized) {
if (hid_keyboard_initialized) { if (keyboard_aoa_initialized) {
sc_hid_keyboard_destroy(&s->keyboard_hid); sc_keyboard_aoa_destroy(&s->keyboard_aoa);
} }
if (hid_mouse_initialized) { if (hid_mouse_initialized) {
sc_hid_mouse_destroy(&s->mouse_hid); sc_hid_mouse_destroy(&s->mouse_hid);

@ -0,0 +1,109 @@
#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;
if (!sc_hid_keyboard_event_from_mods(&hid_event, mods_state)) {
// Nothing to do
return true;
}
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");
}
}

@ -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 scrcpy_otg {
struct sc_usb usb; struct sc_usb usb;
struct sc_aoa aoa; struct sc_aoa aoa;
struct sc_hid_keyboard keyboard; struct sc_keyboard_aoa keyboard;
struct sc_hid_mouse mouse; struct sc_hid_mouse mouse;
struct sc_screen_otg screen_otg; struct sc_screen_otg screen_otg;
@ -73,7 +73,7 @@ scrcpy_otg(struct scrcpy_options *options) {
enum scrcpy_exit_code ret = SCRCPY_EXIT_FAILURE; 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; struct sc_hid_mouse *mouse = NULL;
bool usb_device_initialized = false; bool usb_device_initialized = false;
bool usb_connected = false; bool usb_connected = false;
@ -128,7 +128,7 @@ scrcpy_otg(struct scrcpy_options *options) {
options->mouse_input_mode == SC_MOUSE_INPUT_MODE_AOA; options->mouse_input_mode == SC_MOUSE_INPUT_MODE_AOA;
if (enable_keyboard) { if (enable_keyboard) {
ok = sc_hid_keyboard_init(&s->keyboard, &s->aoa); ok = sc_keyboard_aoa_init(&s->keyboard, &s->aoa);
if (!ok) { if (!ok) {
goto end; goto end;
} }
@ -188,7 +188,7 @@ end:
sc_hid_mouse_destroy(&s->mouse); sc_hid_mouse_destroy(&s->mouse);
} }
if (keyboard) { if (keyboard) {
sc_hid_keyboard_destroy(&s->keyboard); sc_keyboard_aoa_destroy(&s->keyboard);
} }
if (aoa_initialized) { if (aoa_initialized) {

@ -6,11 +6,11 @@
#include <stdbool.h> #include <stdbool.h>
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include "hid_keyboard.h" #include "keyboard_aoa.h"
#include "hid_mouse.h" #include "hid_mouse.h"
struct sc_screen_otg { struct sc_screen_otg {
struct sc_hid_keyboard *keyboard; struct sc_keyboard_aoa *keyboard;
struct sc_hid_mouse *mouse; struct sc_hid_mouse *mouse;
SDL_Window *window; SDL_Window *window;
@ -22,7 +22,7 @@ struct sc_screen_otg {
}; };
struct sc_screen_otg_params { struct sc_screen_otg_params {
struct sc_hid_keyboard *keyboard; struct sc_keyboard_aoa *keyboard;
struct sc_hid_mouse *mouse; struct sc_hid_mouse *mouse;
const char *window_title; const char *window_title;

Loading…
Cancel
Save