Fcitx
key.cpp
1 /*
2  * SPDX-FileCopyrightText: 2015-2015 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 
8 #include "key.h"
9 #include <cstring>
10 #include <unordered_map>
11 #include "charutils.h"
12 #include "i18n.h"
13 #include "keydata.h"
14 #include "keynametable-compat.h"
15 #include "keynametable.h"
16 #include "misc_p.h"
17 #include "stringutils.h"
18 #include "utf8.h"
19 
20 namespace fcitx {
21 
22 namespace {
23 
24 std::unordered_map<KeySym, const char *, EnumHash> makeLookupKeyNameMap() {
25 
26  static const struct {
27  KeySym key;
28  const char name[25];
29  } keyname[] = {
30  {FcitxKey_Alt_L, NC_("Key name", "Left Alt")},
31  {FcitxKey_Alt_R, NC_("Key name", "Right Alt")},
32  {FcitxKey_Shift_L, NC_("Key name", "Left Shift")},
33  {FcitxKey_Shift_R, NC_("Key name", "Right Shift")},
34  {FcitxKey_Control_L, NC_("Key name", "Left Control")},
35  {FcitxKey_Control_R, NC_("Key name", "Right Control")},
36  {FcitxKey_Super_L, NC_("Key name", "Left Super")},
37  {FcitxKey_Super_R, NC_("Key name", "Right Super")},
38  {FcitxKey_Hyper_L, NC_("Key name", "Left Hyper")},
39  {FcitxKey_Hyper_R, NC_("Key name", "Right Hyper")},
40  {FcitxKey_space, NC_("Key name", "Space")},
41  {FcitxKey_Tab, NC_("Key name", "Tab")},
42  {FcitxKey_BackSpace, NC_("Key name", "Backspace")},
43  {FcitxKey_Return, NC_("Key name", "Return")},
44  {FcitxKey_Pause, NC_("Key name", "Pause")},
45  {FcitxKey_Home, NC_("Key name", "Home")},
46  {FcitxKey_End, NC_("Key name", "End")},
47  {FcitxKey_Left, NC_("Key name", "Left")},
48  {FcitxKey_Up, NC_("Key name", "Up")},
49  {FcitxKey_Right, NC_("Key name", "Right")},
50  {FcitxKey_Down, NC_("Key name", "Down")},
51  {FcitxKey_Page_Up, NC_("Key name", "Page Up")},
52  {FcitxKey_Page_Down, NC_("Key name", "Page Down")},
53  {FcitxKey_Caps_Lock, NC_("Key name", "CapsLock")},
54  {FcitxKey_Num_Lock, NC_("Key name", "NumLock")},
55  {FcitxKey_KP_Space, NC_("Key name", "Keypad Space")},
56  {FcitxKey_KP_Tab, NC_("Key name", "Keypad Tab")},
57  {FcitxKey_KP_Enter, NC_("Key name", "Keypad Enter")},
58  {FcitxKey_KP_F1, NC_("Key name", "Keypad F1")},
59  {FcitxKey_KP_F2, NC_("Key name", "Keypad F2")},
60  {FcitxKey_KP_F3, NC_("Key name", "Keypad F3")},
61  {FcitxKey_KP_F4, NC_("Key name", "Keypad F4")},
62  {FcitxKey_KP_Home, NC_("Key name", "Keypad Home")},
63  {FcitxKey_KP_Left, NC_("Key name", "Keypad Left")},
64  {FcitxKey_KP_Up, NC_("Key name", "Keypad Up")},
65  {FcitxKey_KP_Right, NC_("Key name", "Keypad Right")},
66  {FcitxKey_KP_Down, NC_("Key name", "Keypad Down")},
67  {FcitxKey_KP_Page_Up, NC_("Key name", "Keypad Page Up")},
68  {FcitxKey_KP_Page_Down, NC_("Key name", "Keypad Page Down")},
69  {FcitxKey_KP_End, NC_("Key name", "Keypad End")},
70  {FcitxKey_KP_Begin, NC_("Key name", "Keypad Begin")},
71  {FcitxKey_KP_Insert, NC_("Key name", "Keypad Insert")},
72  {FcitxKey_KP_Delete, NC_("Key name", "Keypad Delete")},
73  {FcitxKey_KP_Equal, NC_("Key name", "Keypad =")},
74  {FcitxKey_KP_Multiply, NC_("Key name", "Keypad *")},
75  {FcitxKey_KP_Add, NC_("Key name", "Keypad +")},
76  {FcitxKey_KP_Separator, NC_("Key name", "Keypad ,")},
77  {FcitxKey_KP_Subtract, NC_("Key name", "Keypad -")},
78  {FcitxKey_KP_Decimal, NC_("Key name", "Keypad .")},
79  {FcitxKey_KP_Divide, NC_("Key name", "Keypad /")},
80  {FcitxKey_KP_0, NC_("Key name", "Keypad 0")},
81  {FcitxKey_KP_1, NC_("Key name", "Keypad 1")},
82  {FcitxKey_KP_2, NC_("Key name", "Keypad 2")},
83  {FcitxKey_KP_3, NC_("Key name", "Keypad 3")},
84  {FcitxKey_KP_4, NC_("Key name", "Keypad 4")},
85  {FcitxKey_KP_5, NC_("Key name", "Keypad 5")},
86  {FcitxKey_KP_6, NC_("Key name", "Keypad 6")},
87  {FcitxKey_KP_7, NC_("Key name", "Keypad 7")},
88  {FcitxKey_KP_8, NC_("Key name", "Keypad 8")},
89  {FcitxKey_KP_9, NC_("Key name", "Keypad 9")},
90  {FcitxKey_Scroll_Lock, NC_("Key name", "ScrollLock")},
91  {FcitxKey_Menu, NC_("Key name", "Menu")},
92  {FcitxKey_Help, NC_("Key name", "Help")},
93  {FcitxKey_Back, NC_("Key name", "Back")},
94  {FcitxKey_Forward, NC_("Key name", "Forward")},
95  {FcitxKey_Stop, NC_("Key name", "Stop")},
96  {FcitxKey_Refresh, NC_("Key name", "Refresh")},
97  {FcitxKey_AudioLowerVolume, NC_("Key name", "Volume Down")},
98  {FcitxKey_AudioMute, NC_("Key name", "Volume Mute")},
99  {FcitxKey_AudioRaiseVolume, NC_("Key name", "Volume Up")},
100  {FcitxKey_AudioPlay, NC_("Key name", "Media Play")},
101  {FcitxKey_AudioStop, NC_("Key name", "Media Stop")},
102  {FcitxKey_AudioPrev, NC_("Key name", "Media Previous")},
103  {FcitxKey_AudioNext, NC_("Key name", "Media Next")},
104  {FcitxKey_AudioRecord, NC_("Key name", "Media Record")},
105  {FcitxKey_AudioPause, NC_("Key name", "Media Pause")},
106  {FcitxKey_HomePage, NC_("Key name", "Home Page")},
107  {FcitxKey_Favorites, NC_("Key name", "Favorites")},
108  {FcitxKey_Search, NC_("Key name", "Search")},
109  {FcitxKey_Standby, NC_("Key name", "Standby")},
110  {FcitxKey_OpenURL, NC_("Key name", "Open URL")},
111  {FcitxKey_Mail, NC_("Key name", "Launch Mail")},
112  {FcitxKey_Launch0, NC_("Key name", "Launch (0)")},
113  {FcitxKey_Launch1, NC_("Key name", "Launch (1)")},
114  {FcitxKey_Launch2, NC_("Key name", "Launch (2)")},
115  {FcitxKey_Launch3, NC_("Key name", "Launch (3)")},
116  {FcitxKey_Launch4, NC_("Key name", "Launch (4)")},
117  {FcitxKey_Launch5, NC_("Key name", "Launch (5)")},
118  {FcitxKey_Launch6, NC_("Key name", "Launch (6)")},
119  {FcitxKey_Launch7, NC_("Key name", "Launch (7)")},
120  {FcitxKey_Launch8, NC_("Key name", "Launch (8)")},
121  {FcitxKey_Launch9, NC_("Key name", "Launch (9)")},
122  {FcitxKey_LaunchA, NC_("Key name", "Launch (A)")},
123  {FcitxKey_LaunchB, NC_("Key name", "Launch (B)")},
124  {FcitxKey_LaunchC, NC_("Key name", "Launch (C)")},
125  {FcitxKey_LaunchD, NC_("Key name", "Launch (D)")},
126  {FcitxKey_LaunchE, NC_("Key name", "Launch (E)")},
127  {FcitxKey_LaunchF, NC_("Key name", "Launch (F)")},
128  {FcitxKey_MonBrightnessUp, NC_("Key name", "Monitor Brightness Up")},
129  {FcitxKey_MonBrightnessDown,
130  NC_("Key name", "Monitor Brightness Down")},
131  {FcitxKey_KbdLightOnOff, NC_("Key name", "Keyboard Light On/Off")},
132  {FcitxKey_KbdBrightnessUp, NC_("Key name", "Keyboard Brightness Up")},
133  {FcitxKey_KbdBrightnessDown,
134  NC_("Key name", "Keyboard Brightness Down")},
135  {FcitxKey_PowerOff, NC_("Key name", "Power Off")},
136  {FcitxKey_WakeUp, NC_("Key name", "Wake Up")},
137  {FcitxKey_Eject, NC_("Key name", "Eject")},
138  {FcitxKey_ScreenSaver, NC_("Key name", "Screensaver")},
139  {FcitxKey_WWW, NC_("Key name", "WWW")},
140  {FcitxKey_Sleep, NC_("Key name", "Sleep")},
141  {FcitxKey_LightBulb, NC_("Key name", "LightBulb")},
142  {FcitxKey_Shop, NC_("Key name", "Shop")},
143  {FcitxKey_History, NC_("Key name", "History")},
144  {FcitxKey_AddFavorite, NC_("Key name", "Add Favorite")},
145  {FcitxKey_HotLinks, NC_("Key name", "Hot Links")},
146  {FcitxKey_BrightnessAdjust, NC_("Key name", "Adjust Brightness")},
147  {FcitxKey_Finance, NC_("Key name", "Finance")},
148  {FcitxKey_Community, NC_("Key name", "Community")},
149  {FcitxKey_AudioRewind, NC_("Key name", "Media Rewind")},
150  {FcitxKey_BackForward, NC_("Key name", "Back Forward")},
151  {FcitxKey_ApplicationLeft, NC_("Key name", "Application Left")},
152  {FcitxKey_ApplicationRight, NC_("Key name", "Application Right")},
153  {FcitxKey_Book, NC_("Key name", "Book")},
154  {FcitxKey_CD, NC_("Key name", "CD")},
155  {FcitxKey_Calculator, NC_("Key name", "Calculator")},
156  {FcitxKey_Clear, NC_("Key name", "Clear")},
157  {FcitxKey_Close, NC_("Key name", "Close")},
158  {FcitxKey_Copy, NC_("Key name", "Copy")},
159  {FcitxKey_Cut, NC_("Key name", "Cut")},
160  {FcitxKey_Display, NC_("Key name", "Display")},
161  {FcitxKey_DOS, NC_("Key name", "DOS")},
162  {FcitxKey_Documents, NC_("Key name", "Documents")},
163  {FcitxKey_Excel, NC_("Key name", "Spreadsheet")},
164  {FcitxKey_Explorer, NC_("Key name", "Browser")},
165  {FcitxKey_Game, NC_("Key name", "Game")},
166  {FcitxKey_Go, NC_("Key name", "Go")},
167  {FcitxKey_iTouch, NC_("Key name", "iTouch")},
168  {FcitxKey_LogOff, NC_("Key name", "Logoff")},
169  {FcitxKey_Market, NC_("Key name", "Market")},
170  {FcitxKey_Meeting, NC_("Key name", "Meeting")},
171  {FcitxKey_MenuKB, NC_("Key name", "Keyboard Menu")},
172  {FcitxKey_MenuPB, NC_("Key name", "Menu PB")},
173  {FcitxKey_MySites, NC_("Key name", "My Sites")},
174  {FcitxKey_News, NC_("Key name", "News")},
175  {FcitxKey_OfficeHome, NC_("Key name", "Home Office")},
176  {FcitxKey_Option, NC_("Key name", "Option")},
177  {FcitxKey_Paste, NC_("Key name", "Paste")},
178  {FcitxKey_Phone, NC_("Key name", "Phone")},
179  {FcitxKey_Reply, NC_("Key name", "Reply")},
180  {FcitxKey_Reload, NC_("Key name", "Reload")},
181  {FcitxKey_RotateWindows, NC_("Key name", "Rotate Windows")},
182  {FcitxKey_RotationPB, NC_("Key name", "Rotation PB")},
183  {FcitxKey_RotationKB, NC_("Key name", "Rotation KB")},
184  {FcitxKey_Save, NC_("Key name", "Save")},
185  {FcitxKey_Send, NC_("Key name", "Send")},
186  {FcitxKey_Spell, NC_("Key name", "Spellchecker")},
187  {FcitxKey_SplitScreen, NC_("Key name", "Split Screen")},
188  {FcitxKey_Support, NC_("Key name", "Support")},
189  {FcitxKey_TaskPane, NC_("Key name", "Task Panel")},
190  {FcitxKey_Terminal, NC_("Key name", "Terminal")},
191  {FcitxKey_Tools, NC_("Key name", "Tools")},
192  {FcitxKey_Travel, NC_("Key name", "Travel")},
193  {FcitxKey_Video, NC_("Key name", "Video")},
194  {FcitxKey_Word, NC_("Key name", "Word Processor")},
195  {FcitxKey_Xfer, NC_("Key name", "XFer")},
196  {FcitxKey_ZoomIn, NC_("Key name", "Zoom In")},
197  {FcitxKey_ZoomOut, NC_("Key name", "Zoom Out")},
198  {FcitxKey_Away, NC_("Key name", "Away")},
199  {FcitxKey_Messenger, NC_("Key name", "Messenger")},
200  {FcitxKey_WebCam, NC_("Key name", "WebCam")},
201  {FcitxKey_MailForward, NC_("Key name", "Mail Forward")},
202  {FcitxKey_Pictures, NC_("Key name", "Pictures")},
203  {FcitxKey_Music, NC_("Key name", "Music")},
204  {FcitxKey_Battery, NC_("Key name", "Battery")},
205  {FcitxKey_Bluetooth, NC_("Key name", "Bluetooth")},
206  {FcitxKey_WLAN, NC_("Key name", "Wireless")},
207  {FcitxKey_AudioForward, NC_("Key name", "Media Fast Forward")},
208  {FcitxKey_AudioRepeat, NC_("Key name", "Audio Repeat")},
209  {FcitxKey_AudioRandomPlay, NC_("Key name", "Audio Random Play")},
210  {FcitxKey_Subtitle, NC_("Key name", "Subtitle")},
211  {FcitxKey_AudioCycleTrack, NC_("Key name", "Audio Cycle Track")},
212  {FcitxKey_Time, NC_("Key name", "Time")},
213  {FcitxKey_Hibernate, NC_("Key name", "Hibernate")},
214  {FcitxKey_View, NC_("Key name", "View")},
215  {FcitxKey_TopMenu, NC_("Key name", "Top Menu")},
216  {FcitxKey_PowerDown, NC_("Key name", "Power Down")},
217  {FcitxKey_Suspend, NC_("Key name", "Suspend")},
218  {FcitxKey_AudioMicMute, NC_("Key name", "Microphone Mute")},
219  {FcitxKey_Red, NC_("Key name", "Red")},
220  {FcitxKey_Green, NC_("Key name", "Green")},
221  {FcitxKey_Yellow, NC_("Key name", "Yellow")},
222  {FcitxKey_Blue, NC_("Key name", "Blue")},
223  {FcitxKey_New, NC_("Key name", "New")},
224  {FcitxKey_Open, NC_("Key name", "Open")},
225  {FcitxKey_Find, NC_("Key name", "Find")},
226  {FcitxKey_Undo, NC_("Key name", "Undo")},
227  {FcitxKey_Redo, NC_("Key name", "Redo")},
228  {FcitxKey_Print, NC_("Key name", "Print Screen")},
229  {FcitxKey_Insert, NC_("Key name", "Insert")},
230  {FcitxKey_Delete, NC_("Key name", "Delete")},
231  {FcitxKey_Escape, NC_("Key name", "Escape")},
232  {FcitxKey_Sys_Req, NC_("Key name", "System Request")},
233  {FcitxKey_Select, NC_("Key name", "Select")},
234  {FcitxKey_Kanji, NC_("Key name", "Kanji")},
235  {FcitxKey_Muhenkan, NC_("Key name", "Muhenkan")},
236  {FcitxKey_Henkan, NC_("Key name", "Henkan")},
237  {FcitxKey_Romaji, NC_("Key name", "Romaji")},
238  {FcitxKey_Hiragana, NC_("Key name", "Hiragana")},
239  {FcitxKey_Katakana, NC_("Key name", "Katakana")},
240  {FcitxKey_Hiragana_Katakana, NC_("Key name", "Hiragana Katakana")},
241  {FcitxKey_Zenkaku, NC_("Key name", "Zenkaku")},
242  {FcitxKey_Hankaku, NC_("Key name", "Hankaku")},
243  {FcitxKey_Zenkaku_Hankaku, NC_("Key name", "Zenkaku Hankaku")},
244  {FcitxKey_Touroku, NC_("Key name", "Touroku")},
245  {FcitxKey_Massyo, NC_("Key name", "Massyo")},
246  {FcitxKey_Kana_Lock, NC_("Key name", "Kana Lock")},
247  {FcitxKey_Kana_Shift, NC_("Key name", "Kana Shift")},
248  {FcitxKey_Eisu_Shift, NC_("Key name", "Eisu Shift")},
249  {FcitxKey_Eisu_toggle, NC_("Key name", "Eisu toggle")},
250  {FcitxKey_Codeinput, NC_("Key name", "Code input")},
251  {FcitxKey_MultipleCandidate, NC_("Key name", "Multiple Candidate")},
252  {FcitxKey_PreviousCandidate, NC_("Key name", "Previous Candidate")},
253  {FcitxKey_Hangul, NC_("Key name", "Hangul")},
254  {FcitxKey_Hangul_Start, NC_("Key name", "Hangul Start")},
255  {FcitxKey_Hangul_End, NC_("Key name", "Hangul End")},
256  {FcitxKey_Hangul_Hanja, NC_("Key name", "Hangul Hanja")},
257  {FcitxKey_Hangul_Jamo, NC_("Key name", "Hangul Jamo")},
258  {FcitxKey_Hangul_Romaja, NC_("Key name", "Hangul Romaja")},
259  {FcitxKey_Hangul_Jeonja, NC_("Key name", "Hangul Jeonja")},
260  {FcitxKey_Hangul_Banja, NC_("Key name", "Hangul Banja")},
261  {FcitxKey_Hangul_PreHanja, NC_("Key name", "Hangul PreHanja")},
262  {FcitxKey_Hangul_PostHanja, NC_("Key name", "Hangul PostHanja")},
263  {FcitxKey_Hangul_Special, NC_("Key name", "Hangul Special")},
264  {FcitxKey_Cancel, NC_("Key name", "Cancel")},
265  {FcitxKey_Execute, NC_("Key name", "Execute")},
266  {FcitxKey_TouchpadToggle, NC_("Key name", "Touchpad Toggle")},
267  {FcitxKey_TouchpadOn, NC_("Key name", "Touchpad On")},
268  {FcitxKey_TouchpadOff, NC_("Key name", "Touchpad Off")},
269  {FcitxKey_VoidSymbol, NC_("Key name", "Void Symbol")},
270  };
271  std::unordered_map<KeySym, const char *, EnumHash> result;
272  for (const auto &item : keyname) {
273  result[item.key] = item.name;
274  }
275  return result;
276 }
277 
278 const char *lookupName(KeySym sym) {
279  static const std::unordered_map<KeySym, const char *, EnumHash> map =
280  makeLookupKeyNameMap();
281  const auto *result = findValue(map, sym);
282  return result ? *result : nullptr;
283 }
284 } // namespace
285 
286 Key::Key(const char *keyString) : Key() {
287  KeyStates states;
288  /* old compatible code */
289  const char *p = keyString;
290  const char *lastModifier = keyString;
291  const char *found = nullptr;
292 
293 #define _CHECK_MODIFIER(NAME, VALUE) \
294  if ((found = strstr(p, NAME))) { \
295  states |= KeyState::VALUE; \
296  if (found + strlen(NAME) > lastModifier) { \
297  lastModifier = found + strlen(NAME); \
298  } \
299  }
300 
301  _CHECK_MODIFIER("CTRL_", Ctrl)
302  _CHECK_MODIFIER("Control+", Ctrl)
303  _CHECK_MODIFIER("ALT_", Alt)
304  _CHECK_MODIFIER("Alt+", Alt)
305  _CHECK_MODIFIER("SHIFT_", Shift)
306  _CHECK_MODIFIER("Shift+", Shift)
307  _CHECK_MODIFIER("SUPER_", Super)
308  _CHECK_MODIFIER("Super+", Super)
309  _CHECK_MODIFIER("HYPER_", Mod3)
310  _CHECK_MODIFIER("Hyper+", Mod3)
311 
312 #undef _CHECK_MODIFIER
313 
314  // Special code for keycode baesd parsing.
315  std::string keyValue = lastModifier;
316  if (stringutils::startsWith(keyValue, "<") &&
317  stringutils::endsWith(keyValue, ">")) {
318  try {
319  code_ = std::stoi(keyValue.substr(1, keyValue.size() - 2));
320  } catch (const std::exception &) {
321  }
322  } else {
323  sym_ = keySymFromString(lastModifier);
324  }
325  states_ = states;
326 }
327 
328 bool Key::isReleaseOfModifier(const Key &key) const {
329  if (!key.isModifier()) {
330  return false;
331  }
332  auto states = keySymToStates(key.sym()) | key.states();
333  // Now we need reverse of keySymToStates.
334 
335  std::vector<Key> keys;
336  keys.emplace_back(key.sym(), states);
337  if (key.states() & KeyState::Ctrl) {
338  keys.emplace_back(FcitxKey_Control_L, states);
339  keys.emplace_back(FcitxKey_Control_R, states);
340  }
341  if (key.states() & KeyState::Alt) {
342  keys.emplace_back(FcitxKey_Alt_L, states);
343  keys.emplace_back(FcitxKey_Alt_R, states);
344  keys.emplace_back(FcitxKey_Meta_L, states);
345  keys.emplace_back(FcitxKey_Meta_R, states);
346  }
347  if (key.states() & KeyState::Shift) {
348  keys.emplace_back(FcitxKey_Shift_L, states);
349  keys.emplace_back(FcitxKey_Shift_R, states);
350  }
351  if ((key.states() & KeyState::Super) || (key.states() & KeyState::Super2)) {
352  keys.emplace_back(FcitxKey_Super_L, states);
353  keys.emplace_back(FcitxKey_Super_R, states);
354  }
355  if ((key.states() & KeyState::Super) || (key.states() & KeyState::Hyper2)) {
356  keys.emplace_back(FcitxKey_Hyper_L, states);
357  keys.emplace_back(FcitxKey_Hyper_R, states);
358  }
359 
360  return checkKeyList(keys);
361 }
362 
363 bool Key::check(const Key &key) const {
364  auto states = states_ & KeyStates({KeyState::Ctrl_Alt_Shift,
365  KeyState::Super, KeyState::Mod3});
366  if (states_.test(KeyState::Super2)) {
367  states |= KeyState::Super;
368  }
369 
370  // key is keycode based, do key code based check.
371  if (key.code()) {
372  return key.states_ == states && key.code_ == code_;
373  }
374 
375  if (key.sym() == FcitxKey_None || key.sym() == FcitxKey_VoidSymbol) {
376  return false;
377  }
378 
379  if (isModifier()) {
380  Key keyAlt = *this;
381  auto states = states_ & (~keySymToStates(sym_));
382  keyAlt.states_ |= keySymToStates(sym_);
383 
384  return (key.sym_ == sym_ && key.states_ == states) ||
385  (key.sym_ == keyAlt.sym_ && key.states_ == keyAlt.states_);
386  }
387 
388  return (key.sym_ == sym_ && key.states_ == states);
389 }
390 
391 bool Key::isDigit() const {
392  return !states_ && ((sym_ >= FcitxKey_0 && sym_ <= FcitxKey_9) ||
393  (sym_ >= FcitxKey_KP_0 && sym_ <= FcitxKey_KP_9));
394 }
395 
396 int Key::digit() const {
397  if (states_) {
398  return -1;
399  }
400  if (sym_ >= FcitxKey_0 && sym_ <= FcitxKey_9) {
401  return sym_ - FcitxKey_0;
402  }
403  if (sym_ >= FcitxKey_KP_0 && sym_ <= FcitxKey_KP_9) {
404  return sym_ - FcitxKey_KP_0;
405  }
406  return -1;
407 }
408 
409 int Key::digitSelection(KeyStates states) const {
410  auto filteredStates =
411  states_ &
412  KeyStates({KeyState::Ctrl_Alt_Shift, KeyState::Super, KeyState::Mod3});
413  if (filteredStates != states) {
414  return -1;
415  }
416 
417  const int idx = Key(sym_).digit();
418  if (idx >= 0) {
419  return (idx + 9) % 10;
420  }
421  return -1;
422 }
423 
424 bool Key::isUAZ() const {
425  return !states_ && sym_ >= FcitxKey_A && sym_ <= FcitxKey_Z;
426 }
427 
428 bool Key::isLAZ() const {
429  return !states_ && sym_ >= FcitxKey_a && sym_ <= FcitxKey_z;
430 
431  return false;
432 }
433 
434 bool Key::isSimple() const {
435  return !states_ && sym_ >= FcitxKey_space && sym_ <= FcitxKey_asciitilde;
436 }
437 
438 bool Key::isModifier() const {
439  return (sym_ == FcitxKey_Control_L || sym_ == FcitxKey_Control_R ||
440  sym_ == FcitxKey_Meta_L || sym_ == FcitxKey_Meta_R ||
441  sym_ == FcitxKey_Alt_L || sym_ == FcitxKey_Alt_R ||
442  sym_ == FcitxKey_Super_L || sym_ == FcitxKey_Super_R ||
443  sym_ == FcitxKey_Hyper_L || sym_ == FcitxKey_Hyper_R ||
444  sym_ == FcitxKey_Shift_L || sym_ == FcitxKey_Shift_R);
445 }
446 
447 bool Key::isCursorMove() const {
448  return ((sym_ == FcitxKey_Left || sym_ == FcitxKey_Right ||
449  sym_ == FcitxKey_Up || sym_ == FcitxKey_Down ||
450  sym_ == FcitxKey_Page_Up || sym_ == FcitxKey_Page_Down ||
451  sym_ == FcitxKey_Home || sym_ == FcitxKey_End ||
452  sym_ == FcitxKey_KP_Left || sym_ == FcitxKey_KP_Right ||
453  sym_ == FcitxKey_KP_Up || sym_ == FcitxKey_KP_Down ||
454  sym_ == FcitxKey_KP_Page_Up || sym_ == FcitxKey_KP_Page_Down ||
455  sym_ == FcitxKey_KP_Home || sym_ == FcitxKey_KP_End) &&
456  (states_ == KeyState::Ctrl || states_ == KeyState::Ctrl_Shift ||
457  states_ == KeyState::Shift || states_ == KeyState::NoState));
458 }
459 
460 bool Key::isKeyPad() const {
461  return ((sym_ >= FcitxKey_KP_Multiply && sym_ <= FcitxKey_KP_9) ||
462  (sym_ >= FcitxKey_KP_F1 && sym_ <= FcitxKey_KP_Delete) ||
463  sym_ == FcitxKey_KP_Space || sym_ == FcitxKey_KP_Tab ||
464  sym_ == FcitxKey_KP_Enter || sym_ == FcitxKey_KP_Equal);
465 }
466 
467 bool Key::hasModifier() const { return !!(states_ & KeyState::SimpleMask); }
468 
469 bool Key::isVirtual() const { return states_.test(KeyState::Virtual); }
470 
472  Key key(*this);
473  /* key state != 0 */
474  key.states_ =
475  key.states_ & KeyStates({KeyState::Ctrl_Alt_Shift, KeyState::Super,
476  KeyState::Mod3, KeyState::Super2});
477  if (key.states_.test(KeyState::Super2)) {
478  key.states_ = key.states_.unset(KeyState::Super2);
479  key.states_ |= KeyState::Super;
480  }
481  if (key.states_) {
482  if (key.states_ != KeyState::Shift && Key(key.sym_).isLAZ()) {
483  key.sym_ = static_cast<KeySym>(key.sym_ + FcitxKey_A - FcitxKey_a);
484  }
485  /*
486  * alt shift 1 should be alt + !
487  * shift+s should be S
488  */
489 
490  if (Key(key.sym_).isLAZ() || Key(key.sym_).isUAZ()) {
491  if (key.states_ == KeyState::Shift) {
492  key.states_ = 0;
493  }
494  } else {
495  if ((key.states_ & KeyState::Shift) &&
496  (((Key(key.sym_).isSimple() ||
497  keySymToUnicode(key.sym_) != 0) &&
498  key.sym_ != FcitxKey_space && key.sym_ != FcitxKey_Return) ||
499  (key.sym_ >= FcitxKey_KP_0 && key.sym_ <= FcitxKey_KP_9))) {
500  key.states_ ^= KeyState::Shift;
501  }
502  }
503  }
504 
505  if (key.sym_ == FcitxKey_ISO_Left_Tab) {
506  key.sym_ = FcitxKey_Tab;
507  }
508 
509  return key;
510 }
511 
512 bool Key::isValid() const {
513  // Sym is valid, or code is valid.
514  return (sym_ != FcitxKey_None && sym_ != FcitxKey_VoidSymbol) || code_ != 0;
515 }
516 
517 std::string Key::toString(KeyStringFormat format) const {
518 
519  std::string key;
520  if (code_ && sym_ == FcitxKey_None) {
521  key = "<";
522  key += std::to_string(code_);
523  key += ">";
524  } else {
525  auto sym = sym_;
526  if (sym == FcitxKey_None) {
527  return std::string();
528  }
529 
530  if (sym == FcitxKey_ISO_Left_Tab) {
531  sym = FcitxKey_Tab;
532  }
533  key = keySymToString(sym, format);
534  }
535 
536  if (key.empty()) {
537  return std::string();
538  }
539 
540  std::string str;
541  auto states = states_;
542  if (format == KeyStringFormat::Localized && isModifier()) {
543  states &= (~keySymToStates(sym_));
544  }
545 
546 #define _APPEND_MODIFIER_STRING(STR, VALUE) \
547  if (states & VALUE) { \
548  str += STR; \
549  str += "+"; \
550  }
551  if (format == KeyStringFormat::Portable) {
552  _APPEND_MODIFIER_STRING("Control", KeyState::Ctrl)
553  _APPEND_MODIFIER_STRING("Alt", KeyState::Alt)
554  _APPEND_MODIFIER_STRING("Shift", KeyState::Shift)
555  _APPEND_MODIFIER_STRING("Super",
556  (KeyStates{KeyState::Super, KeyState::Super2}))
557  _APPEND_MODIFIER_STRING("Hyper",
558  (KeyStates{KeyState::Hyper, KeyState::Hyper2}))
559  } else {
560  _APPEND_MODIFIER_STRING(C_("Key name", "Control"), KeyState::Ctrl)
561  _APPEND_MODIFIER_STRING(C_("Key name", "Alt"), KeyState::Alt)
562  _APPEND_MODIFIER_STRING(C_("Key name", "Shift"), KeyState::Shift)
563  _APPEND_MODIFIER_STRING(C_("Key name", "Super"),
564  (KeyStates{KeyState::Super, KeyState::Super2}))
565  _APPEND_MODIFIER_STRING(C_("Key name", "Hyper"),
566  (KeyStates{KeyState::Hyper, KeyState::Hyper2}))
567  }
568 
569 #undef _APPEND_MODIFIER_STRING
570  str += key;
571 
572  return str;
573 }
574 
576  switch (sym) {
577  case FcitxKey_Control_L:
578  case FcitxKey_Control_R:
579  return KeyState::Ctrl;
580  case FcitxKey_Alt_L:
581  case FcitxKey_Alt_R:
582  case FcitxKey_Meta_L:
583  case FcitxKey_Meta_R:
584  return KeyState::Alt;
585  case FcitxKey_Shift_L:
586  case FcitxKey_Shift_R:
587  return KeyState::Shift;
588  case FcitxKey_Super_L:
589  case FcitxKey_Super_R:
590  case FcitxKey_Hyper_L:
591  case FcitxKey_Hyper_R:
592  return KeyState::Super;
593  default:
594  return KeyStates();
595  }
596 }
597 
598 KeySym Key::keySymFromString(const std::string &keyString) {
599  const auto *value = std::lower_bound(
600  keyValueByNameOffset,
601  keyValueByNameOffset + FCITX_ARRAY_SIZE(keyValueByNameOffset),
602  keyString, [](const uint32_t &idx, const std::string &str) {
603  return keyNameList[&idx - keyValueByNameOffset] < str;
604  });
605 
606  if (value !=
607  keyValueByNameOffset + FCITX_ARRAY_SIZE(keyValueByNameOffset) &&
608  keyString == keyNameList[value - keyValueByNameOffset]) {
609  return static_cast<KeySym>(*value);
610  }
611 
612  const auto *compat = std::lower_bound(
613  keyNameListCompat,
614  keyNameListCompat + FCITX_ARRAY_SIZE(keyNameListCompat), keyString,
615  [](const KeyNameListCompat &c, const std::string &str) {
616  return c.name < str;
617  });
618  if (compat != keyNameListCompat + FCITX_ARRAY_SIZE(keyNameListCompat) &&
619  compat->name == keyString) {
620  return compat->sym;
621  }
622 
623  if (fcitx::utf8::lengthValidated(keyString) == 1) {
624  auto chr = fcitx::utf8::getChar(keyString);
625  if (chr > 0) {
626  if (fcitx::utf8::ncharByteLength(keyString.begin(), 1) == 1) {
627  return static_cast<KeySym>(keyString[0]);
628  }
629  return keySymFromUnicode(chr);
630  }
631  }
632 
633  return FcitxKey_None;
634 }
635 
636 std::string Key::keySymToString(KeySym sym, KeyStringFormat format) {
637  if (format == KeyStringFormat::Localized) {
638  if (const auto *name = lookupName(sym)) {
639  return C_("Key name", name);
640  }
641  auto code = keySymToUnicode(sym);
642  if (code < 0x7f) {
643  if (charutils::isprint(code)) {
644  return utf8::UCS4ToUTF8(code);
645  }
646  } else {
647  return utf8::UCS4ToUTF8(code);
648  }
649  }
650 
651  const KeyNameOffsetByValue *result = std::lower_bound(
652  keyNameOffsetByValue,
653  keyNameOffsetByValue + FCITX_ARRAY_SIZE(keyNameOffsetByValue), sym,
654  [](const KeyNameOffsetByValue &item, KeySym key) {
655  return item.sym < key;
656  });
657  if (result !=
658  keyNameOffsetByValue + FCITX_ARRAY_SIZE(keyNameOffsetByValue) &&
659  result->sym == sym) {
660  return keyNameList[result->offset];
661  }
662  return std::string();
663 }
664 
665 KeySym Key::keySymFromUnicode(uint32_t unicode) {
666  const auto &tab = unicode_to_keysym_tab();
667  int min = 0;
668  int max = tab.size() - 1;
669  int mid;
670 
671  /* first check for Latin-1 characters (1:1 mapping) */
672  if ((unicode >= 0x0020 && unicode <= 0x007e) ||
673  (unicode >= 0x00a0 && unicode <= 0x00ff))
674  return static_cast<KeySym>(unicode);
675 
676  /* special keysyms */
677  if ((unicode >= (FcitxKey_BackSpace & 0x7f) &&
678  unicode <= (FcitxKey_Clear & 0x7f)) ||
679  unicode == (FcitxKey_Return & 0x7f) ||
680  unicode == (FcitxKey_Escape & 0x7f))
681  return static_cast<KeySym>(unicode | 0xff00);
682  if (unicode == (FcitxKey_Delete & 0x7f))
683  return FcitxKey_Delete;
684 
685  /* Unicode non-symbols and code points outside Unicode planes */
686  if ((unicode >= 0xd800 && unicode <= 0xdfff) ||
687  (unicode >= 0xfdd0 && unicode <= 0xfdef) || unicode > 0x10ffff ||
688  (unicode & 0xfffe) == 0xfffe)
689  return FcitxKey_None;
690 
691  /* Binary search in table */
692  while (max >= min) {
693  mid = (min + max) / 2;
694  if (tab[mid].ucs < unicode) {
695  min = mid + 1;
696  } else if (tab[mid].ucs > unicode) {
697  max = mid - 1;
698  } else {
699  /* found it */
700  return static_cast<KeySym>(tab[mid].keysym);
701  }
702  }
703 
704  /*
705  * No matching keysym value found, return Unicode value plus 0x01000000
706  * (a convention introduced in the UTF-8 work on xterm).
707  */
708  return static_cast<KeySym>(unicode | 0x01000000);
709 }
710 
711 uint32_t Key::keySymToUnicode(KeySym sym) {
712  int min = 0;
713  int max =
714  sizeof(keysym_to_unicode_tab) / sizeof(keysym_to_unicode_tab[0]) - 1;
715  int mid;
716 
717  /* first check for Latin-1 characters (1:1 mapping) */
718  if ((sym >= 0x0020 && sym <= 0x007e) || (sym >= 0x00a0 && sym <= 0x00ff)) {
719  return sym;
720  }
721 
722  /* patch encoding botch */
723  if (sym == FcitxKey_KP_Space) {
724  return FcitxKey_space & 0x7f;
725  }
726 
727  /* special syms */
728  if ((sym >= FcitxKey_BackSpace && sym <= FcitxKey_Clear) ||
729  (sym >= FcitxKey_KP_Multiply && sym <= FcitxKey_KP_9) ||
730  sym == FcitxKey_Return || sym == FcitxKey_Escape ||
731  sym == FcitxKey_Delete || sym == FcitxKey_KP_Tab ||
732  sym == FcitxKey_KP_Enter || sym == FcitxKey_KP_Equal) {
733  return sym & 0x7f;
734  }
735 
736  /* also check for directly encoded Unicode codepoints */
737 
738  /* Exclude surrogates: they are invalid in UTF-32.
739  * See https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf#G28875
740  * for further details.
741  */
742  if (0x0100d800 <= sym && sym <= 0x0100dfff) {
743  return 0;
744  }
745  /*
746  * In theory, this is supposed to start from 0x100100, such that the ASCII
747  * range, which is already covered by 0x00-0xff, can't be encoded in two
748  * ways. However, changing this after a couple of decades probably won't
749  * go well, so it stays as it is.
750  */
751  if (0x01000000 <= sym && sym <= 0x0110ffff) {
752  const uint32_t code = sym - 0x01000000;
753  if (utf8::UCS4IsValid(code)) {
754  return code;
755  }
756  return 0;
757  }
758 
759  /* binary search in table */
760  while (max >= min) {
761  mid = (min + max) / 2;
762  if (keysym_to_unicode_tab[mid].keysym < sym) {
763  min = mid + 1;
764  } else if (keysym_to_unicode_tab[mid].keysym > sym) {
765  max = mid - 1;
766  } else {
767  /* found it */
768  return keysym_to_unicode_tab[mid].ucs;
769  }
770  }
771 
772  /* No matching Unicode value found */
773  return 0;
774 }
775 
776 std::string Key::keySymToUTF8(KeySym sym) {
777  auto code = keySymToUnicode(sym);
778  if (!utf8::UCS4IsValid(code)) {
779  return "";
780  }
781  return utf8::UCS4ToUTF8(code);
782 }
783 
784 std::vector<Key> Key::keyListFromString(const std::string &str) {
785  std::vector<Key> keyList;
786 
787  auto lastPos = str.find_first_not_of(FCITX_WHITESPACE, 0);
788  auto pos = str.find_first_of(FCITX_WHITESPACE, lastPos);
789 
790  while (std::string::npos != pos || std::string::npos != lastPos) {
791  Key key(str.substr(lastPos, pos - lastPos));
792 
793  if (key.sym() != FcitxKey_None) {
794  keyList.push_back(key);
795  }
796  lastPos = str.find_first_not_of(FCITX_WHITESPACE, pos);
797  pos = str.find_first_of(FCITX_WHITESPACE, lastPos);
798  }
799 
800  return keyList;
801 }
802 } // namespace fcitx
static KeyList keyListFromString(const std::string &str)
Parse a list of key string into a KeyList.
Definition: key.cpp:784
bool check(const Key &key) const
Check if current key match the key.
Definition: key.cpp:363
bool hasModifier() const
Check if states has modifier.
Definition: key.cpp:467
Describe a Key in fcitx.
Definition: key.h:40
bool checkKeyList(const Container &c) const
Check the current key against a key list.
Definition: key.h:221
bool isSimple() const
Check if key is in the range of ascii and has no states.
Definition: key.cpp:434
std::string UCS4ToUTF8(uint32_t code)
Convert UCS4 to UTF8 string.
Definition: utf8.cpp:19
bool endsWith(std::string_view str, std::string_view suffix)
Check if a string ends with a suffix.
Definition: stringutils.cpp:92
static uint32_t keySymToUnicode(KeySym sym)
Convert keysym to a unicode.
Definition: key.cpp:711
bool isCursorMove() const
Check if this key will cause cursor to move, e.g.
Definition: key.cpp:447
Definition: action.cpp:12
bool isValid() const
Check if the sym is not FcitxKey_None or FcitxKey_VoidSymbol.
Definition: key.cpp:512
bool isDigit() const
Check if key is digit key or keypad digit key.
Definition: key.cpp:391
KeyStringFormat
Control the behavior of toString function.
Definition: key.h:32
C++ Utility functions for handling utf8 strings.
int digit() const
Return the value of digit key.
Definition: key.cpp:396
bool isVirtual() const
Check if states has virtual bit.
Definition: key.cpp:469
Can be used to parse from a string.
size_t lengthValidated(Iter start, Iter end)
Validate and return the number UTF-8 characters in the string iterator range.
Definition: utf8.h:53
uint32_t getChar(Iter iter, Iter end)
Get next UCS4 char from iter, do not cross end.
Definition: utf8.h:97
static KeyStates keySymToStates(KeySym sym)
Convert the modifier symbol to its corresponding states.
Definition: key.cpp:575
bool isModifier() const
Check if the key is a modifier press.
Definition: key.cpp:438
static KeySym keySymFromString(const std::string &keyString)
Convert a key symbol string to KeySym.
Definition: key.cpp:598
bool UCS4IsValid(uint32_t code)
Check if a ucs4 is valid.
Definition: utf8.cpp:13
bool isReleaseOfModifier(const Key &key) const
Check if current key is a key release of given modifier only key.
Definition: key.cpp:328
Return the human readable string in localized format.
static std::string keySymToUTF8(KeySym sym)
Convert keysym to a unicode string.
Definition: key.cpp:776
bool isKeyPad() const
Check if this key is a key pad key.
Definition: key.cpp:460
static std::string keySymToString(KeySym sym, KeyStringFormat format=KeyStringFormat::Portable)
Convert keysym to a string.
Definition: key.cpp:636
bool startsWith(std::string_view str, std::string_view prefix)
Check if a string starts with a prefix.
Definition: stringutils.cpp:79
String handle utilities.
int digitSelection(KeyStates states=KeyStates()) const
Return index when using digit key for selection.
Definition: key.cpp:409
Class to represent a key.
std::string toString(KeyStringFormat format=KeyStringFormat::Portable) const
Convert key to a string.
Definition: key.cpp:517
Local independent API to detect character type.
bool isLAZ() const
Check if key is lower case.
Definition: key.cpp:428
static KeySym keySymFromUnicode(uint32_t unicode)
Convert unicode to key symbol.
Definition: key.cpp:665
bool isUAZ() const
Check if key is upper case.
Definition: key.cpp:424
ssize_t ncharByteLength(Iter iter, size_t n)
get the byte length of next N utf-8 character.
Definition: utf8.h:121
Key normalize() const
Normalize a key, usually used when key is from frontend.
Definition: key.cpp:471