diff --git a/README.md b/README.md index 08e47344b0..3d3d0e91a4 100644 --- a/README.md +++ b/README.md @@ -312,6 +312,19 @@ To show physical touches while scrcpy is running: scrcpy -t ``` +For playing games, it may be useful to enable "raw key events" (see [#87] and +[#127]): + +```bash +scrcpy -k +``` + +Note that in this mode, text inputs may not work as expected. As a workaround, +it is still possible to send text using copy-paste. + +[#87]: https://github.com/Genymobile/scrcpy/issues/87 +[#127]: https://github.com/Genymobile/scrcpy/issues/127 + To run without installing: ```bash diff --git a/app/src/convert.c b/app/src/convert.c index 9d947cb348..489d03f0aa 100644 --- a/app/src/convert.c +++ b/app/src/convert.c @@ -70,6 +70,83 @@ static enum android_metastate convert_meta_state(SDL_Keymod mod) { return autocomplete_metastate(metastate); } +// only used if raw_key_events is enabled +static SDL_bool convert_text_keycode(SDL_Keycode from, enum android_keycode *to) { + switch (from) { + MAP(SDLK_SPACE, AKEYCODE_SPACE); + MAP(SDLK_HASH, AKEYCODE_POUND); + MAP(SDLK_QUOTE, AKEYCODE_APOSTROPHE); + MAP(SDLK_ASTERISK, AKEYCODE_STAR); + MAP(SDLK_COMMA, AKEYCODE_COMMA); + MAP(SDLK_MINUS, AKEYCODE_MINUS); + MAP(SDLK_PERIOD, AKEYCODE_PERIOD); + MAP(SDLK_SLASH, AKEYCODE_SLASH); + MAP(SDLK_0, AKEYCODE_0); + MAP(SDLK_1, AKEYCODE_1); + MAP(SDLK_2, AKEYCODE_2); + MAP(SDLK_3, AKEYCODE_3); + MAP(SDLK_4, AKEYCODE_4); + MAP(SDLK_5, AKEYCODE_5); + MAP(SDLK_6, AKEYCODE_6); + MAP(SDLK_7, AKEYCODE_7); + MAP(SDLK_8, AKEYCODE_8); + MAP(SDLK_9, AKEYCODE_9); + MAP(SDLK_SEMICOLON, AKEYCODE_SEMICOLON); + MAP(SDLK_EQUALS, AKEYCODE_EQUALS); + MAP(SDLK_AT, AKEYCODE_AT); + MAP(SDLK_LEFTBRACKET, AKEYCODE_LEFT_BRACKET); + MAP(SDLK_BACKSLASH, AKEYCODE_BACKSLASH); + MAP(SDLK_RIGHTBRACKET, AKEYCODE_RIGHT_BRACKET); + MAP(SDLK_BACKQUOTE, AKEYCODE_GRAVE); + MAP(SDLK_a, AKEYCODE_A); + MAP(SDLK_b, AKEYCODE_B); + MAP(SDLK_c, AKEYCODE_C); + MAP(SDLK_d, AKEYCODE_D); + MAP(SDLK_e, AKEYCODE_E); + MAP(SDLK_f, AKEYCODE_F); + MAP(SDLK_g, AKEYCODE_G); + MAP(SDLK_h, AKEYCODE_H); + MAP(SDLK_i, AKEYCODE_I); + MAP(SDLK_j, AKEYCODE_J); + MAP(SDLK_k, AKEYCODE_K); + MAP(SDLK_l, AKEYCODE_L); + MAP(SDLK_m, AKEYCODE_M); + MAP(SDLK_n, AKEYCODE_N); + MAP(SDLK_o, AKEYCODE_O); + MAP(SDLK_p, AKEYCODE_P); + MAP(SDLK_q, AKEYCODE_Q); + MAP(SDLK_r, AKEYCODE_R); + MAP(SDLK_s, AKEYCODE_S); + MAP(SDLK_t, AKEYCODE_T); + MAP(SDLK_u, AKEYCODE_U); + MAP(SDLK_v, AKEYCODE_V); + MAP(SDLK_w, AKEYCODE_W); + MAP(SDLK_x, AKEYCODE_X); + MAP(SDLK_y, AKEYCODE_Y); + MAP(SDLK_z, AKEYCODE_Z); + MAP(SDLK_KP_ENTER, AKEYCODE_NUMPAD_ENTER); + MAP(SDLK_KP_1, AKEYCODE_NUMPAD_1); + MAP(SDLK_KP_2, AKEYCODE_NUMPAD_2); + MAP(SDLK_KP_3, AKEYCODE_NUMPAD_3); + MAP(SDLK_KP_4, AKEYCODE_NUMPAD_4); + MAP(SDLK_KP_5, AKEYCODE_NUMPAD_5); + MAP(SDLK_KP_6, AKEYCODE_NUMPAD_6); + MAP(SDLK_KP_7, AKEYCODE_NUMPAD_7); + MAP(SDLK_KP_8, AKEYCODE_NUMPAD_8); + MAP(SDLK_KP_9, AKEYCODE_NUMPAD_9); + MAP(SDLK_KP_0, AKEYCODE_NUMPAD_0); + MAP(SDLK_KP_DIVIDE, AKEYCODE_NUMPAD_DIVIDE); + MAP(SDLK_KP_MULTIPLY, AKEYCODE_NUMPAD_MULTIPLY); + MAP(SDLK_KP_MINUS, AKEYCODE_NUMPAD_SUBTRACT); + MAP(SDLK_KP_PLUS, AKEYCODE_NUMPAD_ADD); + MAP(SDLK_KP_PERIOD, AKEYCODE_NUMPAD_DOT); + MAP(SDLK_KP_EQUALS, AKEYCODE_NUMPAD_EQUALS); + MAP(SDLK_KP_LEFTPAREN, AKEYCODE_NUMPAD_LEFT_PAREN); + MAP(SDLK_KP_RIGHTPAREN, AKEYCODE_NUMPAD_RIGHT_PAREN); + FAIL; + } +} + static SDL_bool convert_keycode(SDL_Keycode from, enum android_keycode *to) { switch (from) { MAP(SDLK_RETURN, AKEYCODE_ENTER); @@ -119,7 +196,8 @@ static enum android_motionevent_buttons convert_mouse_buttons(Uint32 state) { } SDL_bool input_key_from_sdl_to_android(const SDL_KeyboardEvent *from, - struct control_event *to) { + struct control_event *to, + SDL_bool raw_key_events) { to->type = CONTROL_EVENT_TYPE_KEYCODE; if (!convert_keycode_action(from->type, &to->keycode_event.action)) { @@ -127,7 +205,10 @@ SDL_bool input_key_from_sdl_to_android(const SDL_KeyboardEvent *from, } if (!convert_keycode(from->keysym.sym, &to->keycode_event.keycode)) { - return SDL_FALSE; + if (!raw_key_events || + !convert_text_keycode(from->keysym.sym, &to->keycode_event.keycode)) { + return SDL_FALSE; + } } to->keycode_event.metastate = convert_meta_state(from->keysym.mod); diff --git a/app/src/convert.h b/app/src/convert.h index 30f0dd3dc6..b4c3b186b6 100644 --- a/app/src/convert.h +++ b/app/src/convert.h @@ -16,7 +16,9 @@ struct complete_mouse_wheel_event { }; SDL_bool input_key_from_sdl_to_android(const SDL_KeyboardEvent *from, - struct control_event *to); + struct control_event *to, + SDL_bool raw_key_events); + SDL_bool mouse_button_from_sdl_to_android(const SDL_MouseButtonEvent *from, struct size screen_size, struct control_event *to); diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 797fd76421..1cbc341ae5 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -129,6 +129,10 @@ static void clipboard_paste(struct controller *controller) { void input_manager_process_text_input(struct input_manager *input_manager, const SDL_TextInputEvent *event) { + if (input_manager->raw_key_events) { + // we will forward the raw key events instead + return; + } struct control_event control_event; control_event.type = CONTROL_EVENT_TYPE_TEXT; control_event.text_event.text = SDL_strdup(event->text); @@ -222,7 +226,8 @@ void input_manager_process_key(struct input_manager *input_manager, } struct control_event control_event; - if (input_key_from_sdl_to_android(event, &control_event)) { + SDL_bool raw_key_events = input_manager->raw_key_events; + if (input_key_from_sdl_to_android(event, &control_event, raw_key_events)) { if (!controller_push_event(input_manager->controller, &control_event)) { LOGW("Cannot send control event"); } diff --git a/app/src/input_manager.h b/app/src/input_manager.h index b9037aa153..2e4cd3646a 100644 --- a/app/src/input_manager.h +++ b/app/src/input_manager.h @@ -11,6 +11,7 @@ struct input_manager { struct controller *controller; struct frames *frames; struct screen *screen; + SDL_bool raw_key_events; }; void input_manager_process_text_input(struct input_manager *input_manager, diff --git a/app/src/main.c b/app/src/main.c index 1317da79a6..6842fc5957 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -14,6 +14,7 @@ struct args { SDL_bool help; SDL_bool version; SDL_bool show_touches; + SDL_bool raw_key_events; Uint16 port; Uint16 max_size; Uint32 bit_rate; @@ -36,6 +37,11 @@ static void usage(const char *arg0) { " (typically, portrait for a phone, landscape for a tablet).\n" " Any --max-size value is computed on the cropped size.\n" "\n" + " -k, --raw-key-events\n" + " Send key events even for text keys, and ignore text input\n" + " events. This is useful when text keys are not used for\n" + " typing text, for example in video games.\n" + "\n" " -h, --help\n" " Print this help.\n" "\n" @@ -198,18 +204,19 @@ static SDL_bool parse_port(char *optarg, Uint16 *port) { static SDL_bool parse_args(struct args *args, int argc, char *argv[]) { static const struct option long_options[] = { - {"bit-rate", required_argument, NULL, 'b'}, - {"crop", required_argument, NULL, 'c'}, - {"help", no_argument, NULL, 'h'}, - {"max-size", required_argument, NULL, 'm'}, - {"port", required_argument, NULL, 'p'}, - {"serial", required_argument, NULL, 's'}, - {"show-touches", no_argument, NULL, 't'}, - {"version", no_argument, NULL, 'v'}, - {NULL, 0, NULL, 0 }, + {"bit-rate", required_argument, NULL, 'b'}, + {"crop", required_argument, NULL, 'c'}, + {"raw-key-events", no_argument, NULL, 'k'}, + {"help", no_argument, NULL, 'h'}, + {"max-size", required_argument, NULL, 'm'}, + {"port", required_argument, NULL, 'p'}, + {"serial", required_argument, NULL, 's'}, + {"show-touches", no_argument, NULL, 't'}, + {"version", no_argument, NULL, 'v'}, + {NULL, 0, NULL, 0 }, }; int c; - while ((c = getopt_long(argc, argv, "b:c:hm:p:s:tv", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "b:c:khm:p:s:tv", long_options, NULL)) != -1) { switch (c) { case 'b': if (!parse_bit_rate(optarg, &args->bit_rate)) { @@ -219,6 +226,9 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) { case 'c': args->crop = optarg; break; + case 'k': + args->raw_key_events = SDL_TRUE; + break; case 'h': args->help = SDL_TRUE; break; @@ -268,6 +278,7 @@ int main(int argc, char *argv[]) { .help = SDL_FALSE, .version = SDL_FALSE, .show_touches = SDL_FALSE, + .raw_key_events = SDL_FALSE, .port = DEFAULT_LOCAL_PORT, .max_size = DEFAULT_MAX_SIZE, .bit_rate = DEFAULT_BIT_RATE, @@ -305,6 +316,7 @@ int main(int argc, char *argv[]) { .max_size = args.max_size, .bit_rate = args.bit_rate, .show_touches = args.show_touches, + .raw_key_events = args.raw_key_events, }; int res = scrcpy(&options) ? 0 : 1; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 3eaa45c0bd..06c550a06a 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -223,6 +223,12 @@ SDL_bool scrcpy(const struct scrcpy_options *options) { show_touches_waited = SDL_TRUE; } + // configure the "raw key events" flag on the input manager + input_manager.raw_key_events = options->raw_key_events; + if (options->raw_key_events) { + LOGI("Raw key events mode enabled"); + } + ret = event_loop(); LOGD("quit..."); diff --git a/app/src/scrcpy.h b/app/src/scrcpy.h index 7ddabf2877..9e5fd051ef 100644 --- a/app/src/scrcpy.h +++ b/app/src/scrcpy.h @@ -10,6 +10,7 @@ struct scrcpy_options { Uint16 max_size; Uint32 bit_rate; SDL_bool show_touches; + SDL_bool raw_key_events; // ignore text input, forward key events instead }; SDL_bool scrcpy(const struct scrcpy_options *options);