Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ enum {
OPT_RAW_KEY_EVENTS,
OPT_NO_DOWNSIZE_ON_ERROR,
OPT_OTG,
OPT_OTG_EMULATE_QWERTY,
OPT_NO_CLEANUP,
OPT_PRINT_FPS,
OPT_NO_POWER_ON,
Expand Down Expand Up @@ -745,6 +746,14 @@ static const struct sc_option options[] = {
"It may only work over USB.\n"
"See --keyboard, --mouse and --gamepad.",
},
{
.longopt_id = OPT_OTG_EMULATE_QWERTY,
.longopt = "otg-emulate-qwerty",
.text = "When running in OTG mode, send the keypress corresponding to "
"the logical layout rather than the physical layout.\n"
"Useful when using an AZERTY, Dvorak or Colemak layout to "
"control a device configured to use QWERTY.",
},
{
.shortopt = 'p',
.longopt = "port",
Expand Down Expand Up @@ -2685,6 +2694,14 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
#else
LOGE("OTG mode (--otg) is disabled.");
return false;
#endif
case OPT_OTG_EMULATE_QWERTY:
#ifdef HAVE_USB
opts->otg_emulate_qwerty = true;
break;
#else
LOGE("OTG mode (--otg) is disabled.");
return false;
#endif
case OPT_V4L2_SINK:
#ifdef HAVE_V4L2
Expand Down
1 change: 1 addition & 0 deletions app/src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const struct scrcpy_options scrcpy_options_default = {
#endif
#ifdef HAVE_USB
.otg = false,
.otg_emulate_qwerty = false,
#endif
.show_touches = false,
.fullscreen = false,
Expand Down
1 change: 1 addition & 0 deletions app/src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ struct scrcpy_options {
#endif
#ifdef HAVE_USB
bool otg;
bool otg_emulate_qwerty;
#endif
bool show_touches;
bool fullscreen;
Expand Down
2 changes: 1 addition & 1 deletion app/src/scrcpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ scrcpy(struct scrcpy_options *options) {

bool aoa_fail = false;
if (use_keyboard_aoa) {
if (sc_keyboard_aoa_init(&s->keyboard_aoa, &s->aoa)) {
if (sc_keyboard_aoa_init(&s->keyboard_aoa, &s->aoa, false)) {
keyboard_aoa_initialized = true;
kp = &s->keyboard_aoa.key_processor;
} else {
Expand Down
8 changes: 8 additions & 0 deletions app/src/trait/key_processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ struct sc_key_processor {
*/
bool hid;

/**
* Set by the implementation to indicate that scancodes should be falsified
* such that keystrokes are sent as though the logical keymap were the
* physical keymap. Useful when using an AZERTY, Dvorak or Colemak layout
* to control a device configured to use QWERTY.
*/
bool use_logical_scancodes;

const struct sc_key_processor_ops *ops;
};

Expand Down
5 changes: 4 additions & 1 deletion app/src/usb/keyboard_aoa.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ sc_key_processor_process_key(struct sc_key_processor *kp,
}

bool
sc_keyboard_aoa_init(struct sc_keyboard_aoa *kb, struct sc_aoa *aoa) {
sc_keyboard_aoa_init(struct sc_keyboard_aoa *kb,
struct sc_aoa *aoa,
bool use_logical_scancodes) {
kb->aoa = aoa;

struct sc_hid_open hid_open;
Expand Down Expand Up @@ -91,6 +93,7 @@ sc_keyboard_aoa_init(struct sc_keyboard_aoa *kb, struct sc_aoa *aoa) {
// to be acknowledged by the device before injecting Ctrl+v.
kb->key_processor.async_paste = true;
kb->key_processor.hid = true;
kb->key_processor.use_logical_scancodes = use_logical_scancodes;
kb->key_processor.ops = &ops;

return true;
Expand Down
4 changes: 3 additions & 1 deletion app/src/usb/keyboard_aoa.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ struct sc_keyboard_aoa {
};

bool
sc_keyboard_aoa_init(struct sc_keyboard_aoa *kb, struct sc_aoa *aoa);
sc_keyboard_aoa_init(struct sc_keyboard_aoa *kb,
struct sc_aoa *aoa,
bool use_logical_scancodes);

void
sc_keyboard_aoa_destroy(struct sc_keyboard_aoa *kb);
Expand Down
2 changes: 1 addition & 1 deletion app/src/usb/scrcpy_otg.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ scrcpy_otg(struct scrcpy_options *options) {
options->gamepad_input_mode == SC_GAMEPAD_INPUT_MODE_AOA;

if (enable_keyboard) {
ok = sc_keyboard_aoa_init(&s->keyboard, &s->aoa);
ok = sc_keyboard_aoa_init(&s->keyboard, &s->aoa, options->otg_emulate_qwerty);
if (!ok) {
goto end;
}
Expand Down
89 changes: 87 additions & 2 deletions app/src/usb/screen_otg.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,101 @@ sc_screen_otg_destroy(struct sc_screen_otg *screen) {
SDL_DestroyWindow(screen->window);
}

static const enum sc_scancode keycode_to_scancode[] = {
/* SDL2 has SDL_GetScancodeFromKey, but that uses the current keymap */

[SC_KEYCODE_RETURN] = SC_SCANCODE_RETURN,
[SC_KEYCODE_ESCAPE] = SC_SCANCODE_ESCAPE,
[SC_KEYCODE_BACKSPACE] = SC_SCANCODE_BACKSPACE,
[SC_KEYCODE_TAB] = SC_SCANCODE_TAB,
[SC_KEYCODE_SPACE] = SC_SCANCODE_SPACE,
[SC_KEYCODE_EXCLAIM] = SC_SCANCODE_1,
[SC_KEYCODE_QUOTEDBL] = SC_SCANCODE_APOSTROPHE,
[SC_KEYCODE_HASH] = SC_SCANCODE_3,
[SC_KEYCODE_PERCENT] = SC_SCANCODE_5,
[SC_KEYCODE_DOLLAR] = SC_SCANCODE_4,
[SC_KEYCODE_AMPERSAND] = SC_SCANCODE_7,
[SC_KEYCODE_QUOTE] = SC_SCANCODE_APOSTROPHE,
[SC_KEYCODE_LEFTPAREN] = SC_SCANCODE_9,
[SC_KEYCODE_RIGHTPAREN] = SC_SCANCODE_0,
[SC_KEYCODE_ASTERISK] = SC_SCANCODE_8,
[SC_KEYCODE_PLUS] = SC_SCANCODE_EQUALS,
[SC_KEYCODE_COMMA] = SC_SCANCODE_COMMA,
[SC_KEYCODE_MINUS] = SC_SCANCODE_MINUS,
[SC_KEYCODE_PERIOD] = SC_SCANCODE_PERIOD,
[SC_KEYCODE_SLASH] = SC_SCANCODE_SLASH,
[SC_KEYCODE_0] = SC_SCANCODE_0,
[SC_KEYCODE_1] = SC_SCANCODE_1,
[SC_KEYCODE_2] = SC_SCANCODE_2,
[SC_KEYCODE_3] = SC_SCANCODE_3,
[SC_KEYCODE_4] = SC_SCANCODE_4,
[SC_KEYCODE_5] = SC_SCANCODE_5,
[SC_KEYCODE_6] = SC_SCANCODE_6,
[SC_KEYCODE_7] = SC_SCANCODE_7,
[SC_KEYCODE_8] = SC_SCANCODE_8,
[SC_KEYCODE_9] = SC_SCANCODE_9,
[SC_KEYCODE_COLON] = SC_SCANCODE_SEMICOLON,
[SC_KEYCODE_SEMICOLON] = SC_SCANCODE_SEMICOLON,
[SC_KEYCODE_LESS] = SC_SCANCODE_COMMA,
[SC_KEYCODE_EQUALS] = SC_SCANCODE_EQUALS,
[SC_KEYCODE_GREATER] = SC_SCANCODE_PERIOD,
[SC_KEYCODE_QUESTION] = SC_SCANCODE_SLASH,
[SC_KEYCODE_AT] = SC_SCANCODE_2,

[SC_KEYCODE_LEFTBRACKET] = SC_SCANCODE_LEFTBRACKET,
[SC_KEYCODE_BACKSLASH] = SC_SCANCODE_BACKSLASH,
[SC_KEYCODE_RIGHTBRACKET] = SC_SCANCODE_RIGHTBRACKET,
[SC_KEYCODE_CARET] = SC_SCANCODE_6,
[SC_KEYCODE_UNDERSCORE] = SC_SCANCODE_MINUS,
[SC_KEYCODE_BACKQUOTE] = SC_SCANCODE_GRAVE,
[SC_KEYCODE_a] = SC_SCANCODE_A,
[SC_KEYCODE_b] = SC_SCANCODE_B,
[SC_KEYCODE_c] = SC_SCANCODE_C,
[SC_KEYCODE_d] = SC_SCANCODE_D,
[SC_KEYCODE_e] = SC_SCANCODE_E,
[SC_KEYCODE_f] = SC_SCANCODE_F,
[SC_KEYCODE_g] = SC_SCANCODE_G,
[SC_KEYCODE_h] = SC_SCANCODE_H,
[SC_KEYCODE_i] = SC_SCANCODE_I,
[SC_KEYCODE_j] = SC_SCANCODE_J,
[SC_KEYCODE_k] = SC_SCANCODE_K,
[SC_KEYCODE_l] = SC_SCANCODE_L,
[SC_KEYCODE_m] = SC_SCANCODE_M,
[SC_KEYCODE_n] = SC_SCANCODE_N,
[SC_KEYCODE_o] = SC_SCANCODE_O,
[SC_KEYCODE_p] = SC_SCANCODE_P,
[SC_KEYCODE_q] = SC_SCANCODE_Q,
[SC_KEYCODE_r] = SC_SCANCODE_R,
[SC_KEYCODE_s] = SC_SCANCODE_S,
[SC_KEYCODE_t] = SC_SCANCODE_T,
[SC_KEYCODE_u] = SC_SCANCODE_U,
[SC_KEYCODE_v] = SC_SCANCODE_V,
[SC_KEYCODE_w] = SC_SCANCODE_W,
[SC_KEYCODE_x] = SC_SCANCODE_X,
[SC_KEYCODE_y] = SC_SCANCODE_Y,
[SC_KEYCODE_z] = SC_SCANCODE_Z,
};

static void
sc_screen_otg_process_key(struct sc_screen_otg *screen,
const SDL_KeyboardEvent *event) {
assert(screen->keyboard);
struct sc_key_processor *kp = &screen->keyboard->key_processor;

enum sc_keycode keycode = sc_keycode_from_sdl(event->keysym.sym);
enum sc_scancode scancode = sc_scancode_from_sdl(event->keysym.scancode);

if (kp->use_logical_scancodes && keycode < ARRAY_LEN(keycode_to_scancode)) {
enum sc_scancode logical_scancode = keycode_to_scancode[keycode];
if (logical_scancode != 0) {
scancode = logical_scancode;
}
}

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),
.keycode = keycode,
.scancode = scancode,
.repeat = event->repeat,
.mods_state = sc_mods_state_from_sdl(event->keysym.mod),
};
Expand Down