kdelibs/kdeui/util/kkeyserver_mac.cpp
2014-11-13 01:04:59 +02:00

249 lines
8.1 KiB
C++

/*
Copyright (C) 2006 Marijn Kruisselbrink <m.kruisselbrink@student.tue.nl>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kkeyserver_mac.h"
#include <config.h>
#include <QtCore/QCOORD>
#ifdef Q_WS_MACX // Only compile this module if we're compiling for Mac OS X
#include "kshortcut.h"
#include <kdebug.h>
#include <QMultiMap>
#include <Carbon/Carbon.h>
namespace KKeyServer {
struct TransKey {
int qt_code;
int mac_code;
};
static TransKey qtKeyToChar[] = {
{Qt::Key_Escape, kEscapeCharCode},
{Qt::Key_Tab, kTabCharCode},
{Qt::Key_Backtab, kTabCharCode}, // Backtab == tab with different modifiers
{Qt::Key_Backspace, kBackspaceCharCode},
{Qt::Key_Return, kReturnCharCode},
{Qt::Key_Enter, kEnterCharCode},
// Insert
{Qt::Key_Delete, kDeleteCharCode},
// Pause, Print, SysReq
{Qt::Key_Clear, kClearCharCode},
{Qt::Key_Home, kHomeCharCode},
{Qt::Key_End, kEndCharCode},
{Qt::Key_Left, kLeftArrowCharCode},
{Qt::Key_Up, kUpArrowCharCode},
{Qt::Key_Right, kRightArrowCharCode},
{Qt::Key_Down, kDownArrowCharCode},
{Qt::Key_PageUp, kPageUpCharCode},
{Qt::Key_PageDown, kPageDownCharCode},
// Shift, Control, Meta, Alt, CapsLock, NumLock, ScrollLock
// Super_L, Super_R, Menu, Hyper_L, Hyper_R
{Qt::Key_Help, kHelpCharCode},
// Direction_L, Direction_R
{Qt::Key_nobreakspace, kNonBreakingSpaceCharCode},
{0, 0}
};
static QMultiMap<int, uint> scancodes;
static long lastLayoutID = -1;
#ifdef QT_MAC_USE_COCOA
static TISInputSourceRef lastLayout = 0;
#else
static KeyboardLayoutRef lastLayout = NULL;
#endif
void updateScancodes() {
#ifdef QT_MAC_USE_COCOA
TISInputSourceRef layout = TISCopyCurrentKeyboardLayoutInputSource();
if (!layout) {
kWarning() << "Error retrieving current layout";
return;
}
if (layout == lastLayout) {
CFRelease(layout);
} else {
// keyboard layout changed
#ifndef NDEBUG
const void *name = TISGetInputSourceProperty(layout, kTISPropertyLocalizedName);
kDebug() << "Layout changed to: " << CFStringGetCStringPtr((CFStringRef)name, 0);
#endif
lastLayout = layout;
scancodes.clear();
CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(layout,
kTISPropertyUnicodeKeyLayoutData));
const UCKeyboardLayout *ucData = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0;
if (!ucData) {
kWarning() << "Error retrieving current layout character data";
return;
}
for (int i = 0; i < 128; ++i) {
UInt32 tmpState = 0;
UniChar str[4];
UniCharCount actualLength = 0;
OSStatus err = UCKeyTranslate(ucData, i, kUCKeyActionDown, 0, LMGetKbdType(),
kUCKeyTranslateNoDeadKeysMask, &tmpState, 4, &actualLength, str);
if (err != noErr) {
kWarning() << "Error translating unicode key" << err;
} else {
if (str[0] && str[0] != kFunctionKeyCharCode)
scancodes.insert(str[0], i);
}
}
}
#else
KeyboardLayoutRef layout;
if (KLGetCurrentKeyboardLayout(&layout) != noErr) {
kWarning() << "Error retrieving current layout";
}
if (layout != lastLayout) {
#ifndef NDEBUG
void *name;
KLGetKeyboardLayoutProperty(layout, kKLName, const_cast<const void**>(&name));
kDebug() << "Layout changed to: " << CFStringGetCStringPtr((CFStringRef) name, 0);
#endif
lastLayout = layout;
scancodes.clear();
void *kchr;
if (KLGetKeyboardLayoutProperty(layout, kKLKCHRData, const_cast<const void**>(&kchr)) != noErr) {
kWarning() << "Couldn't load active keyboard layout";
} else {
for (int i = 0; i < 128; i++) {
UInt32 tmpState = 0;
UInt32 chr = KeyTranslate(kchr, i, &tmpState);
if (chr && chr != kFunctionKeyCharCode) {
scancodes.insert(chr, i);
}
}
}
}
#endif
}
#define SCANCODE(name, value) { Qt::Key_ ## name, value }
static TransKey functionKeys[] = {
SCANCODE(F1, 122),
SCANCODE(F2, 120),
SCANCODE(F3, 99),
SCANCODE(F4, 118),
SCANCODE(F5, 96),
SCANCODE(F6, 97),
SCANCODE(F7, 98),
SCANCODE(F8, 100),
SCANCODE(F9, 101),
SCANCODE(F10, 109),
//TODO: figure out scancodes of other F* keys
{ 0, 0 }
};
#undef SCANCODE
bool keyQtToSymMac( int keyQt, int& sym )
{
// Printable ascii values, before A
if (keyQt >= 0x20 && keyQt < Qt::Key_A) {
sym = keyQt;
return true;
}
// Letters, return lower-case equivalent
if (keyQt >= Qt::Key_A && keyQt <= Qt::Key_Z) {
sym = keyQt - Qt::Key_A + 'a';
return true;
}
// Printable ascii values up to lower-case a
if (keyQt > Qt::Key_Z && keyQt <= 0x60) {
sym = keyQt;
return true;
}
// Remainder of printable ascii values
if (keyQt >= 0x7B && keyQt < 0x7F) {
sym = keyQt;
return true;
}
// Function keys
if (keyQt >= Qt::Key_F1 && keyQt <= Qt::Key_F35) {
sym = kFunctionKeyCharCode;
return true;
}
// Try to find in lookup table
for (int i = 0; qtKeyToChar[i].qt_code; i++) {
if (qtKeyToChar[i].qt_code == keyQt) {
sym = qtKeyToChar[i].mac_code;
return true;
}
}
// Not found
return false;
}
bool keyQtToCodeMac( int keyQt, QList<uint>& keyCodes )
{
updateScancodes();
keyCodes.clear();
keyQt &= ~Qt::KeyboardModifierMask;
int chr;
if (!keyQtToSymMac( keyQt, chr ) ) return false;
if (chr == kFunctionKeyCharCode) {
for (int i = 0; functionKeys[i].qt_code; i++) {
if (functionKeys[i].qt_code == keyQt) {
keyCodes.append(functionKeys[i].mac_code);
}
}
} else {
keyCodes += scancodes.values(chr);
}
return keyCodes.count() > 0;
}
bool keyQtToModMac( int keyQt, uint& mod )
{
mod = 0;
if (keyQt & Qt::ShiftModifier) {
mod |= shiftKey;
}
if (keyQt & Qt::ControlModifier) {
mod |= cmdKey;
}
if (keyQt & Qt::AltModifier) {
mod |= optionKey;
}
if (keyQt & Qt::MetaModifier) {
mod |= controlKey;
}
if (keyQt & Qt::KeypadModifier) {
mod |= kEventKeyModifierNumLockMask;
}
// Special case for Qt::Key_Backtab
if ((keyQt & ~Qt::KeyboardModifierMask) == Qt::Key_Backtab) {
mod |= shiftKey;
}
return true;
}
} // end of namespace KKeyServer
#endif // Q_WS_MACX