1: /*
2: ============================================================================
3: xoblite™ -> an advanced "extended shell" for Microsoft® Windows® 11
4: Copyright © 2002-2026 Karl Henrik Henriksson [qwilk/@xoblite]
5: Copyright © 2001-2004 The Blackbox for Windows Development Team
6: http://xoblite.net/ + https://github.com/xoblite/
7: ============================================================================
8:
9: Blackbox for Windows is free software, released under the
10: GNU General Public License (GPL version 2 or later), with an extension
11: that allows linking of proprietary modules under a controlled interface.
12: What this means is that plugins etc. are allowed to be released
13: under any license the author wishes. Please note, however, that the
14: original Blackbox gradient math code used in Blackbox for Windows
15: is available under the BSD license.
16:
17: http://www.fsf.org/licenses/gpl.html
18: http://www.fsf.org/licenses/gpl-faq.html#LinkingOverControlledInterface
19: http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
20:
21: This program is distributed in the hope that it will be useful,
22: but WITHOUT ANY WARRANTY; without even the implied warranty of
23: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24: GNU General Public License for more details.
25:
26: For additional license information, please read the included license.html.
27:
28: ============================================================================
29: */
30:
31: #include "Hotkeys.h"
32:
33: extern Settings* pSettings;
34:
35: //===========================================================================
36:
37: Hotkeys::Hotkeys(HINSTANCE hInstance)
38: {
39: ZeroMemory(&hotkeyList, sizeof(hotkeyList));
40: RegisterHotkeys();
41: }
42:
43: Hotkeys::~Hotkeys()
44: {
45: UnregisterHotkeys();
46: }
47:
48: //===========================================================================
49:
50: void Hotkeys::RegisterHotkeys()
51: {
52: char temp[MAX_LINE_LENGTH], validator[MAX_LINE_LENGTH], mods[MAX_LINE_LENGTH], key[MAX_LINE_LENGTH], cmd[MAX_LINE_LENGTH], args[MAX_LINE_LENGTH];
53: LPSTR tokens[4];
54: tokens[0] = validator;
55: tokens[1] = mods;
56: tokens[2] = key;
57: tokens[3] = cmd;
58:
59: id = 0, hotkeysRegistered = 0;
60:
61: // Register a default hard-coded "Quit" hotkey -> CtrlWinAlt Escape
62: // if (ParseHotkey("CtrlWinAlt", "Esc", "@xoblite Quit", "")) hotkeysRegistered++;
63:
64: // Parse the hotkeys configuration file... (default path -> hotkeys.rc in the main .exe folder)
65: FILE* fp = FileOpen(pSettings->hotkeysFile);
66: if (fp)
67: {
68: while (ReadNextCommand(fp, temp, sizeof(temp)))
69: {
70: if (temp[0] == '!') continue; // Skip comment lines
71:
72: validator[0] = mods[0] = key[0] = cmd[0] = args[0] = '\0';
73: int count = BBTokenize(temp, tokens, 4, args);
74:
75: if (count == 4 && (!_stricmp(validator, "*Hotkey")))
76: {
77: if (strchr(cmd, ' ')) // Does the command include spaces?
78: {
79: // Since enclosing quotes are removed by BBTokenize,
80: // we need to add them back if the command includes spaces...
81: strcpy_s(temp, sizeofArray(temp), cmd);
82: sprintf_s(cmd, sizeofArray(cmd), "\"%s\"", temp);
83: }
84: if (ParseHotkey(mods, key, cmd, args, true)) hotkeysRegistered++;
85: }
86: }
87: }
88: FileClose(fp);
89:
90: char msg[256];
91: // sprintf_s(msg, sizeofArray(msg), "Creating hotkeys -> %d hotkeys registered.", hotkeysRegistered);
92: sprintf_s(msg, sizeofArray(msg), "Loading hotkeys -> %d hotkeys registered.", hotkeysRegistered);
93: SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_REGULAR_MESSAGE, (LPARAM)msg);
94:
95: //====================
96:
97: // Finally, we add a default ### Ctrl+Alt+Win Space ### hotkey shortcut to the xoblite run box,
98: // if this key combination has not already been assigned to something by the user...
99: ParseHotkey("CtrlAltWin", "Space", "@xoblite Run", "", false);
100: }
101:
102: //===========================================================================
103:
104: void Hotkeys::UnregisterHotkeys()
105: {
106: hotkeysUnregistered = 0;
107:
108: for (int i=0; i<(int)hotkeyList.size(); i++)
109: {
110: UnregisterHotKey(GetBBWnd(), hotkeyList[i]->ID);
111: GlobalDeleteAtom(hotkeyList[i]->ID);
112: delete hotkeyList[i];
113: hotkeysUnregistered++;
114: }
115: hotkeyList.clear();
116: /*
117: char msg[256];
118: sprintf_s(msg, sizeofArray(msg), "Destroying hotkeys -> %d hotkeys unregistered.", hotkeysUnregistered);
119: SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_REGULAR_MESSAGE, (LPARAM)msg);
120: */
121: }
122:
123: //===========================================================================
124:
125: bool Hotkeys::ParseHotkey(char mods[MAX_LINE_LENGTH], char key[MAX_LINE_LENGTH], char cmd[MAX_LINE_LENGTH], char args[MAX_LINE_LENGTH], bool reportErrors)
126: {
127: unsigned int keyCode = 0x00;
128:
129: // Below: Virtual key codes as per https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
130:
131: if (strlen(key) == 1) keyCode = key[0]; // A single character key string -> Regular keys (i.e. A-Z, 0-9)
132:
133: else if (!_stricmp(key, "Space") || !_stricmp(key, "Spacebar")) keyCode = VK_SPACE;
134:
135: else if (!_stricmp(key, "Plus")) keyCode = VK_OEM_PLUS; // +
136: else if (!_stricmp(key, "Comma")) keyCode = VK_OEM_COMMA; // ,
137: else if (!_stricmp(key, "Minus")) keyCode = VK_OEM_MINUS; // -
138: else if (!_stricmp(key, "Period")) keyCode = VK_OEM_PERIOD; // .
139:
140: else if (!_stricmp(key, "OEM1")) keyCode = VK_OEM_1; // Locale specific characters, e.g. US ;: key, or Swedish ¨^~ key.
141: else if (!_stricmp(key, "OEM2")) keyCode = VK_OEM_2; // Locale specific characters, e.g. US /? key, or Swedish '* key.
142: else if (!_stricmp(key, "OEM3")) keyCode = VK_OEM_3; // Locale specific characters, e.g. US `~ key, or Swedish öÖ key.
143: else if (!_stricmp(key, "OEM4")) keyCode = VK_OEM_4; // Locale specific characters, e.g. US [{ key, or Swedish ´` key.
144: else if (!_stricmp(key, "OEM5")) keyCode = VK_OEM_5; // Locale specific characters, e.g. US \| key, or Swedish §½ key.
145: else if (!_stricmp(key, "OEM6")) keyCode = VK_OEM_6; // Locale specific characters, e.g. US ]} key, or Swedish åÅ key.
146: else if (!_stricmp(key, "OEM7")) keyCode = VK_OEM_7; // Locale specific characters, e.g. US single-quote/double-quote key, or Swedish äÄ key.
147: else if (!_stricmp(key, "OEM8")) keyCode = VK_OEM_8; // Locale specific characters, e.g. ...?
148: else if (!_stricmp(key, "OEM102")) keyCode = VK_OEM_102; // Locale specific characters, e.g. Swedish <>| key.
149:
150: else if (!_stricmp(key, "Enter")) keyCode = VK_RETURN;
151: else if (!_stricmp(key, "Num0")) keyCode = VK_NUMPAD0;
152: else if (!_stricmp(key, "Num1")) keyCode = VK_NUMPAD1;
153: else if (!_stricmp(key, "Num2")) keyCode = VK_NUMPAD2;
154: else if (!_stricmp(key, "Num3")) keyCode = VK_NUMPAD3;
155: else if (!_stricmp(key, "Num4")) keyCode = VK_NUMPAD4;
156: else if (!_stricmp(key, "Num5")) keyCode = VK_NUMPAD5;
157: else if (!_stricmp(key, "Num6")) keyCode = VK_NUMPAD6;
158: else if (!_stricmp(key, "Num7")) keyCode = VK_NUMPAD7;
159: else if (!_stricmp(key, "Num8")) keyCode = VK_NUMPAD8;
160: else if (!_stricmp(key, "Num9")) keyCode = VK_NUMPAD9;
161: else if (!_stricmp(key, "Mul")) keyCode = VK_MULTIPLY;
162: else if (!_stricmp(key, "Div")) keyCode = VK_DIVIDE;
163: else if (!_stricmp(key, "Add")) keyCode = VK_ADD;
164: else if (!_stricmp(key, "Sub")) keyCode = VK_SUBTRACT;
165: else if (!_stricmp(key, "Dec")) keyCode = VK_DECIMAL;
166:
167: else if (!_stricmp(key, "F1")) keyCode = VK_F1;
168: else if (!_stricmp(key, "F2")) keyCode = VK_F2;
169: else if (!_stricmp(key, "F3")) keyCode = VK_F3;
170: else if (!_stricmp(key, "F4")) keyCode = VK_F4;
171: else if (!_stricmp(key, "F5")) keyCode = VK_F5;
172: else if (!_stricmp(key, "F6")) keyCode = VK_F6;
173: else if (!_stricmp(key, "F7")) keyCode = VK_F7;
174: else if (!_stricmp(key, "F8")) keyCode = VK_F8;
175: else if (!_stricmp(key, "F9")) keyCode = VK_F9;
176: else if (!_stricmp(key, "F10")) keyCode = VK_F10;
177: else if (!_stricmp(key, "F11")) keyCode = VK_F11;
178: else if (!_stricmp(key, "F12")) keyCode = VK_F12;
179: else if (!_stricmp(key, "F13")) keyCode = VK_F13;
180: else if (!_stricmp(key, "F14")) keyCode = VK_F14;
181: else if (!_stricmp(key, "F15")) keyCode = VK_F15;
182: else if (!_stricmp(key, "F16")) keyCode = VK_F16;
183: else if (!_stricmp(key, "F17")) keyCode = VK_F17;
184: else if (!_stricmp(key, "F18")) keyCode = VK_F18;
185: else if (!_stricmp(key, "F19")) keyCode = VK_F19;
186: else if (!_stricmp(key, "F20")) keyCode = VK_F20;
187: else if (!_stricmp(key, "F21")) keyCode = VK_F21;
188: else if (!_stricmp(key, "F22")) keyCode = VK_F22;
189: else if (!_stricmp(key, "F23")) keyCode = VK_F23;
190: else if (!_stricmp(key, "F24")) keyCode = VK_F24;
191:
192: else if (!_stricmp(key, "Left")) keyCode = VK_LEFT;
193: else if (!_stricmp(key, "Right")) keyCode = VK_RIGHT;
194: else if (!_stricmp(key, "Down")) keyCode = VK_DOWN;
195: else if (!_stricmp(key, "Up")) keyCode = VK_UP;
196:
197: else if (!_stricmp(key, "Insert")) keyCode = VK_INSERT;
198: else if (!_stricmp(key, "Delete")) keyCode = VK_DELETE;
199: else if (!_stricmp(key, "Home")) keyCode = VK_HOME;
200: else if (!_stricmp(key, "End")) keyCode = VK_END;
201: else if (!_stricmp(key, "PageUp")) keyCode = VK_PRIOR;
202: else if (!_stricmp(key, "PageDown")) keyCode = VK_NEXT;
203:
204: else if (!_stricmp(key, "PrtScn")) keyCode = VK_SNAPSHOT;
205: else if (!_stricmp(key, "Pause")) keyCode = VK_PAUSE;
206: else if (!_stricmp(key, "Esc") || !_stricmp(key, "Escape")) keyCode = VK_ESCAPE;
207: else if (!_stricmp(key, "Tab")) keyCode = VK_TAB;
208: else if (!_stricmp(key, "Backspace")) keyCode = VK_BACK;
209: else if (!_stricmp(key, "Apps") || !_stricmp(key, "Menu")) keyCode = VK_APPS;
210: /*
211: // ...some keys available on extended keyboards...
212: // (see https://docs.microsoft.com/en-us/cpp/windows/predefined-accelerator-keys?view=vs-2019 )
213:
214: else if (!_stricmp(key, "xBack")) keyCode = VK_BROWSER_BACK;
215: else if (!_stricmp(key, "xForward")) keyCode = VK_BROWSER_FORWARD;
216: else if (!_stricmp(key, "xStop")) keyCode = VK_BROWSER_STOP;
217: else if (!_stricmp(key, "xRefresh")) keyCode = VK_BROWSER_REFRESH;
218: else if (!_stricmp(key, "xSearch")) keyCode = VK_BROWSER_SEARCH;
219: else if (!_stricmp(key, "xFav")) keyCode = VK_BROWSER_FAVORITES;
220: else if (!_stricmp(key, "xWebHome")) keyCode = VK_BROWSER_HOME;
221:
222: else if (!_stricmp(key, "xVolUp")) keyCode = VK_VOLUME_DOWN;
223: else if (!_stricmp(key, "xVolDown")) keyCode = VK_VOLUME_UP;
224: else if (!_stricmp(key, "xVolMute")) keyCode = VK_VOLUME_MUTE;
225:
226: else if (!_stricmp(key, "xPlayPause")) keyCode = VK_MEDIA_PLAY_PAUSE;
227: else if (!_stricmp(key, "xStop")) keyCode = VK_MEDIA_STOP;
228: else if (!_stricmp(key, "xPrev")) keyCode = VK_MEDIA_PREV_TRACK;
229: else if (!_stricmp(key, "xNext")) keyCode = VK_MEDIA_NEXT_TRACK;
230: else if (!_stricmp(key, "xMedia")) keyCode = VK_LAUNCH_MEDIA_SELECT;
231:
232: else if (!_stricmp(key, "xMail")) keyCode = VK_LAUNCH_MAIL;
233: else if (!_stricmp(key, "xApp1")) keyCode = VK_LAUNCH_APP1;
234: else if (!_stricmp(key, "xApp2")) keyCode = VK_LAUNCH_APP2;
235:
236: else if (!_stricmp(key, "xSleep")) keyCode = VK_SLEEP;
237:
238: // ...and a couple of keys I don't have on my keyboard at least, but here goes anyway...!?
239: // (also listed at https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes )
240: else if (!_stricmp(key, "Clear")) keyCode = VK_CLEAR;
241: else if (!_stricmp(key, "Select")) keyCode = VK_SELECT;
242: else if (!_stricmp(key, "Print")) keyCode = VK_PRINT;
243: else if (!_stricmp(key, "Execute")) keyCode = VK_EXECUTE;
244: else if (!_stricmp(key, "Help")) keyCode = VK_HELP;
245: else if (!_stricmp(key, "Separator")) keyCode = VK_SEPARATOR;
246: else if (!_stricmp(key, "Attn")) keyCode = VK_ATTN;
247: else if (!_stricmp(key, "CrSel")) keyCode = VK_CRSEL;
248: else if (!_stricmp(key, "ExSel")) keyCode = VK_EXSEL;
249: else if (!_stricmp(key, "Play")) keyCode = VK_PLAY;
250: else if (!_stricmp(key, "Zoom")) keyCode = VK_ZOOM;
251: else if (!_stricmp(key, "OEMClear")) keyCode = VK_OEM_CLEAR;
252: */
253: else return false; // -> Invalid key!
254:
255: //====================
256:
257: hotkeyItem* hotkey = new hotkeyItem;
258:
259: hotkey->mods = 0;
260: if (IsInString(mods, "Win")) hotkey->mods |= MOD_WIN;
261: if (IsInString(mods, "Ctrl")) hotkey->mods |= MOD_CONTROL;
262: if (IsInString(mods, "Alt")) hotkey->mods |= MOD_ALT;
263: if (IsInString(mods, "Shift")) hotkey->mods |= MOD_SHIFT;
264: hotkey->mods |= MOD_NOREPEAT; // -> Keyboard auto-repeat does not yield multiple hotkey notifications
265:
266: hotkey->key = keyCode;
267: hotkey->cmd = _strdup(cmd);
268: hotkey->args = _strdup(args);
269: hotkey->keyAsString = _strdup(key);
270:
271: // Assign a unique identifier to the new hotkey...
272: char atomname[64];
273: sprintf_s(atomname, sizeofArray(atomname), "BBHotkey%d", id);
274: hotkey->ID = GlobalAddAtom(atomname);
275: if (hotkey->ID == 0)
276: {
277: // ...adding identifier failed! :(
278: delete hotkey;
279:
280: if (reportErrors)
281: {
282: char msg[256];
283: sprintf_s(msg, sizeofArray(msg), "Error adding atom for hotkey %s %s !", mods, key);
284: SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_ERROR_MESSAGE, (LPARAM)msg);
285: }
286: return false;
287: }
288:
289: //====================
290:
291: // Register the new hotkey...
292: if (!RegisterHotKey(GetBBWnd(), hotkey->ID, hotkey->mods, (int)hotkey->key))
293: {
294: // ...registering the new hotkey failed! :(
295: GlobalDeleteAtom(hotkey->ID);
296: delete hotkey;
297:
298: if (reportErrors)
299: {
300: char msg[256];
301: sprintf_s(msg, sizeofArray(msg), "Error registering hotkey %s %s !", mods, key);
302: SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_ERROR_MESSAGE, (LPARAM)msg);
303: }
304:
305: return false;
306: }
307: else
308: {
309: // ...registering the new hotkey succeeded! :D
310: id++;
311: hotkeyList.push_back(hotkey);
312:
313: return true;
314: }
315: }
316:
317: //===========================================================================
318:
319: void Hotkeys::ExecuteHotkey(int id)
320: {
321: for (int i = 0; i < (int)hotkeyList.size(); i++)
322: {
323: if (hotkeyList[i]->ID == id)
324: {
325: char cmdPlusArgs[MAX_LINE_LENGTH];
326: sprintf_s(cmdPlusArgs, sizeofArray(cmdPlusArgs), "%s %s", hotkeyList[i]->cmd, hotkeyList[i]->args);
327: pSettings->Statistics.usedAHotkey++; // Update statistics... (nb. we do this before executing the hotkey, as the latter is typically used to trigger the @xoblite Statistics...)
328: BBSmartExecute(cmdPlusArgs);
329: if (!_strnicmp(cmdPlusArgs, "@xoblite Menu Show", 18)) PlaySoundFX(SFX_MENU_SHOW);
330: else PlaySoundFX(SFX_HOTKEY);
331: }
332: }
333: }
334:
335: //===========================================================================
336: