diff options
-rw-r--r-- | README.Linux | 15 | ||||
-rw-r--r-- | src/mumble/GlobalShortcut_unix.cpp | 138 | ||||
-rw-r--r-- | src/mumble/GlobalShortcut_unix.h | 12 |
3 files changed, 125 insertions, 40 deletions
diff --git a/README.Linux b/README.Linux index 65f9ea142..ae5b821b9 100644 --- a/README.Linux +++ b/README.Linux @@ -16,8 +16,19 @@ Global Keyboard Events ====================== Unlike Windows, there is no easy way to monitor all key and mouse events -under X11. At the moment, Mumble uses the Xevie extension to accomplish -this, but unfortunately that extension is disabled by default. To enable it +under X11. + +On Linux, Mumble supports reading the kernel input devices. To use this, +make sure mumble can open /dev/input/event* for reading. To test it out, +that should only require: + sudo chmod a+r /dev/input/event* +However, be aware that this will most likely only last until the next reboot +(at which point devfs is recreated) and you also allow any program run by any +user to monitor the physical keyboard. + +If you're not running Linux (or you're unwilling to compromise keyboard +security), you can use Xevie under X11 to accomplish the same thing, but +unfortunately that extension is disabled by default. To enable it (and push-to-talk), add the following to your xorg.conf: Section "Extensions" diff --git a/src/mumble/GlobalShortcut_unix.cpp b/src/mumble/GlobalShortcut_unix.cpp index 24eafd8cd..d1e8801e9 100644 --- a/src/mumble/GlobalShortcut_unix.cpp +++ b/src/mumble/GlobalShortcut_unix.cpp @@ -35,6 +35,11 @@ #include <X11/Xlib.h> #include <X11/extensions/Xevie.h> +#ifdef Q_OS_LINUX +#include <linux/input.h> +#include <fcntl.h> +#endif + static GlobalShortcutX *gsx = NULL; static ConfigWidget *GlobalShortcutXConfigDialogNew() { return new GlobalShortcutXConfig(); @@ -88,6 +93,8 @@ void XInputKeyWidget::mouseDoubleClickEvent(QMouseEvent *) { void XInputKeyWidget::setButton(bool last) { qlButtons = gsx->getCurrentButtons(); bModified = true; + if (qlButtons.isEmpty()) + return; if (last) clearFocus(); else @@ -97,7 +104,7 @@ void XInputKeyWidget::setButton(bool last) { void XInputKeyWidget::displayKeys() { QStringList sl; foreach(int key, qlButtons) { - if (key < 256) { + if ((key < 0x118) || (key >= 0x128)) { KeySym ks=XKeycodeToKeysym(QX11Info::display(), key, 0); if (ks == NoSymbol) { sl << QLatin1String("0x")+QString::number(key,16); @@ -110,7 +117,7 @@ void XInputKeyWidget::displayKeys() { } } } else - sl << QLatin1String("Mouse ")+QString::number(key-256); + sl << QLatin1String("Mouse ")+QString::number(key-0x118); } setText(sl.join(QLatin1String(" "))); } @@ -185,6 +192,39 @@ GlobalShortcutX::GlobalShortcutX() { bNeedRemap = true; int min, maj; bRunning=false; + + display = NULL; + +#ifdef Q_OS_LINUX + QDir d(QLatin1String("/dev/input"), QLatin1String("event*"), 0, QDir::System); + foreach(QFileInfo fi, d.entryInfoList()) { + QFile *f = new QFile(fi.absoluteFilePath(), this); + if (f->open(QIODevice::ReadOnly)) { + int fd = f->handle(); + int version; + char name[256]; + quint32 events = 0; + if ((ioctl(fd, EVIOCGVERSION, &version) >= 0) && (ioctl(fd, EVIOCGNAME(sizeof(name)), name)>=0) && (ioctl(fd, EVIOCGBIT(0,31), &events) >= 0) && (events & (1 << EV_KEY))) { + name[255]=0; + qWarning("GlobalShortcutX: Linux Input v%d.%d.%d: %s", (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF, name); + fcntl(f->handle(), F_SETFL, O_NONBLOCK); + connect(new QSocketNotifier(f->handle(), QSocketNotifier::Read, f), SIGNAL(activated(int)), this, SLOT(inputReadyRead(int))); + qlInputDevices << f; + } else { + delete f; + } + } else { + delete f; + } + } + + if (qlInputDevices.isEmpty()) { + qWarning("GlobalShortcutX: Unable to open any input devices under /dev/input, falling back to XEVIE"); + } else { + return; + } +#endif + display = XOpenDisplay(NULL); if (! display) { @@ -276,13 +316,13 @@ void GlobalShortcutX::remap() { void GlobalShortcutX::resetMap() { bFirstMouseReleased = false; - for (int i=0;i<320;i++) + for (int i=0;i<NUM_BUTTONS;i++) touchMap[i]=false; } QList<int> GlobalShortcutX::getCurrentButtons() { QList<int> keys; - for (int i=0;i<320;i++) + for (int i=0;i<NUM_BUTTONS;i++) if (touchMap[i]) keys << i; return keys; @@ -292,6 +332,7 @@ void GlobalShortcutX::run() { fd_set in_fds; XEvent evt; struct timeval tv; + while (bRunning) { if (bNeedRemap) remap(); @@ -313,39 +354,10 @@ void GlobalShortcutX::run() { if (evt.type == KeyPress || evt.type == KeyRelease) evtcode = evt.xkey.keycode; else - evtcode = 256 + evt.xbutton.button; + evtcode = 0x118 + evt.xbutton.button; - if (down == activeMap[evtcode]) - break; + handleEvent(evtcode, down); - activeMap[evtcode] = down; - if (down) - touchMap[evtcode] = true; - - //Do nothing until we release the mouse button - if (bFirstMouseReleased) - emit buttonPressed(!down); - else - if (evt.type == ButtonRelease) - bFirstMouseReleased = true; - - foreach(Shortcut *s, qmhKeyToShortcut.values(evtcode)) { - if (down) { - s->iNumDown++; - if (s->iNumDown == s->qlButtons.count()) { - s->bActive = true; - emit s->gs->triggered(down); - emit s->gs->down(); - } - } else { - s->iNumDown--; - if (s->bActive) { - s->bActive = false; - emit s->gs->triggered(down); - emit s->gs->up(); - } - } - } } break; @@ -359,6 +371,62 @@ void GlobalShortcutX::run() { XCloseDisplay(display); } +void GlobalShortcutX::inputReadyRead(int) { +#ifdef Q_OS_LINUX + struct input_event ev; + + if (bNeedRemap) + remap(); + + QFile *f=qobject_cast<QFile *>(sender()->parent()); + while (f->read(reinterpret_cast<char *>(&ev), sizeof(ev)) == sizeof(ev)) { + if (ev.type != EV_KEY) + continue; + bool down; + switch (ev.value) { + case 0: + down = false; + break; + case 1: + down = true; + break; + default: + continue; + } + handleEvent(evtcode, down); + } +#endif +} + +void GlobalShortcutX::handleEvent(int evtcode, bool down) { + if (down == activeMap[evtcode]) + return; + + activeMap[evtcode] = down; + if (down) + touchMap[evtcode] = true; + + emit buttonPressed(!down); + + foreach(Shortcut *s, qmhKeyToShortcut.values(evtcode)) { + if (down) { + s->iNumDown++; + if (s->iNumDown == s->qlButtons.count()) { + s->bActive = true; + emit s->gs->triggered(down); + emit s->gs->down(); + } + } else { + s->iNumDown--; + if (s->bActive) { + s->bActive = false; + emit s->gs->triggered(down); + emit s->gs->up(); + } + } + } +} + GlobalShortcut::GlobalShortcut(QObject *p, int index, QString qsName) : QObject(p) { if (! gsx) gsx = new GlobalShortcutX(); diff --git a/src/mumble/GlobalShortcut_unix.h b/src/mumble/GlobalShortcut_unix.h index 1694ec38f..05a977412 100644 --- a/src/mumble/GlobalShortcut_unix.h +++ b/src/mumble/GlobalShortcut_unix.h @@ -68,24 +68,28 @@ class GlobalShortcutXConfig : public ConfigWidget { void accept(); }; +#define NUM_BUTTONS 0x2ff + class GlobalShortcutX : public QThread { Q_OBJECT public: Display *display; int ref; bool bRunning; - bool activeMap[320]; - bool touchMap[320]; + bool activeMap[NUM_BUTTONS]; + bool touchMap[NUM_BUTTONS]; bool bFirstMouseReleased; QHash<int, GlobalShortcut *> qmShortcuts; QHash<GlobalShortcut *, Shortcut *> qhGlobalToX; + QList<QFile *> qlInputDevices; bool globalEvent(XEvent *); bool bGrabbing; bool bNeedRemap; void grab(); void release(); QMultiHash<int, Shortcut *> qmhKeyToShortcut; - public: + void handleEvent(int evtcode, bool down); + GlobalShortcutX(); ~GlobalShortcutX(); void run(); @@ -94,6 +98,8 @@ class GlobalShortcutX : public QThread { void add(GlobalShortcut *); void remove(GlobalShortcut *); QList<int> getCurrentButtons(); + public slots: + void inputReadyRead(int); signals: void buttonPressed(bool); }; |