On Linux, MouseInterceptor treats any BTN_LEFT event whose value is not 1 as a button release. The kernel emits BTN_LEFT with value == 2 (autorepeat) on input devices that advertise EV_REP. Every autorepeat event therefore clears mLeftButtonClicked and calls mShakeDetector.reset(), which makes the shake gesture impossible to complete on those devices.
Environment
Zorin OS (Ubuntu based), X11 session
Tokri v2026.03.01, Flatpak bundle
Logitech MX Master 3 over Bluetooth
User in the input group, Flatpak granted --device=all
Root cause
src/linuxmouseinterceptor.cpp:
cpp
if (event.type == EV_KEY){
if (event.code == BTN_LEFT){
if (event.value == 1){
mLeftButtonClicked = true;
} else { // value == 2 (autorepeat) lands here
mLeftButtonClicked = false;
mShakeDetector.reset();
}
}
}
evdev defines three values for EV_KEY: 0 release, 1 press, 2 autorepeat. The else branch swallows 2.
The MX Master 3 registers a keyboard interface alongside the pointer (Handlers=sysrq kbd mouse1 event14) and has EV_REP set:
I: Bus=0005 Vendor=046d Product=b023 Version=0015
N: Name="Logitech Wireless Mouse MX Master 3"
H: Handlers=sysrq kbd mouse1 event14
B: EV=100017 <-- bit 0x14 (EV_REP) is set
Evidence
evtest capture during a single continuous drag, button never physically released:
Event: time 1785528143.641209, type 1 (EV_KEY), code 272 (BTN_LEFT), value 1
Event: time 1785528143.895010, type 1 (EV_KEY), code 272 (BTN_LEFT), value 2
Event: time 1785528143.928954, type 1 (EV_KEY), code 272 (BTN_LEFT), value 2
Event: time 1785528143.963005, type 1 (EV_KEY), code 272 (BTN_LEFT), value 2
Event: time 1785528143.996953, type 1 (EV_KEY), code 272 (BTN_LEFT), value 2
...
First repeat arrives 254 ms after the press (kernel REP_DELAY), then every 33 ms (REP_PERIOD). Value distribution over the whole capture:
2 x value 0
2 x value 1
208 x value 2
HorizontalShakeDetector needs 3 direction flips with at most 200 ms between qualifying events. Since the detector is reset every 33 ms, the flip counter can never reach 3 — regardless of how the user performs the gesture. The motion data itself was well within range (hundreds of REL_X events with 8 <= |dx| <= 100, none above 100).
Proposed fix
cpp
if (event.code == BTN_LEFT){
if (event.value == 1 || event.value == 2){ // press or autorepeat
mLeftButtonClicked = true;
} else {
mLeftButtonClicked = false;
mShakeDetector.reset();
}
}
Optionally ignore value == 2 entirely, since it carries no state change.
Notes
Windows and macOS interceptors are unaffected; they do not go through evdev.
Workaround for users: disable autorepeat on the device with EVIOCSREP set to {0, 0}. This has to be reapplied on every reconnect.
Devices without EV_REP (most plain mice) never hit this path, which is probably why it went unnoticed.
On Linux, MouseInterceptor treats any BTN_LEFT event whose value is not 1 as a button release. The kernel emits BTN_LEFT with value == 2 (autorepeat) on input devices that advertise EV_REP. Every autorepeat event therefore clears mLeftButtonClicked and calls mShakeDetector.reset(), which makes the shake gesture impossible to complete on those devices.
Environment
Zorin OS (Ubuntu based), X11 session
Tokri v2026.03.01, Flatpak bundle
Logitech MX Master 3 over Bluetooth
User in the input group, Flatpak granted --device=all
Root cause
src/linuxmouseinterceptor.cpp:
cpp
if (event.type == EV_KEY){
if (event.code == BTN_LEFT){
if (event.value == 1){
mLeftButtonClicked = true;
} else { // value == 2 (autorepeat) lands here
mLeftButtonClicked = false;
mShakeDetector.reset();
}
}
}
evdev defines three values for EV_KEY: 0 release, 1 press, 2 autorepeat. The else branch swallows 2.
The MX Master 3 registers a keyboard interface alongside the pointer (Handlers=sysrq kbd mouse1 event14) and has EV_REP set:
I: Bus=0005 Vendor=046d Product=b023 Version=0015
N: Name="Logitech Wireless Mouse MX Master 3"
H: Handlers=sysrq kbd mouse1 event14
B: EV=100017 <-- bit 0x14 (EV_REP) is set
Evidence
evtest capture during a single continuous drag, button never physically released:
Event: time 1785528143.641209, type 1 (EV_KEY), code 272 (BTN_LEFT), value 1
Event: time 1785528143.895010, type 1 (EV_KEY), code 272 (BTN_LEFT), value 2
Event: time 1785528143.928954, type 1 (EV_KEY), code 272 (BTN_LEFT), value 2
Event: time 1785528143.963005, type 1 (EV_KEY), code 272 (BTN_LEFT), value 2
Event: time 1785528143.996953, type 1 (EV_KEY), code 272 (BTN_LEFT), value 2
...
First repeat arrives 254 ms after the press (kernel REP_DELAY), then every 33 ms (REP_PERIOD). Value distribution over the whole capture:
HorizontalShakeDetector needs 3 direction flips with at most 200 ms between qualifying events. Since the detector is reset every 33 ms, the flip counter can never reach 3 — regardless of how the user performs the gesture. The motion data itself was well within range (hundreds of REL_X events with 8 <= |dx| <= 100, none above 100).
Proposed fix
cpp
if (event.code == BTN_LEFT){
if (event.value == 1 || event.value == 2){ // press or autorepeat
mLeftButtonClicked = true;
} else {
mLeftButtonClicked = false;
mShakeDetector.reset();
}
}
Optionally ignore value == 2 entirely, since it carries no state change.
Notes
Windows and macOS interceptors are unaffected; they do not go through evdev.
Workaround for users: disable autorepeat on the device with EVIOCSREP set to {0, 0}. This has to be reapplied on every reconnect.
Devices without EV_REP (most plain mice) never hit this path, which is probably why it went unnoticed.