From 23b6cfb14457063eb8121f97c2d5371b7e8fe3ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 13 Feb 2015 10:36:40 +0100 Subject: [PATCH] [screenlocker] Also grab XInput2 devices With XInput2 it's possible that multiple pairs of keyboard and pointers are connected. As the lock screen only grabbed keyboard and pointer using the core protocol any additional input devices were still reporting input events to non-lockscreen windows creating the risk of interaction with the system and accidentially typing a password where it doesn't belong. This change ensures that all additional master devices are also grabbed. Unfortunately there are no xcb bindings for xinput2 (considered experimental and thus not build on at least all debian based distros) and because of that the XLib library is used. This brings some problems as we cannot process the events (for that we would need xcb bindings, to get the events). To still be able to get any keyboard and mouse events we grab using the core protocol as it used to be and then ignore the "Virtual core" devices and don't grab them with XInput2. Input events from additional devices are grabbed and ignored, but definately no longer delivered to other windows. BUG: 341469 FIXED-IN: 5.3.0 REVIEW: 122558 --- CMakeLists.txt | 2 + config-X11.h.cmake | 5 +- ksmserver/screenlocker/CMakeLists.txt | 4 ++ ksmserver/screenlocker/ksldapp.cpp | 91 ++++++++++++++++++++++++++- ksmserver/screenlocker/ksldapp.h | 1 + 5 files changed, 101 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aa08381b1..63fbb2ded 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,6 +79,8 @@ if(X11_FOUND) message(FATAL_ERROR "\nThe X11 Session Management (SM) development package could not be found.\nPlease install libSM.\n") endif(NOT X11_SM_FOUND) + add_feature_info("XInput" X11_Xinput_FOUND "Required for grabbing XInput2 devices in the screen locker") + find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS X11Extras) endif() diff --git a/config-X11.h.cmake b/config-X11.h.cmake index ee5183a63..0da0f47ab 100644 --- a/config-X11.h.cmake +++ b/config-X11.h.cmake @@ -41,4 +41,7 @@ #cmakedefine HAS_RANDR_1_3 1 /* Define if you have X11 at all */ -#define HAVE_X11 ${X11_FOUND} \ No newline at end of file +#define HAVE_X11 ${X11_FOUND} + +/* Define if you have the XInput extension */ +#cmakedefine X11_Xinput_FOUND 1 \ No newline at end of file diff --git a/ksmserver/screenlocker/CMakeLists.txt b/ksmserver/screenlocker/CMakeLists.txt index 12c1603a8..649917268 100644 --- a/ksmserver/screenlocker/CMakeLists.txt +++ b/ksmserver/screenlocker/CMakeLists.txt @@ -56,6 +56,10 @@ target_link_libraries(screenlocker_static Wayland::Server ) +if (X11_Xinput_FOUND) + target_link_libraries(screenlocker_static ${X11_Xinput_LIB}) +endif() + # Needed to compile on Arm target. set_target_properties(screenlocker_static PROPERTIES COMPILE_FLAGS "-fPIC") diff --git a/ksmserver/screenlocker/ksldapp.cpp b/ksmserver/screenlocker/ksldapp.cpp index e23b50fbc..6c41f4eca 100644 --- a/ksmserver/screenlocker/ksldapp.cpp +++ b/ksmserver/screenlocker/ksldapp.cpp @@ -27,6 +27,7 @@ along with this program. If not, see . #include "logind.h" #include "kscreensaversettings.h" #include +#include #include "waylandserver.h" // workspace #include @@ -47,6 +48,9 @@ along with this program. If not, see . // X11 #include #include +#ifdef X11_Xinput_FOUND +#include +#endif // other #include #include @@ -108,9 +112,35 @@ void KSldApp::cleanUp() static bool s_graceTimeKill = false; static bool s_logindExit = false; +static bool hasXInput() +{ +#ifdef X11_Xinput_FOUND + Display *dpy = QX11Info::display(); + int xi_opcode, event, error; + // init XInput extension + if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error)) { + return false; + } + + // verify that the XInput extension is at at least version 2.0 + int major = 2, minor = 0; + int result = XIQueryVersion(dpy, &major, &minor); + if (result == BadImplementation) { + // Xinput 2.2 returns BadImplementation if checked against 2.0 + major = 2; + minor = 2; + return XIQueryVersion(dpy, &major, &minor) == Success; + } + return result == Success; +#else + return false; +#endif +} + void KSldApp::initialize() { KCrash::setFlags(KCrash::AutoRestart); + m_hasXInput2 = hasXInput(); // Save X screensaver parameters XGetScreenSaver(QX11Info::display(), &s_XTimeout, &s_XInterval, &s_XBlanking, &s_XExposures); // And disable it. The internal X screensaver is not used at all, but we use its @@ -351,7 +381,6 @@ static bool grabMouse(); bool KSldApp::establishGrab() { XSync(QX11Info::display(), False); - if (!grabKeyboard()) { sleep(1); if (!grabKeyboard()) { @@ -367,6 +396,53 @@ bool KSldApp::establishGrab() } } +#ifdef X11_Xinput_FOUND + if (m_hasXInput2) { + // get all devices + Display *dpy = QX11Info::display(); + int numMasters; + XIDeviceInfo *masters = XIQueryDevice(dpy, XIAllMasterDevices, &numMasters); + bool success = true; + for (int i = 0; i < numMasters; ++i) { + // ignoring core pointer and core keyboard as we already grabbed them + if (qstrcmp(masters[i].name, "Virtual core pointer") == 0) { + continue; + } + if (qstrcmp(masters[i].name, "Virtual core keyboard") == 0) { + continue; + } + XIEventMask mask; + uchar bitmask[] = { 0, 0 }; + mask.deviceid = masters[i].deviceid; + mask.mask = bitmask; + mask.mask_len = sizeof(bitmask); + XISetMask(bitmask, XI_ButtonPress); + XISetMask(bitmask, XI_ButtonRelease); + XISetMask(bitmask, XI_Motion); + XISetMask(bitmask, XI_Enter); + XISetMask(bitmask, XI_Leave); + const int result = XIGrabDevice(dpy, masters[i].deviceid, QX11Info::appRootWindow(), + XCB_TIME_CURRENT_TIME, XCB_CURSOR_NONE, XIGrabModeAsync, + XIGrabModeAsync, True, &mask); + if (result != XIGrabSuccess) { + success = false; + break; + } + } + if (!success) { + // ungrab all devices again + for (int i = 0; i < numMasters; ++i) { + XIUngrabDevice(dpy, masters[i].deviceid, XCB_TIME_CURRENT_TIME); + } + xcb_connection_t *c = QX11Info::connection(); + xcb_ungrab_keyboard(c, XCB_CURRENT_TIME); + xcb_ungrab_pointer(c, XCB_CURRENT_TIME); + } + XIFreeDeviceInfo(masters); + return success; + } +#endif + return true; } @@ -397,6 +473,19 @@ void KSldApp::doUnlock() xcb_ungrab_keyboard(c, XCB_CURRENT_TIME); xcb_ungrab_pointer(c, XCB_CURRENT_TIME); xcb_flush(c); +#ifdef X11_Xinput_FOUND + if (m_hasXInput2) { + // get all devices + Display *dpy = QX11Info::display(); + int numMasters; + XIDeviceInfo *masters = XIQueryDevice(dpy, XIAllMasterDevices, &numMasters); + // ungrab all devices again + for (int i = 0; i < numMasters; ++i) { + XIUngrabDevice(dpy, masters[i].deviceid, XCB_TIME_CURRENT_TIME); + } + XIFreeDeviceInfo(masters); + } +#endif hideLockWindow(); // delete the window again, to get rid of event filter delete m_lockWindow; diff --git a/ksmserver/screenlocker/ksldapp.h b/ksmserver/screenlocker/ksldapp.h index 7a32fb114..03ca2580f 100644 --- a/ksmserver/screenlocker/ksldapp.h +++ b/ksmserver/screenlocker/ksldapp.h @@ -132,6 +132,7 @@ private: int m_inhibitCounter; LogindIntegration *m_logind; GlobalAccel *m_globalAccel = nullptr; + bool m_hasXInput2 = false; // for auto tests friend KSldTest;