Subject: [Kwrite-devel] I just wanted to be the first to post here )
Welcome to kwrite-devel
I hope this is an active list and we can attract some more developers
Anyone have any ideas on coding style,enhancements problems
please feel free to post your questions/comments here.
or, depending which mail arrived faster in your inbox:
Subject: [Kwrite-devel] Welcome to this mailinglist.
Hello,
Welcome to the kwrite development mailinglist. *test*test*
Cheers,
Waldo
The journey
Like the first mail wanted, that list was very active for a long period of time.
The initial posters no longer are active in the project, but some people like me still stick around even after more than two decades.
A lot of important design decisions were discussed on the mailing list and many user questions got answered.
The end
The list traffic slowed down more and more over the last years even as the actual amount of contributions (and presumable the user base) did increase.
Reasons are for sure that for development, we use mostly our KDE GitLab instance for communication.
It is just that easier to couple discussions with code there and link development issues to merge requests or commits.
I can not remember any serious discussion on larger development topics outside our GitLab in the last years.
For users I assume mailing lists are just too arcane today.
Perhaps that is a misconception I have, but at least from most people I know in real life, most of the online support questions went of to either websites or random other channels.
Some people survive without any mail account beside the one needed to create some online accounts or install thei mobile phone.
I need to moderate away at least 10 to 100 spam mails for any real mail on the list, that is just a not needed overhead nobody should waste time with.
Therefore in the near future we will close that list, it will not get a 25 years birthday party :-)
But where to ask & discuss stuff now?
I already updated our documentation and web site to point to the current contact points.
Like every year, one of the highlights is Akademy! This time we were in Berlin, making it quite easy to get there from Hamburg:)
The weather was surprisingly nice, especially when heading out in the evening to try lots of different restaurants.
And of course - since being in Berlin - you gotta try a local Döner there :D.
One talk I was particularly surprised about was Saturday’s keynote “Open by Design: How Governments Can Lead the Shift to Digital Sovereignty” by Alexander Rosenthal.
Besides the information about OpenSource-Software being used on different levels of federal/state/local, the aspects of OpenData.
This made me realize that software is not the only thing one should focus on being open. Also, the huge amount of memes in the slides made the talk super refreshing and a nice start into Akademy!
Nate’s talk “Minding the Big Picture: Opportunity From Chaos” also fit this topic. OpenSource can provide a stable foundation
and reduced dependence on individual companies.
My main development focus and also the most frequent topic of the blog is the Clazy project. Akademy is a good occasion to tell other people about it.
So I took the occasion and held a fast-track talk about Clazy to tell how awesome and useful it is. It was also quite good to get people talking about it and share their ideas/problems.
I also did a decent amount of coding on Clazy. This included a request from aacid about Clazy not working properly with a Qt variant
that is build into a specific namespace. This was quite the rabbithole, but I managed to get the passing tests from 50% to 90%.
The last few edgecases are not as relevant if most of Clazy works properly.
For the use-arrow-operator-instead-of-data a false-positive is fixed where the check complains if you do a .data() call and then cast the pointer.
This is in most cases needed, if not, clang-tidy should warn about unneeded casts. Finally, fixits for detaching-temporary and detaching-member are more reliable when multiple calls are chained.
The biggest surprise though came at the Akademy awards. I am very honored to have received one, this left me quite speechless.
Me finally catching some words after getting the Award <3
This also means I am responsible for choosing the person for next year’s award. So you better get busy doing cool stuff 😎👀.
Finally, I want to thank everyone who helped organize Akademy and made it as awesome as it was!
Kaidan supports the simultaneous usage of multiple accounts now.
Imagine you could use the same chat app at work and with your friends.
All your favorite and accustomed features would always be accessible without switching apps.
It is possible with XMPP and finally with Kaidan too!
In order to quickly distinguish the account a chat belongs to, there is a small avatar of the corresponding account in the corner of a chat’s avatar.
Furthermore, Kaidan makes sure that you do not accidentally add a new contact to the wrong account.
That is achieved by selecting the account before you enter the contact’s chat address or scan their QR code.
The same applies to the group chat actions.
Secure Password Storage
The account passwords are stored in the device’s password manager.
You do not need to keep passwords in your mind.
Instead, you can use random ones.
They are securely stored in a central place.
Mark Messages
If you already read the latest messages from a contact but do not have time now to respond, you can simply mark them.
A separate counter is shown for the marked messages.
Take your time and come back to those messages later.
You will not forget to reply anymore!
Forward Messages
You can forward messages from one chat to another.
After clicking the corresponding context menu button, you can choose a chat to forward the message to.
By default, only the chats of the current account are listed to make it as simple as possible for you.
But you are able to list chats of other accounts as well.
Once you selected a chat, the message is added to its input field.
You can directly send it or adjust it beforehand.
Changelog
There are several other improvements.
Have a look at the following changelog for more details.
Features:
Add support for using multiple accounts simultaneously (melvo)
List accounts and show button to add new accounts (melvo)
Show dialog to select account for global action such as adding a contact (melvo)
Allow to enable/disable accounts instead of connecting/disconnecting them manually (melvo)
Update nicknames of own accounts once connected (melvo)
Show small account avatars next to regular avatars if multiple accounts are used (melvo)
Hide global drawer handle on chat if window is narrow (melvo)
Use PNG/.png instead of JPEG/.jpg for thumbnails to allow transparency (melvo)
Use AAC/.m4a instead of MP3/.mp3 for voice messages to improve compatibility (melvo)
Provide size of sent images to recipients allowing receiving client to scale thumbnails to size of original image (melvo)
Provide size of generated thumbnails to recipients (melvo)
Increase size of generated thumbnails (melvo)
Show circle instead of bar for upload/download progress (melvo)
Try all providers on connection error during automatic registration (melvo)
Add message forwarding (melvo)
Enable voice message recording via Flatpak (melvo)
Store account passwords encrypted if password manager is available (fazevedo)
Apply consistent criteria for all message corrections (melvo)
Add support to mark messages locally in order to reply to them later or to quickly find important messages (melvo)
Reuse SASL 2 user agent and FAST token on every restart for faster connection establishment (melvo)
Bugfixes:
Fix selecting media via long press in media overview (melvo)
Fix OMEMO initialization (melvo)
Fix displaying geo location map (melvo)
Fix showing hints on invalid input of various input fields (melvo)
Fix name/date of chat list item moving if counter for unread messages dis-/appears (melvo)
Fix counter for unread messages (melvo)
Fix handling removed message reactions (melvo)
Fix canceling personal data sharing via contact details (melvo)
Fix finding existing notifications for personal data sharing requests (melvo)
Fix cursor behavior in message input field by allowing vertical cursor movements while participant picker is closed and prohibiting horizontal cursor movements while participant picker is open (melvo)
This post is to celebrate a few things despite the events that are clouding our feelings. 😠
Another thing to not celebrate is the slaughtering by Sourceforge of my developer web site, which they are calling "sunsetting", by October. I've already migrated it.
On the other, brighter hand, I'm celebrating this week La Mercè, which is the local festivity of Barcelona.
Castellers of Barcelona
Another event to celebrate is the first 2 million downloads of VMPK for Linux, Windows and Mac. The Sourceforge statistics do not include the installs thru Flatpak, but you may realize that more than 75% of the Sourceforge downloads are the Windows packages. The 2 mil download happened some past day of this year 2025. I've promised a celebration, and now, I have released the Android port of VMPK under the GPLv3 license in GitHub.
If you already have the F-Droid app, you only need to add the IzzyOnDroid repository in Settings>Repositories and install it today, or you may prefer to use the official F-Droid repo.
I would like to add to the celebration a video live streaming concerto, but I am too lazy and odd playing for that. Better use this wonderful rendering of the Tchaikovsky Violin concerto by TwoSet Violin, with Brett Yang playing the soloist and Eddy Chen the rest of the orchestra. Enjoy!
A few weeks ago was KDE’s Akademy which was hosted in Berlin, Germany this year! This was my first time visiting Berlin, which I’m surprised by considering I’ve been visiting Germany practically non-stop since 2023. There was lots of fun this year, and it’s always great seeing everyone again ❤️
Travel
Getting to Berlin for me and back was 100% uneventful, which is odd. (This time, the travel bug bit Nate Graham 😬) So much so, both arriving planes were early! Also because of the location of BER there was literally only one direction to go, so even the train travel was simple. Within the city, it was the S-Bahn all the time and the occasional U-Bahn.
Of course, I kept track of my itinerary is KDE Itinerary this year again. I only had to fix one bug with my United Airlines parser, that’s pretty impressive!
Hotel
Despite booking late, I did manage to snag a week-long reservation at the B&B Berlin-Tiergarten. It was close both to the venue at the TU, the Tiergarten and the Tiergarten S-Bahn station which was super useful. The hotel itself was nice, but ~€12 for cold cuts during breakfast was a bit much (I didn’t realize that were was a bakery close by until after I bought breakfast, whatever…)
This was also my first hotel where instead of a room key, you have to punch in a number on a keypad. Like no, literally one of these kinds of keypads:
As it is every year, it’s a tough choice deciding which talks to attend. Fortunately we only have two tracks and livestreams, so you’re never missing too much but it’s always a choice. Here’s some of my favorites from this year, in bullet-list form! (They’re ordered by how they’re scheduled.)
I gave a talk about bridging the gap between artists and Wayland, a lot of it being my own personal experience/journey and all that fun stuff. As I feared, there were one too many slides so unfortunately I skipped a few but I hope most it still came across. You can view the recording on PeerTube.
There were a lot more non-concrete hacking and discussion happening too, I’m glad to see some progress moving in the Kirigami and Add-ons space! Tokodon has become the testbed for a new way to declaratively create and use actions, which is something Tokodon already does but we wanted create a proper, centralized framework.
So overall, a pretty productive Akademy for the community I think! And not just on a technical level, but there’s a lot of talk about the CWG (Community Working Group) and handling community matters spearheaded by Victoria Fischer. I do just a bit of moderation work on our Discuss site, but it makes me so happy this is becoming a new focus. I think we as a community is already a pretty safe space, but that is absolutely something we should not take for granted especially during these times.
Day Trip
The day trip this year was Berlin! Instead of taking a bus somewhere, we were tasked with walking from the Tiergarten area to the Brandenburg Gate and take pictures. Unfortunately for me, during one morning I walked almost the same path accidentally so a lot of it wasn’t new to me. But it was still an overall fun experience, way more fun than falling asleep on a bus somewhere.
We also visited the Computerspielemuseum which was somewhat cool. I’m of the opinion that once you’ve seen one video game museum, you pretty much seen them all (this applies to anything computer-related in general.) However this one had cool statues, and a Painstation!
Another thing that made me happy is seeing new faces at Akademy, so if you have the means to travel to Europe and are even just interested in KDE - please join us for a weekend! (There is an RSS feed for Akademy news, a mailing list and more to keep track of us.)
I’m also just one guy, I recommend reading other people’s Akademy experiences this year at Planet KDE, they’re all fantastic of course. Now, back to hacking!
Akademy 2025 is history. What an Akademy it was - and it was grand!
Akademy is KDE’s annual conference.
This year it happend to be in Berlin, so I hopped on a short 🤥 10 hour train to the north
to meet friends - old and new - and discuss the latest ventures of KDE.
The biggest topic of them all was, of course, KDE Linux.
Once again we sent the conference buzzing.
This year with the news of it entering alpha status. There were bananas a plenty,
inspired by the original codename of the distribution: project banana.
We even had a self defense course against fruit: How to defend yourself against someone
armed with a banana.
Talks were great all around. Well done everyone!
As is tradition the ad-hoc hallway track was well attended and yielded many useful
results.
The weekend closed out with a great social bash on Sunday at c-base, a crashed
space station in the middle of Berlin! We had fun, pizza, and discussed input
methods as well as the ultimate question of life, the universe, and everything, before
ending the night at a Späti, Berlin’s characteristic late night one-stop shops
for everything from cheap beer to expensive beer.
During the week we had Birds of a Feather sessions, our informal discussion format.
Every day we had naturally something KDE Linux related to keep our fresh fruit intake
up.
On Monday we mused on shared immutable distro topics and resolved to lean more
onto kde-builder respectively our repo-metadata
as original source of truth for packaging information.
We discussed automatic data migration onto a different machine,
considered the various types of data a user might want to migrate,
and how to even implement this.
Plasma’s solution for backups is a bit wanting. We think it’d be a good
idea to double down on Kup, an existing backup solution,
to produce a tidier, more integrated backup experience.
Volunteers welcome!
On Tuesday and Thursday we rendered a whole bunch of decisions on KDE Linux issues
that were in need of decision making. No large changes from the status quo though.
Well, the biggest change is that we now have manpages. For now 🥸
Wednesday was the traditional day trip. It took us on a scavenger hunt through Berlin in
an attempt to secure as many scavenged points as possible. I regretfully don’t know
which team won as I got distracted by lunch, but it was great fun all the same.
In the afternoon Aleix and I took to a tea house for some Ostfriesentee and
light afternoon hacking.
To close the event on Thursday, Eike, Aleix and I headed to a pub for tomfoolery and baby guinesses.
Thanks so much to everyone who attended, the Akademy team for organizing, the sponsors and the KDE e.V. for financing, and Techpaladin LLC for sponsoring my attendance.
Maybe next year we will learn how to defend yourself against a pointed stick.
Activities' future is a bit unclear at the moment. It overlaps with functionality provided by other services and the user story for how it fits into the desktop is unclear at best. However we also have to acknowledge that some people use activities to create a workflow that works for them. Rather than taking a sledgehammer to the concept, we're performing a more surgical approach of working out what use-cases people are trying to fix at finding the best solution to deliver that.
With that comes cleaning out some of the parts we don't like and one part decided at the Plasma sprint was dropping the activity based sub-session management.
What's changing
At the moment activities can be started and stopped at runtime.
This start/stop feature (pictured above) is being dropped.
In theory there is is XSMP code performs session management on a per-activity basis. When an activity is stopped, those applications are frozen, closed as running processes and restored when the activity next gets restarted.
It's a neat idea, but in practice this rarely happens, for a range of reasons:
XSMP is not supported in an increasing amount of apps and toolkits. Subsessions even less so, and even more less so with Wayland.
It is blocked in almost all Flatpak/Snap applications as there's a fundamental sandbox escape in the protocol.
The vast majority of apps just move to the neighbouring activity.
Even if it did work, there's a fundamental design flaw with the entire concept.
A window/app can be on two activities at once, if it should remain open on the other activity we can't suspend it. The concept of session restoration is at odds with the cardinality of windows to activities.
Many applications don't allow multiple instances; so if you resume an activity with the application already running, it won't do anything and that's unfixable.
The end result is one that's inconsistent and unpredictable which is a state worse than not existing so we're dropping this feature.
Will I see an impact?
Not much. If you do use activities you can still switch between them as before, just with no "stop" and "start" button.
Akademy 2025 was hosted in Berlin, and this was my first time attending Akademy in person.
I was very excited both to see Berlin (and put my very shaky German skills to
practice), as well as to attend our big yearly event and meet some more cool
folks in person.
The trip out was fairly uneventful, if long (~17 hours of bus, planes, and
train). The biggest difference compared to travelling to the Plasma sprint is
that this time I had some good noise cancelling over-ear headphones -- what a
lifesaver! Especially when you end up on a flight across the ocean sandwiched
between 2 screaming, jumping, grabbing toddlers. 10/10 can recommend! The
headphones, not the toddlers. 😂
As expected when I arrived at my hotel I crashed and slept for 11 hours straight.
I had a good time at the welcome event. I offered to help and spent the evening
handing out food and drink tickets to everyone and chatting to a few people --
including an excellent conversation about the merits of QA & testing, which
still surprises me not everyone is on board with!
We had a huge amount of talks and BoFs to attend. (Sidenote: "BoF" stands for
"Birds of a Feather", and is apparently a more informal/less structured time for
talk/discussion/hacking/etc -- I was lost on what these letters meant for a long
time before now.)
I don't think I could hope to cover even a fraction of what went on, and indeed
because the schedule is structured in such a way that there are multiple things
going on at any given time, I had a lot of trouble choosing what to go to --
what if I want to attend them all?! Well, that is kind of what I am doing as I
am catching up on the recordings of what I missed after the fact.
I am very happy that the audio and video quality of the recordings appear to be
massively improved over last year's! Paired with the auto-generated captions on
YouTube it actually makes for an excellent way to consume the talks, and is
valuable even for the talks I attended in person since my auditory processing
disorder means that I have trouble understanding what people are saying
(massively worsened with low volume/echo/noisy environment/etc). If we can
figure out the infrastructure to also record the BoFs for next year that would
be another great step up for accessibility and allowing a wider audience to
participate & benefit. 💙
We had a BoF about the KDE Out-Of-Box-Experience (OOBE), where Neal Gompa and I
went through some of the history of the project (a lot, Neal has been
championing it forever!) and its current state (nearly ready for early testing
and adoption). There were some good questions, and I am consistently gratified
to see the interest and excitement there is for the project.
The schedule for Akademy was a lot for me. On a normal day my available energy
is already quite limited, and the venue had a few challenges for me such as the
only restrooms and the elevator we were using being on the other side of the
building, which meant a whole lot of walking & standing to wait on hard floors
so I was feeling more fatigued and in pain than usual.
To top that off my anxiety was sky high, and I have no idea how much I was
masking but I think I may have ended up seeming like an unapproachable
wallflower.. I promise I am very friendly, just extremely anxious and awkward!
Despite that I ended up having a bunch of great conversations during the
conference with a bunch of people, and it was amazing to get together with so
many other Linux / FOSS / KDE nerds. 🤓
Around the end of Tuesday it was becoming apparent that I had caught a cold or
something from one of the many sneezes or coughs I had attempted to dodge during
this trip, and I missed out on the social activities on Wednesday and the last
day of activities of Thursday. I enjoyed watching the progress on Matrix anyway!
I am only now just starting to improve (I always struggle to fight off
infections like this).
Several people had said they wanted to talk to me at some point, and we didn't
get the chance before this cold knocked me out. Wie Schade! It isn't the same,
but feel free to reach out to me on Matrix! @merritt:kde.org
It was really awesome to get together in Berlin, to meet so many new great
people and to see familiar faces again, to work on so many interesting things
together. I wish I had more energy after a long day to do more social stuff,
because I loved getting the chance to interact with everyone in a more relaxed
and informal way. Maybe we can figure out some more relaxed / quiet / sensory
friendly social & food options next time for those of us who struggle with the
loud, crowded, and chaotic environments that are often the default for social
events.
Thanks Berlin for having us, even if the city is a bit noisy and has a
surprising number of smokers, it was very nice to visit! Tschüss!! 😀 💙
People have been a bit weirded by what happened during this period.
People have been interested in this after the post by Jonathan Riddell
was written.
Yeah, it's really messy. And I wish it had gone differently. But that's how things go.
I have been laid off before, twice.
First time when I was a very fresh programmer, I was suddenly told in middle of day that my contract will end
right before my probation period will end.
Then second time, I went for a leave due to burnout, come back to work, and on same day I'm told I'm getting laid off.
It sucked.
So Jonathan, I can sympathize with your feelings about the situation.
I hope you get help for your issues and warmth to your life. Sincerely. I do not have anything bad against you.
I'm sure I could negotiate it into something more shiny with help of lawyers, money and time.
But I trust TechPaladin to not screw me over.
And if they would do so, I would leave immediately and cut my losses.
I've never been "abused" in any way.
People care about me there.
They notice if I'm burning out (looking at you Dolphin) and help me switch projects.
Sure, I might be naive for trusting a company like this.
But I'm just like this. If I like the people who I work with, I trust them.
This is not the first company I trust like this. It won't be the last either, probably.
And I just want to fix bugs in KDE software without having to think about the corporate stuff,
but still have money for food and rent. TechPaladin lets me do that.
In the end, the contract is my choice and this is the choice I've made.
So far so good.
And my friends and family know me. If TechPaladin would go against my values or rights,
all of them would know.
Maybe this post helps shine light on things, anyway. Not that I really need to do this, since it's all private matters,
but since it's been blown open, meh. Might as well. I'm tired of the misinformation I've seen around.
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 programming logic that lives inside KWin to: detect gaming controllers and their input events, keeps Plasma awake on controller activity, handles hot-plug and pre-existing connections on startup, and lays down the first mappings from controller input to keyboard/mouse actions without stepping on other apps utilizing the controllers.
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 advised me to start off with a KWin::Plugin. This would allow us to start introducing the gaming controller functionalities to KWin while avoiding having to edit the core or guts of KWin. It would also be a great entry point for current and future game controller input objectives, allowing us to start small with a 1st party KWin plugin, build on it, and possibly integrate it into core functionality.
When it comes to creating KWin plugins I had a few options:
Scripts: Written in QML/JavaScript and used for automating window management, tiling, shortcuts, etc.
Effects: Implement visual effects on windows, the desktop, or transitions.
Core/Native: These are built into KWin itself and extend KWin’s internal functionality.
Since the plugin needs low-level device access, such as monitoring /dev/input/event*, listening to udev hotplugs, opening fds, and reacting to evdev events the best choice was to go with Core / Native plugin. As opposed to Effect and Script plugins which aren’t designed to open devices or do long-running I/O, most simply just live inside the rendering/scripting layers.
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
gamepad.{cpp/h} // Game Controller Object: Wrapper Class for Physical Controller
Implementation notes
GamepadManagerFactory
GamepadManagerFactory Class serves 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. It initializes the plugin through its create() function which returns a GamepadManager.
GamepadManager
GamepadManager class serves as the central coordinator (the “brain” or “hub”) of the entire project. While creating this I took a lot of inspiration from src/backend/drm/drm_backend.{cpp/h}, which itself is responsible for handling drm/gpu devices. 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.
Detect hot-plug and pre-existing device detection:
For this part many of the DRM backend pattern were used. The first thing the manager class does on initialization is create two QMetaObject::Connections that monitor the current KWin session for devicePaused and deviceResumed signals. This helps track devices when Plasma goes in and out of sleep/suspend which causes devices to be Paused and Resumed. It then enumerates over all event devices located in /dev/input/event* to handle any pre-existing connections to game controllers. If it discovers an event device it adds the gamepad ( start tracking it and its input ).
// On init:
// Enumerate current input nodes to filter and add ONLY event nodes
QDirdir(QStringLiteral("/dev/input"));constautofiles=dir.entryList({QStringLiteral("event*")},QDir::Files|QDir::Readable|QDir::System);for(constQString&file:files){constQStringpath=dir.absoluteFilePath(file);if(!isTracked(path)){addGamepad(path);}}
Finally using udev it monitors the subsystems and filter for only "input" subsystem events. It uses QSocketNotifier to produce signal notifications from udev events and creates a connections between that notifier and a memeber function, handleUdevEvent, that handles events coming from the udev monitor when an input device is detecetd. Some checks are performed to verify if the device is a gaming controller, such as expected input events and input event types. This include input events like BTN_JOYSTICK and BTN_GAMEPAD, which are commonly defined in gaming controllers. As well as checking for joystick or D-pad capabilities. If the checks pass the game controller is "added", or in other words, the device is wrapped in a Gamepad class, kept track of and its presence monitored.
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 seperate files but as it is, it handles a lot. As with the GamepadManager, this class takes a lot of inspiration from DRM backend patterns.
Detect Input Events:
Once a gaming controller device is detected it gets wrapped in a Gamepad class object. Which in turn wraps the controller in a libevdev object pointer. This is the part that gives access to the controller through the libevdev API, making it easier to work with it and monitor its input events. Like GamepadManager the first thing this class does is use QSocketNotifier to produce notifications from the controllers fd, i.e monitor for input. It then creates a connections between that notifier and a member function, handleEvdevEvent, which handles all incoming input events from that device.
libevdev*evdev=createEvDevice();if(evdev){m_evdev.reset(evdev);m_notifier=std::make_unique<QSocketNotifier>(m_fd,QSocketNotifier::Read,this);connect(m_notifier.get(),&QSocketNotifier::activated,this,&Gamepad::handleEvdevEvent);qCDebug(KWIN_GAMEPAD)<<"Connected to Gamepad ( new libevdev* ): "<<libevdev_get_name(m_evdev.get())<<"at"<<m_path;}
Plasma Idle Refresh On Controller Activity
With the ability to monitor for all input events from the device, the plugin then uses that information to know when to reset Plasma idle timer. For this Gamepad imports/includes input.h file and makes a call to input()->simulateUserActivity() when an input event is detected from the controller. This causes Plasma idle timer to be reset and prevents the system from going into sleep/suspend mode while using only gaming controller.
// reset idle time
input()->simulateUserActivity();
Controller -> Keyboard & Mouse Mapping
Gamepad uses API function from libevdev to check for input events, identify the specific input event and map that to a keyboard or mouse input event. Using libevdev_next_event() it checks for the input event coming from that game controller. It then identifies the specific input event through its input event type, code, and value. To simulate a mouse and keyboard the core/inputdevice.h file is imported and used to declare GenericInputDevice which inherits from InputDevice. That GenericInputDevice effectively behaves like a virtual keyboard and mouse inside KWin’s input stack.
When specific libevdev input event are identified, such as EV_KEY + BTN_SOUTH ( A button press ) OR EV_KEY + BTN_EAST ( B button press ), it call InputDevice::sendKey() to simulate keyboard key press and inject the desired keys into KWin input pipeline. In this case Enter for A ( BTN_SOUTH ) and Escape for B ( BTN_EAST ). To emulate mouse/pointer the plugin makes calls to InputDevice::sendPointerButton() for left and right mouse buttons, and InputDevice::sendPointerMotionDelta() for pointer movement.
Here is a list of all the buttons to keyboard/mouse mappings:
Face Buttons
------------
BTN_SOUTH → Enter (Qt::Key_Return)
BTN_EAST → Escape (Qt::Key_Escape)
BTN_NORTH
BTN_WEST
Bumpers
-------
BTN_TL → Alt (Qt::Key_Alt)
BTN_TR → Tab (Qt::Key_Tab)
Trigger Buttons
---------------
ABS_Z → Mouse Left Click
ABS_RZ → Mouse Right Click
D-Pad
-----
BTN_DPAD_LEFT → Arrow Left (Qt::Key_Left)
BTN_DPAD_RIGHT → Arrow Right (Qt::Key_Right)
BTN_DPAD_UP → Arrow Up (Qt::Key_Up)
BTN_DPAD_DOWN → Arrow Down (Qt::Key_Down)
Analog Sticks
-------------
ABS_RX / ABS_RY → Pointer Motion
Center Buttons
--------------
BTN_SELECT → Show On-Screen Keyboard ( WIP )
BTN_START → Meta/Super (Qt::Key_Meta)
Prevent Stepping On Other Apps
It's essential that the plugin doesn't emulate keyboard and mouse for the gaming controller when another app is reading from it. Most likely in such cases the device is being used for something else and not being used to navigate the desktop. To achieve this the GamepadManager class creates an instance of inotify object, and adds a watch device to the fd of each game controller that’s added as a Gamepad. Whenever inotify produces a notification a function, GamepadManager::handleFdAccess, is called which increments a counter in Gamepad, Gamepad::m_usageCount by +1 if the event value is IN_OPEN or Gamepad::m_usageCount by -1 if the event value is IN_CLOSE_WRITE | IN_CLOSE_NOWRITE. The plugin will only attempt to emualte keyboard/mouse if m_usageCount is 0. This prevents emulation of keyboard and mouse when other apps have the game controller opened / in use.
// Process all inotify events in the buffer
for(char*ptr=buffer;ptr<buffer+length;){structinotify_event*event=reinterpret_cast<structinotify_event*>(ptr);autoit=m_watchesToGamepads.find(event->wd);if(it!=m_watchesToGamepads.end()){Gamepad*pad=it.value();if(event->mask&IN_OPEN){pad->countUsage(+1);}elseif(event->mask&(IN_CLOSE_WRITE|IN_CLOSE_NOWRITE)){pad->countUsage(-1);}qCDebug(KWIN_GAMEPAD)<<"Device"<<pad->path()<<"in use by:"<<pad->usageCount()<<" other apps";}ptr+=sizeof(structinotify_event)+event->len;}
Opt-In
Many of the native plugins that ship with KWin are enabled by default but for our gaming controller plugin we will disable it by default and make it an opt-in option. This will allow users to start experimenting and benefiting from the plugin without risking the possibility of breaking current game controller input on their system.
{"KPlugin":{"Category":"Input","Description":"Enable KWin game controller input detection","EnabledByDefault":false,<----------Notenabledbydefault."License":"GPL","Name":"gamepad"},"X-KDE-ServiceTypes":["KWin/Plugin"]}
Testing
Controller awareness at startup and hot-plugging: tested in development session, KWin logs show the plugin picking up controllers in both scenarios, works as expected.
Preventing sleep/suspend: tested in development session. Set suspend timer to 1min, repeatedly press A and B back and forth, and at 5min no suspend was initiated, works as expected.
USB and Bluetooth connectivity support: tested in development session, KWin logs show plugin picking up on the controllers in both scenarios, works as expected.
Mapping from controller to keyboard and mouse: tested in development session, all buttons are map to expected keyboard and mouse, works as expected.
Backoff On Grab: tested in development session. Verified mapping work, started Steam app, verify mapping no longer enabled.
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.