diff options
author | Robert Griebl <rgriebl@trolltech.com> | 2009-03-03 17:36:58 (GMT) |
---|---|---|
committer | Robert Griebl <rgriebl@trolltech.com> | 2009-04-02 19:21:57 (GMT) |
commit | 3f51c30f030a5517273a62bdd48dfe96c98852ee (patch) | |
tree | aa40f43a4d2d0b8e68b8b4ebbd86113bc8bc4f60 /src/gui/embedded/qkbd_qws.cpp | |
parent | 86723ca856dbd07bc7809ed9c3f9a219a99e67bd (diff) | |
download | Qt-3f51c30f030a5517273a62bdd48dfe96c98852ee.zip Qt-3f51c30f030a5517273a62bdd48dfe96c98852ee.tar.gz Qt-3f51c30f030a5517273a62bdd48dfe96c98852ee.tar.bz2 |
Keymap support for QWS.
This patch adds support for keymaps to the QWSKeyboardHandler. The keymaps
can be generated by the kmap2qmap tool (see tools/ directory). The source
keymaps can be standard Linux .kmap files.
Changes to Qt:
* completely refactored the Tty and Usb (now known as LinuxInput) handlers
* removed the LinuxIS plugin (handled by LinuxInput now)
* removed support for iPAQ, EBX and Zylonite keypads (obsolete hardware)
* removed support for RAW tty mode. This could be re-added, for non-Linux
systems by implementing a PC/AT scan-code to Linux keycode converter.
New features for Tty and LinuxInput (ex Usb) handlers:
* support for keymaps
QWS_KEYBOARD=..:keymap=/path/to/x.qmap:..
* support for dead keys and the compose key
* support for lock LEDs
* support for key repeat rates (in ms):
QWS_KEYBOARD=..:repeat-delay=x:repear-rate=y:..
* the default keymap supports latin1 composing via AltGr:
QWS_KEYBOARD=..:enable-compose:..
* ctrl+alt+backspace application zapping can be disabled:
QWS_KEYBOARD=..:disable-zap:..
* added virtual filter functions to both the Tty and the LinuxInput
handlers. (QWSKeyboardHandler::processKeycode should be virtual in the
first place, but that would be BIC)
Still missing:
* extended documentation for QWS_KEYBOARD
Reviewed-By: Paul Olav Tvete
Reviewed-By: Harald Fernengel
Diffstat (limited to 'src/gui/embedded/qkbd_qws.cpp')
-rw-r--r-- | src/gui/embedded/qkbd_qws.cpp | 480 |
1 files changed, 452 insertions, 28 deletions
diff --git a/src/gui/embedded/qkbd_qws.cpp b/src/gui/embedded/qkbd_qws.cpp index 8cf87db..120d2a1 100644 --- a/src/gui/embedded/qkbd_qws.cpp +++ b/src/gui/embedded/qkbd_qws.cpp @@ -40,56 +40,216 @@ ****************************************************************************/ #include "qkbd_qws.h" +#include "qkbd_qws_p.h" #ifndef QT_NO_QWS_KEYBOARD +#include <QFile> +#include <QDataStream> + #include "qwindowsystem_qws.h" #include "qscreen_qws.h" #include "qtimer.h" #include <stdlib.h> +//#define QT_DEBUG_KEYMAP + + QT_BEGIN_NAMESPACE class QWSKbPrivate : public QObject { Q_OBJECT public: - QWSKbPrivate(QWSKeyboardHandler *h) { - handler = h; - arTimer = new QTimer(this); - arTimer->setSingleShot(true); - connect(arTimer, SIGNAL(timeout()), SLOT(autoRepeat())); - repeatdelay = 400; - repeatperiod = 80; + QWSKbPrivate(QWSKeyboardHandler *h, const QString &device) + : m_handler(h), m_modifiers(0), m_composing(0), m_dead_unicode(0xffff), + m_no_zap(false), m_do_compose(false), + m_keymap(0), m_keymap_size(0), m_keycompose(0), m_keycompose_size(0) + { + m_ar_timer = new QTimer(this); + m_ar_timer->setSingleShot(true); + connect(m_ar_timer, SIGNAL(timeout()), SLOT(autoRepeat())); + m_ar_delay = 400; + m_ar_period = 80; + + memset(m_locks, 0, sizeof(m_locks)); + + QString keymap; + QStringList args = device.split(QLatin1Char(':')); + foreach (const QString &arg, args) { + if (arg.startsWith(QLatin1String("keymap="))) + keymap = arg.mid(7); + else if (arg == QLatin1String("disable-zap")) + m_no_zap = true; + else if (arg == QLatin1String("enable-compose")) + m_do_compose = true; + else if (arg.startsWith(QLatin1String("repeat-delay="))) + m_ar_delay = arg.mid(13).toInt(); + else if (arg.startsWith(QLatin1String("repeat-rate="))) + m_ar_period = arg.mid(12).toInt(); + } + + if (keymap.isEmpty() || !loadKeymap(keymap)) + unloadKeymap(); } - void beginAutoRepeat(int uni, int code, Qt::KeyboardModifiers mod) { - unicode = uni; - keycode = code; - modifier = mod; - arTimer->start(repeatdelay); + ~QWSKbPrivate() + { + unloadKeymap(); } - void endAutoRepeat() { - arTimer->stop(); + + void beginAutoRepeat(int uni, int code, Qt::KeyboardModifiers mod) + { + m_ar_unicode = uni; + m_ar_keycode = code; + m_ar_modifier = mod; + m_ar_timer->start(m_ar_delay); } + void endAutoRepeat() + { + m_ar_timer->stop(); + } + + static Qt::KeyboardModifiers toQtModifiers(quint8 mod) + { + Qt::KeyboardModifiers qtmod = Qt::NoModifier; + + if (mod & (QWSKeyboard::ModShift | QWSKeyboard::ModShiftL | QWSKeyboard::ModShiftR)) + qtmod |= Qt::ShiftModifier; + if (mod & (QWSKeyboard::ModControl | QWSKeyboard::ModCtrlL | QWSKeyboard::ModCtrlR)) + qtmod |= Qt::ControlModifier; + if (mod & QWSKeyboard::ModAlt) + qtmod |= Qt::AltModifier; + + return qtmod; + } + + void unloadKeymap(); + bool loadKeymap(const QString &file); + private slots: - void autoRepeat() { - handler->processKeyEvent(unicode, keycode, modifier, false, true); - handler->processKeyEvent(unicode, keycode, modifier, true, true); - arTimer->start(repeatperiod); + void autoRepeat() + { + m_handler->processKeyEvent(m_ar_unicode, m_ar_keycode, m_ar_modifier, false, true); + m_handler->processKeyEvent(m_ar_unicode, m_ar_keycode, m_ar_modifier, true, true); + m_ar_timer->start(m_ar_period); } private: - QWSKeyboardHandler *handler; - int unicode; - int keycode; - Qt::KeyboardModifiers modifier; - int repeatdelay; - int repeatperiod; - QTimer *arTimer; + QWSKeyboardHandler *m_handler; + + // auto repeat simulation + int m_ar_unicode; + int m_ar_keycode; + Qt::KeyboardModifiers m_ar_modifier; + int m_ar_delay; + int m_ar_period; + QTimer *m_ar_timer; + + // keymap handling + quint8 m_modifiers; + quint8 m_locks[3]; + int m_composing; + quint16 m_dead_unicode; + + bool m_no_zap; + bool m_do_compose; + + const QWSKeyboard::Mapping *m_keymap; + int m_keymap_size; + const QWSKeyboard::Composing *m_keycompose; + int m_keycompose_size; + + static const QWSKeyboard::Mapping s_keymap_default[]; + static const QWSKeyboard::Composing s_keycompose_default[]; + + friend class QWSKeyboardHandler; }; +// simple builtin US keymap +#include "qkbd_defaultmap_qws_p.h" + +// the unloadKeymap() function needs to be AFTER the defaultmap include, +// since the sizeof(s_keymap_default) wouldn't work otherwise. + +void QWSKbPrivate::unloadKeymap() +{ + if (m_keymap && m_keymap != s_keymap_default) + delete [] m_keymap; + if (m_keycompose && m_keycompose != s_keycompose_default) + delete [] m_keycompose; + + m_keymap = s_keymap_default; + m_keymap_size = sizeof(s_keymap_default) / sizeof(s_keymap_default[0]); + m_keycompose = s_keycompose_default; + m_keycompose_size = sizeof(s_keycompose_default) / sizeof(s_keycompose_default[0]); + + // reset state, so we could switch keymaps at runtime + m_modifiers = 0; + memset(m_locks, 0, sizeof(m_locks)); + m_composing = 0; + m_dead_unicode = 0xffff; +} + +bool QWSKbPrivate::loadKeymap(const QString &file) +{ + QFile f(file); + + if (!f.open(QIODevice::ReadOnly)) { + qWarning("Could not open keymap file '%s'", qPrintable(file)); + return false; + } + + // .qmap files have a very simple structure: + // quint32 magic (QWSKeyboard::FileMagic) + // quint32 version (1) + // quint32 keymap_size (# of struct QWSKeyboard::Mappings) + // quint32 keycompose_size (# of struct QWSKeyboard::Composings) + // all QWSKeyboard::Mappings via QDataStream::operator(<<|>>) + // all QWSKeyboard::Composings via QDataStream::operator(<<|>>) + + quint32 qmap_magic, qmap_version, qmap_keymap_size, qmap_keycompose_size; + + QDataStream ds(&f); + + ds >> qmap_magic >> qmap_version >> qmap_keymap_size >> qmap_keycompose_size; + + if (ds.status() != QDataStream::Ok || qmap_magic != QWSKeyboard::FileMagic || qmap_version != 1 || qmap_keymap_size == 0) { + qWarning("'%s' is ot a valid.qmap keymap file.", qPrintable(file)); + return false; + } + + QWSKeyboard::Mapping *qmap_keymap = new QWSKeyboard::Mapping[qmap_keymap_size]; + QWSKeyboard::Composing *qmap_keycompose = qmap_keycompose_size ? new QWSKeyboard::Composing[qmap_keycompose_size] : 0; + + for (quint32 i = 0; i < qmap_keymap_size; ++i) + ds >> qmap_keymap[i]; + for (quint32 i = 0; i < qmap_keycompose_size; ++i) + ds >> qmap_keycompose[i]; + + if (ds.status() != QDataStream::Ok) { + delete [] qmap_keymap; + delete [] qmap_keycompose; + + qWarning("Keymap file '%s' can not be loaded.", qPrintable(file)); + return false; + } + + // unload currently active and clear state + unloadKeymap(); + + m_keymap = qmap_keymap; + m_keymap_size = qmap_keymap_size; + m_keycompose = qmap_keycompose; + m_keycompose_size = qmap_keycompose_size; + + m_do_compose = true; + + return true; +} + + /*! \class QWSKeyboardHandler \ingroup qws @@ -132,18 +292,29 @@ private: /*! - Constructs a keyboard driver. + Constructs a keyboard driver. The \a device argument is passed by the + QWS_KEYBOARD environment variable. Call the QWSServer::setKeyboardHandler() function to make the newly created keyboard driver, the primary driver. Note that the primary driver is controlled by the system, i.e., the system will delete it upon exit. */ +QWSKeyboardHandler::QWSKeyboardHandler(const QString &device) +{ + d = new QWSKbPrivate(this, device); +} + +/*! + \overload +*/ QWSKeyboardHandler::QWSKeyboardHandler() { - d = new QWSKbPrivate(this); + d = new QWSKbPrivate(this, QString()); } + + /*! Destroys this keyboard driver. @@ -172,7 +343,10 @@ QWSKeyboardHandler::~QWSKeyboardHandler() event is caused by an auto-repeat mechanism and not an actual key press. - \sa beginAutoRepeat(), endAutoRepeat(), transformDirKey() + Note that this function does not handle key mapping. Please use + processKeycode() if you need that functionality. + + \sa processKeycode(), beginAutoRepeat(), endAutoRepeat(), transformDirKey() */ void QWSKeyboardHandler::processKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers, bool isPress, bool autoRepeat) @@ -241,6 +415,256 @@ void QWSKeyboardHandler::endAutoRepeat() d->endAutoRepeat(); } +/*! + \enum QWSKeyboardHandler::KeycodeAction + + This enum describes the various special actions that actual + QWSKeyboardHandler implementations have to take care of. + + \value None No further action required. + + \value CapsLockOn Set the state of the Caps lock LED to on. + \value CapsLockOff Set the state of the Caps lock LED to off. + \value NumLockOn Set the state of the Num lock LED to on. + \value NumLockOff Set the state of the Num lock LED to off. + \value ScrollLockOn Set the state of the Scroll lock LED to on. + \value ScrollLockOff Set the state of the Scroll lock LED to off. + + \value PreviousConsole Switch to the previous virtual console (by + default Ctrl+Alt+Left on Linux). + \value NextConsole Switch to the next virtual console (by default + Ctrl+Alt+Right on Linux). + \value SwitchConsoleFirst Switch to the first virtual console (0). + \value SwitchConsoleLast Switch to the last virtual console (255). + \value SwitchConsoleMask If the KeyAction value is between SwitchConsoleFirst + and SwitchConsoleLast, you can use this mask to get + the specific virtual console number to switch to. + + \value Reboot Reboot the machine - this is ignored in both the TTY and + LinuxInput handlers though (by default Ctrl+Alt+Del on Linux). + + \sa processKeycode() +*/ + +/*! + \fn QWSKeyboardHandler::KeycodeAction QWSKeyboardHandler::processKeycode(quint16 keycode, bool isPress, bool autoRepeat) + + Maps \a keycode according to a keymap and sends that key event to the + \l{Qt for Embedded Linux} server application. + + Please see the QWS_KEYBOARD documentation for a description on how to + create and use keymap files. + + The key event is identified by its \a keycode value and the \a isPress + and \a autoRepeat parameters. + + The \a keycode parameter is \bold NOT the Qt keycode value as defined by + the Qt::Key enum. This functions expects a standard Linux 16 bit kernel + keycode as it is used in the Linux Input Event sub-system. This + \a keycode is transformed to a Qt::Key code by using either a + compiled-in US keyboard layout or by dynamically loading a keymap at + startup which can be specified via the QWS_KEYBOARD environment + variable. + + The \a isPress parameter is true if the event is a key press event and + \a autoRepeat is true if the event is caused by an auto-repeat mechanism + and not an actual key press. + + The return value indicates if the actual QWSKeyboardHandler + implementation needs to take care of a special action, like console + switching or LED handling. + + Standard Linux console keymaps can be found at the + \l {http://lct.sourceforege.net}{LCT project} + + If standard Linux console keymaps are used, \a keycode must be one of the + standardized values defined in \c /usr/include/linux/input.h + + \sa processKeyEvent(), KeycodeAction +*/ + +QWSKeyboardHandler::KeycodeAction QWSKeyboardHandler::processKeycode(quint16 keycode, bool pressed, bool autorepeat) +{ + KeycodeAction result = None; + bool first_press = pressed && !autorepeat; + + const QWSKeyboard::Mapping *map_plain = 0; + const QWSKeyboard::Mapping *map_withmod = 0; + + // get a specific and plain mapping for the keycode and the current modifiers + for (int i = 0; i < d->m_keymap_size && !(map_plain && map_withmod); ++i) { + const QWSKeyboard::Mapping *m = d->m_keymap + i; + if (m->keycode == keycode) { + if (m->modifiers == 0) + map_plain = m; + + quint8 testmods = d->m_modifiers; + if (d->m_locks[0] /*CapsLock*/ && (m->flags & QWSKeyboard::IsLetter)) + testmods ^= QWSKeyboard::ModShift; + if (m->modifiers == testmods) + map_withmod = m; + } + } + +#ifdef QT_DEBUG_KEYMAP + qWarning("Processing key event: keycode=%3d, modifiers=%02x pressed=%d, autorepeat=%d | plain=%d, withmod=%d, size=%d", \ + keycode, d->m_modifiers, pressed ? 1 : 0, autorepeat ? 1 : 0, \ + map_plain ? map_plain - d->m_keymap : -1, \ + map_withmod ? map_withmod - d->m_keymap : -1, \ + d->m_keymap_size); +#endif + + const QWSKeyboard::Mapping *it = map_withmod ? map_withmod : map_plain; + + if (!it) { +#ifdef QT_DEBUG_KEYMAP + // we couldn't even find a plain mapping + qWarning("Could not find a suitable mapping for keycode: %3d, modifiers: %02x", keycode, d->m_modifiers); +#endif + return result; + } + + bool skip = false; + quint16 unicode = it->unicode; + quint32 qtcode = it->qtcode; + + if ((it->flags & QWSKeyboard::IsModifier) && it->special) { + // this is a modifier, i.e. Shift, Alt, ... + if (pressed) + d->m_modifiers |= quint8(it->special); + else + d->m_modifiers &= ~quint8(it->special); + } else if (qtcode >= Qt::Key_CapsLock && qtcode <= Qt::Key_ScrollLock) { + // (Caps|Num|Scroll)Lock + if (first_press) { + quint8 &lock = d->m_locks[qtcode - Qt::Key_CapsLock]; + lock ^= 1; + + switch (qtcode) { + case Qt::Key_CapsLock : result = lock ? CapsLockOn : CapsLockOff; break; + case Qt::Key_NumLock : result = lock ? NumLockOn : NumLockOff; break; + case Qt::Key_ScrollLock: result = lock ? ScrollLockOn : ScrollLockOff; break; + default : break; + } + } + } else if ((it->flags & QWSKeyboard::IsSystem) && it->special && first_press) { + switch (it->special) { + case QWSKeyboard::SystemReboot: + result = Reboot; + break; + + case QWSKeyboard::SystemZap: + if (!d->m_no_zap) + qApp->quit(); + break; + + case QWSKeyboard::SystemConsolePrevious: + result = PreviousConsole; + break; + + case QWSKeyboard::SystemConsoleNext: + result = NextConsole; + break; + + default: + if (it->special >= QWSKeyboard::SystemConsoleFirst && + it->special <= QWSKeyboard::SystemConsoleLast) { + result = KeycodeAction(SwitchConsoleFirst + ((it->special & QWSKeyboard::SystemConsoleMask) & SwitchConsoleMask)); + } + break; + } + + skip = true; // no need to tell QWS about it + } else if ((qtcode == Qt::Key_Multi_key) && d->m_do_compose) { + // the Compose key was pressed + if (first_press) + d->m_composing = 2; + skip = true; + } else if ((it->flags & QWSKeyboard::IsDead) && d->m_do_compose) { + // a Dead key was pressed + if (first_press && d->m_composing == 1 && d->m_dead_unicode == unicode) { // twice + d->m_composing = 0; + qtcode = Qt::Key_unknown; // otherwise it would be Qt::Key_Dead... + } else if (first_press && unicode != 0xffff) { + d->m_dead_unicode = unicode; + d->m_composing = 1; + skip = true; + } else { + skip = true; + } + } + + if (!skip) { + // a normal key was pressed + const int modmask = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier; + + // we couldn't find a specific mapping for the current modifiers, + // or that mapping didn't have special modifiers: + // so just report the plain mapping with additional modifiers. + if ((it == map_plain && it != map_withmod) || + (map_withmod && !(map_withmod->qtcode & modmask))) { + qtcode |= QWSKbPrivate::toQtModifiers(d->m_modifiers); + } + + if (d->m_composing == 2 && first_press && !(it->flags & QWSKeyboard::IsModifier)) { + // the last key press was the Compose key + if (unicode != 0xffff) { + int idx = 0; + // check if this code is in the compose table at all + for ( ; idx < d->m_keycompose_size; ++idx) { + if (d->m_keycompose[idx].first == unicode) + break; + } + if (idx < d->m_keycompose_size) { + // found it -> simulate a Dead key press + d->m_dead_unicode = unicode; + unicode = 0xffff; + d->m_composing = 1; + skip = true; + } else { + d->m_composing = 0; + } + } else { + d->m_composing = 0; + } + } else if (d->m_composing == 1 && first_press && !(it->flags & QWSKeyboard::IsModifier)) { + // the last key press was a Dead key + bool valid = false; + if (unicode != 0xffff) { + int idx = 0; + // check if this code is in the compose table at all + for ( ; idx < d->m_keycompose_size; ++idx) { + if (d->m_keycompose[idx].first == d->m_dead_unicode && d->m_keycompose[idx].second == unicode) + break; + } + if (idx < d->m_keycompose_size) { + quint16 composed = d->m_keycompose[idx].result; + if (composed != 0xffff) { + unicode = composed; + qtcode = Qt::Key_unknown; + valid = true; + } + } + } + if (!valid) { + unicode = d->m_dead_unicode; + qtcode = Qt::Key_unknown; + } + d->m_composing = 0; + } + + if (!skip) { +#ifdef QT_DEBUG_KEYMAP + qWarning("Processing: uni=%04x, qt=%08x, qtmod=%08x", unicode, qtcode & ~modmask, (qtcode & modmask)); +#endif + + // send the result to the QWS server + processKeyEvent(unicode, qtcode & ~modmask, Qt::KeyboardModifiers(qtcode & modmask), pressed, autorepeat); + } + } + return result; +} + QT_END_NAMESPACE #include "qkbd_qws.moc" |