The KDE global shortcuts installed by kde/install-shortcuts.sh never bound anything. System Settings reported "No default shortcuts" for every entry, and pressing the keys did nothing, while the same actions worked fine when run from a shell.
Root cause
kglobalacceld reads the _launch key of a [services][*.desktop] group as a bare key sequence and passes it straight to QKeySequence::fromString. Plasma 5 stored a keys,default_shortcut,friendly_name triplet in that key; Plasma 6 does not. So the value we wrote, Meta+Alt+T,none,Insta360: Toggle AI tracking, parsed to an empty sequence and bound no key, silently and with nothing logged.
This is confirmed against KServiceActionComponent::loadSettings in the kglobalacceld source rather than inferred from behaviour. Alternates in that value are separated by a tab, and the literal none means unbound.
What changed
_launch is now written as just the key sequence.
The generated desktop entries carry X-KDE-Shortcuts, which is where kglobalacceld reads an entry's default shortcut from. Without it an entry can function yet still display as having no shortcut, which is exactly the confusing state this started in.
The entries are no longer NoDisplay by default. KApplicationTrader::query skips NoDisplay services, so hiding them from the application launcher also hid them from the Shortcuts page in System Settings, making the bindings impossible to inspect or edit through the GUI. HIDE_FROM_MENU=1 restores the previous behaviour for anyone who would rather keep their launcher clean.
--list is now a diagnostic instead of a bare dump. It reports the registered key, the default read from the desktop entry, and whether the entry file exists, and it flags any comma-bearing _launch value as the unparseable Plasma 5 form. It would have identified this fault immediately.
New: joystick widget
plasmoid/ adds a Plasma 6 applet: a velocity-style virtual joystick for pan and tilt, a zoom slider, and Home, Center and Tracking buttons. Push further to move faster; it stops on release. Degrees per step, step interval, deadzone, axis inversion, the linkctl path and an optional device are all configurable.
It drives the gimbal with relative moves rather than absolute ones. linkctl status reports no pan or tilt at all while the camera is idle, and returns before --force is even considered, so there is no trustworthy position to aim an absolute target from. Relative steps work whether the camera is idle or streaming, cannot drift out of sync with the hardware, and are clamped by linkctl to the device's own range, so holding the stick against a limit is harmless.
The applet's pure logic lives in contents/ui/logic.js specifically so it can be tested outside Plasma. QML itself is not reachable by a test harness here, but shell quoting, command construction and output parsing are the parts where mistakes would actually bite.
Testing
89 checks pass across three suites, and shellcheck is clean on all three shell scripts:
node tests/test_joystick_logic.js adds 36 checks over shell quoting, tick command construction, axis inversion, the per-axis threshold that stops a horizontal push creeping vertically, and the multi-object JSON merge a diagonal tick produces.
lua tests/test_obs_linkctl.lua and lua tests/test_obs_lifecycle.lua continue to pass unchanged.
Each new suite was mutation-tested to confirm it is not passing vacuously: shrinking the per-axis threshold, making the JSON merge replace instead of merge, disabling the quote escaping, and treating tracking: false as absent are all caught.
The reported fault was reproduced in a throwaway HOME by writing the Plasma 5 triplet form, then confirmed that --list flags it and that reinstalling repairs it to the bare form.
All QML passes qmllint with no syntax errors. The KDE and Plasma QML modules are not installable in the test environment, so their imports are unresolvable there and every remaining warning is of that kind.
Not verified
The applet has not been loaded in a running Plasma session, and the kwriteconfig6 calls were exercised against stubs rather than the real binary. The shortcut fix rests on reading KDE's source and on a reproduction against stubs, which is strong evidence but is not the same as a live session confirming the key now fires.
If the applet fails to load, journalctl --user -b -u plasma-plasmashell will name the QML error.
**Summary generated by LLM agent**
## Why
The KDE global shortcuts installed by `kde/install-shortcuts.sh` never bound anything. System Settings reported "No default shortcuts" for every entry, and pressing the keys did nothing, while the same actions worked fine when run from a shell.
## Root cause
`kglobalacceld` reads the `_launch` key of a `[services][*.desktop]` group as a **bare key sequence** and passes it straight to `QKeySequence::fromString`. Plasma 5 stored a `keys,default_shortcut,friendly_name` triplet in that key; Plasma 6 does not. So the value we wrote, `Meta+Alt+T,none,Insta360: Toggle AI tracking`, parsed to an empty sequence and bound no key, silently and with nothing logged.
This is confirmed against `KServiceActionComponent::loadSettings` in the `kglobalacceld` source rather than inferred from behaviour. Alternates in that value are separated by a tab, and the literal `none` means unbound.
## What changed
`_launch` is now written as just the key sequence.
The generated desktop entries carry `X-KDE-Shortcuts`, which is where `kglobalacceld` reads an entry's *default* shortcut from. Without it an entry can function yet still display as having no shortcut, which is exactly the confusing state this started in.
The entries are no longer `NoDisplay` by default. `KApplicationTrader::query` skips `NoDisplay` services, so hiding them from the application launcher also hid them from the Shortcuts page in System Settings, making the bindings impossible to inspect or edit through the GUI. `HIDE_FROM_MENU=1` restores the previous behaviour for anyone who would rather keep their launcher clean.
`--list` is now a diagnostic instead of a bare dump. It reports the registered key, the default read from the desktop entry, and whether the entry file exists, and it flags any comma-bearing `_launch` value as the unparseable Plasma 5 form. It would have identified this fault immediately.
## New: joystick widget
`plasmoid/` adds a Plasma 6 applet: a velocity-style virtual joystick for pan and tilt, a zoom slider, and Home, Center and Tracking buttons. Push further to move faster; it stops on release. Degrees per step, step interval, deadzone, axis inversion, the `linkctl` path and an optional device are all configurable.
It drives the gimbal with **relative** moves rather than absolute ones. `linkctl status` reports no pan or tilt at all while the camera is idle, and returns before `--force` is even considered, so there is no trustworthy position to aim an absolute target from. Relative steps work whether the camera is idle or streaming, cannot drift out of sync with the hardware, and are clamped by `linkctl` to the device's own range, so holding the stick against a limit is harmless.
The applet's pure logic lives in `contents/ui/logic.js` specifically so it can be tested outside Plasma. QML itself is not reachable by a test harness here, but shell quoting, command construction and output parsing are the parts where mistakes would actually bite.
## Testing
89 checks pass across three suites, and `shellcheck` is clean on all three shell scripts:
- `node tests/test_joystick_logic.js` adds 36 checks over shell quoting, tick command construction, axis inversion, the per-axis threshold that stops a horizontal push creeping vertically, and the multi-object JSON merge a diagonal tick produces.
- `lua tests/test_obs_linkctl.lua` and `lua tests/test_obs_lifecycle.lua` continue to pass unchanged.
Each new suite was mutation-tested to confirm it is not passing vacuously: shrinking the per-axis threshold, making the JSON merge replace instead of merge, disabling the quote escaping, and treating `tracking: false` as absent are all caught.
The reported fault was reproduced in a throwaway `HOME` by writing the Plasma 5 triplet form, then confirmed that `--list` flags it and that reinstalling repairs it to the bare form.
All QML passes `qmllint` with no syntax errors. The KDE and Plasma QML modules are not installable in the test environment, so their imports are unresolvable there and every remaining warning is of that kind.
## Not verified
The applet has not been loaded in a running Plasma session, and the `kwriteconfig6` calls were exercised against stubs rather than the real binary. The shortcut fix rests on reading KDE's source and on a reproduction against stubs, which is strong evidence but is not the same as a live session confirming the key now fires.
If the applet fails to load, `journalctl --user -b -u plasma-plasmashell` will name the QML error.
The shortcuts wrote the Plasma 5 value format and so never bound anything,
which is what produced "No default shortcuts" in System Settings.
kglobalacceld's KServiceActionComponent::loadSettings reads the services
_launch key as a bare key sequence and hands it straight to
QKeySequence::fromString. Plasma 5 stored a "keys,default,name" triplet
there and Plasma 6 does not, so "Meta+Alt+T,none,Insta360: Toggle AI
tracking" parsed to an empty sequence and bound no key. The value is now
just the key sequence; alternates would be tab-separated and "none" means
unbound.
Two supporting changes. The desktop entries now carry X-KDE-Shortcuts,
which is where kglobalacceld reads an entry's default shortcut from, so the
binding shows up in System Settings as a default instead of as nothing.
And they are no longer NoDisplay by default: KApplicationTrader skips
NoDisplay services, so hiding them from the launcher also hid them from the
Shortcuts page, which is the opposite of helpful. HIDE_FROM_MENU=1 restores
the old behaviour.
--list is now a diagnostic rather than a bare dump: it reports the
registered key, the default from the desktop entry, whether the entry file
exists, and flags any comma-bearing value as the unparseable Plasma 5 form.
It would have identified this fault directly.
The new plasmoid is a Plasma 6 applet: a velocity-style virtual joystick
plus zoom slider and Home/Center/Tracking buttons. It drives the gimbal
with relative moves, not absolute ones, because linkctl status reports no
pan or tilt while the camera is idle, leaving no trustworthy position to aim
from; relative steps work in either state and cannot drift, and linkctl
clamps them to the device range. The pure logic lives in a .js library so it
is testable outside Plasma.
Tested: 36 new node checks over shell quoting, tick command construction,
inversion, the per-axis threshold and the multi-object JSON merge, each
confirmed meaningful by mutation. Reproduced the broken shortcut state in a
throwaway HOME and verified --list flags it and reinstalling repairs it. All
QML passes qmllint with no syntax errors (KDE modules are unresolvable
offline). shellcheck clean. The applet has not been loaded in a real Plasma
session.
branderson
approved these changes 2026-10-07 21:57:40 -07:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary generated by LLM agent
Why
The KDE global shortcuts installed by
kde/install-shortcuts.shnever bound anything. System Settings reported "No default shortcuts" for every entry, and pressing the keys did nothing, while the same actions worked fine when run from a shell.Root cause
kglobalacceldreads the_launchkey of a[services][*.desktop]group as a bare key sequence and passes it straight toQKeySequence::fromString. Plasma 5 stored akeys,default_shortcut,friendly_nametriplet in that key; Plasma 6 does not. So the value we wrote,Meta+Alt+T,none,Insta360: Toggle AI tracking, parsed to an empty sequence and bound no key, silently and with nothing logged.This is confirmed against
KServiceActionComponent::loadSettingsin thekglobalacceldsource rather than inferred from behaviour. Alternates in that value are separated by a tab, and the literalnonemeans unbound.What changed
_launchis now written as just the key sequence.The generated desktop entries carry
X-KDE-Shortcuts, which is wherekglobalacceldreads an entry's default shortcut from. Without it an entry can function yet still display as having no shortcut, which is exactly the confusing state this started in.The entries are no longer
NoDisplayby default.KApplicationTrader::queryskipsNoDisplayservices, so hiding them from the application launcher also hid them from the Shortcuts page in System Settings, making the bindings impossible to inspect or edit through the GUI.HIDE_FROM_MENU=1restores the previous behaviour for anyone who would rather keep their launcher clean.--listis now a diagnostic instead of a bare dump. It reports the registered key, the default read from the desktop entry, and whether the entry file exists, and it flags any comma-bearing_launchvalue as the unparseable Plasma 5 form. It would have identified this fault immediately.New: joystick widget
plasmoid/adds a Plasma 6 applet: a velocity-style virtual joystick for pan and tilt, a zoom slider, and Home, Center and Tracking buttons. Push further to move faster; it stops on release. Degrees per step, step interval, deadzone, axis inversion, thelinkctlpath and an optional device are all configurable.It drives the gimbal with relative moves rather than absolute ones.
linkctl statusreports no pan or tilt at all while the camera is idle, and returns before--forceis even considered, so there is no trustworthy position to aim an absolute target from. Relative steps work whether the camera is idle or streaming, cannot drift out of sync with the hardware, and are clamped bylinkctlto the device's own range, so holding the stick against a limit is harmless.The applet's pure logic lives in
contents/ui/logic.jsspecifically so it can be tested outside Plasma. QML itself is not reachable by a test harness here, but shell quoting, command construction and output parsing are the parts where mistakes would actually bite.Testing
89 checks pass across three suites, and
shellcheckis clean on all three shell scripts:node tests/test_joystick_logic.jsadds 36 checks over shell quoting, tick command construction, axis inversion, the per-axis threshold that stops a horizontal push creeping vertically, and the multi-object JSON merge a diagonal tick produces.lua tests/test_obs_linkctl.luaandlua tests/test_obs_lifecycle.luacontinue to pass unchanged.Each new suite was mutation-tested to confirm it is not passing vacuously: shrinking the per-axis threshold, making the JSON merge replace instead of merge, disabling the quote escaping, and treating
tracking: falseas absent are all caught.The reported fault was reproduced in a throwaway
HOMEby writing the Plasma 5 triplet form, then confirmed that--listflags it and that reinstalling repairs it to the bare form.All QML passes
qmllintwith no syntax errors. The KDE and Plasma QML modules are not installable in the test environment, so their imports are unresolvable there and every remaining warning is of that kind.Not verified
The applet has not been loaded in a running Plasma session, and the
kwriteconfig6calls were exercised against stubs rather than the real binary. The shortcut fix rests on reading KDE's source and on a reproduction against stubs, which is strong evidence but is not the same as a live session confirming the key now fires.If the applet fails to load,
journalctl --user -b -u plasma-plasmashellwill name the QML error.