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