diff --git a/core/internal/config/embedded/niri-binds.kdl b/core/internal/config/embedded/niri-binds.kdl index feab1fbb9..a1459a5f4 100644 --- a/core/internal/config/embedded/niri-binds.kdl +++ b/core/internal/config/embedded/niri-binds.kdl @@ -90,6 +90,11 @@ binds { spawn "dms" "ipc" "call" "brightness" "decrement" "5" ""; } + // === Input === + XF86TouchpadToggle allow-when-locked=true repeat=false { + spawn "dms" "ipc" "call" "niri" "toggleTouchpad"; + } + // === Window Management === Mod+Q repeat=false { close-window; } Mod+F { maximize-column; } diff --git a/core/internal/keybinds/providers/niri.go b/core/internal/keybinds/providers/niri.go index 13d329443..aef65a57d 100644 --- a/core/internal/keybinds/providers/niri.go +++ b/core/internal/keybinds/providers/niri.go @@ -133,6 +133,7 @@ func (n *NiriProvider) categorizeByAction(action string) string { action == "suspend" || action == "do-screen-transition" || action == "toggle-keyboard-shortcuts-inhibit" || + action == "toggle-touchpad" || strings.Contains(action, "dpms"): return "System" case action == "spawn": diff --git a/core/internal/keybinds/providers/niri_test.go b/core/internal/keybinds/providers/niri_test.go index 751fc3ae9..357d655ef 100644 --- a/core/internal/keybinds/providers/niri_test.go +++ b/core/internal/keybinds/providers/niri_test.go @@ -79,6 +79,7 @@ func TestNiriCategorizeByAction(t *testing.T) { {"close-window", "Window"}, {"spawn", "Execute"}, {"power-off-monitors", "System"}, + {"toggle-touchpad", "System"}, {"screenshot-window", "Screenshot"}, {"toggle-overview", "Overview"}, {"next-window", "Alt-Tab"}, diff --git a/distro/nix/niri.nix b/distro/nix/niri.nix index 12b3ed510..229f43bfe 100644 --- a/distro/nix/niri.nix +++ b/distro/nix/niri.nix @@ -155,6 +155,11 @@ in { allow-when-locked = true; action = dms-ipc "brightness" "decrement" "5" ""; }; + "XF86TouchpadToggle" = { + allow-when-locked = true; + repeat = false; + action = dms-ipc "niri" "toggleTouchpad"; + }; "Mod+Alt+N" = { allow-when-locked = true; action = dms-ipc "night" "toggle"; diff --git a/quickshell/Common/KeyUtils.js b/quickshell/Common/KeyUtils.js index f31c474f6..8e99c1f93 100644 --- a/quickshell/Common/KeyUtils.js +++ b/quickshell/Common/KeyUtils.js @@ -39,6 +39,7 @@ const KEY_MAP = { 16777328: "XF86AudioLowerVolume", 16777329: "XF86AudioMute", 16842808: "XF86AudioMicMute", + 16777488: "XF86TouchpadToggle", 16777344: "XF86AudioPlay", 16777345: "XF86AudioStop", 16777346: "XF86AudioPrev", diff --git a/quickshell/Common/KeybindActions.js b/quickshell/Common/KeybindActions.js index 4437f8034..3f7e4fad4 100644 --- a/quickshell/Common/KeybindActions.js +++ b/quickshell/Common/KeybindActions.js @@ -849,6 +849,10 @@ const NIRI_ACTIONS = { id: "toggle-keyboard-shortcuts-inhibit", label: "Toggle Shortcuts Inhibit" }, + { + id: "toggle-touchpad", + label: "Toggle Touchpad" + }, { id: "quit", label: "Quit niri" diff --git a/quickshell/Common/SettingsData.qml b/quickshell/Common/SettingsData.qml index 53da07653..f28cf4cee 100644 --- a/quickshell/Common/SettingsData.qml +++ b/quickshell/Common/SettingsData.qml @@ -1106,6 +1106,7 @@ Singleton { property bool osdMicMuteEnabled: Spec.SPEC.osdMicMuteEnabled.def property bool osdMicVolumeEnabled: Spec.SPEC.osdMicVolumeEnabled.def property bool osdCapsLockEnabled: Spec.SPEC.osdCapsLockEnabled.def + property bool osdTouchpadEnabled: Spec.SPEC.osdTouchpadEnabled.def property bool osdPowerProfileEnabled: Spec.SPEC.osdPowerProfileEnabled.def property bool osdAudioOutputEnabled: Spec.SPEC.osdAudioOutputEnabled.def property bool osdWorkspaceEnabled: Spec.SPEC.osdWorkspaceEnabled.def diff --git a/quickshell/Common/settings/SettingsSpec.js b/quickshell/Common/settings/SettingsSpec.js index a2bc2bf5f..1eead5273 100644 --- a/quickshell/Common/settings/SettingsSpec.js +++ b/quickshell/Common/settings/SettingsSpec.js @@ -1398,6 +1398,9 @@ var LOCAL_SPEC = { osdCapsLockEnabled: { def: true }, + osdTouchpadEnabled: { + def: true + }, osdPowerProfileEnabled: { def: false }, diff --git a/quickshell/DMSShell.qml b/quickshell/DMSShell.qml index 6f2c004db..7eb711e08 100644 --- a/quickshell/DMSShell.qml +++ b/quickshell/DMSShell.qml @@ -1315,6 +1315,12 @@ Item { delegate: CapsLockOSD {} } + Variants { + model: (SettingsData.osdTouchpadEnabled && CompositorService.isNiri) ? root.legacySystemLevelOsdScreens : [] + + delegate: TouchpadOSD {} + } + Variants { model: SettingsData.osdAudioOutputEnabled ? SettingsData.getFilteredScreens("osd") : [] diff --git a/quickshell/Modules/OSD/TouchpadOSD.qml b/quickshell/Modules/OSD/TouchpadOSD.qml new file mode 100644 index 000000000..4690618ac --- /dev/null +++ b/quickshell/Modules/OSD/TouchpadOSD.qml @@ -0,0 +1,34 @@ +import QtQuick +import qs.Common +import qs.Services +import qs.Widgets + +DOSD { + id: root + + osdKind: "touchpad" + + osdWidth: Theme.osdHeight + osdHeight: Theme.osdHeight + autoHideInterval: 2000 + enableMouseInteraction: false + + Connections { + target: NiriService + + function onTouchpadToggled() { + if (SettingsData.osdTouchpadEnabled) { + root.show(); + } + } + } + + content: Item { + OsdIcon { + tonal: false + anchors.centerIn: parent + iconName: NiriService.touchpadDisabled ? "touchpad_mouse_off" : "touchpad_mouse" + iconColor: NiriService.touchpadDisabled ? Theme.outline : Theme.primary + } + } +} diff --git a/quickshell/Modules/Settings/OSDTab.qml b/quickshell/Modules/Settings/OSDTab.qml index 358b714ec..e20cd1cbb 100644 --- a/quickshell/Modules/Settings/OSDTab.qml +++ b/quickshell/Modules/Settings/OSDTab.qml @@ -1,5 +1,6 @@ import QtQuick import qs.Common +import qs.Services import qs.DCommon.Widgets import qs.Modules.Settings.Widgets @@ -48,6 +49,11 @@ Item { "label": I18n.tr("Caps lock"), "shown": SettingsData.osdCapsLockEnabled }, + { + "value": "touchpad", + "label": I18n.tr("Touchpad"), + "shown": SettingsData.osdTouchpadEnabled && CompositorService.isNiri + }, { "value": "powerProfile", "label": I18n.tr("Power profile"), @@ -184,6 +190,14 @@ Item { onToggled: checked => SettingsData.set("osdCapsLockEnabled", checked) } + SettingsToggleRow { + settingKey: "osdTouchpadEnabled" + visible: CompositorService.isNiri + text: I18n.tr("Touchpad") + checked: SettingsData.osdTouchpadEnabled + onToggled: checked => SettingsData.set("osdTouchpadEnabled", checked) + } + SettingsToggleRow { settingKey: "osdPowerProfileEnabled" text: I18n.tr("Power profile") diff --git a/quickshell/Services/NiriService.qml b/quickshell/Services/NiriService.qml index 12a451cb5..f796aeefb 100644 --- a/quickshell/Services/NiriService.qml +++ b/quickshell/Services/NiriService.qml @@ -41,6 +41,10 @@ Singleton { property int currentKeyboardLayoutIndex: 0 property var keyboardLayoutNames: [] + // niri never reports toggle-touchpad state, so this only tracks toggles issued through DMS. + property bool touchpadDisabled: false + signal touchpadToggled + property string configValidationOutput: "" property bool hasInitialConnection: false property bool suppressConfigToast: true @@ -810,6 +814,19 @@ Singleton { }); } + function toggleTouchpad() { + if (!CompositorService.isNiri) + return; + Proc.runCommand("niri-toggle-touchpad", ["niri", "msg", "action", "toggle-touchpad"], (output, exitCode) => { + if (exitCode !== 0) { + log.warn("toggle-touchpad failed, exit code:", exitCode); + return; + } + touchpadDisabled = !touchpadDisabled; + touchpadToggled(); + }); + } + function toggleOverview() { return send({ "Action": { @@ -1923,6 +1940,14 @@ window-rule { return "SCREENSHOT_WINDOW_FAILED"; } + function toggleTouchpad(): string { + if (!CompositorService.isNiri) { + return "NIRI_NOT_AVAILABLE"; + } + NiriService.toggleTouchpad(); + return "TOUCHPAD_TOGGLED"; + } + target: "niri" } } diff --git a/quickshell/translations/settings_search_index.json b/quickshell/translations/settings_search_index.json index 988925422..c67d33bc8 100644 --- a/quickshell/translations/settings_search_index.json +++ b/quickshell/translations/settings_search_index.json @@ -8792,6 +8792,21 @@ "show" ] }, + { + "section": "osdTouchpadEnabled", + "label": "Touchpad", + "tabIndex": 18, + "category": "On-screen displays", + "keywords": [ + "displays", + "indicator", + "osd", + "popup", + "screen", + "touchpad" + ], + "conditionKey": "isNiri" + }, { "section": "osdVolumeEnabled", "label": "Volume",