1 | /*
|
---|
2 | * globalaccelman_mac.cpp - manager for global hotkeys
|
---|
3 | * Copyright (C) 2003 Eric Smith
|
---|
4 | *
|
---|
5 | * This library is free software; you can redistribute it and/or
|
---|
6 | * modify it under the terms of the GNU Lesser General Public
|
---|
7 | * License as published by the Free Software Foundation; either
|
---|
8 | * version 2.1 of the License, or (at your option) any later version.
|
---|
9 | *
|
---|
10 | * This library is distributed in the hope that it will be useful,
|
---|
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
13 | * Lesser General Public License for more details.
|
---|
14 | *
|
---|
15 | * You should have received a copy of the GNU Lesser General Public
|
---|
16 | * License along with this library; if not, write to the Free Software
|
---|
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
---|
18 | *
|
---|
19 | */
|
---|
20 |
|
---|
21 | // TODO:
|
---|
22 | // - don't invoke hotkey if there is a modal dialog?
|
---|
23 | // - do multi-mapping, like the x11 version
|
---|
24 |
|
---|
25 | #include"globalaccelman.h"
|
---|
26 |
|
---|
27 | #include<qkeysequence.h>
|
---|
28 | #include<qwidget.h>
|
---|
29 | #include<qptrlist.h>
|
---|
30 |
|
---|
31 | #include <Carbon/Carbon.h>
|
---|
32 |
|
---|
33 | typedef struct {
|
---|
34 | short kchrID;
|
---|
35 | Str255 KCHRname;
|
---|
36 | short transtable[256];
|
---|
37 | } Ascii2KeyCodeTable;
|
---|
38 |
|
---|
39 |
|
---|
40 |
|
---|
41 | /* InitAscii2KeyCodeTable initializes the ascii to key code
|
---|
42 | look up table using the currently active KCHR resource. This
|
---|
43 | routine calls GetResource so it cannot be called at interrupt time.*/
|
---|
44 | OSStatus InitAscii2KeyCodeTable(Ascii2KeyCodeTable *ttable);
|
---|
45 |
|
---|
46 | /* ValidateAscii2KeyCodeTable verifies that the ascii to key code
|
---|
47 | lookup table is synchronized with the current KCHR resource. If
|
---|
48 | it is not synchronized, then the table is re-built. This routine calls
|
---|
49 | GetResource so it cannot be called at interrupt time.*/
|
---|
50 | OSStatus ValidateAscii2KeyCodeTable(Ascii2KeyCodeTable *ttable,Boolean *wasChanged);
|
---|
51 |
|
---|
52 | /* AsciiToKeyCode looks up the ascii character in the key
|
---|
53 | code look up table and returns the virtual key code for that
|
---|
54 | letter. If there is no virtual key code for that letter, then
|
---|
55 | the value -1 will be returned. */
|
---|
56 | short AsciiToKeyCode(Ascii2KeyCodeTable *ttable, short asciiCode);
|
---|
57 |
|
---|
58 | enum {
|
---|
59 | kTableCountOffset = 256+2,
|
---|
60 | kFirstTableOffset = 256+4,
|
---|
61 | kTableSize = 128
|
---|
62 | };
|
---|
63 |
|
---|
64 | OSStatus InitAscii2KeyCodeTable(Ascii2KeyCodeTable *ttable) {
|
---|
65 | unsigned char *theCurrentKCHR, *ithKeyTable;
|
---|
66 | short count, i, j, resID;
|
---|
67 | Handle theKCHRRsrc;
|
---|
68 | ResType rType;
|
---|
69 | /* set up our table to all minus ones */
|
---|
70 | for (i=0;i<256; i++) ttable->transtable[i] = -1;
|
---|
71 | /* find the current kchr resource ID */
|
---|
72 | ttable->kchrID = (short) GetScriptVariable(smCurrentScript, smScriptKeys);
|
---|
73 | /* get the current KCHR resource */
|
---|
74 | theKCHRRsrc = GetResource('KCHR', ttable->kchrID);
|
---|
75 | if (theKCHRRsrc == NULL) return resNotFound;
|
---|
76 | GetResInfo(theKCHRRsrc,&resID,&rType,ttable->KCHRname);
|
---|
77 | /* dereference the resource */
|
---|
78 | theCurrentKCHR = (unsigned char *) (*theKCHRRsrc);
|
---|
79 | /* get the count from the resource */
|
---|
80 | count = * (short *) (theCurrentKCHR + kTableCountOffset);
|
---|
81 | /* build inverse table by merging all key tables */
|
---|
82 | for (i=0; i<count; i++) {
|
---|
83 | ithKeyTable = theCurrentKCHR + kFirstTableOffset + (i * kTableSize);
|
---|
84 | for (j=0; j<kTableSize; j++) {
|
---|
85 | if ( ttable->transtable[ ithKeyTable[j] ] == -1)
|
---|
86 | ttable->transtable[ ithKeyTable[j] ] = j;
|
---|
87 | }
|
---|
88 | }
|
---|
89 | /* all done */
|
---|
90 | return noErr;
|
---|
91 | }
|
---|
92 |
|
---|
93 |
|
---|
94 | // Should probably call this at some point, in case the user has switched keyboard
|
---|
95 | // layouts while we were running.
|
---|
96 | OSStatus ValidateAscii2KeyCodeTable(Ascii2KeyCodeTable *ttable,Boolean *wasChanged) {
|
---|
97 | short theID;
|
---|
98 | theID = (short) GetScriptVariable(smCurrentScript, smScriptKeys);
|
---|
99 | if (theID != ttable->kchrID)
|
---|
100 | {
|
---|
101 | *wasChanged=true;
|
---|
102 | return InitAscii2KeyCodeTable(ttable);
|
---|
103 | }
|
---|
104 | else
|
---|
105 | {
|
---|
106 | *wasChanged=false;
|
---|
107 | return noErr;
|
---|
108 | }
|
---|
109 | }
|
---|
110 |
|
---|
111 | short AsciiToKeyCode(Ascii2KeyCodeTable *ttable, short asciiCode) {
|
---|
112 | if (asciiCode >= 0 && asciiCode <= 255)
|
---|
113 | return ttable->transtable[asciiCode];
|
---|
114 | else return false;
|
---|
115 | }
|
---|
116 |
|
---|
117 |
|
---|
118 | // Not used.
|
---|
119 | char KeyCodeToAscii(short virtualKeyCode) {
|
---|
120 | unsigned long state;
|
---|
121 | long keyTrans;
|
---|
122 | char charCode;
|
---|
123 | Ptr kchr;
|
---|
124 | state = 0;
|
---|
125 | kchr = (Ptr) GetScriptVariable(smCurrentScript, smKCHRCache);
|
---|
126 | keyTrans = KeyTranslate(kchr, virtualKeyCode, &state);
|
---|
127 | charCode = keyTrans;
|
---|
128 | if (!charCode) charCode = (keyTrans>>16);
|
---|
129 | return charCode;
|
---|
130 | }
|
---|
131 |
|
---|
132 |
|
---|
133 | EventHotKeyID hotKeyID = {
|
---|
134 | 'QtHK',
|
---|
135 | 0
|
---|
136 | };
|
---|
137 |
|
---|
138 |
|
---|
139 | class MacHotKey
|
---|
140 | {
|
---|
141 | public:
|
---|
142 | MacHotKey(EventHotKeyRef _ref, int _id)
|
---|
143 | {
|
---|
144 | hot_key_ref = _ref;
|
---|
145 | id = _id;
|
---|
146 | }
|
---|
147 |
|
---|
148 | ~MacHotKey()
|
---|
149 | {
|
---|
150 | UnregisterEventHotKey(hot_key_ref);
|
---|
151 | }
|
---|
152 |
|
---|
153 | EventHotKeyRef hot_key_ref;
|
---|
154 | int id; // GlobalAccelManager unique ID for this hotkey
|
---|
155 | };
|
---|
156 |
|
---|
157 |
|
---|
158 | // The following table is from Apple sample-code.
|
---|
159 | // Apple's headers don't appear to define any constants for the virtual key
|
---|
160 | // codes of special keys, but these constants are somewhat documented in the chart at
|
---|
161 | // <http://developer.apple.com/techpubs/mac/Text/Text-571.html#MARKER-9-18>
|
---|
162 | //
|
---|
163 | // The constants on the chartappear to be the same values as are used in Apple's iGetKeys
|
---|
164 | // sample.
|
---|
165 | // <http://developer.apple.com/samplecode/Sample_Code/Human_Interface_Toolbox/Mac_OS_High_Level_Toolbox/iGetKeys.htm>.
|
---|
166 | //
|
---|
167 | // See also <http://www.mactech.com/articles/mactech/Vol.04/04.12/Macinkeys/>.
|
---|
168 | //
|
---|
169 | struct QtKeyMap
|
---|
170 | {
|
---|
171 | int qt_key;
|
---|
172 | int mac_key;
|
---|
173 | } qt_key_map[] = {
|
---|
174 | { Qt::Key_Escape, 0x35 },
|
---|
175 | { Qt::Key_Tab, 0x30 },
|
---|
176 | { Qt::Key_Backtab, 0 },
|
---|
177 | { Qt::Key_Backspace, 0x33 },
|
---|
178 | { Qt::Key_Return, 0x24 },
|
---|
179 | { Qt::Key_Enter, 0x4c }, // Return & Enter are different on the Mac
|
---|
180 | { Qt::Key_Insert, 0 },
|
---|
181 | { Qt::Key_Delete, 0x75 },
|
---|
182 | { Qt::Key_Pause, 0 },
|
---|
183 | { Qt::Key_Print, 0 },
|
---|
184 | { Qt::Key_SysReq, 0 },
|
---|
185 | { Qt::Key_Clear, 0x47 },
|
---|
186 | { Qt::Key_Home, 0x73 },
|
---|
187 | { Qt::Key_End, 0x77 },
|
---|
188 | { Qt::Key_Left, 0x7b },
|
---|
189 | { Qt::Key_Up, 0x7e },
|
---|
190 | { Qt::Key_Right, 0x7c },
|
---|
191 | { Qt::Key_Down, 0x7d },
|
---|
192 | { Qt::Key_Prior, 0x74 }, // Page Up
|
---|
193 | { Qt::Key_Next, 0x79 }, // Page Down
|
---|
194 | { Qt::Key_Shift, 0x38 },
|
---|
195 | { Qt::Key_Control, 0x3b },
|
---|
196 | { Qt::Key_Meta, 0x37 }, // Command
|
---|
197 | { Qt::Key_Alt, 0x3a }, // Option
|
---|
198 | { Qt::Key_CapsLock, 57 },
|
---|
199 | { Qt::Key_NumLock, 0 },
|
---|
200 | { Qt::Key_ScrollLock, 0 },
|
---|
201 | { Qt::Key_F1, 0x7a },
|
---|
202 | { Qt::Key_F2, 0x78 },
|
---|
203 | { Qt::Key_F3, 0x63 },
|
---|
204 | { Qt::Key_F4, 0x76 },
|
---|
205 | { Qt::Key_F5, 0x60 },
|
---|
206 | { Qt::Key_F6, 0x61 },
|
---|
207 | { Qt::Key_F7, 0x62 },
|
---|
208 | { Qt::Key_F8, 0x64 },
|
---|
209 | { Qt::Key_F9, 0x65 },
|
---|
210 | { Qt::Key_F10, 0x6d },
|
---|
211 | { Qt::Key_F11, 0x67 },
|
---|
212 | { Qt::Key_F12, 0x6f },
|
---|
213 | { Qt::Key_F13, 0x69 },
|
---|
214 | { Qt::Key_F14, 0x6b },
|
---|
215 | { Qt::Key_F15, 0x71 },
|
---|
216 | { Qt::Key_F16, 0 },
|
---|
217 | { Qt::Key_F17, 0 },
|
---|
218 | { Qt::Key_F18, 0 },
|
---|
219 | { Qt::Key_F19, 0 },
|
---|
220 | { Qt::Key_F20, 0 },
|
---|
221 | { Qt::Key_F21, 0 },
|
---|
222 | { Qt::Key_F22, 0 },
|
---|
223 | { Qt::Key_F23, 0 },
|
---|
224 | { Qt::Key_F24, 0 },
|
---|
225 | { Qt::Key_F25, 0 },
|
---|
226 | { Qt::Key_F26, 0 },
|
---|
227 | { Qt::Key_F27, 0 },
|
---|
228 | { Qt::Key_F28, 0 },
|
---|
229 | { Qt::Key_F29, 0 },
|
---|
230 | { Qt::Key_F30, 0 },
|
---|
231 | { Qt::Key_F31, 0 },
|
---|
232 | { Qt::Key_F32, 0 },
|
---|
233 | { Qt::Key_F33, 0 },
|
---|
234 | { Qt::Key_F34, 0 },
|
---|
235 | { Qt::Key_F35, 0 },
|
---|
236 | { Qt::Key_Super_L, 0 },
|
---|
237 | { Qt::Key_Super_R, 0 },
|
---|
238 | { Qt::Key_Menu, 0 },
|
---|
239 | { Qt::Key_Hyper_L, 0 },
|
---|
240 | { Qt::Key_Hyper_R, 0 },
|
---|
241 | { Qt::Key_Help, 0x72 },
|
---|
242 | { Qt::Key_Direction_L,0 },
|
---|
243 | { Qt::Key_Direction_R,0 },
|
---|
244 |
|
---|
245 | { Qt::Key_unknown, 0 }
|
---|
246 | };
|
---|
247 |
|
---|
248 |
|
---|
249 | class GlobalAccelManager::Private
|
---|
250 | {
|
---|
251 | public:
|
---|
252 | Private(GlobalAccelManager *par)
|
---|
253 | {
|
---|
254 | id_at = 1;
|
---|
255 | list.setAutoDelete(true);
|
---|
256 |
|
---|
257 | InitAscii2KeyCodeTable(&key_codes);
|
---|
258 | hot_key_function = NewEventHandlerUPP(HotKeyHandler);
|
---|
259 | EventTypeSpec type;
|
---|
260 | type.eventClass = kEventClassKeyboard;
|
---|
261 | type.eventKind = kEventHotKeyPressed;
|
---|
262 | InstallApplicationEventHandler(hot_key_function, 1, &type, par, NULL);
|
---|
263 | }
|
---|
264 |
|
---|
265 | ~Private() {
|
---|
266 | }
|
---|
267 |
|
---|
268 | private:
|
---|
269 | static pascal OSStatus HotKeyHandler(EventHandlerCallRef /*nextHandler*/, EventRef theEvent, void *userData);
|
---|
270 |
|
---|
271 | public:
|
---|
272 | bool convertKeySequence(const QKeySequence &ks, UInt32 *_mod, UInt32 *_key)
|
---|
273 | {
|
---|
274 | int code = ks[0];
|
---|
275 |
|
---|
276 | UInt32 mod = 0;
|
---|
277 | if(code & Qt::META)
|
---|
278 | mod |= cmdKey;
|
---|
279 | if(code & Qt::SHIFT)
|
---|
280 | mod |= shiftKey;
|
---|
281 | if(code & Qt::CTRL)
|
---|
282 | mod |= controlKey;
|
---|
283 | if(code & Qt::ALT)
|
---|
284 | mod |= optionKey;
|
---|
285 |
|
---|
286 | code &= 0xffff;
|
---|
287 | UInt32 key = 0;
|
---|
288 | for(int n = 0; qt_key_map[n].qt_key != Qt::Key_unknown; ++n) {
|
---|
289 | if(qt_key_map[n].qt_key == code) {
|
---|
290 | key = qt_key_map[n].mac_key;
|
---|
291 | break;
|
---|
292 | }
|
---|
293 | }
|
---|
294 | if (key == 0) {
|
---|
295 | key = AsciiToKeyCode(&key_codes, code & 0xffff);
|
---|
296 | }
|
---|
297 |
|
---|
298 | if(_mod)
|
---|
299 | *_mod = mod;
|
---|
300 | if(_key)
|
---|
301 | *_key = key;
|
---|
302 |
|
---|
303 | return true;
|
---|
304 | }
|
---|
305 |
|
---|
306 |
|
---|
307 | int insertHotKey(int key, int mod)
|
---|
308 | {
|
---|
309 | EventHotKeyID hotKeyID;
|
---|
310 | hotKeyID.signature = 'QtHK';
|
---|
311 | hotKeyID.id = id_at++;
|
---|
312 | EventHotKeyRef hotKeyRef = 0;
|
---|
313 |
|
---|
314 | OSStatus foo = RegisterEventHotKey(key, mod, hotKeyID, GetApplicationEventTarget(), 0, &hotKeyRef);
|
---|
315 | if (foo != 0) {
|
---|
316 | fprintf(stderr, "RegisterEventHotKey(%x,%x) returned %ld\n", key, mod, foo);
|
---|
317 | return 0;
|
---|
318 | }
|
---|
319 |
|
---|
320 | MacHotKey *hk = new MacHotKey(hotKeyRef, hotKeyID.id);
|
---|
321 | list.append(hk);
|
---|
322 | return hk->id;
|
---|
323 | }
|
---|
324 |
|
---|
325 |
|
---|
326 | int id_at;
|
---|
327 | QPtrList<MacHotKey> list;
|
---|
328 | EventHandlerUPP hot_key_function;
|
---|
329 | Ascii2KeyCodeTable key_codes;
|
---|
330 | };
|
---|
331 |
|
---|
332 |
|
---|
333 | // Callback function invoked when the user hits a hot-key.
|
---|
334 | pascal OSStatus GlobalAccelManager::Private::HotKeyHandler(EventHandlerCallRef /*nextHandler*/, EventRef theEvent, void *userData)
|
---|
335 | {
|
---|
336 | EventHotKeyID hkID;
|
---|
337 | GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(EventHotKeyID), NULL, &hkID);
|
---|
338 | reinterpret_cast<GlobalAccelManager*>(userData)->triggerActivated(hkID.id);
|
---|
339 | return noErr;
|
---|
340 | }
|
---|
341 |
|
---|
342 |
|
---|
343 | GlobalAccelManager::GlobalAccelManager()
|
---|
344 | {
|
---|
345 | d = new Private(this);
|
---|
346 | }
|
---|
347 |
|
---|
348 | GlobalAccelManager::~GlobalAccelManager()
|
---|
349 | {
|
---|
350 | delete d;
|
---|
351 | }
|
---|
352 |
|
---|
353 | int GlobalAccelManager::setAccel(const QKeySequence &ks)
|
---|
354 | {
|
---|
355 |
|
---|
356 | UInt32 mod, key;
|
---|
357 | if(!d->convertKeySequence(ks, &mod, &key))
|
---|
358 | return 0;
|
---|
359 |
|
---|
360 | return d->insertHotKey(key, mod);
|
---|
361 | }
|
---|
362 |
|
---|
363 | void GlobalAccelManager::removeAccel(int id)
|
---|
364 | {
|
---|
365 | QPtrListIterator<MacHotKey> it(d->list);
|
---|
366 | for(MacHotKey *hk = 0; (hk = it.current()); ++it) {
|
---|
367 | if(hk->id == id) {
|
---|
368 | d->list.removeRef(hk);
|
---|
369 | return;
|
---|
370 | }
|
---|
371 | }
|
---|
372 | }
|
---|