summaryrefslogtreecommitdiffhomepage
path: root/qxt-mini
diff options
context:
space:
mode:
Diffstat (limited to 'qxt-mini')
-rw-r--r--qxt-mini/CMakeLists.txt2
-rw-r--r--qxt-mini/qxtglobalshortcut.cpp36
-rw-r--r--qxt-mini/qxtglobalshortcut.h4
-rw-r--r--qxt-mini/qxtglobalshortcut_mac.cpp4
-rw-r--r--qxt-mini/qxtglobalshortcut_p.h3
-rw-r--r--qxt-mini/qxtglobalshortcut_x11.cpp221
-rw-r--r--qxt-mini/x11-keymap.cpp136
-rw-r--r--qxt-mini/x11-keymap.hpp15
8 files changed, 336 insertions, 85 deletions
diff --git a/qxt-mini/CMakeLists.txt b/qxt-mini/CMakeLists.txt
index a6c2908e..84e66732 100644
--- a/qxt-mini/CMakeLists.txt
+++ b/qxt-mini/CMakeLists.txt
@@ -1,6 +1,8 @@
if(UNIX OR APPLE)
otr_module(qxt-mini NO-COMPAT BIN)
if(NOT APPLE)
+ pkg_check_modules(xprotopkg REQUIRED xproto)
+ target_include_directories(opentrack-qxt-mini SYSTEM PRIVATE ${xprotopkg_INCLUDE_DIRS})
target_link_libraries(opentrack-qxt-mini X11)
else()
otr_prop(TARGET opentrack-qxt-mini LINK_FLAGS "-framework Carbon -framework CoreFoundation")
diff --git a/qxt-mini/qxtglobalshortcut.cpp b/qxt-mini/qxtglobalshortcut.cpp
index 298472b5..fc0984b6 100644
--- a/qxt-mini/qxtglobalshortcut.cpp
+++ b/qxt-mini/qxtglobalshortcut.cpp
@@ -61,13 +61,13 @@ void QxtGlobalShortcutPrivate::event_filter_installer::ensure_event_filter()
}
QxtGlobalShortcutPrivate::QxtGlobalShortcutPrivate(QxtGlobalShortcutPrivate::tag) :
- enabled(false), key(Qt::Key(0)), mods(Qt::NoModifier)
+ keystate(false), enabled(false), key(Qt::Key(0)), mods(Qt::NoModifier)
{
qDebug() << "qxt-mini: adding event filter";
}
QxtGlobalShortcutPrivate::QxtGlobalShortcutPrivate() :
- enabled(true), key(Qt::Key(0)), mods(Qt::NoModifier)
+ keystate(false), enabled(true), key(Qt::Key(0)), mods(Qt::NoModifier)
{
QxtGlobalShortcutPrivate::event_filter_installer::ensure_event_filter();
}
@@ -143,25 +143,49 @@ bool QxtGlobalShortcutPrivate::unsetShortcut()
return res;
}
-void QxtGlobalShortcutPrivate::activateShortcut(quint32 nativeKey, quint32 nativeMods)
+void QxtGlobalShortcutPrivate::activateShortcut(quint32 nativeKey, quint32 nativeMods, bool is_down)
{
#ifndef Q_OS_MAC
using IT = decltype(shortcuts.end());
const auto pair = qMakePair(nativeKey, nativeMods);
IT it = shortcuts.find(pair);
+ bool once = false;
+
for (; it != shortcuts.end(); it++)
{
if (it.key() != pair) // DO NOT REMOVE
break;
+
auto ptr = *it;
+ auto& priv = ptr->qxt_d();
+
+ if (priv.keystate == is_down)
+ {
+ continue;
+ }
+
+ if (!once)
+ {
+ once = true;
+ qDebug() << "qxt-mini:" << (is_down ? "keydown" : "keyup") << priv.key << priv.mods;
+ }
+
+ priv.keystate = is_down;
+
if (ptr->isEnabled())
- emit ptr->activated();
+ emit ptr->activated(is_down);
}
#else
QxtGlobalShortcut* shortcut = shortcuts.value(qMakePair(nativeKey, nativeMods));
- if (shortcut && shortcut->isEnabled())
- emit shortcut->activated();
+
+ if (shortcut)
+ {
+ shortcut->qxt_d().keystate = false;
+
+ if (shortcut->isEnabled())
+ emit shortcut->activated(true);
+ }
#endif
}
diff --git a/qxt-mini/qxtglobalshortcut.h b/qxt-mini/qxtglobalshortcut.h
index bf26180f..be62a984 100644
--- a/qxt-mini/qxtglobalshortcut.h
+++ b/qxt-mini/qxtglobalshortcut.h
@@ -43,6 +43,8 @@ class QXT_GUI_EXPORT QxtGlobalShortcut : public QObject
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut)
+ bool keystate;
+
public:
explicit QxtGlobalShortcut(QObject* parent = 0);
explicit QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent = 0);
@@ -58,7 +60,7 @@ public Q_SLOTS:
void setDisabled(bool disabled = true);
Q_SIGNALS:
- void activated();
+ void activated(bool keydown = true);
};
#endif // QXTGLOBALSHORTCUT_H
diff --git a/qxt-mini/qxtglobalshortcut_mac.cpp b/qxt-mini/qxtglobalshortcut_mac.cpp
index c6b20fa2..55684667 100644
--- a/qxt-mini/qxtglobalshortcut_mac.cpp
+++ b/qxt-mini/qxtglobalshortcut_mac.cpp
@@ -55,7 +55,7 @@ OSStatus qxt_mac_handle_hot_key(EventHandlerCallRef nextHandler, EventRef event,
GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(keyID), NULL, &keyID);
Identifier id = keyIDs.key(keyID.id);
if(id != Identifier())
- QxtGlobalShortcutPrivate::activateShortcut(id.second, id.first);
+ QxtGlobalShortcutPrivate::activateShortcut(id.second, id.first, true);
}
return noErr;
}
@@ -76,7 +76,7 @@ quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifier
return native;
}
-quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key)
+quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key keys)
{
UTF16Char ch;
// Constants found in NSEvent.h from AppKit.framework
diff --git a/qxt-mini/qxtglobalshortcut_p.h b/qxt-mini/qxtglobalshortcut_p.h
index 1835f956..7864a7d8 100644
--- a/qxt-mini/qxtglobalshortcut_p.h
+++ b/qxt-mini/qxtglobalshortcut_p.h
@@ -45,6 +45,7 @@ public:
QxtGlobalShortcutPrivate();
~QxtGlobalShortcutPrivate() override;
+ bool keystate;
bool enabled;
Qt::Key key;
Qt::KeyboardModifiers mods;
@@ -56,7 +57,7 @@ public:
static bool eventFilter(void* message);
bool nativeEventFilter(const QByteArray & eventType, void * message, long * result) override;
- static void activateShortcut(quint32 nativeKey, quint32 nativeMods);
+ static void activateShortcut(quint32 nativeKey, quint32 nativeMods, bool is_down);
private:
struct event_filter_installer;
diff --git a/qxt-mini/qxtglobalshortcut_x11.cpp b/qxt-mini/qxtglobalshortcut_x11.cpp
index 0db1ece5..01dfe8df 100644
--- a/qxt-mini/qxtglobalshortcut_x11.cpp
+++ b/qxt-mini/qxtglobalshortcut_x11.cpp
@@ -32,40 +32,57 @@
#include <QHash>
#include <QMutex>
-#include <QMutexLocker>
#include <QDebug>
-
+#include <QPair>
+#include <QKeyEvent>
#include <QApplication>
-// include private header for great justice -sh 20131015
+
#include <X11/Xlib.h>
+#include <X11/XKBlib.h>
#include <xcb/xcb.h>
#include "qplatformnativeinterface.h"
#include "compat/util.hpp"
+#include <iterator>
+#include "x11-keymap.hpp"
+
static constexpr quint32 AllMods = ShiftMask|LockMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask;
+static constexpr quint32 evil_mods[] = {
+#if 0
+ LockMask, // caps lock
+ Mod3Mask, // scroll lock
+#endif
+ Mod2Mask, // num lock
+ Mod5Mask, // altgr
+
+ Mod2Mask | Mod5Mask,
+};
+
typedef int (*X11ErrorHandler)(Display *display, XErrorEvent *event);
+using pair = QPair<quint32, quint32>;
+
struct keybinding final
{
- quint32 code;
+ quint32 code, mods;
int refcnt;
- static QHash<quint32, keybinding> list;
+ static QHash<pair, keybinding> list;
static QMutex lock;
- static bool incf(quint32 code);
- static bool decf(quint32 code);
+ static bool incf(quint32 code, quint32 mods);
+ static bool decf(quint32 code, quint32 mods);
~keybinding();
private:
- keybinding(quint32 code);
+ keybinding(quint32 code, quint32 mods);
};
bool operator==(const keybinding& k1, const keybinding& k2)
{
- return k1.code == k2.code;
+ return k1.code == k2.code && k1.mods == k2.mods;
}
inline bool operator!=(const keybinding& k1, const keybinding& k2)
@@ -75,15 +92,17 @@ inline bool operator!=(const keybinding& k1, const keybinding& k2)
uint qHash(const keybinding& k)
{
- return qHash(k.code);
+ return uint(k.code * 41) ^ qHash(k.mods);
}
uint qHash(const keybinding& k, uint seed)
{
- return qHash(k.code, seed);
+ return qHash(uint(k.code * 41) ^ qHash(k.mods), seed);
}
-keybinding::keybinding(quint32 code) : code(code), refcnt(0)
+keybinding::keybinding(quint32 code, quint32 mods) :
+ code(code), mods(mods),
+ refcnt(0)
{
}
@@ -91,11 +110,11 @@ keybinding::~keybinding()
{
}
-bool keybinding::incf(quint32 code)
+bool keybinding::incf(quint32 code, quint32 mods)
{
QMutexLocker l(&lock);
- keybinding k = list.value(code, keybinding(code));
+ keybinding k = list.value(pair(code, mods), keybinding(code, mods));
const bool ret = k.refcnt == 0;
@@ -105,18 +124,18 @@ bool keybinding::incf(quint32 code)
}
k.refcnt++;
- list.insert(code, k);
+ list.insert(pair(code, mods), k);
//qDebug() << "qxt-mini: incf: refcount for" << code << "now" << k.refcnt;
return ret;
}
-bool keybinding::decf(quint32 code)
+bool keybinding::decf(quint32 code, quint32 mods)
{
QMutexLocker l(&lock);
- auto it = list.find(code);
+ auto it = list.find(pair(code, mods));
if (it == list.end())
{
@@ -139,7 +158,7 @@ bool keybinding::decf(quint32 code)
return false;
}
-QHash<quint32, keybinding> keybinding::list;
+QHash<pair, keybinding> keybinding::list;
QMutex keybinding::lock;
class QxtX11ErrorHandler {
@@ -206,57 +225,65 @@ public:
return DefaultRootWindow(display());
}
- bool grabKey(quint32 keycode, quint32 modifiers, Window window)
+ static constexpr quint32 filter_evil_mods(quint32 mods)
{
- //TODO: search keybinding by code and modifiers, so keys can be assigned multiple times using different modifiers
- if (keybinding::incf(keycode))
+ for (quint32 mod : evil_mods)
+ mods &= ~mod;
+ return mods;
+ }
+
+ bool grabKey(quint32 code, quint32 mods, Window window)
+ {
+ // qDebug() << "grabbing key" << code << mods;
+
+ mods = filter_evil_mods(mods);
+
+ // qDebug() << "mods now" << mods;
+
+ if (keybinding::incf(code, mods))
{
QxtX11ErrorHandler errorHandler;
- bool error = false;
- XGrabKey(display(), keycode, modifiers, window, True,
- GrabModeAsync, GrabModeAsync);
- if (errorHandler.error) {
- error=true;
- }
- //Also grab key with num lock = on
- XGrabKey(display(), keycode, modifiers | Mod2Mask, window, True,
- GrabModeAsync, GrabModeAsync);
- if (errorHandler.error) {
- error=true;
- }
+ XGrabKey(display(), code, mods, window, True, GrabModeAsync, GrabModeAsync);
- //...and with scroll lock = on
- XGrabKey(display(), keycode, modifiers | Mod5Mask, window, True,
- GrabModeAsync, GrabModeAsync);
- if (errorHandler.error) {
- error=true;
- }
+ for (quint32 evil : evil_mods)
+ {
+ quint32 m = mods | evil;
- //...and with bot = on
- XGrabKey(display(), keycode, modifiers | Mod2Mask | Mod5Mask, window, True,
- GrabModeAsync, GrabModeAsync);
- if (errorHandler.error) {
- error=true;
+ XGrabKey(display(), code, m, window, True, GrabModeAsync, GrabModeAsync);
}
- if(error){
- ungrabKey(keycode, window);
+ if (errorHandler.error)
+ {
+ qDebug() << "qxt-mini: error while binding to" << code << mods;
+ ungrabKey(code, mods, window);
return false;
}
-
}
return true;
}
- bool ungrabKey(quint32 keycode, Window window)
+ bool ungrabKey(quint32 code, quint32 mods, Window window)
{
- if (keybinding::decf(keycode))
+ mods = filter_evil_mods(mods);
+
+ if (keybinding::decf(code, mods))
{
QxtX11ErrorHandler errorHandler;
- XUngrabKey(display(), keycode, AnyModifier, window);
- return !errorHandler.error;
+ XUngrabKey(display(), code, mods, window);
+
+ for (quint32 evil : evil_mods)
+ {
+ quint32 m = mods | evil;
+ XUngrabKey(display(), code, m, window);
+ }
+
+ if (errorHandler.error)
+ {
+ qDebug() << "qxt-mini: error while unbinding" << code << mods;
+ return false;
+ }
}
return true;
}
@@ -270,14 +297,65 @@ bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType,
{
Q_UNUSED(result);
+ {
+ static bool once_ = false;
+ if (!once_)
+ {
+ QxtX11Data x11;
+ if (x11.isValid())
+ {
+ once_ = true;
+ Bool val;
+
+ (void) XkbSetDetectableAutoRepeat(x11.display(), True, &val);
+
+ if (val)
+ qDebug() << "qxt-mini: fixed x11 autorepeat";
+ else
+ qDebug() << "qxt-mini: can't fix x11 autorepeat";
+ }
+ }
+ }
+
+ bool is_release = false;
+
xcb_key_press_event_t *kev = 0;
if (eventType == "xcb_generic_event_t") {
xcb_generic_event_t *ev = static_cast<xcb_generic_event_t *>(message);
- if ((ev->response_type & 127) == XCB_KEY_PRESS)
- kev = static_cast<xcb_key_press_event_t *>(message);
+ switch (ev->response_type & 127)
+ {
+ case XCB_KEY_RELEASE:
+ is_release = true;
+ /*FALLTHROUGH*/
+ case XCB_KEY_PRESS:
+ kev = static_cast<xcb_key_press_event_t *>(message);
+ /*FALLTHROUGH*/
+ default:
+ break;
+ }
}
- if (kev != 0) {
+ if (kev) {
+#if 0
+ using event_type = decltype((xcb_key_press_event_t{}).detail);
+
+ static event_type prev_event = 0;
+ static bool prev_is_release = false;
+
+ if (is_release == prev_is_release &&
+ prev_event != 0 &&
+ prev_event == kev->detail)
+ {
+ // ignore repeated keystrokes
+ return false;
+ }
+ else
+ {
+ prev_event = kev->detail;
+ prev_is_release = is_release;
+ }
+#endif
+
unsigned int keycode = kev->detail;
unsigned int keystate = 0;
if(kev->state & XCB_MOD_MASK_1) // alt
@@ -293,14 +371,15 @@ bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType,
keystate |= AltGrMask;
#endif
- activateShortcut(keycode, keystate);
+ // qDebug() << "qxt-mini:" << (is_release ? "keyup" : "keydown") << keycode << keystate;
+
+ activateShortcut(keycode, keystate, !is_release);
}
return false;
}
quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers)
{
- // XXX TODO make a lookup table
quint32 native = 0;
if (modifiers & Qt::AltModifier)
native |= Mod1Mask;
@@ -312,11 +391,8 @@ quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifier
native |= ShiftMask;
if (modifiers & Qt::KeypadModifier)
native |= Mod2Mask;
-
-#if 0
- if (modifiers & Qt::MetaModifier) // dunno the native mask
+ if (modifiers & Qt::MetaModifier) // Super aka Windows key
native |= Mod4Mask;
-#endif
if (modifiers & Qt::KeypadModifier) // numlock
native |= Mod2Mask;
@@ -328,31 +404,26 @@ quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifier
quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key)
{
QxtX11Data x11;
- if (!x11.isValid())
- return 0;
- QByteArray tmp(QKeySequence(key).toString().toLatin1());
+ if (x11.isValid())
+ return qt_key_to_x11(x11.display(), key);
- KeySym keysym = XStringToKeysym(tmp.data());
- if (keysym == NoSymbol)
- keysym = static_cast<ushort>(key);
-
- const quint32 ret = XKeysymToKeycode(x11.display(), keysym);
-
- //qDebug() << "key is" << key << QKeySequence(key).toString(QKeySequence::PortableText) << ret;
-
- return ret;
+ return unsigned(-1);
}
-bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, unused(quint32, nativeMods))
+bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods)
{
QxtX11Data x11;
+ if (nativeKey == unsigned(-1))
+ return false;
return x11.isValid() && x11.grabKey(nativeKey, nativeMods, x11.rootWindow());
}
-bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, unused(quint32, nativeMods))
+bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods)
{
QxtX11Data x11;
- return x11.isValid() && x11.ungrabKey(nativeKey, x11.rootWindow());
+ if (nativeKey == unsigned(-1))
+ return false;
+ return x11.isValid() && x11.ungrabKey(nativeKey, nativeMods, x11.rootWindow());
}
#endif
diff --git a/qxt-mini/x11-keymap.cpp b/qxt-mini/x11-keymap.cpp
new file mode 100644
index 00000000..b4438a3c
--- /dev/null
+++ b/qxt-mini/x11-keymap.cpp
@@ -0,0 +1,136 @@
+#include "x11-keymap.hpp"
+
+#ifndef __APPLE__
+
+#include <tuple>
+
+#define XK_MISCELLANY
+#define XK_LATIN1
+#include <X11/keysymdef.h>
+
+using tt = std::tuple<Qt::Key, unsigned>;
+
+static tt keymap[] =
+{
+ { Qt::Key_Return, XK_Return },
+ { Qt::Key_Enter, XK_Return },
+ { Qt::Key_Delete, XK_Delete },
+ { Qt::Key_Pause, XK_Pause },
+ { Qt::Key_SysReq, XK_Sys_Req },
+ { Qt::Key_Home, XK_Home },
+ { Qt::Key_End, XK_End },
+ { Qt::Key_Left, XK_Left },
+ { Qt::Key_Up, XK_Up },
+ { Qt::Key_Right, XK_Right },
+ { Qt::Key_Down, XK_Down },
+ { Qt::Key_PageUp, XK_Prior },
+ { Qt::Key_PageDown, XK_Next },
+
+ { Qt::Key_F1, XK_F1 },
+ { Qt::Key_F2, XK_F2 },
+ { Qt::Key_F3, XK_F3 },
+ { Qt::Key_F4, XK_F4 },
+ { Qt::Key_F5, XK_F5 },
+ { Qt::Key_F6, XK_F6 },
+ { Qt::Key_F7, XK_F7 },
+ { Qt::Key_F8, XK_F8 },
+ { Qt::Key_F9, XK_F9 },
+ { Qt::Key_F10, XK_F10 },
+ { Qt::Key_F11, XK_F11 },
+ { Qt::Key_F12, XK_F12 },
+
+ { Qt::Key_Space, XK_space },
+ { Qt::Key_Exclam, XK_exclam },
+ { Qt::Key_QuoteDbl, XK_quotedbl },
+ { Qt::Key_NumberSign, XK_numbersign },
+ { Qt::Key_Dollar, XK_dollar },
+ { Qt::Key_Percent, XK_percent },
+ { Qt::Key_Ampersand, XK_ampersand },
+ { Qt::Key_Apostrophe, XK_apostrophe },
+ { Qt::Key_ParenLeft, XK_parenleft },
+ { Qt::Key_ParenRight, XK_parenright },
+ { Qt::Key_Asterisk, XK_asterisk },
+ { Qt::Key_Plus, XK_plus },
+ { Qt::Key_Comma, XK_comma },
+ { Qt::Key_Minus, XK_minus },
+ { Qt::Key_Period, XK_period },
+ { Qt::Key_Slash, XK_slash },
+
+ { Qt::Key_0, XK_0 },
+ { Qt::Key_1, XK_1},
+ { Qt::Key_2, XK_2 },
+ { Qt::Key_3, XK_3 },
+ { Qt::Key_4, XK_4 },
+ { Qt::Key_5, XK_5 },
+ { Qt::Key_6, XK_6 },
+ { Qt::Key_7, XK_7 },
+ { Qt::Key_8, XK_8 },
+ { Qt::Key_9, XK_9 },
+
+ { Qt::Key_Colon, XK_colon },
+ { Qt::Key_Semicolon, XK_semicolon },
+ { Qt::Key_Less, XK_less },
+ { Qt::Key_Equal, XK_equal },
+ { Qt::Key_Greater, XK_greater },
+ { Qt::Key_Question, XK_question },
+ { Qt::Key_At, XK_at },
+
+ { Qt::Key_A, XK_a },
+ { Qt::Key_B, XK_b },
+ { Qt::Key_C, XK_c },
+ { Qt::Key_D, XK_d },
+ { Qt::Key_E, XK_e },
+ { Qt::Key_F, XK_f },
+ { Qt::Key_G, XK_g },
+ { Qt::Key_H, XK_h },
+ { Qt::Key_I, XK_i },
+ { Qt::Key_J, XK_j },
+ { Qt::Key_K, XK_k },
+ { Qt::Key_L, XK_l },
+ { Qt::Key_M, XK_m },
+ { Qt::Key_N, XK_n },
+ { Qt::Key_O, XK_o },
+ { Qt::Key_P, XK_p },
+ { Qt::Key_Q, XK_q },
+ { Qt::Key_R, XK_r },
+ { Qt::Key_S, XK_s },
+ { Qt::Key_T, XK_t },
+ { Qt::Key_U, XK_u },
+ { Qt::Key_V, XK_v },
+ { Qt::Key_W, XK_w },
+ { Qt::Key_X, XK_x },
+ { Qt::Key_Y, XK_y },
+ { Qt::Key_Z, XK_z },
+
+ { Qt::Key_BracketLeft, XK_bracketleft },
+ { Qt::Key_Backslash, XK_backslash },
+ { Qt::Key_BracketRight, XK_bracketright },
+ { Qt::Key_AsciiCircum, XK_asciicircum },
+ { Qt::Key_Underscore, XK_underscore },
+ { Qt::Key_QuoteLeft, XK_grave },
+};
+
+unsigned qt_key_to_x11(Display* disp, Qt::Key k)
+{
+ Qt::Key k_;
+ unsigned keysym;
+
+ for (const tt& tuple : keymap)
+ {
+ std::tie(k_, keysym) = tuple;
+
+ if (k == k_)
+ {
+ const unsigned ret = XKeysymToKeycode(disp, keysym);
+
+ if (ret == 0)
+ return unsigned(-1);
+
+ return ret;
+ }
+ }
+
+ qDebug() << "qxt-mini: no keysym for" << k;
+ return unsigned(-1);
+}
+#endif
diff --git a/qxt-mini/x11-keymap.hpp b/qxt-mini/x11-keymap.hpp
new file mode 100644
index 00000000..b35097bc
--- /dev/null
+++ b/qxt-mini/x11-keymap.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#ifndef BUILD_QXT_MINI
+# error "internal header"
+#endif
+
+#ifndef __APPLE__
+
+#include <Qt>
+#include <QDebug>
+#include <X11/Xlib.h>
+
+unsigned qt_key_to_x11(Display* disp, Qt::Key k);
+
+#endif