Skip to content

Commit

Permalink
[screenlocker] Also grab XInput2 devices
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mgraesslin committed Feb 19, 2015
1 parent 9e69e84 commit 23b6cfb
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
5 changes: 4 additions & 1 deletion config-X11.h.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,7 @@
#cmakedefine HAS_RANDR_1_3 1

/* Define if you have X11 at all */
#define HAVE_X11 ${X11_FOUND}
#define HAVE_X11 ${X11_FOUND}

/* Define if you have the XInput extension */
#cmakedefine X11_Xinput_FOUND 1
4 changes: 4 additions & 0 deletions ksmserver/screenlocker/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down
91 changes: 90 additions & 1 deletion ksmserver/screenlocker/ksldapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "logind.h"
#include "kscreensaversettings.h"
#include <config-ksmserver.h>
#include <config-X11.h>
#include "waylandserver.h"
// workspace
#include <kdisplaymanager.h>
Expand All @@ -47,6 +48,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// X11
#include <X11/Xlib.h>
#include <xcb/xcb.h>
#ifdef X11_Xinput_FOUND
#include <X11/extensions/XInput2.h>
#endif
// other
#include <unistd.h>
#include <signal.h>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -351,7 +381,6 @@ static bool grabMouse();
bool KSldApp::establishGrab()
{
XSync(QX11Info::display(), False);

if (!grabKeyboard()) {
sleep(1);
if (!grabKeyboard()) {
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions ksmserver/screenlocker/ksldapp.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ private Q_SLOTS:
int m_inhibitCounter;
LogindIntegration *m_logind;
GlobalAccel *m_globalAccel = nullptr;
bool m_hasXInput2 = false;

// for auto tests
friend KSldTest;
Expand Down

0 comments on commit 23b6cfb

Please sign in to comment.