1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
#include <kmx/kmx_processevent.h>
#include <linux/input-event-codes.h>
#include <list>
#include <stack>
#include "keycodes.h"
#include "KeymanSystemServiceClient.h"
#include "testfixture.h"
#include "KeyHandling.h"
#define KEYMAN_LCTRL 29 // 0x1D
#define KEYMAN_LALT 56 // 0x38
#define KEYMAN_RCTRL 97 // 0x61
#define KEYMAN_RALT 100 // 0x64
extern GdkWindow* window;
typedef struct {
guint modifiers_keydown;
guint modifiers_keyup;
guint keyval;
guint16 keycode;
guint is_modifier;
} gdk_key_event;
static gdk_key_event get_key_event_for_modifier(guint modifier, guint& prev_modifiers, guint keyval, guint keycode) {
gdk_key_event event;
event.modifiers_keydown = prev_modifiers;
event.modifiers_keyup = prev_modifiers | modifier | IBUS_RELEASE_MASK;
prev_modifiers |= modifier;
event.keyval = keyval;
event.keycode = keycode;
event.is_modifier = 1;
return event;
}
static unsigned short vk_to_keycode(unsigned short vk) {
for (int i = 0; i < (int)sizeof(keycode_to_vk); i++) {
if (keycode_to_vk[i] == vk)
return i;
}
return -1;
}
static bool is_modifier(guint16 keycode) {
switch (keycode) {
case KEY_LEFTSHIFT:
case KEY_RIGHTSHIFT:
case KEY_LEFTALT:
case KEY_RIGHTALT:
case KEY_LEFTCTRL:
case KEY_RIGHTCTRL:
case KEY_CAPSLOCK:
return true;
}
return false;
}
static std::list<gdk_key_event> get_involved_keys(km::tests::key_event test_event) {
guint modifiers = 0;
if (get_capslock_indicator()) {
modifiers = IBUS_LOCK_MASK;
}
std::list<gdk_key_event> result;
if (test_event.vk == KEYMAN_F24_KEYCODE_OUTPUT_SENTINEL) {
gdk_key_event event;
event.modifiers_keydown = modifiers;
event.modifiers_keyup = modifiers | IBUS_RELEASE_MASK;
event.keyval = 0;
event.keycode = KEYMAN_F24_KEYCODE_OUTPUT_SENTINEL;
event.is_modifier = is_modifier(event.keycode);
result.push_back(event);
return result;
}
// process all modifiers
if (test_event.modifier_state & KM_CORE_MODIFIER_SHIFT) {
// we don't distinguish between R and L Shift
result.push_back(get_key_event_for_modifier(IBUS_SHIFT_MASK, modifiers, GDK_KEY_Shift_L, KEY_LEFTSHIFT));
}
if (test_event.modifier_state & KM_CORE_MODIFIER_ALT || test_event.modifier_state & KM_CORE_MODIFIER_LALT) {
result.push_back(get_key_event_for_modifier(IBUS_MOD1_MASK, modifiers, GDK_KEY_Alt_L, KEY_LEFTALT));
}
if (test_event.modifier_state & KM_CORE_MODIFIER_RALT) {
if (test_event.vk == KEYMAN_RALT)
result.push_back(get_key_event_for_modifier(IBUS_MOD1_MASK, modifiers, GDK_KEY_Alt_R, KEY_RIGHTALT));
else
result.push_back(get_key_event_for_modifier(IBUS_MOD5_MASK, modifiers, GDK_KEY_Alt_R, KEY_RIGHTALT));
}
if (test_event.modifier_state & KM_CORE_MODIFIER_CTRL || test_event.modifier_state & KM_CORE_MODIFIER_LCTRL) {
result.push_back(get_key_event_for_modifier(IBUS_CONTROL_MASK, modifiers, GDK_KEY_Control_L, KEY_LEFTCTRL));
}
if (test_event.modifier_state & KM_CORE_MODIFIER_RCTRL) {
result.push_back(get_key_event_for_modifier(IBUS_CONTROL_MASK, modifiers, GDK_KEY_Control_R, KEY_RIGHTCTRL));
}
// process key
guint keyval = (test_event.modifier_state & KM_CORE_MODIFIER_SHIFT) ? gdk_keyval_to_upper(test_event.vk)
: gdk_keyval_to_lower(test_event.vk);
// REVIEW: the keyval we use here are not correct for < 0x20 and >= 0x7f
// See /usr/include/X11/keysymdef.h and /usr/include/ibus-1.0/ibuskeysyms.h
// Currently this is not a problem because we don't use keyval, but if that ever changes we
// should be aware that our test behaves different than the real client.
gdk_key_event event;
event.modifiers_keydown = modifiers;
event.modifiers_keyup = modifiers | IBUS_RELEASE_MASK;
event.keyval = keyval;
event.keycode = vk_to_keycode(test_event.vk);
event.is_modifier = is_modifier(event.keycode);
result.push_back(event);
return result;
}
void press_key(IBusKeymanTestsFixture* fixture, km::tests::key_event key_event) {
auto involved_keys = get_involved_keys(key_event);
// press and hold the individual modifier keys, finally the actual key
std::stack<GdkEventKey> keys_to_release;
for (auto key = involved_keys.begin(); key != involved_keys.end(); key++) {
bool old_caps_lock = get_capslock_indicator();
guint lock_modifier = 0;
// KEY_CAPSLOCK behaves a bit strange:
// If capslock is off:
// - on keypress we turn on capslock, but the modifier is still not set.
// - The LOCK_MASK modifier gets set on release.
// When capslock is already on:
// - the LOCK_MASK modifier is set on both keypress and release
// - capslock gets turned off on release
if (key->keycode == KEY_CAPSLOCK && !old_caps_lock) {
set_capslock_indicator((guint32) true);
lock_modifier = IBUS_LOCK_MASK;
}
GdkEventKey keyEvent = {
.type = GDK_KEY_PRESS,
.window = window,
.send_event = 0,
.time = 0,
.state = key->modifiers_keydown,
.keyval = key->keyval,
.length = 0,
.string = NULL,
.hardware_keycode = key->keycode,
.group = 0,
.is_modifier = key->is_modifier};
gtk_im_context_filter_keypress(fixture->context, &keyEvent);
keyEvent.type = GDK_KEY_RELEASE;
keyEvent.state = key->modifiers_keyup | lock_modifier;
keys_to_release.push(keyEvent);
if (key->keycode == KEY_CAPSLOCK && old_caps_lock) {
set_capslock_indicator((guint32) false);
}
}
// then release the keys in reverse order
while (!keys_to_release.empty()) {
auto keyEvent = keys_to_release.top();
gtk_im_context_filter_keypress(fixture->context, &keyEvent);
keys_to_release.pop();
}
}
|