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