File: KeyHandling.cpp

package info (click to toggle)
keyman 18.0.245-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 21,316 kB
  • sloc: python: 52,784; cpp: 21,278; sh: 7,633; ansic: 4,823; xml: 3,617; perl: 959; makefile: 139; javascript: 138
file content (167 lines) | stat: -rw-r--r-- 5,938 bytes parent folder | download | duplicates (2)
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();
  }
}