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