1 /****************************************************************************
2  **
3  ** Copyright (C) Qxt Foundation. Some rights reserved.
4  **
5  ** This file is part of the QxtGui module of the Qxt library.
6  **
7  ** This library is free software; you can redistribute it and/or modify it
8  ** under the terms of the Common Public License, version 1.0, as published
9  ** by IBM, and/or under the terms of the GNU Lesser General Public License,
10  ** version 2.1, as published by the Free Software Foundation.
11  **
12  ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
13  ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
14  ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
15  ** FITNESS FOR A PARTICULAR PURPOSE.
16  **
17  ** You should have received a copy of the CPL and the LGPL along with this
18  ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
19  ** included with the source distribution for more information.
20  ** If you did not receive a copy of the licenses, contact the Qxt Foundation.
21  **
22  ** <http://libqxt.org>  <foundation@libqxt.org>
23  **
24  ****************************************************************************/
25 #include <Carbon/Carbon.h>
26 #include "qxtglobalshortcut_p.h"
27 #include <QMap>
28 #include <QHash>
29 #include <QtDebug>
30 #include <QApplication>
31 
32 typedef QPair<uint, uint> Identifier;
33 static QMap<quint32, EventHotKeyRef> keyRefs;
34 static QHash<Identifier, quint32> keyIDs;
35 static quint32 hotKeySerial = 0;
36 static bool qxt_mac_handler_installed = false;
37 
qxt_mac_handle_hot_key(EventHandlerCallRef nextHandler,EventRef event,void * data)38 OSStatus qxt_mac_handle_hot_key(EventHandlerCallRef nextHandler, EventRef event, void* data)
39 {
40     // pass event to the app event filter
41     Q_UNUSED(data);
42     qApp->macEventFilter(nextHandler, event);
43     return noErr;
44 }
45 
46 #if QT_VERSION<0x050000
eventFilter(void * message)47 bool QxtGlobalShortcutPrivate::eventFilter(void* message)
48 #else
49 bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray &, void *message, long *result)
50 #endif
51 //bool QxtGlobalShortcutPrivate::macEventFilter(EventHandlerCallRef caller, EventRef event)
52 {
53     EventRef event = (EventRef) message;
54     if (GetEventClass(event) == kEventClassKeyboard && GetEventKind(event) == kEventHotKeyPressed)
55     {
56         EventHotKeyID keyID;
57         GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(keyID), NULL, &keyID);
58         Identifier id = keyIDs.key(keyID.id);
59         activateShortcut(id.second, id.first);
60     }
61     return false;
62 }
63 
nativeModifiers(Qt::KeyboardModifiers modifiers)64 quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers)
65 {
66     quint32 native = 0;
67     if (modifiers & Qt::ShiftModifier)
68         native |= shiftKeyBit;
69     if (modifiers & Qt::ControlModifier)
70         native |= cmdKey;
71     if (modifiers & Qt::AltModifier)
72         native |= optionKey;
73     if (modifiers & Qt::MetaModifier)
74         native |= controlKey;
75     if (modifiers & Qt::KeypadModifier)
76         native |= kEventKeyModifierNumLockMask;
77     return native;
78 }
79 
nativeKeycode(Qt::Key key)80 quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key)
81 {
82     UTF16Char ch;
83     // Constants found in NSEvent.h from AppKit.framework
84     if (key == Qt::Key_Up)				ch = 0xF700;
85     else if (key == Qt::Key_Down)		ch = 0xF701;
86     else if (key == Qt::Key_Left)		ch = 0xF702;
87     else if (key == Qt::Key_Right)		ch = 0xF703;
88     else if (key >= Qt::Key_F1 && key <= Qt::Key_F35)
89         ch = key - Qt::Key_F1 + 0xF704;
90     else if (key == Qt::Key_Insert)		ch = 0xF727;
91     else if (key == Qt::Key_Delete)		ch = 0xF728;
92     else if (key == Qt::Key_Home)		ch = 0xF729;
93     else if (key == Qt::Key_End)			ch = 0xF72B;
94     else if (key == Qt::Key_PageUp)		ch = 0xF72C;
95     else if (key == Qt::Key_PageDown)	ch = 0xF72D;
96     else if (key == Qt::Key_Print)		ch = 0xF72E;
97     else if (key == Qt::Key_ScrollLock)	ch = 0xF72F;
98     else if (key == Qt::Key_Pause)		ch = 0xF730;
99     else if (key == Qt::Key_SysReq)		ch = 0xF731;
100     else if (key == Qt::Key_Stop)		ch = 0xF734;
101     else if (key == Qt::Key_Menu)		ch = 0xF735;
102     else if (key == Qt::Key_Select)		ch = 0xF741;
103     else if (key == Qt::Key_Execute)		ch = 0xF742;
104     else if (key == Qt::Key_Help)		ch = 0xF746;
105     else if (key == Qt::Key_Mode_switch)	ch = 0xF747;
106     else if (key == Qt::Key_Escape)		ch = 27;
107     else if (key == Qt::Key_Return)		ch = 13;
108     else if (key == Qt::Key_Enter)		ch = 3;
109     else if (key == Qt::Key_Tab)			ch = 9;
110     else								ch = key;
111 
112     KeyboardLayoutRef layout;
113     KeyboardLayoutKind layoutKind;
114     KLGetCurrentKeyboardLayout(&layout);
115     KLGetKeyboardLayoutProperty(layout, kKLKind, const_cast<const void**>(reinterpret_cast<void**>(&layoutKind)));
116 
117     if (layoutKind == kKLKCHRKind)
118     { // no Unicode available
119         if (ch > 255) return 0;
120 
121         char* data;
122         KLGetKeyboardLayoutProperty(layout, kKLKCHRData, const_cast<const void**>(reinterpret_cast<void**>(&data)));
123         int ct = *reinterpret_cast<short*>(data + 258);
124         for (int i = 0; i < ct; i++)
125         {
126             char* keyTable = data + 260 + 128 * i;
127             for (int j = 0; j < 128; j++)
128             {
129                 if (keyTable[j] == ch) return j;
130             }
131         }
132 
133         return 0;
134     }
135 
136     char* data;
137     KLGetKeyboardLayoutProperty(layout, kKLuchrData, const_cast<const void**>(reinterpret_cast<void**>(&data)));
138     UCKeyboardLayout* header = reinterpret_cast<UCKeyboardLayout*>(data);
139     UCKeyboardTypeHeader* table = header->keyboardTypeList;
140 
141     for (quint32 i=0; i < header->keyboardTypeCount; i++)
142     {
143         UCKeyStateRecordsIndex* stateRec = 0;
144         if (table[i].keyStateRecordsIndexOffset != 0)
145         {
146             stateRec = reinterpret_cast<UCKeyStateRecordsIndex*>(data + table[i].keyStateRecordsIndexOffset);
147             if (stateRec->keyStateRecordsIndexFormat != kUCKeyStateRecordsIndexFormat) stateRec = 0;
148         }
149 
150         UCKeyToCharTableIndex* charTable = reinterpret_cast<UCKeyToCharTableIndex*>(data + table[i].keyToCharTableIndexOffset);
151         if (charTable->keyToCharTableIndexFormat != kUCKeyToCharTableIndexFormat) continue;
152 
153         for (quint32 j=0; j < charTable->keyToCharTableCount; j++)
154         {
155             UCKeyOutput* keyToChar = reinterpret_cast<UCKeyOutput*>(data + charTable->keyToCharTableOffsets[j]);
156             for (quint32 k=0; k < charTable->keyToCharTableSize; k++)
157             {
158                 if (keyToChar[k] & kUCKeyOutputTestForIndexMask)
159                 {
160                     long idx = keyToChar[k] & kUCKeyOutputGetIndexMask;
161                     if (stateRec && idx < stateRec->keyStateRecordCount)
162                     {
163                         UCKeyStateRecord* rec = reinterpret_cast<UCKeyStateRecord*>(data + stateRec->keyStateRecordOffsets[idx]);
164                         if (rec->stateZeroCharData == ch) return k;
165                     }
166                 }
167                 else if (!(keyToChar[k] & kUCKeyOutputSequenceIndexMask) && keyToChar[k] < 0xFFFE)
168                 {
169                     if (keyToChar[k] == ch) return k;
170                 }
171             } // for k
172         } // for j
173     } // for i
174 
175     return 0;
176 }
177 
registerShortcut(quint32 nativeKey,quint32 nativeMods)178 bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods)
179 {
180     if (!qxt_mac_handler_installed)
181     {
182         EventTypeSpec t;
183         t.eventClass = kEventClassKeyboard;
184         t.eventKind = kEventHotKeyPressed;
185         InstallApplicationEventHandler(&qxt_mac_handle_hot_key, 1, &t, NULL, NULL);
186     }
187 
188     EventHotKeyID keyID;
189     keyID.signature = 'cute';
190     keyID.id = ++hotKeySerial;
191 
192     EventHotKeyRef ref = 0;
193     bool rv = !RegisterEventHotKey(nativeKey, nativeMods, keyID, GetApplicationEventTarget(), 0, &ref);
194     if (rv)
195     {
196         keyIDs.insert(Identifier(nativeMods, nativeKey), keyID.id);
197         keyRefs.insert(keyID.id, ref);
198     }
199     qDebug() << ref;
200     return rv;
201 }
202 
unregisterShortcut(quint32 nativeKey,quint32 nativeMods)203 bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods)
204 {
205     Identifier id(nativeMods, nativeKey);
206     if (!keyIDs.contains(id)) return false;
207 
208     EventHotKeyRef ref = keyRefs.take(keyIDs[id]);
209     keyIDs.remove(id);
210     return !UnregisterEventHotKey(ref);
211 }
212