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
makeLookupKeyNameMap()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", "PgUp")},
52 {FcitxKey_Page_Down, NC_("Key name", "PgDown")},
53 {FcitxKey_Caps_Lock, NC_("Key name", "CapsLock")},
54 {FcitxKey_Num_Lock, NC_("Key name", "NumLock")},
55 {FcitxKey_Scroll_Lock, NC_("Key name", "ScrollLock")},
56 {FcitxKey_Menu, NC_("Key name", "Menu")},
57 {FcitxKey_Help, NC_("Key name", "Help")},
58 {FcitxKey_Back, NC_("Key name", "Back")},
59 {FcitxKey_Forward, NC_("Key name", "Forward")},
60 {FcitxKey_Stop, NC_("Key name", "Stop")},
61 {FcitxKey_Refresh, NC_("Key name", "Refresh")},
62 {FcitxKey_AudioLowerVolume, NC_("Key name", "Volume Down")},
63 {FcitxKey_AudioMute, NC_("Key name", "Volume Mute")},
64 {FcitxKey_AudioRaiseVolume, NC_("Key name", "Volume Up")},
65 {FcitxKey_AudioPlay, NC_("Key name", "Media Play")},
66 {FcitxKey_AudioStop, NC_("Key name", "Media Stop")},
67 {FcitxKey_AudioPrev, NC_("Key name", "Media Previous")},
68 {FcitxKey_AudioNext, NC_("Key name", "Media Next")},
69 {FcitxKey_AudioRecord, NC_("Key name", "Media Record")},
70 {FcitxKey_AudioPause, NC_("Key name", "Media Pause")},
71 {FcitxKey_HomePage, NC_("Key name", "Home Page")},
72 {FcitxKey_Favorites, NC_("Key name", "Favorites")},
73 {FcitxKey_Search, NC_("Key name", "Search")},
74 {FcitxKey_Standby, NC_("Key name", "Standby")},
75 {FcitxKey_OpenURL, NC_("Key name", "Open URL")},
76 {FcitxKey_Mail, NC_("Key name", "Launch Mail")},
77 {FcitxKey_Launch0, NC_("Key name", "Launch (0)")},
78 {FcitxKey_Launch1, NC_("Key name", "Launch (1)")},
79 {FcitxKey_Launch2, NC_("Key name", "Launch (2)")},
80 {FcitxKey_Launch3, NC_("Key name", "Launch (3)")},
81 {FcitxKey_Launch4, NC_("Key name", "Launch (4)")},
82 {FcitxKey_Launch5, NC_("Key name", "Launch (5)")},
83 {FcitxKey_Launch6, NC_("Key name", "Launch (6)")},
84 {FcitxKey_Launch7, NC_("Key name", "Launch (7)")},
85 {FcitxKey_Launch8, NC_("Key name", "Launch (8)")},
86 {FcitxKey_Launch9, NC_("Key name", "Launch (9)")},
87 {FcitxKey_LaunchA, NC_("Key name", "Launch (A)")},
88 {FcitxKey_LaunchB, NC_("Key name", "Launch (B)")},
89 {FcitxKey_LaunchC, NC_("Key name", "Launch (C)")},
90 {FcitxKey_LaunchD, NC_("Key name", "Launch (D)")},
91 {FcitxKey_LaunchE, NC_("Key name", "Launch (E)")},
92 {FcitxKey_LaunchF, NC_("Key name", "Launch (F)")},
93 {FcitxKey_MonBrightnessUp, NC_("Key name", "Monitor Brightness Up")},
94 {FcitxKey_MonBrightnessDown,
95 NC_("Key name", "Monitor Brightness Down")},
96 {FcitxKey_KbdLightOnOff, NC_("Key name", "Keyboard Light On/Off")},
97 {FcitxKey_KbdBrightnessUp, NC_("Key name", "Keyboard Brightness Up")},
98 {FcitxKey_KbdBrightnessDown,
99 NC_("Key name", "Keyboard Brightness Down")},
100 {FcitxKey_PowerOff, NC_("Key name", "Power Off")},
101 {FcitxKey_WakeUp, NC_("Key name", "Wake Up")},
102 {FcitxKey_Eject, NC_("Key name", "Eject")},
103 {FcitxKey_ScreenSaver, NC_("Key name", "Screensaver")},
104 {FcitxKey_WWW, NC_("Key name", "WWW")},
105 {FcitxKey_Sleep, NC_("Key name", "Sleep")},
106 {FcitxKey_LightBulb, NC_("Key name", "LightBulb")},
107 {FcitxKey_Shop, NC_("Key name", "Shop")},
108 {FcitxKey_History, NC_("Key name", "History")},
109 {FcitxKey_AddFavorite, NC_("Key name", "Add Favorite")},
110 {FcitxKey_HotLinks, NC_("Key name", "Hot Links")},
111 {FcitxKey_BrightnessAdjust, NC_("Key name", "Adjust Brightness")},
112 {FcitxKey_Finance, NC_("Key name", "Finance")},
113 {FcitxKey_Community, NC_("Key name", "Community")},
114 {FcitxKey_AudioRewind, NC_("Key name", "Media Rewind")},
115 {FcitxKey_BackForward, NC_("Key name", "Back Forward")},
116 {FcitxKey_ApplicationLeft, NC_("Key name", "Application Left")},
117 {FcitxKey_ApplicationRight, NC_("Key name", "Application Right")},
118 {FcitxKey_Book, NC_("Key name", "Book")},
119 {FcitxKey_CD, NC_("Key name", "CD")},
120 {FcitxKey_Calculator, NC_("Key name", "Calculator")},
121 {FcitxKey_Clear, NC_("Key name", "Clear")},
122 {FcitxKey_Close, NC_("Key name", "Close")},
123 {FcitxKey_Copy, NC_("Key name", "Copy")},
124 {FcitxKey_Cut, NC_("Key name", "Cut")},
125 {FcitxKey_Display, NC_("Key name", "Display")},
126 {FcitxKey_DOS, NC_("Key name", "DOS")},
127 {FcitxKey_Documents, NC_("Key name", "Documents")},
128 {FcitxKey_Excel, NC_("Key name", "Spreadsheet")},
129 {FcitxKey_Explorer, NC_("Key name", "Browser")},
130 {FcitxKey_Game, NC_("Key name", "Game")},
131 {FcitxKey_Go, NC_("Key name", "Go")},
132 {FcitxKey_iTouch, NC_("Key name", "iTouch")},
133 {FcitxKey_LogOff, NC_("Key name", "Logoff")},
134 {FcitxKey_Market, NC_("Key name", "Market")},
135 {FcitxKey_Meeting, NC_("Key name", "Meeting")},
136 {FcitxKey_MenuKB, NC_("Key name", "Keyboard Menu")},
137 {FcitxKey_MenuPB, NC_("Key name", "Menu PB")},
138 {FcitxKey_MySites, NC_("Key name", "My Sites")},
139 {FcitxKey_News, NC_("Key name", "News")},
140 {FcitxKey_OfficeHome, NC_("Key name", "Home Office")},
141 {FcitxKey_Option, NC_("Key name", "Option")},
142 {FcitxKey_Paste, NC_("Key name", "Paste")},
143 {FcitxKey_Phone, NC_("Key name", "Phone")},
144 {FcitxKey_Reply, NC_("Key name", "Reply")},
145 {FcitxKey_Reload, NC_("Key name", "Reload")},
146 {FcitxKey_RotateWindows, NC_("Key name", "Rotate Windows")},
147 {FcitxKey_RotationPB, NC_("Key name", "Rotation PB")},
148 {FcitxKey_RotationKB, NC_("Key name", "Rotation KB")},
149 {FcitxKey_Save, NC_("Key name", "Save")},
150 {FcitxKey_Send, NC_("Key name", "Send")},
151 {FcitxKey_Spell, NC_("Key name", "Spellchecker")},
152 {FcitxKey_SplitScreen, NC_("Key name", "Split Screen")},
153 {FcitxKey_Support, NC_("Key name", "Support")},
154 {FcitxKey_TaskPane, NC_("Key name", "Task Panel")},
155 {FcitxKey_Terminal, NC_("Key name", "Terminal")},
156 {FcitxKey_Tools, NC_("Key name", "Tools")},
157 {FcitxKey_Travel, NC_("Key name", "Travel")},
158 {FcitxKey_Video, NC_("Key name", "Video")},
159 {FcitxKey_Word, NC_("Key name", "Word Processor")},
160 {FcitxKey_Xfer, NC_("Key name", "XFer")},
161 {FcitxKey_ZoomIn, NC_("Key name", "Zoom In")},
162 {FcitxKey_ZoomOut, NC_("Key name", "Zoom Out")},
163 {FcitxKey_Away, NC_("Key name", "Away")},
164 {FcitxKey_Messenger, NC_("Key name", "Messenger")},
165 {FcitxKey_WebCam, NC_("Key name", "WebCam")},
166 {FcitxKey_MailForward, NC_("Key name", "Mail Forward")},
167 {FcitxKey_Pictures, NC_("Key name", "Pictures")},
168 {FcitxKey_Music, NC_("Key name", "Music")},
169 {FcitxKey_Battery, NC_("Key name", "Battery")},
170 {FcitxKey_Bluetooth, NC_("Key name", "Bluetooth")},
171 {FcitxKey_WLAN, NC_("Key name", "Wireless")},
172 {FcitxKey_AudioForward, NC_("Key name", "Media Fast Forward")},
173 {FcitxKey_AudioRepeat, NC_("Key name", "Audio Repeat")},
174 {FcitxKey_AudioRandomPlay, NC_("Key name", "Audio Random Play")},
175 {FcitxKey_Subtitle, NC_("Key name", "Subtitle")},
176 {FcitxKey_AudioCycleTrack, NC_("Key name", "Audio Cycle Track")},
177 {FcitxKey_Time, NC_("Key name", "Time")},
178 {FcitxKey_Hibernate, NC_("Key name", "Hibernate")},
179 {FcitxKey_View, NC_("Key name", "View")},
180 {FcitxKey_TopMenu, NC_("Key name", "Top Menu")},
181 {FcitxKey_PowerDown, NC_("Key name", "Power Down")},
182 {FcitxKey_Suspend, NC_("Key name", "Suspend")},
183 {FcitxKey_AudioMicMute, NC_("Key name", "Microphone Mute")},
184 {FcitxKey_Red, NC_("Key name", "Red")},
185 {FcitxKey_Green, NC_("Key name", "Green")},
186 {FcitxKey_Yellow, NC_("Key name", "Yellow")},
187 {FcitxKey_Blue, NC_("Key name", "Blue")},
188 {FcitxKey_New, NC_("Key name", "New")},
189 {FcitxKey_Open, NC_("Key name", "Open")},
190 {FcitxKey_Find, NC_("Key name", "Find")},
191 {FcitxKey_Undo, NC_("Key name", "Undo")},
192 {FcitxKey_Redo, NC_("Key name", "Redo")},
193 {FcitxKey_Print, NC_("Key name", "Print Screen")},
194 {FcitxKey_Insert, NC_("Key name", "Insert")},
195 {FcitxKey_Delete, NC_("Key name", "Delete")},
196 {FcitxKey_Escape, NC_("Key name", "Escape")},
197 {FcitxKey_Sys_Req, NC_("Key name", "System Request")},
198 {FcitxKey_Select, NC_("Key name", "Select")},
199 {FcitxKey_Kanji, NC_("Key name", "Kanji")},
200 {FcitxKey_Muhenkan, NC_("Key name", "Muhenkan")},
201 {FcitxKey_Henkan, NC_("Key name", "Henkan")},
202 {FcitxKey_Romaji, NC_("Key name", "Romaji")},
203 {FcitxKey_Hiragana, NC_("Key name", "Hiragana")},
204 {FcitxKey_Katakana, NC_("Key name", "Katakana")},
205 {FcitxKey_Hiragana_Katakana, NC_("Key name", "Hiragana Katakana")},
206 {FcitxKey_Zenkaku, NC_("Key name", "Zenkaku")},
207 {FcitxKey_Hankaku, NC_("Key name", "Hankaku")},
208 {FcitxKey_Zenkaku_Hankaku, NC_("Key name", "Zenkaku Hankaku")},
209 {FcitxKey_Touroku, NC_("Key name", "Touroku")},
210 {FcitxKey_Massyo, NC_("Key name", "Massyo")},
211 {FcitxKey_Kana_Lock, NC_("Key name", "Kana Lock")},
212 {FcitxKey_Kana_Shift, NC_("Key name", "Kana Shift")},
213 {FcitxKey_Eisu_Shift, NC_("Key name", "Eisu Shift")},
214 {FcitxKey_Eisu_toggle, NC_("Key name", "Eisu toggle")},
215 {FcitxKey_Codeinput, NC_("Key name", "Code input")},
216 {FcitxKey_MultipleCandidate, NC_("Key name", "Multiple Candidate")},
217 {FcitxKey_PreviousCandidate, NC_("Key name", "Previous Candidate")},
218 {FcitxKey_Hangul, NC_("Key name", "Hangul")},
219 {FcitxKey_Hangul_Start, NC_("Key name", "Hangul Start")},
220 {FcitxKey_Hangul_End, NC_("Key name", "Hangul End")},
221 {FcitxKey_Hangul_Hanja, NC_("Key name", "Hangul Hanja")},
222 {FcitxKey_Hangul_Jamo, NC_("Key name", "Hangul Jamo")},
223 {FcitxKey_Hangul_Romaja, NC_("Key name", "Hangul Romaja")},
224 {FcitxKey_Hangul_Jeonja, NC_("Key name", "Hangul Jeonja")},
225 {FcitxKey_Hangul_Banja, NC_("Key name", "Hangul Banja")},
226 {FcitxKey_Hangul_PreHanja, NC_("Key name", "Hangul PreHanja")},
227 {FcitxKey_Hangul_PostHanja, NC_("Key name", "Hangul PostHanja")},
228 {FcitxKey_Hangul_Special, NC_("Key name", "Hangul Special")},
229 {FcitxKey_Cancel, NC_("Key name", "Cancel")},
230 {FcitxKey_Execute, NC_("Key name", "Execute")},
231 {FcitxKey_TouchpadToggle, NC_("Key name", "Touchpad Toggle")},
232 {FcitxKey_TouchpadOn, NC_("Key name", "Touchpad On")},
233 {FcitxKey_TouchpadOff, NC_("Key name", "Touchpad Off")},
234 };
235 std::unordered_map<KeySym, const char *, EnumHash> result;
236 for (const auto &item : keyname) {
237 result[item.key] = item.name;
238 }
239 return result;
240 }
241
lookupName(KeySym sym)242 const char *lookupName(KeySym sym) {
243 static const std::unordered_map<KeySym, const char *, EnumHash> map =
244 makeLookupKeyNameMap();
245 const auto *result = findValue(map, sym);
246 return result ? *result : nullptr;
247 }
248 } // namespace
249
Key(const char * keyString)250 Key::Key(const char *keyString) : Key() {
251 KeyStates states;
252 /* old compatible code */
253 const char *p = keyString;
254 const char *lastModifier = keyString;
255 const char *found = nullptr;
256
257 #define _CHECK_MODIFIER(NAME, VALUE) \
258 if ((found = strstr(p, NAME))) { \
259 states |= KeyState::VALUE; \
260 if (found + strlen(NAME) > lastModifier) { \
261 lastModifier = found + strlen(NAME); \
262 } \
263 }
264
265 _CHECK_MODIFIER("CTRL_", Ctrl)
266 _CHECK_MODIFIER("Control+", Ctrl)
267 _CHECK_MODIFIER("ALT_", Alt)
268 _CHECK_MODIFIER("Alt+", Alt)
269 _CHECK_MODIFIER("SHIFT_", Shift)
270 _CHECK_MODIFIER("Shift+", Shift)
271 _CHECK_MODIFIER("SUPER_", Super)
272 _CHECK_MODIFIER("Super+", Super)
273 _CHECK_MODIFIER("HYPER_", Mod3)
274 _CHECK_MODIFIER("Hyper+", Mod3)
275
276 #undef _CHECK_MODIFIER
277
278 // Special code for keycode baesd parsing.
279 std::string keyValue = lastModifier;
280 if (stringutils::startsWith(keyValue, "<") &&
281 stringutils::endsWith(keyValue, ">")) {
282 try {
283 code_ = std::stoi(keyValue.substr(1, keyValue.size() - 2));
284 } catch (const std::exception &) {
285 }
286 } else {
287 sym_ = keySymFromString(lastModifier);
288 }
289 states_ = states;
290 }
291
isReleaseOfModifier(const Key & key) const292 bool Key::isReleaseOfModifier(const Key &key) const {
293 if (!key.isModifier()) {
294 return false;
295 }
296 auto states = keySymToStates(key.sym()) | key.states();
297 // Now we need reverse of keySymToStates.
298
299 std::vector<Key> keys;
300 keys.emplace_back(key.sym(), states);
301 if (key.states() & KeyState::Ctrl) {
302 keys.emplace_back(FcitxKey_Control_L, states);
303 keys.emplace_back(FcitxKey_Control_R, states);
304 }
305 if (key.states() & KeyState::Alt) {
306 keys.emplace_back(FcitxKey_Alt_L, states);
307 keys.emplace_back(FcitxKey_Alt_R, states);
308 keys.emplace_back(FcitxKey_Meta_L, states);
309 keys.emplace_back(FcitxKey_Meta_R, states);
310 }
311 if (key.states() & KeyState::Shift) {
312 keys.emplace_back(FcitxKey_Shift_L, states);
313 keys.emplace_back(FcitxKey_Shift_R, states);
314 }
315 if (key.states() & KeyState::Super) {
316 keys.emplace_back(FcitxKey_Super_L, states);
317 keys.emplace_back(FcitxKey_Super_R, states);
318 }
319 if (key.states() & KeyState::Mod3) {
320 keys.emplace_back(FcitxKey_Hyper_L, states);
321 keys.emplace_back(FcitxKey_Hyper_R, states);
322 }
323
324 return checkKeyList(keys);
325 }
326
check(const Key & key) const327 bool Key::check(const Key &key) const {
328 auto states = states_ & KeyStates({KeyState::Ctrl_Alt_Shift,
329 KeyState::Super, KeyState::Mod3});
330
331 // key is keycode based, do key code based check.
332 if (key.code()) {
333 return key.states_ == states && key.code_ == code_;
334 }
335
336 if (isModifier()) {
337 Key keyAlt = *this;
338 auto states = states_ & (~keySymToStates(sym_));
339 keyAlt.states_ |= keySymToStates(sym_);
340
341 return (key.sym_ == sym_ && key.states_ == states) ||
342 (key.sym_ == keyAlt.sym_ && key.states_ == keyAlt.states_);
343 }
344
345 return (key.sym_ == sym_ && key.states_ == states);
346 }
347
isDigit() const348 bool Key::isDigit() const {
349 return !states_ && sym_ >= FcitxKey_0 && sym_ <= FcitxKey_9;
350 }
351
isUAZ() const352 bool Key::isUAZ() const {
353 return !states_ && sym_ >= FcitxKey_A && sym_ <= FcitxKey_Z;
354 }
355
isLAZ() const356 bool Key::isLAZ() const {
357 return !states_ && sym_ >= FcitxKey_a && sym_ <= FcitxKey_z;
358
359 return false;
360 }
361
isSimple() const362 bool Key::isSimple() const {
363 return !states_ && sym_ >= FcitxKey_space && sym_ <= FcitxKey_asciitilde;
364 }
365
isModifier() const366 bool Key::isModifier() const {
367 return (sym_ == FcitxKey_Control_L || sym_ == FcitxKey_Control_R ||
368 sym_ == FcitxKey_Meta_L || sym_ == FcitxKey_Meta_R ||
369 sym_ == FcitxKey_Alt_L || sym_ == FcitxKey_Alt_R ||
370 sym_ == FcitxKey_Super_L || sym_ == FcitxKey_Super_R ||
371 sym_ == FcitxKey_Hyper_L || sym_ == FcitxKey_Hyper_R ||
372 sym_ == FcitxKey_Shift_L || sym_ == FcitxKey_Shift_R);
373 }
374
isCursorMove() const375 bool Key::isCursorMove() const {
376 return ((sym_ == FcitxKey_Left || sym_ == FcitxKey_Right ||
377 sym_ == FcitxKey_Up || sym_ == FcitxKey_Down ||
378 sym_ == FcitxKey_Page_Up || sym_ == FcitxKey_Page_Down ||
379 sym_ == FcitxKey_Home || sym_ == FcitxKey_End ||
380 sym_ == FcitxKey_KP_Left || sym_ == FcitxKey_KP_Right ||
381 sym_ == FcitxKey_KP_Up || sym_ == FcitxKey_KP_Down ||
382 sym_ == FcitxKey_KP_Page_Up || sym_ == FcitxKey_KP_Page_Down ||
383 sym_ == FcitxKey_KP_Home || sym_ == FcitxKey_KP_End) &&
384 (states_ == KeyState::Ctrl || states_ == KeyState::Ctrl_Shift ||
385 states_ == KeyState::Shift || states_ == KeyState::NoState));
386 }
387
isKeyPad() const388 bool Key::isKeyPad() const {
389 return ((sym_ >= FcitxKey_KP_Multiply && sym_ <= FcitxKey_KP_9) ||
390 (sym_ >= FcitxKey_KP_F1 && sym_ <= FcitxKey_KP_Delete) ||
391 sym_ == FcitxKey_KP_Space || sym_ == FcitxKey_KP_Tab ||
392 sym_ == FcitxKey_KP_Enter || sym_ == FcitxKey_KP_Equal);
393 }
394
hasModifier() const395 bool Key::hasModifier() const { return !!(states_ & KeyState::SimpleMask); }
396
normalize() const397 Key Key::normalize() const {
398 Key key(*this);
399 /* key state != 0 */
400 key.states_ = key.states_ & KeyStates({KeyState::Ctrl_Alt_Shift,
401 KeyState::Super, KeyState::Mod3});
402 if (key.states_) {
403 if (key.states_ != KeyState::Shift && Key(key.sym_).isLAZ()) {
404 key.sym_ = static_cast<KeySym>(key.sym_ + FcitxKey_A - FcitxKey_a);
405 }
406 /*
407 * alt shift 1 should be alt + !
408 * shift+s should be S
409 */
410
411 if (Key(key.sym_).isLAZ() || Key(key.sym_).isUAZ()) {
412 if (key.states_ == KeyState::Shift) {
413 key.states_ = 0;
414 }
415 } else {
416 if ((key.states_ & KeyState::Shift) &&
417 (((Key(key.sym_).isSimple() ||
418 keySymToUnicode(key.sym_) != 0) &&
419 key.sym_ != FcitxKey_space && key.sym_ != FcitxKey_Return) ||
420 (key.sym_ >= FcitxKey_KP_0 && key.sym_ <= FcitxKey_KP_9))) {
421 key.states_ ^= KeyState::Shift;
422 }
423 }
424 }
425
426 if (key.sym_ == FcitxKey_ISO_Left_Tab) {
427 key.sym_ = FcitxKey_Tab;
428 }
429
430 return key;
431 }
432
isValid() const433 bool Key::isValid() const {
434 // Sym is valid, or code is valid.
435 return (sym_ != FcitxKey_None && sym_ != FcitxKey_VoidSymbol) || code_ != 0;
436 }
437
toString(KeyStringFormat format) const438 std::string Key::toString(KeyStringFormat format) const {
439
440 std::string key;
441 if (code_ && sym_ == FcitxKey_None) {
442 key = "<";
443 key += std::to_string(code_);
444 key += ">";
445 } else {
446 auto sym = sym_;
447 if (sym == FcitxKey_None) {
448 return std::string();
449 }
450
451 if (sym == FcitxKey_ISO_Left_Tab) {
452 sym = FcitxKey_Tab;
453 }
454 key = keySymToString(sym, format);
455 }
456
457 if (key.empty()) {
458 return std::string();
459 }
460
461 std::string str;
462 auto states = states_;
463 if (format == KeyStringFormat::Localized && isModifier()) {
464 states &= (~keySymToStates(sym_));
465 }
466
467 #define _APPEND_MODIFIER_STRING(STR, VALUE) \
468 if (states & KeyState::VALUE) { \
469 str += STR; \
470 str += "+"; \
471 }
472 if (format == KeyStringFormat::Portable) {
473 _APPEND_MODIFIER_STRING("Control", Ctrl)
474 _APPEND_MODIFIER_STRING("Alt", Alt)
475 _APPEND_MODIFIER_STRING("Shift", Shift)
476 _APPEND_MODIFIER_STRING("Super", Super)
477 _APPEND_MODIFIER_STRING("Hyper", Mod3)
478 } else {
479 _APPEND_MODIFIER_STRING(C_("Key name", "Control"), Ctrl)
480 _APPEND_MODIFIER_STRING(C_("Key name", "Alt"), Alt)
481 _APPEND_MODIFIER_STRING(C_("Key name", "Shift"), Shift)
482 _APPEND_MODIFIER_STRING(C_("Key name", "Super"), Super)
483 _APPEND_MODIFIER_STRING(C_("Key name", "Hyper"), Mod3)
484 }
485
486 #undef _APPEND_MODIFIER_STRING
487 str += key;
488
489 return str;
490 }
491
keySymToStates(KeySym sym)492 KeyStates Key::keySymToStates(KeySym sym) {
493 switch (sym) {
494 case FcitxKey_Control_L:
495 case FcitxKey_Control_R:
496 return KeyState::Ctrl;
497 case FcitxKey_Alt_L:
498 case FcitxKey_Alt_R:
499 case FcitxKey_Meta_L:
500 case FcitxKey_Meta_R:
501 return KeyState::Alt;
502 case FcitxKey_Shift_L:
503 case FcitxKey_Shift_R:
504 return KeyState::Shift;
505 case FcitxKey_Super_L:
506 case FcitxKey_Super_R:
507 return KeyState::Super;
508 case FcitxKey_Hyper_L:
509 case FcitxKey_Hyper_R:
510 return KeyState::Mod3;
511 default:
512 return KeyStates();
513 }
514 }
515
keySymFromString(const std::string & keyString)516 KeySym Key::keySymFromString(const std::string &keyString) {
517 const auto *value = std::lower_bound(
518 keyValueByNameOffset,
519 keyValueByNameOffset + FCITX_ARRAY_SIZE(keyValueByNameOffset),
520 keyString, [](const uint32_t &idx, const std::string &str) {
521 return keyNameList[&idx - keyValueByNameOffset] < str;
522 });
523
524 if (value !=
525 keyValueByNameOffset + FCITX_ARRAY_SIZE(keyValueByNameOffset) &&
526 keyString == keyNameList[value - keyValueByNameOffset]) {
527 return static_cast<KeySym>(*value);
528 }
529
530 const auto *compat = std::lower_bound(
531 keyNameListCompat,
532 keyNameListCompat + FCITX_ARRAY_SIZE(keyNameListCompat), keyString,
533 [](const KeyNameListCompat &c, const std::string &str) {
534 return c.name < str;
535 });
536 if (compat != keyNameListCompat + FCITX_ARRAY_SIZE(keyNameListCompat) &&
537 compat->name == keyString) {
538 return compat->sym;
539 }
540
541 if (fcitx::utf8::lengthValidated(keyString) == 1) {
542 auto chr = fcitx::utf8::getChar(keyString);
543 if (chr > 0) {
544 if (fcitx::utf8::ncharByteLength(keyString.begin(), 1) == 1) {
545 return static_cast<KeySym>(keyString[0]);
546 }
547 return keySymFromUnicode(chr);
548 }
549 }
550
551 return FcitxKey_None;
552 }
553
keySymToString(KeySym sym,KeyStringFormat format)554 std::string Key::keySymToString(KeySym sym, KeyStringFormat format) {
555 if (format == KeyStringFormat::Localized) {
556 if (const auto *name = lookupName(sym)) {
557 return C_("Key name", name);
558 }
559 auto code = keySymToUnicode(sym);
560 if (code < 0x7f) {
561 if (charutils::isprint(code)) {
562 return utf8::UCS4ToUTF8(code);
563 }
564 } else {
565 return utf8::UCS4ToUTF8(code);
566 }
567 }
568
569 const KeyNameOffsetByValue *result = std::lower_bound(
570 keyNameOffsetByValue,
571 keyNameOffsetByValue + FCITX_ARRAY_SIZE(keyNameOffsetByValue), sym,
572 [](const KeyNameOffsetByValue &item, KeySym key) {
573 return item.sym < key;
574 });
575 if (result !=
576 keyNameOffsetByValue + FCITX_ARRAY_SIZE(keyNameOffsetByValue) &&
577 result->sym == sym) {
578 return keyNameList[result->offset];
579 }
580 return std::string();
581 }
582
keySymFromUnicode(uint32_t unicode)583 KeySym Key::keySymFromUnicode(uint32_t unicode) {
584 int min = 0;
585 int max = sizeof(gdk_unicode_to_keysym_tab) /
586 sizeof(gdk_unicode_to_keysym_tab[0]) -
587 1;
588 int mid;
589
590 /* First check for Latin-1 characters (1:1 mapping) */
591 if ((unicode >= 0x0020 && unicode <= 0x007e) ||
592 (unicode >= 0x00a0 && unicode <= 0x00ff)) {
593 return static_cast<KeySym>(unicode);
594 }
595
596 /* Binary search in table */
597 while (max >= min) {
598 mid = (min + max) / 2;
599 if (gdk_unicode_to_keysym_tab[mid].ucs < unicode) {
600 min = mid + 1;
601 } else if (gdk_unicode_to_keysym_tab[mid].ucs > unicode) {
602 max = mid - 1;
603 } else {
604 /* found it */
605 return static_cast<KeySym>(gdk_unicode_to_keysym_tab[mid].keysym);
606 }
607 }
608
609 /*
610 * No matching keysym value found, return Unicode value plus 0x01000000
611 * (a convention introduced in the UTF-8 work on xterm).
612 */
613 return static_cast<KeySym>(unicode | 0x01000000);
614 }
615
keySymToUnicode(KeySym sym)616 uint32_t Key::keySymToUnicode(KeySym sym) {
617 int min = 0;
618 int max = sizeof(gdk_keysym_to_unicode_tab) /
619 sizeof(gdk_keysym_to_unicode_tab[0]) -
620 1;
621 int mid;
622
623 /* First check for Latin-1 characters (1:1 mapping) */
624 if ((sym >= 0x0020 && sym <= 0x007e) || (sym >= 0x00a0 && sym <= 0x00ff)) {
625 return sym;
626 }
627
628 /* Also check for directly encoded 24-bit UCS characters:
629 */
630 if ((sym & 0xff000000) == 0x01000000) {
631 return sym & 0x00ffffff;
632 }
633
634 /* binary search in table */
635 while (max >= min) {
636 mid = (min + max) / 2;
637 if (gdk_keysym_to_unicode_tab[mid].keysym < sym) {
638 min = mid + 1;
639 } else if (gdk_keysym_to_unicode_tab[mid].keysym > sym) {
640 max = mid - 1;
641 } else {
642 /* found it */
643 return gdk_keysym_to_unicode_tab[mid].ucs;
644 }
645 }
646
647 /* No matching Unicode value found */
648 return 0;
649 }
650
keySymToUTF8(KeySym sym)651 std::string Key::keySymToUTF8(KeySym sym) {
652 return utf8::UCS4ToUTF8(keySymToUnicode(sym));
653 }
654
keyListFromString(const std::string & str)655 std::vector<Key> Key::keyListFromString(const std::string &str) {
656 std::vector<Key> keyList;
657
658 auto lastPos = str.find_first_not_of(FCITX_WHITESPACE, 0);
659 auto pos = str.find_first_of(FCITX_WHITESPACE, lastPos);
660
661 while (std::string::npos != pos || std::string::npos != lastPos) {
662 Key key(str.substr(lastPos, pos - lastPos));
663
664 if (key.sym() != FcitxKey_None) {
665 keyList.push_back(key);
666 }
667 lastPos = str.find_first_not_of(FCITX_WHITESPACE, pos);
668 pos = str.find_first_of(FCITX_WHITESPACE, lastPos);
669 }
670
671 return keyList;
672 }
673 } // namespace fcitx
674