208 lines
7.9 KiB
C++
208 lines
7.9 KiB
C++
|
/****************************************************************************
|
||
|
**
|
||
|
** Copyright (C) Qxt Foundation. Some rights reserved.
|
||
|
**
|
||
|
** This file is part of the QxtGui module of the Qxt library.
|
||
|
**
|
||
|
** This library is free software; you can redistribute it and/or modify it
|
||
|
** under the terms of the Common Public License, version 1.0, as published
|
||
|
** by IBM, and/or under the terms of the GNU Lesser General Public License,
|
||
|
** version 2.1, as published by the Free Software Foundation.
|
||
|
**
|
||
|
** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
|
||
|
** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
|
||
|
** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
|
||
|
** FITNESS FOR A PARTICULAR PURPOSE.
|
||
|
**
|
||
|
** You should have received a copy of the CPL and the LGPL along with this
|
||
|
** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
|
||
|
** included with the source distribution for more information.
|
||
|
** If you did not receive a copy of the licenses, contact the Qxt Foundation.
|
||
|
**
|
||
|
** <http://libqxt.org> <foundation@libqxt.org>
|
||
|
**
|
||
|
****************************************************************************/
|
||
|
#include <Carbon/Carbon.h>
|
||
|
#include "qxtglobalshortcut_p.h"
|
||
|
#include <QMap>
|
||
|
#include <QHash>
|
||
|
#include <QtDebug>
|
||
|
#include <QApplication>
|
||
|
|
||
|
typedef QPair<uint, uint> Identifier;
|
||
|
static QMap<quint32, EventHotKeyRef> keyRefs;
|
||
|
static QHash<Identifier, quint32> keyIDs;
|
||
|
static quint32 hotKeySerial = 0;
|
||
|
static bool qxt_mac_handler_installed = false;
|
||
|
|
||
|
OSStatus qxt_mac_handle_hot_key(EventHandlerCallRef nextHandler, EventRef event, void* data)
|
||
|
{
|
||
|
// pass event to the app event filter
|
||
|
Q_UNUSED(data);
|
||
|
qApp->macEventFilter(nextHandler, event);
|
||
|
return noErr;
|
||
|
}
|
||
|
|
||
|
bool QxtGlobalShortcutPrivate::eventFilter(void* message)
|
||
|
//bool QxtGlobalShortcutPrivate::macEventFilter(EventHandlerCallRef caller, EventRef event)
|
||
|
{
|
||
|
EventRef event = (EventRef) message;
|
||
|
if (GetEventClass(event) == kEventClassKeyboard && GetEventKind(event) == kEventHotKeyPressed)
|
||
|
{
|
||
|
EventHotKeyID keyID;
|
||
|
GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(keyID), NULL, &keyID);
|
||
|
Identifier id = keyIDs.key(keyID.id);
|
||
|
activateShortcut(id.second, id.first);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers)
|
||
|
{
|
||
|
quint32 native = 0;
|
||
|
if (modifiers & Qt::ShiftModifier)
|
||
|
native |= shiftKeyBit;
|
||
|
if (modifiers & Qt::ControlModifier)
|
||
|
native |= cmdKey;
|
||
|
if (modifiers & Qt::AltModifier)
|
||
|
native |= optionKey;
|
||
|
if (modifiers & Qt::MetaModifier)
|
||
|
native |= controlKey;
|
||
|
if (modifiers & Qt::KeypadModifier)
|
||
|
native |= kEventKeyModifierNumLockMask;
|
||
|
return native;
|
||
|
}
|
||
|
|
||
|
quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key)
|
||
|
{
|
||
|
UTF16Char ch;
|
||
|
// Constants found in NSEvent.h from AppKit.framework
|
||
|
if (key == Qt::Key_Up) ch = 0xF700;
|
||
|
else if (key == Qt::Key_Down) ch = 0xF701;
|
||
|
else if (key == Qt::Key_Left) ch = 0xF702;
|
||
|
else if (key == Qt::Key_Right) ch = 0xF703;
|
||
|
else if (key >= Qt::Key_F1 && key <= Qt::Key_F35)
|
||
|
ch = key - Qt::Key_F1 + 0xF704;
|
||
|
else if (key == Qt::Key_Insert) ch = 0xF727;
|
||
|
else if (key == Qt::Key_Delete) ch = 0xF728;
|
||
|
else if (key == Qt::Key_Home) ch = 0xF729;
|
||
|
else if (key == Qt::Key_End) ch = 0xF72B;
|
||
|
else if (key == Qt::Key_PageUp) ch = 0xF72C;
|
||
|
else if (key == Qt::Key_PageDown) ch = 0xF72D;
|
||
|
else if (key == Qt::Key_Print) ch = 0xF72E;
|
||
|
else if (key == Qt::Key_ScrollLock) ch = 0xF72F;
|
||
|
else if (key == Qt::Key_Pause) ch = 0xF730;
|
||
|
else if (key == Qt::Key_SysReq) ch = 0xF731;
|
||
|
else if (key == Qt::Key_Stop) ch = 0xF734;
|
||
|
else if (key == Qt::Key_Menu) ch = 0xF735;
|
||
|
else if (key == Qt::Key_Select) ch = 0xF741;
|
||
|
else if (key == Qt::Key_Execute) ch = 0xF742;
|
||
|
else if (key == Qt::Key_Help) ch = 0xF746;
|
||
|
else if (key == Qt::Key_Mode_switch) ch = 0xF747;
|
||
|
else if (key == Qt::Key_Escape) ch = 27;
|
||
|
else if (key == Qt::Key_Return) ch = 13;
|
||
|
else if (key == Qt::Key_Enter) ch = 3;
|
||
|
else if (key == Qt::Key_Tab) ch = 9;
|
||
|
else ch = key;
|
||
|
|
||
|
KeyboardLayoutRef layout;
|
||
|
KeyboardLayoutKind layoutKind;
|
||
|
KLGetCurrentKeyboardLayout(&layout);
|
||
|
KLGetKeyboardLayoutProperty(layout, kKLKind, const_cast<const void**>(reinterpret_cast<void**>(&layoutKind)));
|
||
|
|
||
|
if (layoutKind == kKLKCHRKind)
|
||
|
{ // no Unicode available
|
||
|
if (ch > 255) return 0;
|
||
|
|
||
|
char* data;
|
||
|
KLGetKeyboardLayoutProperty(layout, kKLKCHRData, const_cast<const void**>(reinterpret_cast<void**>(&data)));
|
||
|
int ct = *reinterpret_cast<short*>(data + 258);
|
||
|
for (int i = 0; i < ct; i++)
|
||
|
{
|
||
|
char* keyTable = data + 260 + 128 * i;
|
||
|
for (int j = 0; j < 128; j++)
|
||
|
{
|
||
|
if (keyTable[j] == ch) return j;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
char* data;
|
||
|
KLGetKeyboardLayoutProperty(layout, kKLuchrData, const_cast<const void**>(reinterpret_cast<void**>(&data)));
|
||
|
UCKeyboardLayout* header = reinterpret_cast<UCKeyboardLayout*>(data);
|
||
|
UCKeyboardTypeHeader* table = header->keyboardTypeList;
|
||
|
|
||
|
for (quint32 i=0; i < header->keyboardTypeCount; i++)
|
||
|
{
|
||
|
UCKeyStateRecordsIndex* stateRec = 0;
|
||
|
if (table[i].keyStateRecordsIndexOffset != 0)
|
||
|
{
|
||
|
stateRec = reinterpret_cast<UCKeyStateRecordsIndex*>(data + table[i].keyStateRecordsIndexOffset);
|
||
|
if (stateRec->keyStateRecordsIndexFormat != kUCKeyStateRecordsIndexFormat) stateRec = 0;
|
||
|
}
|
||
|
|
||
|
UCKeyToCharTableIndex* charTable = reinterpret_cast<UCKeyToCharTableIndex*>(data + table[i].keyToCharTableIndexOffset);
|
||
|
if (charTable->keyToCharTableIndexFormat != kUCKeyToCharTableIndexFormat) continue;
|
||
|
|
||
|
for (quint32 j=0; j < charTable->keyToCharTableCount; j++)
|
||
|
{
|
||
|
UCKeyOutput* keyToChar = reinterpret_cast<UCKeyOutput*>(data + charTable->keyToCharTableOffsets[j]);
|
||
|
for (quint32 k=0; k < charTable->keyToCharTableSize; k++)
|
||
|
{
|
||
|
if (keyToChar[k] & kUCKeyOutputTestForIndexMask)
|
||
|
{
|
||
|
long idx = keyToChar[k] & kUCKeyOutputGetIndexMask;
|
||
|
if (stateRec && idx < stateRec->keyStateRecordCount)
|
||
|
{
|
||
|
UCKeyStateRecord* rec = reinterpret_cast<UCKeyStateRecord*>(data + stateRec->keyStateRecordOffsets[idx]);
|
||
|
if (rec->stateZeroCharData == ch) return k;
|
||
|
}
|
||
|
}
|
||
|
else if (!(keyToChar[k] & kUCKeyOutputSequenceIndexMask) && keyToChar[k] < 0xFFFE)
|
||
|
{
|
||
|
if (keyToChar[k] == ch) return k;
|
||
|
}
|
||
|
} // for k
|
||
|
} // for j
|
||
|
} // for i
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods)
|
||
|
{
|
||
|
if (!qxt_mac_handler_installed)
|
||
|
{
|
||
|
EventTypeSpec t;
|
||
|
t.eventClass = kEventClassKeyboard;
|
||
|
t.eventKind = kEventHotKeyPressed;
|
||
|
InstallApplicationEventHandler(&qxt_mac_handle_hot_key, 1, &t, NULL, NULL);
|
||
|
}
|
||
|
|
||
|
EventHotKeyID keyID;
|
||
|
keyID.signature = 'cute';
|
||
|
keyID.id = ++hotKeySerial;
|
||
|
|
||
|
EventHotKeyRef ref = 0;
|
||
|
bool rv = !RegisterEventHotKey(nativeKey, nativeMods, keyID, GetApplicationEventTarget(), 0, &ref);
|
||
|
if (rv)
|
||
|
{
|
||
|
keyIDs.insert(Identifier(nativeMods, nativeKey), keyID.id);
|
||
|
keyRefs.insert(keyID.id, ref);
|
||
|
}
|
||
|
qDebug() << ref;
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods)
|
||
|
{
|
||
|
Identifier id(nativeMods, nativeKey);
|
||
|
if (!keyIDs.contains(id)) return false;
|
||
|
|
||
|
EventHotKeyRef ref = keyRefs.take(keyIDs[id]);
|
||
|
keyIDs.remove(id);
|
||
|
return !UnregisterEventHotKey(ref);
|
||
|
}
|