diff --git a/CMakeLists.txt b/CMakeLists.txt
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -43,6 +43,11 @@
set(USING_X_LIBRARIES ${X11_LIBRARIES} ${X11_Xinput_LIB})
endif()
+if(${LIBWACOM_VERSION} VERSION_LESS "0.29")
+ message(STATUS "Button detection with libwacom requires version at least 0.29. Detected version is: " ${LIBWACOM_VERSION})
+ add_definitions(-DLIBWACOM_EVDEV_MISSING)
+endif()
+
add_definitions( -DQT_STRICT_ITERATORS )
add_definitions( -DQT_NO_CAST_FROM_ASCII )
add_definitions( -DQT_NO_CAST_TO_ASCII )
diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt
--- a/autotests/CMakeLists.txt
+++ b/autotests/CMakeLists.txt
@@ -15,6 +15,7 @@
add_subdirectory( common/deviceprofileconfigadaptor )
add_subdirectory( common/deviceproperty )
add_subdirectory( common/enum )
+add_subdirectory( common/libwacomdata )
add_subdirectory( common/profilemanager )
add_subdirectory( common/property )
add_subdirectory( common/propertyset )
diff --git a/autotests/common/libwacomdata/CMakeLists.txt b/autotests/common/libwacomdata/CMakeLists.txt
new file mode 100644
--- /dev/null
+++ b/autotests/common/libwacomdata/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_executable(Test.Common.LibwacomData testlibwacomdata.cpp)
+
+# This is technically a test but it's very long, verbose and would fail most of the time
+# so for now make it for humans only
+
+#add_test(NAME Test.Common.LibwacomData COMMAND Test.Common.LibwacomData)
+#ecm_mark_as_test(Test.Common.LibwacomData)
+target_link_libraries(Test.Common.LibwacomData ${WACOM_COMMON_TEST_LIBS})
diff --git a/autotests/common/libwacomdata/testlibwacomdata.cpp b/autotests/common/libwacomdata/testlibwacomdata.cpp
new file mode 100644
--- /dev/null
+++ b/autotests/common/libwacomdata/testlibwacomdata.cpp
@@ -0,0 +1,142 @@
+/*
+ * This file is part of the KDE wacomtablet project. For copyright
+ * information and license terms see the AUTHORS and COPYING files
+ * in the top-level directory of this distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "common/libwacomwrapper.h"
+#include "common/tabletdatabase.h"
+#include "common/tabletinformation.h"
+
+//#include
+#include
+#include
+
+#include
+
+using namespace Wacom;
+
+class TestLibwacomData: public QObject
+{
+ Q_OBJECT
+
+public:
+ void testData();
+};
+
+static bool compare(TabletInformation &left, TabletInformation &right, const TabletInfo &info) {
+
+ if (info == TabletInfo::NumPadButtons) {
+ if (left.getInt(info) != right.getInt(info)) {
+ qWarning() << "MISMATCH: Property" << info.key() << "mismatch for device" << left.get(TabletInfo::TabletName)
+ << "DB:" << left.getInt(info) << "vs LIB:" << right.getInt(info);
+ return false;
+ }
+
+ return true;
+ }
+
+ if (left.get(info) != right.get(info)) {
+ qWarning() << "MISMATCH: Property" << info.key() << "mismatch for device" << left.get(TabletInfo::TabletName)
+ << "DB:" << left.get(info) << "vs LIB:" << right.get(info);
+ return false;
+ }
+
+ return true;
+}
+
+void TestLibwacomData::testData()
+{
+ using namespace Wacom;
+
+ int missingLocal = 0;
+ int missingLibwacom = 0;
+ int okay = 0;
+ int mismatch = 0;
+
+ int max = 0x0FFF;
+
+ for (int deviceId = 0; deviceId < max; ++deviceId) {
+ TabletInformation localInfo(deviceId);
+ TabletInformation libwacomInfo(deviceId);
+
+ localInfo.set(TabletInfo::CompanyId, QString::fromLatin1("056A"));
+ libwacomInfo.set(TabletInfo::CompanyId, QString::fromLatin1("056A"));
+
+ bool inLocal = false;
+ bool inLibWacom = false;
+
+ if (TabletDatabase::instance().lookupTablet(localInfo.get (TabletInfo::TabletId), localInfo)) {
+ inLocal = true;
+ }
+
+ // lookup information in libWacom tablet database
+ auto tabletId = localInfo.get(TabletInfo::TabletId).toInt(nullptr, 16);
+ auto vendorId = localInfo.get(TabletInfo::CompanyId).toInt(nullptr, 16);
+ if (libWacomWrapper::instance().lookupTabletInfo(tabletId, vendorId, libwacomInfo)) {
+ inLibWacom = true;
+ }
+
+ if (inLocal && inLibWacom) {
+ //qDebug() << "Comparing" << deviceId << inLocal << inLibWacom;
+ bool identical = true;
+ identical &= compare(localInfo, libwacomInfo, TabletInfo::HasLeftTouchStrip);
+ identical &= compare(localInfo, libwacomInfo, TabletInfo::HasRightTouchStrip);
+ identical &= compare(localInfo, libwacomInfo, TabletInfo::HasTouchRing);
+ identical &= compare(localInfo, libwacomInfo, TabletInfo::HasWheel);
+ identical &= compare(localInfo, libwacomInfo, TabletInfo::NumPadButtons);
+ identical &= compare(localInfo, libwacomInfo, TabletInfo::StatusLEDs);
+
+ // We don't really care about button order here, just compare the indices
+ auto localMap = localInfo.getButtonMap().values();
+ auto libwacomMap = libwacomInfo.getButtonMap().values();
+
+ std::sort(localMap.begin(), localMap.end());
+ std::sort(libwacomMap.begin(), libwacomMap.end());
+
+ bool identicalmapping = localMap.size() == libwacomMap.size()
+ && std::equal(localMap.begin(), localMap.end(), libwacomMap.begin());
+
+ if (!identicalmapping) {
+ qWarning() << "MISMATCH: Mapping for device" << localInfo.get(TabletInfo::TabletName) << "differs."
+ << "Local:" << localInfo.getButtonMap() << "Libwacom:" << libwacomInfo.getButtonMap();
+ }
+
+ identical &= identicalmapping;
+
+ if (identical) {
+ okay++;
+ qDebug() << "Device" << localInfo.get(TabletInfo::TabletName) << "is identical";
+ } else {
+ mismatch++;
+ }
+ } else if(inLocal) {
+ qDebug() << "Device" << localInfo.get(TabletInfo::TabletName) << "is missing from LibWacom";
+ missingLibwacom++;
+ } else if(inLibWacom) {
+ missingLocal++;
+ }
+ }
+
+ qDebug() << "OK:" << okay << "MISMATCH:" << mismatch << "ONLYLOCAL" << missingLibwacom << "ONLYWACOM" << missingLocal;
+}
+
+int main() {
+ TestLibwacomData test;
+ test.testData();
+}
+
+#include "testlibwacomdata.moc"
diff --git a/cmake/modules/FindLibWacom.cmake b/cmake/modules/FindLibWacom.cmake
--- a/cmake/modules/FindLibWacom.cmake
+++ b/cmake/modules/FindLibWacom.cmake
@@ -43,13 +43,15 @@
NAMES wacom libwacom
HINTS ${LIBWACOM_PKGCONF_LIBDIR} ${LIBWACOM_PKGCONF_LIBRARY_DIRS} )
-set(LIBWACOM_LIBRARIES ${LIBWACOM_LIBRARY} )
-set(LIBWACOM_INCLUDE_DIRS ${LIBWACOM_INCLUDE_DIR} )
+set(LIBWACOM_LIBRARIES ${LIBWACOM_LIBRARY})
+set(LIBWACOM_INCLUDE_DIRS ${LIBWACOM_INCLUDE_DIR})
+set(LIBWACOM_VERSION ${LIBWACOM_PKGCONF_VERSION})
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set LIBWACOM_FOUND to TRUE
# if all listed variables are TRUE
-find_package_handle_standard_args(LIBWACOM DEFAULT_MSG
- LIBWACOM_LIBRARY LIBWACOM_INCLUDE_DIR)
+find_package_handle_standard_args(LIBWACOM
+ REQUIRED_VARS LIBWACOM_LIBRARY LIBWACOM_INCLUDE_DIR
+ VERSION_VAR LIBWACOM_VERSION)
-mark_as_advanced(LIBWACOM_INCLUDE_DIR LIBWACOM_LIBRARY )
+mark_as_advanced(LIBWACOM_INCLUDE_DIR LIBWACOM_LIBRARY)
diff --git a/src/common/libwacomwrapper.cpp b/src/common/libwacomwrapper.cpp
--- a/src/common/libwacomwrapper.cpp
+++ b/src/common/libwacomwrapper.cpp
@@ -29,6 +29,16 @@
namespace Wacom {
+static int skipWheelButtons(int button) {
+ // skip buttons 4-7, which correspond to vertical/horizontal wheel up/down events
+ if (button > 3) {
+ return button + 4;
+ } else {
+ return button;
+ }
+}
+static int convertEvdevToXsetwacomButton(int evdevCode);
+
libWacomWrapper::libWacomWrapper()
{
db = libwacom_database_new();
@@ -74,17 +84,94 @@
tabletInfo.set(TabletInfo::StatusLEDs, QString::number(0));
tabletInfo.set(TabletInfo::TabletName, QString::fromLatin1(libwacom_get_name(device.get())));
- tabletInfo.set(TabletInfo::NumPadButtons, QString::number(libwacom_get_num_buttons(device.get())));
- int numStrips = libwacom_get_num_strips(device.get());
- bool hasLeftStrip = numStrips > 0;
- bool hasRightStrip = numStrips > 1;
- bool hasRing = libwacom_has_ring(device.get()) != 0;
+ const int padButtonNumber = libwacom_get_num_buttons(device.get());
+ tabletInfo.set(TabletInfo::NumPadButtons, QString::number(padButtonNumber));
+
+ // Convert button evdev codes to buttonMap
+ if (libwacom_get_num_buttons(device.get()) > 0) {
+ QMap buttonMapping;
+ for (char i = 1; i < padButtonNumber + 1; i++) {
+#ifdef LIBWACOM_EVDEV_MISSING
+ dbgWacom << "Your libwacom version is too old. We will try and guess button mapping, "
+ << "but it's going to be broken for quirky tablets. Use kde_wacomtablet_finder instead.";
+ const int buttonIndex = skipWheelButtons(i);
+ buttonMapping[QString::number(i)] = QString::number(buttonIndex);
+#else
+ const char buttonChar = 'A' + (i - 1); // libwacom marks buttons as 'A', 'B', 'C'...
+ const int buttonEvdevCode = libwacom_get_button_evdev_code(device.get(), buttonChar);
+ const int buttonIndex = convertEvdevToXsetwacomButton(buttonEvdevCode);
+ buttonMapping[QString::number(i)] = QString::number(buttonIndex);
+
+ if (buttonIndex < 1) {
+ errWacom << "Unrecognized evdev code. "
+ << "Device:" << tabletId << "Vendor:" << vendorId
+ << "Button:" << buttonChar << "EvdevCode:" << buttonEvdevCode;
+ return false;
+ }
+#endif
+ }
+ tabletInfo.setButtonMap(buttonMapping);
+ }
+
+ const int numStrips = libwacom_get_num_strips(device.get());
+ const bool hasLeftStrip = numStrips > 0;
+ const bool hasRightStrip = numStrips > 1;
+ const bool hasRing = libwacom_has_ring(device.get()) != 0;
tabletInfo.set(TabletInfo::HasLeftTouchStrip, hasLeftStrip);
tabletInfo.set(TabletInfo::HasRightTouchStrip, hasRightStrip);
tabletInfo.set(TabletInfo::HasTouchRing, hasRing);
return true;
}
+static int convertMouseEvdevToXsetwacomButton(int evdevCode) {
+ // some quirky consumer tablets, e.g. Bamboo/Graphire, use mouse button events
+ // instead of just numbered express keys. Translate them back to numbers
+ static const int BTN_LEFT = 0x110;
+ static const int BTN_RIGHT = 0x111;
+ static const int BTN_MIDDLE = 0x112;
+ static const int BTN_FORWARD = 0x115;
+ static const int BTN_BACK = 0x116;
+
+ switch (evdevCode) {
+ case BTN_LEFT:
+ return 1;
+ case BTN_RIGHT:
+ return 3;
+ case BTN_MIDDLE:
+ return 2;
+ case BTN_FORWARD:
+ return 9;
+ case BTN_BACK:
+ return 8;
+
+ default:
+ return 0;
+ }
+}
+
+static int convertEvdevToXsetwacomButton(int evdevCode) {
+ // based on set_button_codes_from_heuristics from libwacom/libwacom-database.c
+ static const int BTN_MISC = 0x100;
+ static const int BTN_MOUSE = 0x110;
+ static const int BTN_BASE = 0x126;
+ static const int BTN_GAMEPAD = 0x130;
+
+ int translatedCode = 0;
+ if (evdevCode >= BTN_GAMEPAD) {
+ translatedCode = evdevCode - BTN_GAMEPAD + 10;
+ } else if (evdevCode >= BTN_BASE) {
+ translatedCode = evdevCode - BTN_BASE + 16;
+ } else if (evdevCode >= BTN_MOUSE) {
+ return convertMouseEvdevToXsetwacomButton(evdevCode);
+ } else if (evdevCode >= BTN_MISC) {
+ translatedCode = evdevCode - BTN_MISC;
+ } else {
+ return 0;
+ }
+
+ return skipWheelButtons(translatedCode + 1);
+}
+
}