Jump to content

GSoC/2025/StatusReports/Yelsin 'yorisoft' Sepulveda

From KDE Community Wiki

Improving Game Controller Support in KWin

KWin's handling of game controller input is fragmented. Applications directly manage controller input, leading to inconsistencies, the inability of the system to recognize controller input for power management, and unintentionally enabling/disabling "lizard mode" in certain controllers.

In this project I'll be creating a solution to unify game controller input within KWin by capturing controller events, creating a virtual controller emulation layer, and ensuring proper routing of input to applications.

Mentors

Jakob Petsovits

Xaver Hugl

Blog Post

Intro Blog to GSoC'25 - KWin Project

Week 1-2

These first two weeks were focused on research and early prototyping. I explored how input devices, especially game controllers, are handled in Linux. Most of my time went into reviewing KWin’s input code and studying libraries like libevdev and uinput, with the goal of creating a virtual controller interface. I also looked into force feedback support and validated my findings using tools like evtest and fftest.

Linux input library device support
Possible architecture of KWin::Plugin Gamepad
Possible architecture of KWin::Plugin Gamepad w. uinput

What I Learned

  • How Linux exposes input events through evdev, and how to work with them using libevdev.
  • How to create and manage virtual input devices with uinput.
  • Permissions, device access, and testing workflows for input-related development.
  • Initial understanding of how KWin handles input internally.

Challenges

  • Understanding the lower-level details of input device handling.
  • Managing device permissions and verifying virtual devices were correctly created and functional.
  • Differentiating between physical and virtual device behavior in testing.

Blog Post

GSoC'25 Kwin Project Blog Post: Week 1-2

Week 3-4

Picking up from weeks 1+2 ( research + prototypes with libevdev/uinput ), these two past weeks were about moving from “research-only mode” to turning ideas into programing logic that lives inside KWin. From the start my mentors and I have had a general idea of the features we wanted to add but weren't too sure how to implement them. After some thinking and experimenting they adviced me to start off with a KWin::Plugin.

I started off by searching for an example of how to build a KWin plugin so I could start learning how to build my own. Thankfully my mentor @zamundaaa provided me with some great examples:

  • Example / Tutorial plugin located in src/plugin/examples/plugin
  • Screenshots plugin located in src/plugins

Between both of these examples and mentoring I was able to piece together the scaffolding ( essential parts ) of a KWin plugin and was able to put together the first version of this plugin, gamepad plugin, located in: kwin/src/plugins/gamepad. At this point the plugin is structured as follows:

main.cpp                // Entry point & Defines GamepadManagerFactory Class
metadata.json           // Declares the plugin to KWin, define information about plugin
CMakeLists.txt          // C++ Build/Installation/Logging wiring
gamepadManager.{cpp/h}  // Plugin Logic: Defines GamepadManager Class
gamepadManager.{cpp/h}  // Plugin Logic: Defines Gamepad Class

GamepadManagerFactory

GamepadManagerFactory Class servers simply as the entry point for the plugin. It's a factory class, or a class used to create other classes / object types. Like the examples, it inherits from PluginFactory and declares it as its interface as well as pointing to the metadata.json file for this plugin.

GamepadManager

GamepadManager class serves as the central coordinator (the “brain” or “hub”) of the entire project. GamepadManager covers many responsibilities. It owns and manages all gamepad devices, handles discovery (startup enumeration, hot-plug), lifecycle (adding/removing), and communication (signals when pads are added/removed, or when their state changes). Overall its responsible for keeping track of the current set of controllers and their status.

Gamepad

Gamepad is a wrapper class. It's purpose is to be tied to a physical controller. One Gamepad object per physical game controller. This enables quick access/reference to the device and allows for the physical controller to be treated like an object.This class is also responsible for device input handling, Plasma Idle refresh, and button to keyboard/mouse mappings. In the future things might get split up into separate files but as it is, it handles a lot.

KWin::Plugin Gamepad Architecture Diagram 1
KWin::Plugin Gamepad Architecture Diagram 2
KWin::Plugin Gamepad Architecture Diagram 3
KWin::Plugin Gamepad Architecture Diagram 4
KWin::Plugin Gamepad – UAT
Pass/Fail Test Method
Pre-existing controllers detection on startup Boot into a Plasma Wayland session with a controller already plugged in. Check logs/debug.
Hot-plugged controllers detected Plug in a controller after session start. Verify detection in logs and via input response.
Controller input prevents system sleep/suspend Leave system idle with controller activity (button press/stick move). Confirm sleep is blocked.
Controller input wakes up screen (dimmed but not suspended) Dim screen via DPMS timeout. Press button/move stick. Verify screen wakes without suspend.
Controllers detected after wake from suspend mode Suspend system, resume, check that plugin re-detects controller.
Controller → Keyboard/Mouse mapping: enabled when no game running Close Steam, press mapped controller buttons. Verify keyboard/mouse events are emitted.
Controller → Keyboard/Mouse mapping: disabled when game running Launch a Steam game. Press mapped controller buttons. Verify no keyboard/mouse events emitted.

