Skip to content

Conversation

@itz-me-zappex
Copy link
Contributor

@itz-me-zappex itz-me-zappex commented Dec 27, 2025

In case you all interested in isolating keyboard from keyloggers and unfocused windows (as that is done in Wayland), we could implement this in X server. My idea is that we could provide IsolateKeyboard option (or whatever name fits it) to xorg.conf so everybody who interested in hardening their systems can enable this feature. Like this: Option "IsolateKeyboard" "true".

This is just testing, and you guys know better where and how that should be implemented, there is just variable with hardcoded true (should be set in xorg.conf as option) and changes I did to achieve proper behavior.

After some tinkering I found out that keyloggers may listen raw input while applications do not listen those (at least I did not find any). So I decided to not handle these events if IsolateKeyboard is true. More details in commented lines. As a result, CLI tools which listen raw input just do not see any input. Test: xinput test <keyboard>.

Also it seems that DeliverOneEvent() is used to deliver input-related events to clients, so I added filter that denies keyboard-related events in case those are requested by an unfocused window or even CLI tool if IsolateKeyboard is true. As a result, applications see input when those are focused, while CLI tools like xinput can see only pointer motion events and presses on Super button (only FocusIn and FocusOut, it does not see any key press). Test: xinput test-xi2 --root.

I can't do these checks in TryClientEvents() (which is executed before sending event to client and all checks should be done there) as there is no way to get information about whether window we want to send event is focused or not.

I did not touch passive grab, so that DE/WM and apps (like OBS) can handle keyboard properly without being focused. As far as I understand, passive grab does not allow listen input and just waits until X server will tell to application that expected keybinding is pressed. Right?

Total (from my testing):
Broken:

  • Keylogging software that listens raw input (test: xinput test <keyboard>) or XI2 input (test: xinput test-xi2 --root). Those do not have a window, so those can't be focused, so those will not get any event.

