| /* |
| * This work is licensed under the terms of the GNU GPL, version 2 or |
| * (at your option) any later version. See the COPYING file in the |
| * top-level directory. |
| */ |
| #include "qemu/osdep.h" |
| #include "qemu/bitmap.h" |
| #include "qemu/queue.h" |
| #include "ui/console.h" |
| #include "ui/input.h" |
| #include "ui/kbd-state.h" |
| |
| struct QKbdState { |
| QemuConsole *con; |
| int key_delay_ms; |
| DECLARE_BITMAP(keys, Q_KEY_CODE__MAX); |
| DECLARE_BITMAP(mods, QKBD_MOD__MAX); |
| }; |
| |
| static void qkbd_state_modifier_update(QKbdState *kbd, |
| QKeyCode qcode1, QKeyCode qcode2, |
| QKbdModifier mod) |
| { |
| if (test_bit(qcode1, kbd->keys) || test_bit(qcode2, kbd->keys)) { |
| set_bit(mod, kbd->mods); |
| } else { |
| clear_bit(mod, kbd->mods); |
| } |
| } |
| |
| bool qkbd_state_modifier_get(QKbdState *kbd, QKbdModifier mod) |
| { |
| return test_bit(mod, kbd->mods); |
| } |
| |
| bool qkbd_state_key_get(QKbdState *kbd, QKeyCode qcode) |
| { |
| return test_bit(qcode, kbd->keys); |
| } |
| |
| void qkbd_state_key_event(QKbdState *kbd, QKeyCode qcode, bool down) |
| { |
| bool state = test_bit(qcode, kbd->keys); |
| |
| if (state == down) { |
| /* |
| * Filter out events which don't change the keyboard state. |
| * |
| * Most notably this allows to simply send along all key-up |
| * events, and this function will filter out everything where |
| * the corresponding key-down event wasn't send to the guest, |
| * for example due to being a host hotkey. |
| */ |
| return; |
| } |
| |
| /* update key and modifier state */ |
| change_bit(qcode, kbd->keys); |
| switch (qcode) { |
| case Q_KEY_CODE_SHIFT: |
| case Q_KEY_CODE_SHIFT_R: |
| qkbd_state_modifier_update(kbd, Q_KEY_CODE_SHIFT, Q_KEY_CODE_SHIFT_R, |
| QKBD_MOD_SHIFT); |
| break; |
| case Q_KEY_CODE_CTRL: |
| case Q_KEY_CODE_CTRL_R: |
| qkbd_state_modifier_update(kbd, Q_KEY_CODE_CTRL, Q_KEY_CODE_CTRL_R, |
| QKBD_MOD_CTRL); |
| break; |
| case Q_KEY_CODE_ALT: |
| qkbd_state_modifier_update(kbd, Q_KEY_CODE_ALT, Q_KEY_CODE_ALT, |
| QKBD_MOD_ALT); |
| break; |
| case Q_KEY_CODE_ALT_R: |
| qkbd_state_modifier_update(kbd, Q_KEY_CODE_ALT_R, Q_KEY_CODE_ALT_R, |
| QKBD_MOD_ALTGR); |
| break; |
| case Q_KEY_CODE_CAPS_LOCK: |
| if (down) { |
| change_bit(QKBD_MOD_CAPSLOCK, kbd->mods); |
| } |
| break; |
| case Q_KEY_CODE_NUM_LOCK: |
| if (down) { |
| change_bit(QKBD_MOD_NUMLOCK, kbd->mods); |
| } |
| break; |
| default: |
| /* keep gcc happy */ |
| break; |
| } |
| |
| /* send to guest */ |
| if (qemu_console_is_graphic(kbd->con)) { |
| qemu_input_event_send_key_qcode(kbd->con, qcode, down); |
| if (kbd->key_delay_ms) { |
| qemu_input_event_send_key_delay(kbd->key_delay_ms); |
| } |
| } |
| } |
| |
| void qkbd_state_lift_all_keys(QKbdState *kbd) |
| { |
| int qcode; |
| |
| for (qcode = 0; qcode < Q_KEY_CODE__MAX; qcode++) { |
| if (test_bit(qcode, kbd->keys)) { |
| qkbd_state_key_event(kbd, qcode, false); |
| } |
| } |
| } |
| |
| void qkbd_state_set_delay(QKbdState *kbd, int delay_ms) |
| { |
| kbd->key_delay_ms = delay_ms; |
| } |
| |
| void qkbd_state_free(QKbdState *kbd) |
| { |
| g_free(kbd); |
| } |
| |
| QKbdState *qkbd_state_init(QemuConsole *con) |
| { |
| QKbdState *kbd = g_new0(QKbdState, 1); |
| |
| kbd->con = con; |
| |
| return kbd; |
| } |