Testing device: 8Bitdo Gaming Controller (USB/2.4h/Bluetooth)


What’s next from here

  • Integration into Kwin Proper: Start pushing changes upstream for others to test.
  • Map to Virtual Keyboard: Allow users to navigate over and get input from a virtual keyboard. Might open the way for logging in using only game controller.
  • Test Cases: As per best practices when developing for KWin.
  • KCM integration: A GUI option for users to toggle plugin ON/OFF. Ground work for more robust, user defined, button remapping.
  • Use Config for Mapping: Using a config file to keep track of and read from all the button to keyboard/mouse button mapping.

Blog Post

GSoC'25 Kwin Project Blog Post: Week 3-4

Week 5-6

The primary change was splitting up GameController into two classes. The new one being GenericInputDevice which lives in emulatedInputDevice.{cpp/h}. This allowed me to separate the GameController logic responsible for emulating keyboard and mouse into it's own separate class. Now GameController wrapper class is just responsible for monitoring controller input, resetting idle timer on user activity, and logging.

GenericInputDevice

GenericInputDevice is a class that inherits from InputDevice and is used to emulated Keyboard/Mouse in order to send those inputs through KWins input pipeline. The input_events come from GameController and get processed exactly like they were previously. Each GameController has access to an instance of GenericInputDevice to make its own calls. In the near future I plan on creating a static instance of this class for all GameController to access.

Integration Test: Qt Test

Part of the requirements for proposing significant contributions to KWin is creating integration test. This provides some assurance that things, like core functionality of the plugin, won't break so easily in the future as new code gets added. For testing KWin uses Qt Test Framework. It's been fairly simple and straightforward learning how to use the framework to create my own tests. Still, what exactly to test and how to test it was not so straightforward. I learned along the way that I'd be creating integration test instead of unit test. The test don't reference the plugins directly, instead they test the effect of the plugins on the system over all. That meant that things which required an instance of the plugin to test were not possible in this case. That included testing hot-plug capability or number of applications that plugin thinks has opened an input device. Thankfully there were a few very important functionalities that could be tested! Those include:

cpp
// Test system idle time reset. Prevents suspend
void testResetIdleTime();

// Test Controller To Keyboard Input Emulation
void testKeyboardMapping();

// Test Controller To Pointer/Mouse Input Emulation
void testPointerMapping();

System Settings KCM

It was agreed upon early on that this plugin would be opt-in, allowing the user to enable and disable it when they choose. For that I created a KDE Control Module or KCM. Or better put, I built on the existing Game Controller KCM :) I added a new UI element, a toggle, for users to enable and disable the plugin. On the back-end I added a Q_PROPERTY, pluginEnabled that is responsible for checking the kwinrc Plugin configs and writing to it for managing the state of this plugin. This is what it currently looks like (subject to change):

GameController_KCM_Preview

Handling Lizard Mode

After some testing and the smallest change I've had to make all project, the steamdeck controller was able to be detected by the plugin including its input events! Even better than that, and not sure why I did not put this together before, steamdeck already maps its input to keyboard/mouse. Duh. So this gamepad plugin doesn't need to worry about mapping for steamdeck controller, just use its input events to prevent system sleep when activity from that controller is detected.

GameController_KCM_Testint_0
GameController_KCM_Testint_0
GameController_KCM_Testint_1
GameController_KCM_Testint_1

What’s next from here

  • Integration into Kwin Proper: "Draft" label has been removed from MR and is ready for review.
  • Final Fixes and Touch-up: Get Virtual Keyboard working, KCM toggle hot-plug, improve analog -> pointer emulation.

Blog Post

GSoC'25 Kwin Project Blog Post: Week 5-6


Future Plans

Links

Blogs on Planet KDE

Intro Blog to GSoC'25 - KWin Project

GSoC'25 Kwin Project Blog Post: Week 1-2

GSoC'25 Kwin Project Blog Post: Week 3-4

GSoC'25 Kwin Project Blog Post: Week 5-6

Merge Requests & Issues

Issues

Merge Request to KWin

Merge Request to Plasma Desktop


Contact

[email protected]

@yorisoft:matrix.org