Works:

  • Keybindings in: applications (OBS Studio), DEs (tested on Cinnamon), WMs, keybinding daemons (sxhkd and xbindkeys work, I've checked). As far as I understand, those are using passive grabbing.
  • Input in display manager's login screen (tested with SDDM).
  • Regular input in all applications.

metux and others added 7 commits December 22, 2025 13:51
Reduce the number of total workflows, so the list isn't so crowded.

Signed-off-by: Enrico Weigelt, metux IT consult <info@metux.net>
Signed-off-by: artist <artist@artixlinux.org>
Signed-off-by: 9olaris <251053665+9olaris@users.noreply.github.com>
Use the new macros to make request struct parsing / field swapping
much easier.

Signed-off-by: Enrico Weigelt, metux IT consult <info@metux.net>
Use the new macros to make request struct parsing / field swapping
much easier.

Signed-off-by: Enrico Weigelt, metux IT consult <info@metux.net>
Use the new macros to make request struct parsing / field swapping
much easier.

Signed-off-by: Enrico Weigelt, metux IT consult <info@metux.net>
Signed-off-by: callmetango <callmetango@users.noreply.github.com>
@itz-me-zappex itz-me-zappex marked this pull request as draft December 27, 2025 21:36
@Winnetou17
Copy link

Sorry if I'm very wrong because of my limited knowledge, but isn't this already achieved (or in progress to be achieved) with Xnamespaces ? Check the documentation here #551 and https://github.com/KendricksLCamille/xserver/blob/master/doc/Xnamespace.md

I also remember seeing a screenshot somewhere, but I wasn't able to find it anymore showcasing that keylogging doesn't work across namespaces.

@itz-me-zappex
Copy link
Contributor Author

itz-me-zappex commented Dec 28, 2025

Sorry if I'm very wrong because of my limited knowledge, but isn't this already achieved (or in progress to be achieved) with Xnamespaces ? Check the documentation here #551 and https://github.com/KendricksLCamille/xserver/blob/master/doc/Xnamespace.md

I also remember seeing a screenshot somewhere, but I wasn't able to find it anymore showcasing that keylogging doesn't work across namespaces.

@Winnetou17 Yeah, I've seen that, but it seems to be pretty complicated. As far as I understand, to isolate application, I need to generate a token and launch it with this token (e.g. XAUTHORITY=/<path>/<name>.xauth <command>), but that is very hard to do with all existing applications and requires wrapping or modifying .desktop files.

I may be wrong, so... explanations and criticism are welcome.

@itz-me-zappex
Copy link
Contributor Author

Laugh if you all want, but I can't figure out how to use the option value after parsing, instead of hardcoded value.
This is what I have now:

diff --git a/hw/xfree86/common/xf86Config.c b/hw/xfree86/common/xf86Config.c
index 0a2e3a745..fce7d6ff1 100644
--- a/hw/xfree86/common/xf86Config.c
+++ b/hw/xfree86/common/xf86Config.c
@@ -657,6 +657,7 @@ typedef enum {
     FLAG_IGLX,
     FLAG_DEBUG,
     FLAG_ALLOW_BYTE_SWAPPED_CLIENTS,
+    FLAG_ISOLATE_KEYBOARD,
 } FlagValues;
 
 /**
@@ -718,6 +719,8 @@ static OptionInfoRec FlagOptions[] = {
      {0}, FALSE},
     {FLAG_ALLOW_BYTE_SWAPPED_CLIENTS, "AllowByteSwappedClients", OPTV_BOOLEAN,
      {0}, FALSE},
+    {FLAG_ISOLATE_KEYBOARD, "IsolateKeyboard", OPTV_BOOLEAN,
+     {0}, FALSE},
     {-1, NULL, OPTV_NONE,
      {0}, FALSE},
 };
@@ -754,6 +757,8 @@ configServerFlags(XF86ConfFlagsPtr flagsconf, XF86OptionPtr layoutopts)
     xf86GetOptValBool(FlagOptions, FLAG_DONTZAP, &xf86Info.dontZap);
     xf86GetOptValBool(FlagOptions, FLAG_DONTZOOM, &xf86Info.dontZoom);
 
+    xf86GetOptValBool(FlagOptions, FLAG_ISOLATE_KEYBOARD, &xf86Info.isolateKeyboard);
+
     xf86GetOptValBool(FlagOptions, FLAG_IGNORE_ABI, &xf86Info.ignoreABI);
     if (xf86Info.ignoreABI) {
         LogMessageVerb(X_CONFIG, 1, "Ignoring ABI Version\n");
diff --git a/hw/xfree86/common/xf86Globals.c b/hw/xfree86/common/xf86Globals.c
index 58e20dcde..4d1726a9c 100644
--- a/hw/xfree86/common/xf86Globals.c
+++ b/hw/xfree86/common/xf86Globals.c
@@ -131,6 +131,7 @@ xf86InfoRec xf86Info = {
     .autoAddGPU = FALSE,
 #endif
     .autoBindGPU = TRUE,
+    .isolateKeyboard = FALSE,
 };
 
 const char *xf86ConfigFile = NULL;
diff --git a/hw/xfree86/common/xf86Privstr.h b/hw/xfree86/common/xf86Privstr.h
index 5a1db5d1c..f1aafa515 100644
--- a/hw/xfree86/common/xf86Privstr.h
+++ b/hw/xfree86/common/xf86Privstr.h
@@ -94,6 +94,8 @@ typedef struct {
     Bool autoAddGPU;
     const char *debug;
     Bool autoBindGPU;
+
+    Bool isolateKeyboard;
 } xf86InfoRec, *xf86InfoPtr;
 
 /* ISC's cc can't handle ~ of UL constants, so explicitly type cast them. */
diff --git a/hw/xfree86/parser/Flags.c b/hw/xfree86/parser/Flags.c
index 362028358..ae52faef5 100644
--- a/hw/xfree86/parser/Flags.c
+++ b/hw/xfree86/parser/Flags.c
@@ -80,6 +80,7 @@ static const xf86ConfigSymTabRec ServerFlagsTab[] = {
     {SUSPENDTIME, "suspendtime"},
     {OFFTIME, "offtime"},
     {DEFAULTLAYOUT, "defaultserverlayout"},
+    {ISOLATEKEYBOARD, "isolatekeyboard"},
     {-1, ""},
 };
 
@@ -127,6 +128,7 @@ xf86parseFlagsSection(XF86ConfFlagsPtr ptr)
         case DISABLEMODINDEV:
         case MODINDEVALLOWNONLOCAL:
         case ALLOWMOUSEOPENFAIL:
+        case ISOLATEKEYBOARD:
         {
             int i = 0;
 
diff --git a/hw/xfree86/parser/xf86tokens.h b/hw/xfree86/parser/xf86tokens.h
index 087f707b4..347fc73b1 100644
--- a/hw/xfree86/parser/xf86tokens.h
+++ b/hw/xfree86/parser/xf86tokens.h
@@ -107,6 +107,7 @@ typedef enum {
     SUSPENDTIME,
     OFFTIME,
     DEFAULTLAYOUT,
+    ISOLATEKEYBOARD,
 
     /* Monitor tokens */
     MODEL,
2025-12-28_18-22-24

As far as I understand, it can see this option and able to parse its value, because options that do not exist in XLibre are not visible in log at all. But I can't use this value in DeliverRawEvent() and DeliverOneEvent().

Help is appreciated...

@itz-me-zappex
Copy link
Contributor Author

itz-me-zappex commented Dec 28, 2025

Well, I figured out, it works as it should, so in case with Option "IsolateKeyboard" "true", xinput does not see either raw or XI2 keyboard input, if set to false - works as unpatched. But that does not seem to look beautiful in code. I mean this part:

    xf86GetOptValBool(FlagOptions, FLAG_ISOLATE_KEYBOARD, &xf86Info.isolateKeyboard);
    isolateKeyboard = xf86Info.isolateKeyboard;

@itz-me-zappex
Copy link
Contributor Author

itz-me-zappex commented Dec 28, 2025

Video demonstration of results I achieved with this is here.

With IsolateKeyboard on false:

off.mp4

With IsolateKeyboard on true:

on.mp4

@metux any thoughts on this? I wonder what I can improve and how, since I'm not very well knowledged in X server development.

@Gabrielcarvfer
Copy link

Question: does alt-tab works to switch focus?

@chankongus
Copy link

Question: does alt-tab works to switch focus?

Yes, you can see it in his 2nd example video at 0:49, he's presumably switching through alt-tab.

@itz-me-zappex
Copy link
Contributor Author

itz-me-zappex commented Dec 29, 2025

Question: does alt-tab works to switch focus?

@Gabrielcarvfer yeah, sure.

2025-12-29_14-40-43.mp4

I mentioned that in first comment:

Total (from my testing):
Broken:

  • Keylogging software that listens raw input (test: xinput test <keyboard>) or XI2 input (test: xinput test-xi2 --root). Those do not have a window, so those can't be focused, so those will not get any event.

Works:

  • Keybindings in: applications (OBS Studio), DEs (tested on Cinnamon), WMs, keybinding daemons (sxhkd and xbindkeys work, I've checked). As far as I understand, those are using passive grabbing.
  • Input in display manager's login screen (tested with SDDM).
  • Regular input in all applications.

Everything that relies on passive grabbing works.

@itz-me-zappex itz-me-zappex marked this pull request as ready for review December 29, 2025 21:25
@itz-me-zappex
Copy link
Contributor Author

I think this is ready.

@itz-me-zappex
Copy link
Contributor Author

No, it is not. I noticed that it prevents raw mouse input in Wine/Proton games, so it is impossible to move camera in shooters, but games that don't rely on this work fine, e.g. RTS, this is why I did not notice that before. I will allow mouse events in DeliverRawEvent() a bit later. Or, just skip not grabbed raw keyboard events there, instead of all ones.

@itz-me-zappex
Copy link
Contributor Author

itz-me-zappex commented Dec 30, 2025

Fixed, now RawMotion events are allowed and xinput in test mode still does not see any keyboard input.

What I have now with IsolateKeyboard turned on:

  • xinput can detect only Motion and RawMotion events (and others that unrelated to keyboard input).
  • Keybindings in Cinnamon desktop work.
  • Keybindings in IceWM work.
  • Sxhkd and Xbindkeys work.
  • Global hotkeys in OBS Studio work.
  • Games running through Wine/Proton work.

Is there anyone else who could test this? Maybe there is DE/WM that behaves weird with this option turned on? Also I'd like to see whether it is possible to bypass this keyboard isolation with tricky keylogger or not. Except ones that listen /dev/input/events* as root of course.

@github-actions
Copy link

Merge Conflict found

itz-me-zappex referenced this pull request Dec 31, 2025
It is patch 1/3 of a series that makes adding GPU screens
more controllable.

If SingleDriver option is set to "on", then only the first
successfully probed driver adds non-GPU screens, others
may add secondary GPU screens only.

Fixes github.com//issues/1669

Signed-off-by: Oleh Nykyforchyn <oleh.nyk@gmail.com>
@itz-me-zappex
Copy link
Contributor Author

Nice force push. I will duplicate my question here.

c79f376: @metux Should not it also be included in Flags.c and xf86tokens.h? I see comment about that server flag tokens are deprecated, but those are still in the code. Asking because it requires rebasing my PR #1751, and I either need to wait until SingleDriver become included to Flags.c and xf86tokens.h or rebase on top of this commit and remove IsolateKeyboard from tokens in my PR.

@itz-me-zappex
Copy link
Contributor Author

Rebased on top of master branch, signed-off-by check fails for unknown reason, also I included IsolateKeyboard to Flags.c and xf86tokens.h to follow how that is done with other options.

Adds "IsolateKeyboard" option to "ServerFlags" section.

If enabled: Disallows raw keyboard events and prevents
keyboard input events to unfocused windows and clients
without window to prevent keylogging.

If disabled: Follows default X behavior.

Signed-off-by: itz-me-zappex <85901674+itz-me-zappex@users.noreply.github.com>
@itz-me-zappex itz-me-zappex reopened this Jan 2, 2026
@itz-me-zappex
Copy link
Contributor Author

This is me tried to fix commit history here after syncing to master branch. Failed successfully. Well, I will keep them then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants