From 780575744738fdd46901f0044ab0e409c0c85a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Panzenb=C3=B6ck?= Date: Sat, 22 Feb 2014 23:24:05 +0100 Subject: [PATCH] optional libudev support for auto update devices --- CMakeLists.txt | 21 +++++- src/CMakeLists.txt | 34 ++++----- src/config.h.in | 2 + src/layout.cpp | 172 ++++++++++++++++++++++++++++++++++++++------- src/layout.h | 20 +++++- 5 files changed, 202 insertions(+), 47 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5533a43..41a5105 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,14 +8,31 @@ set(QJOYPAD_PATCH 0) find_package(Qt4 REQUIRED) +set(WITH_LIBUDEV ON CACHE PATH "Use libudev for automatically updating joypad devices.") + +if(WITH_LIBUDEV) + find_package(PkgConfig REQUIRED) + + pkg_check_modules(LIBUDEV libudev) + + if(LIBUDEV_FOUND) + message(STATUS "libudev found") + else() + message(ERROR "libudev not found. If you don't want to compile with libudev support use -DWITH_LIBUDEV=OFF") + endif() + + link_directories(${LIBUDEV_LIBRARY_DIRS}) + include_directories(${LIBUDEV_INCLUDE_DIRS}) +endif() + set(DEVICE_DIR "/dev/input" CACHE PATH "Set the path where QJoyPad will look for your joystick devices. If your devices are /dev/js0, /dev/js1, etc., this should be just \"/dev\". By default, this is /dev/input.") set(PLAIN_KEYS OFF CACHE BOOL "Force QJoyPad to use standard XWindows keynames without filtering them for appearance. This will make displays less attractive and readable, but will save processor power and ensure that you see the right names for keys you press.") -message("-- Using device directory: ${DEVICE_DIR}") +message(STATUS "Using device directory: ${DEVICE_DIR}") if(PLAIN_KEYS) - message("-- Using regular XWindows key names.") + message(STATUS "Using regular XWindows key names.") add_definitions(-DPLAIN_KEYS) endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d63e776..51bbfb1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,25 +23,25 @@ set(qjoypad_SOURCES quickset.cpp) set(qjoypad_QOBJECT_HEADERS - axis_edit.h - axis.h - axisw.h - button_edit.h - button.h - buttonw.h - flash.h - getkey.h - icon.h - joypad.h - joypadw.h - joyslider.h - keycode.h - layout_edit.h - layout.h - quickset.h) + axis_edit.h + axis.h + axisw.h + button_edit.h + button.h + buttonw.h + flash.h + getkey.h + icon.h + joypad.h + joypadw.h + joyslider.h + keycode.h + layout_edit.h + layout.h + quickset.h) QT4_WRAP_CPP(qjoypad_HEADERS_MOC ${qjoypad_QOBJECT_HEADERS}) add_executable(qjoypad ${qjoypad_SOURCES} ${qjoypad_HEADERS_MOC}) -target_link_libraries(qjoypad ${QT_LIBRARIES} Xtst X11) +target_link_libraries(qjoypad ${QT_LIBRARIES} Xtst X11 ${LIBUDEV_LIBRARIES}) install(TARGETS qjoypad RUNTIME DESTINATION "bin") diff --git a/src/config.h.in b/src/config.h.in index 467e576..2a142f3 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -12,4 +12,6 @@ #define QJOYPAD_ICON24 "@CMAKE_INSTALL_PREFIX@/share/icons/hicolor/24x24/apps/qjoypad.png" #define QJOYPAD_ICON64 "@CMAKE_INSTALL_PREFIX@/share/icons/hicolor/64x64/apps/qjoypad.png" +#cmakedefine WITH_LIBUDEV + #endif diff --git a/src/layout.cpp b/src/layout.cpp index 739a5e8..e2b09e6 100644 --- a/src/layout.cpp +++ b/src/layout.cpp @@ -15,6 +15,18 @@ LayoutManager::LayoutManager( bool useTrayIcon, const QString &devdir, const QSt updateLayoutsAction(new QAction(QIcon::fromTheme("view-refresh"),"Update &Layout List",this)), quitAction(new QAction(QIcon::fromTheme("application-exit"),"&Quit",this)), le(0) { + +#ifdef WITH_LIBUDEV + udevNotifier = 0; + udev = 0; + monitor = 0; + + if (!initUDev()) { + errorBox("UDev Error", "Error creating udev monitor. " + "QJoyPad will still work, but it won't automatically update the joypad device list."); + } +#endif + //prepare the popup first. titleAction->setEnabled(false); fillPopup(); @@ -49,8 +61,99 @@ LayoutManager::~LayoutManager() { le->close(); le = 0; } +#ifdef WITH_LIBUDEV + if (udevNotifier) { + udevNotifier->blockSignals(true); + } + if (monitor) { + udev_monitor_unref(monitor); + monitor = 0; + } + if (udev) { + udev_unref(udev); + udev = 0; + } +#endif } +#ifdef WITH_LIBUDEV +bool LayoutManager::initUDev() { + udev = udev_new(); + debug_mesg("init udev\n"); + + if (udev) { + debug_mesg("udev ok\n"); + monitor = udev_monitor_new_from_netlink(udev, "udev"); + + if (monitor) { + debug_mesg("monitor ok\n"); + int errnum = udev_monitor_filter_add_match_subsystem_devtype( + monitor, "input", NULL); + if (errnum != 0) { + debug_mesg("udev_monitor_filter_add_match_subsystem_devtype: %s\n", + strerror(errnum)); + udev_monitor_unref(monitor); + udev_unref(udev); + monitor = 0; + udev = 0; + return false; + } + + errnum = udev_monitor_enable_receiving(monitor); + if (errnum != 0) { + debug_mesg("udev_monitor_enable_receiving: %s\n", + strerror(errnum)); + udev_monitor_unref(monitor); + udev_unref(udev); + monitor = 0; + udev = 0; + return false; + } + + udevNotifier = new QSocketNotifier(udev_monitor_get_fd(monitor), QSocketNotifier::Read, this); + connect(udevNotifier, SIGNAL(activated(int)), this, SLOT(udevUpdate())); + debug_mesg("notifier ok\n"); + } + else { + udev_unref(udev); + udev = 0; + } + } + + return udev != 0; +} + +void LayoutManager::udevUpdate() { + struct udev_device *dev = udev_monitor_receive_device(monitor); + if (dev) { + QRegExp devicename("/js(\\d+)$"); + QString path = QString("/sys%1").arg(udev_device_get_devpath(dev)); + const char *action = udev_device_get_action(dev); + + if (devicename.indexIn(path) >= 0) { + int index = devicename.cap(1).toInt(); + + if (strcmp(action,"add") == 0 || strcmp(action,"online") == 0) { + addJoyPad(index, path); + } + else if (strcmp(action,"remove") == 0 || strcmp(action,"offline") == 0) { + removeJoyPad(index); + } + else if (strcmp(action,"change") == 0) { + removeJoyPad(index); + addJoyPad(index, path); + } + + fillPopup(); + if (le) { + le->updateJoypadWidgets(); + } + } + udev_device_unref(dev); + } +} +#endif + QString LayoutManager::getFileName(const QString& layoutname ) { return QString("%1%2.lyt").arg(settingsDir, layoutname); } @@ -390,42 +493,57 @@ void LayoutManager::updateJoyDevs() { QDir deviceDir(devdir); QStringList devices = deviceDir.entryList(QStringList("js*"), QDir::System); QRegExp devicename("js(\\d+)"); - int joydev = -1; - int index = -1; //for every joystick device in the directory listing... //(note, with devfs, only available devices are listed) foreach (const QString &device, devices) { - QString devpath = QString("%1/%2").arg(devdir, device); - debug_mesg("found a device file, %s\n", qPrintable(devpath)); - //try opening the device. - joydev = open( qPrintable(devpath), O_RDONLY | O_NONBLOCK); - //if it worked, then we have a live joystick! Make sure it's properly - //setup. - if (joydev >= 0) { - devicename.indexIn(device); - index = devicename.cap(1).toInt(); - JoyPad* joypad = joypads[index]; - //if we've never seen this device before, make a new one! - if (joypad == 0) { - joypad = new JoyPad( index, joydev, this ); - joypads.insert(index,joypad); - } - else { - debug_mesg("found previously open joypad with index %d, ignoring", index); - joypad->open(joydev); - } - //make this joystick device available. - available.insert(index,joypad); - } - else { - perror(qPrintable(devpath)); + if (devicename.indexIn(device) >= 0) { + int index = devicename.cap(1).toInt(); + QString devpath = QString("%1/%2").arg(devdir, device); + addJoyPad(index, devpath); } } //when it's all done, rebuild the popup menu so it displays the correct //information. fillPopup(); - if(le) { + if (le) { le->updateJoypadWidgets(); } debug_mesg("done updating joydevs\n"); } + +void LayoutManager::addJoyPad(int index) { + addJoyPad(index, QString("%1/js%2").arg(devdir, index)); +} + +void LayoutManager::addJoyPad(int index, const QString& devpath) { + debug_mesg("opening %s\n", qPrintable(devpath)); + //try opening the device. + int joydev = open(qPrintable(devpath), O_RDONLY | O_NONBLOCK); + //if it worked, then we have a live joystick! Make sure it's properly + //setup. + if (joydev >= 0) { + JoyPad* joypad = joypads[index]; + //if we've never seen this device before, make a new one! + if (joypad == 0) { + joypad = new JoyPad( index, joydev, this ); + joypads.insert(index,joypad); + } + else { + debug_mesg("found previously open joypad with index %d, ignoring", index); + joypad->open(joydev); + } + //make this joystick device available. + available.insert(index,joypad); + } + else { + perror(qPrintable(devpath)); + } +} + +void LayoutManager::removeJoyPad(int index) { + JoyPad *joypad = available[index]; + if (joypad) { + joypad->close(); + available.remove(index); + } +} diff --git a/src/layout.h b/src/layout.h index adbf8db..63a43a7 100644 --- a/src/layout.h +++ b/src/layout.h @@ -17,6 +17,12 @@ #include #include +#include "config.h" + +#ifdef WITH_LIBUDEV +#include +#endif + //a layout handles several joypads #include "joypad.h" //for errors @@ -66,7 +72,10 @@ class LayoutManager : public QObject { private slots: //when the user selects an item on the tray's popup menu void layoutTriggered(); - private: + private: + void addJoyPad(int index); + void addJoyPad(int index, const QString& devpath); + void removeJoyPad(int index); //change to the given layout name and make all the necesary adjustments void setLayoutName(const QString& name); //get the file name for a layout name @@ -90,6 +99,15 @@ class LayoutManager : public QObject { QHash available; QHash joypads; + +#ifdef WITH_LIBUDEV + bool initUDev(); + QSocketNotifier *udevNotifier; + struct udev *udev; + struct udev_monitor *monitor; + private slots: + void udevUpdate(); +#endif }; #endif