Extract keyboard processor trait
This will allow to provide alternative key processors.
This commit is contained in:
parent
056d36ce4a
commit
bcf5a9750f
9 changed files with 335 additions and 227 deletions
|
@ -14,6 +14,7 @@ src = [
|
|||
'src/fps_counter.c',
|
||||
'src/frame_buffer.c',
|
||||
'src/input_manager.c',
|
||||
'src/keyboard_inject.c',
|
||||
'src/opengl.c',
|
||||
'src/receiver.c',
|
||||
'src/recorder.c',
|
||||
|
|
|
@ -3,157 +3,6 @@
|
|||
#define MAP(FROM, TO) case FROM: *to = TO; return true
|
||||
#define FAIL default: return false
|
||||
|
||||
bool
|
||||
convert_keycode_action(SDL_EventType from, enum android_keyevent_action *to) {
|
||||
switch (from) {
|
||||
MAP(SDL_KEYDOWN, AKEY_EVENT_ACTION_DOWN);
|
||||
MAP(SDL_KEYUP, AKEY_EVENT_ACTION_UP);
|
||||
FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
static enum android_metastate
|
||||
autocomplete_metastate(enum android_metastate metastate) {
|
||||
// fill dependent flags
|
||||
if (metastate & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
|
||||
metastate |= AMETA_SHIFT_ON;
|
||||
}
|
||||
if (metastate & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
|
||||
metastate |= AMETA_CTRL_ON;
|
||||
}
|
||||
if (metastate & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
|
||||
metastate |= AMETA_ALT_ON;
|
||||
}
|
||||
if (metastate & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
|
||||
metastate |= AMETA_META_ON;
|
||||
}
|
||||
|
||||
return metastate;
|
||||
}
|
||||
|
||||
enum android_metastate
|
||||
convert_meta_state(SDL_Keymod mod) {
|
||||
enum android_metastate metastate = 0;
|
||||
if (mod & KMOD_LSHIFT) {
|
||||
metastate |= AMETA_SHIFT_LEFT_ON;
|
||||
}
|
||||
if (mod & KMOD_RSHIFT) {
|
||||
metastate |= AMETA_SHIFT_RIGHT_ON;
|
||||
}
|
||||
if (mod & KMOD_LCTRL) {
|
||||
metastate |= AMETA_CTRL_LEFT_ON;
|
||||
}
|
||||
if (mod & KMOD_RCTRL) {
|
||||
metastate |= AMETA_CTRL_RIGHT_ON;
|
||||
}
|
||||
if (mod & KMOD_LALT) {
|
||||
metastate |= AMETA_ALT_LEFT_ON;
|
||||
}
|
||||
if (mod & KMOD_RALT) {
|
||||
metastate |= AMETA_ALT_RIGHT_ON;
|
||||
}
|
||||
if (mod & KMOD_LGUI) { // Windows key
|
||||
metastate |= AMETA_META_LEFT_ON;
|
||||
}
|
||||
if (mod & KMOD_RGUI) { // Windows key
|
||||
metastate |= AMETA_META_RIGHT_ON;
|
||||
}
|
||||
if (mod & KMOD_NUM) {
|
||||
metastate |= AMETA_NUM_LOCK_ON;
|
||||
}
|
||||
if (mod & KMOD_CAPS) {
|
||||
metastate |= AMETA_CAPS_LOCK_ON;
|
||||
}
|
||||
if (mod & KMOD_MODE) { // Alt Gr
|
||||
// no mapping?
|
||||
}
|
||||
|
||||
// fill the dependent fields
|
||||
return autocomplete_metastate(metastate);
|
||||
}
|
||||
|
||||
bool
|
||||
convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod,
|
||||
bool prefer_text) {
|
||||
switch (from) {
|
||||
MAP(SDLK_RETURN, AKEYCODE_ENTER);
|
||||
MAP(SDLK_KP_ENTER, AKEYCODE_NUMPAD_ENTER);
|
||||
MAP(SDLK_ESCAPE, AKEYCODE_ESCAPE);
|
||||
MAP(SDLK_BACKSPACE, AKEYCODE_DEL);
|
||||
MAP(SDLK_TAB, AKEYCODE_TAB);
|
||||
MAP(SDLK_PAGEUP, AKEYCODE_PAGE_UP);
|
||||
MAP(SDLK_DELETE, AKEYCODE_FORWARD_DEL);
|
||||
MAP(SDLK_HOME, AKEYCODE_MOVE_HOME);
|
||||
MAP(SDLK_END, AKEYCODE_MOVE_END);
|
||||
MAP(SDLK_PAGEDOWN, AKEYCODE_PAGE_DOWN);
|
||||
MAP(SDLK_RIGHT, AKEYCODE_DPAD_RIGHT);
|
||||
MAP(SDLK_LEFT, AKEYCODE_DPAD_LEFT);
|
||||
MAP(SDLK_DOWN, AKEYCODE_DPAD_DOWN);
|
||||
MAP(SDLK_UP, AKEYCODE_DPAD_UP);
|
||||
MAP(SDLK_LCTRL, AKEYCODE_CTRL_LEFT);
|
||||
MAP(SDLK_RCTRL, AKEYCODE_CTRL_RIGHT);
|
||||
MAP(SDLK_LSHIFT, AKEYCODE_SHIFT_LEFT);
|
||||
MAP(SDLK_RSHIFT, AKEYCODE_SHIFT_RIGHT);
|
||||
}
|
||||
|
||||
if (!(mod & (KMOD_NUM | KMOD_SHIFT))) {
|
||||
// Handle Numpad events when Num Lock is disabled
|
||||
// If SHIFT is pressed, a text event will be sent instead
|
||||
switch(from) {
|
||||
MAP(SDLK_KP_0, AKEYCODE_INSERT);
|
||||
MAP(SDLK_KP_1, AKEYCODE_MOVE_END);
|
||||
MAP(SDLK_KP_2, AKEYCODE_DPAD_DOWN);
|
||||
MAP(SDLK_KP_3, AKEYCODE_PAGE_DOWN);
|
||||
MAP(SDLK_KP_4, AKEYCODE_DPAD_LEFT);
|
||||
MAP(SDLK_KP_6, AKEYCODE_DPAD_RIGHT);
|
||||
MAP(SDLK_KP_7, AKEYCODE_MOVE_HOME);
|
||||
MAP(SDLK_KP_8, AKEYCODE_DPAD_UP);
|
||||
MAP(SDLK_KP_9, AKEYCODE_PAGE_UP);
|
||||
MAP(SDLK_KP_PERIOD, AKEYCODE_FORWARD_DEL);
|
||||
}
|
||||
}
|
||||
|
||||
if (prefer_text && !(mod & KMOD_CTRL)) {
|
||||
// do not forward alpha and space key events (unless Ctrl is pressed)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mod & (KMOD_LALT | KMOD_RALT | KMOD_LGUI | KMOD_RGUI)) {
|
||||
return false;
|
||||
}
|
||||
// if ALT and META are not pressed, also handle letters and space
|
||||
switch (from) {
|
||||
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_SPACE, AKEYCODE_SPACE);
|
||||
FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
enum android_motionevent_buttons
|
||||
convert_mouse_buttons(uint32_t state) {
|
||||
enum android_motionevent_buttons buttons = 0;
|
||||
|
|
|
@ -8,16 +8,6 @@
|
|||
|
||||
#include "control_msg.h"
|
||||
|
||||
bool
|
||||
convert_keycode_action(SDL_EventType from, enum android_keyevent_action *to);
|
||||
|
||||
enum android_metastate
|
||||
convert_meta_state(SDL_Keymod mod);
|
||||
|
||||
bool
|
||||
convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod,
|
||||
bool prefer_text);
|
||||
|
||||
enum android_motionevent_buttons
|
||||
convert_mouse_buttons(uint32_t state);
|
||||
|
||||
|
|
|
@ -53,15 +53,15 @@ is_shortcut_mod(struct input_manager *im, uint16_t sdl_mod) {
|
|||
|
||||
void
|
||||
input_manager_init(struct input_manager *im, struct controller *controller,
|
||||
struct screen *screen,
|
||||
struct screen *screen, struct sc_key_processor *kp,
|
||||
const struct scrcpy_options *options) {
|
||||
assert(!options->control || (kp && kp->ops));
|
||||
|
||||
im->controller = controller;
|
||||
im->screen = screen;
|
||||
im->repeat = 0;
|
||||
im->kp = kp;
|
||||
|
||||
im->control = options->control;
|
||||
im->forward_key_repeat = options->forward_key_repeat;
|
||||
im->prefer_text = options->prefer_text;
|
||||
im->forward_all_clicks = options->forward_all_clicks;
|
||||
im->legacy_paste = options->legacy_paste;
|
||||
|
||||
|
@ -323,26 +323,8 @@ input_manager_process_text_input(struct input_manager *im,
|
|||
// A shortcut must never generate text events
|
||||
return;
|
||||
}
|
||||
if (!im->prefer_text) {
|
||||
char c = event->text[0];
|
||||
if (isalpha(c) || c == ' ') {
|
||||
assert(event->text[1] == '\0');
|
||||
// letters and space are handled as raw key event
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
|
||||
msg.inject_text.text = strdup(event->text);
|
||||
if (!msg.inject_text.text) {
|
||||
LOGW("Could not strdup input text");
|
||||
return;
|
||||
}
|
||||
if (!controller_push_msg(im->controller, &msg)) {
|
||||
free(msg.inject_text.text);
|
||||
LOGW("Could not request 'inject text'");
|
||||
}
|
||||
im->kp->ops->process_text(im->kp, event);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -375,27 +357,6 @@ inverse_point(struct point point, struct size size) {
|
|||
return point;
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to,
|
||||
bool prefer_text, uint32_t repeat) {
|
||||
to->type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
||||
|
||||
if (!convert_keycode_action(from->type, &to->inject_keycode.action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t mod = from->keysym.mod;
|
||||
if (!convert_keycode(from->keysym.sym, &to->inject_keycode.keycode, mod,
|
||||
prefer_text)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
to->inject_keycode.repeat = repeat;
|
||||
to->inject_keycode.metastate = convert_meta_state(mod);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
input_manager_process_key(struct input_manager *im,
|
||||
const SDL_KeyboardEvent *event) {
|
||||
|
@ -549,15 +510,6 @@ input_manager_process_key(struct input_manager *im,
|
|||
return;
|
||||
}
|
||||
|
||||
if (event->repeat) {
|
||||
if (!im->forward_key_repeat) {
|
||||
return;
|
||||
}
|
||||
++im->repeat;
|
||||
} else {
|
||||
im->repeat = 0;
|
||||
}
|
||||
|
||||
if (ctrl && !shift && keycode == SDLK_v && down && !repeat) {
|
||||
if (im->legacy_paste) {
|
||||
// inject the text as input events
|
||||
|
@ -569,12 +521,7 @@ input_manager_process_key(struct input_manager *im,
|
|||
set_device_clipboard(controller, false);
|
||||
}
|
||||
|
||||
struct control_msg msg;
|
||||
if (convert_input_key(event, &msg, im->prefer_text, im->repeat)) {
|
||||
if (!controller_push_msg(controller, &msg)) {
|
||||
LOGW("Could not request 'inject keycode'");
|
||||
}
|
||||
}
|
||||
im->kp->ops->process_key(im->kp, event);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
|
@ -11,18 +11,15 @@
|
|||
#include "fps_counter.h"
|
||||
#include "scrcpy.h"
|
||||
#include "screen.h"
|
||||
#include "trait/key_processor.h"
|
||||
|
||||
struct input_manager {
|
||||
struct controller *controller;
|
||||
struct screen *screen;
|
||||
|
||||
// SDL reports repeated events as a boolean, but Android expects the actual
|
||||
// number of repetitions. This variable keeps track of the count.
|
||||
unsigned repeat;
|
||||
struct sc_key_processor *kp;
|
||||
|
||||
bool control;
|
||||
bool forward_key_repeat;
|
||||
bool prefer_text;
|
||||
bool forward_all_clicks;
|
||||
bool legacy_paste;
|
||||
|
||||
|
@ -43,7 +40,8 @@ struct input_manager {
|
|||
|
||||
void
|
||||
input_manager_init(struct input_manager *im, struct controller *controller,
|
||||
struct screen *screen, const struct scrcpy_options *options);
|
||||
struct screen *screen, struct sc_key_processor *kp,
|
||||
const struct scrcpy_options *options);
|
||||
|
||||
bool
|
||||
input_manager_handle_event(struct input_manager *im, SDL_Event *event);
|
||||
|
|
254
app/src/keyboard_inject.c
Normal file
254
app/src/keyboard_inject.c
Normal file
|
@ -0,0 +1,254 @@
|
|||
#include "keyboard_inject.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <SDL2/SDL_events.h>
|
||||
|
||||
#include "android/input.h"
|
||||
#include "control_msg.h"
|
||||
#include "controller.h"
|
||||
#include "util/log.h"
|
||||
|
||||
/** Downcast key processor to sc_keyboard_inject */
|
||||
#define DOWNCAST(KP) \
|
||||
container_of(KP, struct sc_keyboard_inject, key_processor)
|
||||
|
||||
#define MAP(FROM, TO) case FROM: *to = TO; return true
|
||||
#define FAIL default: return false
|
||||
static bool
|
||||
convert_keycode_action(SDL_EventType from, enum android_keyevent_action *to) {
|
||||
switch (from) {
|
||||
MAP(SDL_KEYDOWN, AKEY_EVENT_ACTION_DOWN);
|
||||
MAP(SDL_KEYUP, AKEY_EVENT_ACTION_UP);
|
||||
FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod,
|
||||
bool prefer_text) {
|
||||
switch (from) {
|
||||
MAP(SDLK_RETURN, AKEYCODE_ENTER);
|
||||
MAP(SDLK_KP_ENTER, AKEYCODE_NUMPAD_ENTER);
|
||||
MAP(SDLK_ESCAPE, AKEYCODE_ESCAPE);
|
||||
MAP(SDLK_BACKSPACE, AKEYCODE_DEL);
|
||||
MAP(SDLK_TAB, AKEYCODE_TAB);
|
||||
MAP(SDLK_PAGEUP, AKEYCODE_PAGE_UP);
|
||||
MAP(SDLK_DELETE, AKEYCODE_FORWARD_DEL);
|
||||
MAP(SDLK_HOME, AKEYCODE_MOVE_HOME);
|
||||
MAP(SDLK_END, AKEYCODE_MOVE_END);
|
||||
MAP(SDLK_PAGEDOWN, AKEYCODE_PAGE_DOWN);
|
||||
MAP(SDLK_RIGHT, AKEYCODE_DPAD_RIGHT);
|
||||
MAP(SDLK_LEFT, AKEYCODE_DPAD_LEFT);
|
||||
MAP(SDLK_DOWN, AKEYCODE_DPAD_DOWN);
|
||||
MAP(SDLK_UP, AKEYCODE_DPAD_UP);
|
||||
MAP(SDLK_LCTRL, AKEYCODE_CTRL_LEFT);
|
||||
MAP(SDLK_RCTRL, AKEYCODE_CTRL_RIGHT);
|
||||
MAP(SDLK_LSHIFT, AKEYCODE_SHIFT_LEFT);
|
||||
MAP(SDLK_RSHIFT, AKEYCODE_SHIFT_RIGHT);
|
||||
}
|
||||
|
||||
if (!(mod & (KMOD_NUM | KMOD_SHIFT))) {
|
||||
// Handle Numpad events when Num Lock is disabled
|
||||
// If SHIFT is pressed, a text event will be sent instead
|
||||
switch(from) {
|
||||
MAP(SDLK_KP_0, AKEYCODE_INSERT);
|
||||
MAP(SDLK_KP_1, AKEYCODE_MOVE_END);
|
||||
MAP(SDLK_KP_2, AKEYCODE_DPAD_DOWN);
|
||||
MAP(SDLK_KP_3, AKEYCODE_PAGE_DOWN);
|
||||
MAP(SDLK_KP_4, AKEYCODE_DPAD_LEFT);
|
||||
MAP(SDLK_KP_6, AKEYCODE_DPAD_RIGHT);
|
||||
MAP(SDLK_KP_7, AKEYCODE_MOVE_HOME);
|
||||
MAP(SDLK_KP_8, AKEYCODE_DPAD_UP);
|
||||
MAP(SDLK_KP_9, AKEYCODE_PAGE_UP);
|
||||
MAP(SDLK_KP_PERIOD, AKEYCODE_FORWARD_DEL);
|
||||
}
|
||||
}
|
||||
|
||||
if (prefer_text && !(mod & KMOD_CTRL)) {
|
||||
// do not forward alpha and space key events (unless Ctrl is pressed)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mod & (KMOD_LALT | KMOD_RALT | KMOD_LGUI | KMOD_RGUI)) {
|
||||
return false;
|
||||
}
|
||||
// if ALT and META are not pressed, also handle letters and space
|
||||
switch (from) {
|
||||
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_SPACE, AKEYCODE_SPACE);
|
||||
FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
static enum android_metastate
|
||||
autocomplete_metastate(enum android_metastate metastate) {
|
||||
// fill dependent flags
|
||||
if (metastate & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
|
||||
metastate |= AMETA_SHIFT_ON;
|
||||
}
|
||||
if (metastate & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
|
||||
metastate |= AMETA_CTRL_ON;
|
||||
}
|
||||
if (metastate & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
|
||||
metastate |= AMETA_ALT_ON;
|
||||
}
|
||||
if (metastate & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
|
||||
metastate |= AMETA_META_ON;
|
||||
}
|
||||
|
||||
return metastate;
|
||||
}
|
||||
|
||||
static enum android_metastate
|
||||
convert_meta_state(SDL_Keymod mod) {
|
||||
enum android_metastate metastate = 0;
|
||||
if (mod & KMOD_LSHIFT) {
|
||||
metastate |= AMETA_SHIFT_LEFT_ON;
|
||||
}
|
||||
if (mod & KMOD_RSHIFT) {
|
||||
metastate |= AMETA_SHIFT_RIGHT_ON;
|
||||
}
|
||||
if (mod & KMOD_LCTRL) {
|
||||
metastate |= AMETA_CTRL_LEFT_ON;
|
||||
}
|
||||
if (mod & KMOD_RCTRL) {
|
||||
metastate |= AMETA_CTRL_RIGHT_ON;
|
||||
}
|
||||
if (mod & KMOD_LALT) {
|
||||
metastate |= AMETA_ALT_LEFT_ON;
|
||||
}
|
||||
if (mod & KMOD_RALT) {
|
||||
metastate |= AMETA_ALT_RIGHT_ON;
|
||||
}
|
||||
if (mod & KMOD_LGUI) { // Windows key
|
||||
metastate |= AMETA_META_LEFT_ON;
|
||||
}
|
||||
if (mod & KMOD_RGUI) { // Windows key
|
||||
metastate |= AMETA_META_RIGHT_ON;
|
||||
}
|
||||
if (mod & KMOD_NUM) {
|
||||
metastate |= AMETA_NUM_LOCK_ON;
|
||||
}
|
||||
if (mod & KMOD_CAPS) {
|
||||
metastate |= AMETA_CAPS_LOCK_ON;
|
||||
}
|
||||
if (mod & KMOD_MODE) { // Alt Gr
|
||||
// no mapping?
|
||||
}
|
||||
|
||||
// fill the dependent fields
|
||||
return autocomplete_metastate(metastate);
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to,
|
||||
bool prefer_text, uint32_t repeat) {
|
||||
to->type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
||||
|
||||
if (!convert_keycode_action(from->type, &to->inject_keycode.action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t mod = from->keysym.mod;
|
||||
if (!convert_keycode(from->keysym.sym, &to->inject_keycode.keycode, mod,
|
||||
prefer_text)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
to->inject_keycode.repeat = repeat;
|
||||
to->inject_keycode.metastate = convert_meta_state(mod);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
sc_key_processor_process_key(struct sc_key_processor *kp,
|
||||
const SDL_KeyboardEvent *event) {
|
||||
struct sc_keyboard_inject *ki = DOWNCAST(kp);
|
||||
|
||||
if (event->repeat) {
|
||||
if (!ki->forward_key_repeat) {
|
||||
return;
|
||||
}
|
||||
++ki->repeat;
|
||||
} else {
|
||||
ki->repeat = 0;
|
||||
}
|
||||
|
||||
struct control_msg msg;
|
||||
if (convert_input_key(event, &msg, ki->prefer_text, ki->repeat)) {
|
||||
if (!controller_push_msg(ki->controller, &msg)) {
|
||||
LOGW("Could not request 'inject keycode'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_key_processor_process_text(struct sc_key_processor *kp,
|
||||
const SDL_TextInputEvent *event) {
|
||||
struct sc_keyboard_inject *ki = DOWNCAST(kp);
|
||||
|
||||
if (!ki->prefer_text) {
|
||||
char c = event->text[0];
|
||||
if (isalpha(c) || c == ' ') {
|
||||
assert(event->text[1] == '\0');
|
||||
// letters and space are handled as raw key event
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
|
||||
msg.inject_text.text = strdup(event->text);
|
||||
if (!msg.inject_text.text) {
|
||||
LOGW("Could not strdup input text");
|
||||
return;
|
||||
}
|
||||
if (!controller_push_msg(ki->controller, &msg)) {
|
||||
free(msg.inject_text.text);
|
||||
LOGW("Could not request 'inject text'");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sc_keyboard_inject_init(struct sc_keyboard_inject *ki,
|
||||
struct controller *controller,
|
||||
const struct scrcpy_options *options) {
|
||||
ki->controller = controller;
|
||||
ki->prefer_text = options->prefer_text;
|
||||
ki->forward_key_repeat = options->forward_key_repeat;
|
||||
|
||||
ki->repeat = 0;
|
||||
|
||||
static const struct sc_key_processor_ops ops = {
|
||||
.process_key = sc_key_processor_process_key,
|
||||
.process_text = sc_key_processor_process_text,
|
||||
};
|
||||
|
||||
ki->key_processor.ops = &ops;
|
||||
}
|
30
app/src/keyboard_inject.h
Normal file
30
app/src/keyboard_inject.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef SC_KEYBOARD_INJECT_H
|
||||
#define SC_KEYBOARD_INJECT_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "controller.h"
|
||||
#include "scrcpy.h"
|
||||
#include "trait/key_processor.h"
|
||||
|
||||
struct sc_keyboard_inject {
|
||||
struct sc_key_processor key_processor; // key processor trait
|
||||
|
||||
struct controller *controller;
|
||||
|
||||
// SDL reports repeated events as a boolean, but Android expects the actual
|
||||
// number of repetitions. This variable keeps track of the count.
|
||||
unsigned repeat;
|
||||
|
||||
bool prefer_text;
|
||||
bool forward_key_repeat;
|
||||
};
|
||||
|
||||
void
|
||||
sc_keyboard_inject_init(struct sc_keyboard_inject *ki,
|
||||
struct controller *controller,
|
||||
const struct scrcpy_options *options);
|
||||
|
||||
#endif
|
|
@ -18,6 +18,7 @@
|
|||
#include "events.h"
|
||||
#include "file_handler.h"
|
||||
#include "input_manager.h"
|
||||
#include "keyboard_inject.h"
|
||||
#include "recorder.h"
|
||||
#include "screen.h"
|
||||
#include "server.h"
|
||||
|
@ -39,6 +40,7 @@ struct scrcpy {
|
|||
#endif
|
||||
struct controller controller;
|
||||
struct file_handler file_handler;
|
||||
struct sc_keyboard_inject keyboard_inject;
|
||||
struct input_manager input_manager;
|
||||
};
|
||||
|
||||
|
@ -411,7 +413,15 @@ scrcpy(const struct scrcpy_options *options) {
|
|||
}
|
||||
stream_started = true;
|
||||
|
||||
input_manager_init(&s->input_manager, &s->controller, &s->screen, options);
|
||||
struct sc_key_processor *kp = NULL;
|
||||
|
||||
if (options->control) {
|
||||
sc_keyboard_inject_init(&s->keyboard_inject, &s->controller, options);
|
||||
kp = &s->keyboard_inject.key_processor;
|
||||
}
|
||||
|
||||
input_manager_init(&s->input_manager, &s->controller, &s->screen, kp,
|
||||
options);
|
||||
|
||||
ret = event_loop(s, options);
|
||||
LOGD("quit...");
|
||||
|
|
29
app/src/trait/key_processor.h
Normal file
29
app/src/trait/key_processor.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef SC_KEY_PROCESSOR_H
|
||||
#define SC_KEY_PROCESSOR_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <SDL2/SDL_events.h>
|
||||
|
||||
/**
|
||||
* Key processor trait.
|
||||
*
|
||||
* Component able to process and inject keys should implement this trait.
|
||||
*/
|
||||
struct sc_key_processor {
|
||||
const struct sc_key_processor_ops *ops;
|
||||
};
|
||||
|
||||
struct sc_key_processor_ops {
|
||||
void
|
||||
(*process_key)(struct sc_key_processor *kp, const SDL_KeyboardEvent *event);
|
||||
|
||||
void
|
||||
(*process_text)(struct sc_key_processor *kp,
|
||||
const SDL_TextInputEvent *event);
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue