mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-29 22:47:47 -04:00
feat(mango): native mouse/touchpad & keyboard input support
- Hide blur toggle options until mango support is added
This commit is contained in:
1 parent
ea68399ef3
commit
e10102edc0
21 files changed
+199
-91
No files matched your search
@@ -44,6 +44,7 @@ var resolveIncludeCmd = &cobra.Command{
|
||||
"binds.kdl",
|
||||
"input.kdl",
|
||||
"cursor.conf",
|
||||
"input.conf",
|
||||
"layout.conf",
|
||||
"outputs.conf",
|
||||
"binds.conf",
|
||||
|
||||
@@ -145,10 +145,12 @@ var dmsConfigSpecs = map[string]dmsConfigSpec{
|
||||
niriContent: func(_ string) string { return config.NiriAlttabConfig },
|
||||
},
|
||||
"input": {
|
||||
niriFile: "input.kdl",
|
||||
hyprFile: "input.lua",
|
||||
niriContent: func(_ string) string { return config.NiriInputConfig },
|
||||
hyprContent: func(_ string) string { return config.DMSInputLuaConfig },
|
||||
niriFile: "input.kdl",
|
||||
hyprFile: "input.lua",
|
||||
mangoFile: "input.conf",
|
||||
niriContent: func(_ string) string { return config.NiriInputConfig },
|
||||
hyprContent: func(_ string) string { return config.DMSInputLuaConfig },
|
||||
mangoContent: func(_ string) string { return "" },
|
||||
},
|
||||
"outputs": {
|
||||
niriFile: "outputs.kdl",
|
||||
|
||||
@@ -12,6 +12,7 @@ exec-once=dms run
|
||||
source=./dms/colors.conf
|
||||
source=./dms/layout.conf
|
||||
source=./dms/cursor.conf
|
||||
source=./dms/input.conf
|
||||
source=./dms/outputs.conf
|
||||
source=./dms/windowrules.conf
|
||||
source=./dms/binds.conf
|
||||
@@ -174,6 +174,7 @@ func (cd *ConfigDeployer) deployMangoDmsConfigs(dmsDir, terminalCommand string,
|
||||
{"layout.conf", MangoLayoutConfig},
|
||||
{"outputs.conf", extractMangoLines(oldMain, mangoMonitorLine)},
|
||||
{"cursor.conf", ""},
|
||||
{"input.conf", ""},
|
||||
{"windowrules.conf", extractMangoLines(oldMain, mangoWindowLine)},
|
||||
}
|
||||
for _, cfg := range configs {
|
||||
|
||||
@@ -51,6 +51,8 @@ func TestMangoDeployMovesUserRulesAndKeepsBindsByDefault(t *testing.T) {
|
||||
assert.NotEmpty(t, result.BackupPath)
|
||||
|
||||
assert.Contains(t, readMango(t, home, "config.conf"), "exec-once=dms run")
|
||||
assert.Contains(t, readMango(t, home, "config.conf"), "source=./dms/input.conf")
|
||||
assert.Equal(t, "", readMango(t, home, "dms/input.conf"))
|
||||
binds := readMango(t, home, "dms/binds.conf")
|
||||
assert.Contains(t, binds, "bind=SUPER,Return,spawn,foot\nkeymode=resize\nbind=NONE,h,resizewin,-10,0")
|
||||
assert.NotContains(t, binds, "{{TERMINAL_COMMAND}}")
|
||||
|
||||
@@ -34,14 +34,18 @@ func TestMigrateLeavesUserConfigUnlessAsked(t *testing.T) {
|
||||
require.NoError(t, os.MkdirAll(filepath.Join(dir, "dms"), 0o755))
|
||||
main := filepath.Join(dir, "config.conf")
|
||||
layout := filepath.Join(dir, "dms", "layout.conf")
|
||||
input := filepath.Join(dir, "dms", "input.conf")
|
||||
require.NoError(t, os.WriteFile(main, []byte("exec-once=dms run\n"), 0o644))
|
||||
require.NoError(t, os.WriteFile(layout, []byte("borderpx=2\n"), 0o644))
|
||||
require.NoError(t, os.WriteFile(input, []byte("numlockon=1\n"), 0o644))
|
||||
|
||||
changed, err := Snake.Migrate(dir, false)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{layout}, changed)
|
||||
assert.Equal(t, []string{layout, input}, changed)
|
||||
data, _ := os.ReadFile(main)
|
||||
assert.Equal(t, "exec-once=dms run\n", string(data))
|
||||
data, _ = os.ReadFile(input)
|
||||
assert.Equal(t, "numlock_on=1\n", string(data))
|
||||
|
||||
changed, err = Snake.Migrate(dir, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
// DMS-owned fragments under mango/dms; config.conf is the user's and only
|
||||
// migrated on request.
|
||||
var fragments = []string{"binds.conf", "colors.conf", "layout.conf", "cursor.conf", "outputs.conf", "windowrules.conf"}
|
||||
var fragments = []string{"binds.conf", "colors.conf", "layout.conf", "cursor.conf", "input.conf", "outputs.conf", "windowrules.conf"}
|
||||
|
||||
// Migrate respells the dms fragments, and config.conf (after a backup) when includeMain is set.
|
||||
// It returns the files rewritten; an unwritable file is reported without stopping the others.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
.pragma library
|
||||
|
||||
// One shell-wide blur reach in logical px, solved into each compositor's own knobs.
|
||||
// Hyprland's stock 8/1 reaches 16px and reads as plain transparency, so DMS defaults it near niri.
|
||||
// One shell-wide blur reach in logical px, per-compositor
|
||||
var HYPRLAND_DEFAULT = 48;
|
||||
var NIRI_DEFAULT = 42;
|
||||
var AQUEOUS_DEFAULT = 40;
|
||||
|
||||
@@ -105,7 +105,8 @@ const includeTable = {
|
||||
},
|
||||
input: {
|
||||
niri: [kdlInclude("input")],
|
||||
hyprland: [luaInclude("input")]
|
||||
hyprland: [luaInclude("input")],
|
||||
mango: [confInclude("input")]
|
||||
},
|
||||
windowrules: {
|
||||
niri: [kdlInclude("windowrules")],
|
||||
|
||||
@@ -245,7 +245,6 @@ Singleton {
|
||||
property int mangoLayoutGapsOutOverride: Spec.SPEC.mangoLayoutGapsOutOverride.def
|
||||
property int mangoLayoutRadiusOverride: Spec.SPEC.mangoLayoutRadiusOverride.def
|
||||
property int mangoLayoutBorderSize: Spec.SPEC.mangoLayoutBorderSize.def
|
||||
property bool mangoTrackpadNaturalScrolling: Spec.SPEC.mangoTrackpadNaturalScrolling.def
|
||||
property string mouseAccelProfile: Spec.SPEC.mouseAccelProfile.def
|
||||
property real mouseAccelSpeed: Spec.SPEC.mouseAccelSpeed.def
|
||||
property bool mouseLeftHanded: Spec.SPEC.mouseLeftHanded.def
|
||||
|
||||
@@ -315,10 +315,6 @@ var LOCAL_SPEC = {
|
||||
def: -1,
|
||||
onChange: "updateCompositorLayout"
|
||||
},
|
||||
mangoTrackpadNaturalScrolling: {
|
||||
def: true,
|
||||
onChange: "updateCompositorCursor"
|
||||
},
|
||||
mouseAccelProfile: {
|
||||
def: "default",
|
||||
onChange: "updateCompositorInput"
|
||||
|
||||
@@ -114,6 +114,7 @@ Item {
|
||||
}
|
||||
|
||||
SettingsTextFieldRow {
|
||||
visible: CompositorService.isNiri || CompositorService.isHyprland
|
||||
resetKeys: ["keyboardKeymapFile"]
|
||||
leftIconName: "description"
|
||||
text: I18n.tr("Keymap file")
|
||||
|
||||
@@ -113,7 +113,7 @@ Item {
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
visible: CompositorService.isNiri
|
||||
visible: CompositorService.isNiri || CompositorService.isMango
|
||||
tags: ["mouse", "middle", "click", "emulation"]
|
||||
settingKey: "mouseMiddleEmulation"
|
||||
text: I18n.tr("Middle click emulation")
|
||||
@@ -122,23 +122,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
width: parent.width
|
||||
visible: CompositorService.isMango
|
||||
tags: ["touchpad", "trackpad", "natural", "scrolling", "invert"]
|
||||
title: I18n.tr("Touchpad")
|
||||
settingKey: "mangoTouchpadSettings"
|
||||
iconName: "trackpad_input_2"
|
||||
|
||||
SettingsToggleRow {
|
||||
tags: ["touchpad", "trackpad", "natural", "scrolling", "invert"]
|
||||
settingKey: "mangoTrackpadNaturalScrolling"
|
||||
text: I18n.tr("Natural scrolling")
|
||||
checked: SettingsData.mangoTrackpadNaturalScrolling
|
||||
onToggled: checked => SettingsData.set("mangoTrackpadNaturalScrolling", checked)
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
width: parent.width
|
||||
visible: CompositorService.supportsInputConfig
|
||||
@@ -270,7 +253,7 @@ Item {
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
visible: CompositorService.isNiri
|
||||
visible: CompositorService.isNiri || CompositorService.isMango
|
||||
tags: ["touchpad", "disable", "external", "mouse"]
|
||||
settingKey: "touchpadDisableOnExternalMouse"
|
||||
text: I18n.tr("Disable with external mouse")
|
||||
|
||||
@@ -192,9 +192,8 @@ Column {
|
||||
settingKey: "blurEnabled"
|
||||
valueKeys: ["blurStrength"]
|
||||
text: I18n.tr("Background blur")
|
||||
description: BlurService.available ? I18n.tr("Sets the compositor's blur for every window, not just the shell", "Blur strength slider description") : I18n.tr("Your compositor does not support background blur (ext-background-effect-v1)")
|
||||
visible: BlurService.available
|
||||
checked: SettingsData.blurEnabled ?? false
|
||||
enabled: BlurService.available
|
||||
value: BlurService.strength
|
||||
minimum: 16
|
||||
maximum: 160
|
||||
@@ -210,11 +209,19 @@ Column {
|
||||
onSliderValueChanged: newValue => SettingsData.set("blurStrength", newValue)
|
||||
}
|
||||
|
||||
SettingsNoteRow {
|
||||
visible: BlurService.probed && !BlurService.available
|
||||
noteIconName: "info"
|
||||
tint: Theme.surfaceVariantText
|
||||
tintBackground: Theme.surfaceVariantAlpha
|
||||
text: I18n.tr("Your compositor does not support ext-background blur")
|
||||
}
|
||||
|
||||
SettingsNavRow {
|
||||
tab: "theme"
|
||||
tags: ["blur", "xray", "compositor", "layout"]
|
||||
settingKey: "blurXrayLink"
|
||||
visible: CompositorService.isNiri || CompositorService.isHyprland
|
||||
visible: BlurService.available && (CompositorService.isNiri || CompositorService.isHyprland)
|
||||
title: I18n.tr("Xray options are in Compositor → Layout")
|
||||
onClicked: keyboard => root.parentModal?.navigateTo("compositor_layout", keyboard)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ Singleton {
|
||||
|
||||
property bool compositorSupported: false
|
||||
property bool _probed: false
|
||||
property bool probed: false
|
||||
readonly property bool available: compositorSupported
|
||||
readonly property bool blurRequested: SettingsData.blurEnabled ?? false
|
||||
onBlurRequestedChanged: {
|
||||
@@ -64,6 +65,7 @@ Singleton {
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
root.compositorSupported = text.trim() === "supported";
|
||||
root.probed = true;
|
||||
if (root.compositorSupported)
|
||||
log.info("Compositor supports ext-background-effect-v1");
|
||||
else
|
||||
|
||||
@@ -151,7 +151,7 @@ Singleton {
|
||||
readonly property bool supportsSmartDock: isNiri || isHyprland || isMango || isAqueous
|
||||
readonly property bool supportsNativeOverview: isNiri || isAqueous
|
||||
readonly property bool supportsPointerConfig: isNiri || isHyprland || isMango
|
||||
readonly property bool supportsInputConfig: isNiri || isHyprland
|
||||
readonly property bool supportsInputConfig: isNiri || isHyprland || isMango
|
||||
|
||||
readonly property string displayName: {
|
||||
switch (compositor) {
|
||||
@@ -2184,6 +2184,8 @@ Singleton {
|
||||
NiriService.generateNiriInputConfig();
|
||||
if (root.isHyprland && typeof HyprlandService !== "undefined")
|
||||
HyprlandService.generateInputConfig();
|
||||
if (root.isMango && typeof MangoService !== "undefined")
|
||||
MangoService.generateInputConfig();
|
||||
}
|
||||
|
||||
function onCompositorCursorRefreshNeeded() {
|
||||
|
||||
@@ -28,10 +28,14 @@ Singleton {
|
||||
readonly property string outputsPath: mangoDmsDir + "/outputs.conf"
|
||||
readonly property string layoutPath: mangoDmsDir + "/layout.conf"
|
||||
readonly property string cursorPath: mangoDmsDir + "/cursor.conf"
|
||||
readonly property string inputPath: mangoDmsDir + "/input.conf"
|
||||
readonly property string windowRulesPath: mangoDmsDir + "/windowrules.conf"
|
||||
|
||||
// Mango after 0.17.5 renamed every config key to snake_case with no aliases.
|
||||
property bool snakeKeys: false
|
||||
property bool dialectReady: false
|
||||
property bool inputGenerationPending: false
|
||||
property bool inputGenerationRunning: false
|
||||
property int _lastGapValue: -1
|
||||
property real _ignoreWatchedReloadUntil: 0
|
||||
property string _lastMonitorsLine: ""
|
||||
@@ -83,6 +87,13 @@ Singleton {
|
||||
onFileChanged: root.handleWatchedConfigChanged()
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: mangoInputWatcher
|
||||
path: CompositorService.isMango ? root.inputPath : ""
|
||||
watchChanges: CompositorService.isMango
|
||||
onFileChanged: root.handleWatchedConfigChanged()
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: mangoOutputsWatcher
|
||||
path: CompositorService.isMango ? root.outputsPath : ""
|
||||
@@ -453,6 +464,11 @@ Singleton {
|
||||
onTriggered: root.reloadConfig(false, false)
|
||||
}
|
||||
|
||||
DeferredAction {
|
||||
id: inputGenerationAction
|
||||
onTriggered: root.doGenerateInputConfig()
|
||||
}
|
||||
|
||||
function handleWatchedConfigChanged() {
|
||||
if (!CompositorService.isMango || !root.available)
|
||||
return;
|
||||
@@ -582,7 +598,9 @@ Singleton {
|
||||
if (exitCode !== 0)
|
||||
log.warn("mango-migrate exited", exitCode, "- some fragments may keep the old key spelling");
|
||||
root.snakeKeys = output.trim().split("\n").pop() === "snake";
|
||||
root.dialectReady = true;
|
||||
generateLayoutConfig();
|
||||
generateInputConfig();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -673,10 +691,8 @@ gappov=${gapsOut}
|
||||
const themeName = settings.theme === "System Default" ? (SettingsData.systemDefaultCursorTheme || "") : settings.theme;
|
||||
const size = settings.size || 24;
|
||||
const hideTimeout = settings.mango?.cursorHideTimeout || 0;
|
||||
const naturalScrolling = SettingsData.mangoTrackpadNaturalScrolling ? 1 : 0;
|
||||
|
||||
let content = `# Auto-generated by DMS - do not edit manually
|
||||
trackpad_natural_scrolling=${naturalScrolling}
|
||||
cursor_size=${size}`;
|
||||
|
||||
if (themeName)
|
||||
@@ -697,4 +713,99 @@ cursor_size=${size}`;
|
||||
reloadConfig(false);
|
||||
});
|
||||
}
|
||||
|
||||
function generateInputConfig() {
|
||||
if (!CompositorService.isMango)
|
||||
return;
|
||||
inputGenerationPending = true;
|
||||
inputGenerationAction.schedule();
|
||||
}
|
||||
|
||||
function doGenerateInputConfig() {
|
||||
if (inputGenerationRunning)
|
||||
return;
|
||||
inputGenerationPending = false;
|
||||
if (!CompositorService.isMango)
|
||||
return;
|
||||
// numlock is spelled per dialect, so the first write waits for mango-migrate
|
||||
if (!dialectReady) {
|
||||
inputGenerationPending = true;
|
||||
return;
|
||||
}
|
||||
inputGenerationRunning = true;
|
||||
|
||||
const content = ["# Auto-generated by DMS - do not edit manually"].concat(mangoInputLines(SettingsData, snakeKeys), [""]).join("\n");
|
||||
// Exit 3 = unchanged, so a shell start does not reload Mango for nothing
|
||||
const script = `mkdir -p "${mangoDmsDir}" && cat > "${inputPath}.tmp" << 'EOF'\n${content}EOF\nif cmp -s "${inputPath}.tmp" "${inputPath}"; then rm -f "${inputPath}.tmp"; exit 3; fi\nmv -f "${inputPath}.tmp" "${inputPath}"`;
|
||||
suppressWatchedConfigReloads(1500);
|
||||
Proc.runCommand("mango-write-input", ["sh", "-c", script], (output, exitCode) => {
|
||||
inputGenerationRunning = false;
|
||||
if (exitCode === 3) {
|
||||
if (inputGenerationPending)
|
||||
inputGenerationAction.schedule();
|
||||
return;
|
||||
}
|
||||
if (exitCode !== 0) {
|
||||
log.warn("Failed to write input config:", output);
|
||||
} else {
|
||||
log.info("Generated input config at", inputPath);
|
||||
reloadConfig(false);
|
||||
}
|
||||
if (inputGenerationPending)
|
||||
inputGenerationAction.schedule();
|
||||
});
|
||||
}
|
||||
|
||||
// Mouse and trackpad keys are the split names Mango has had since 0.16.1; libinput enums are numeric
|
||||
function mangoInputLines(s, snake) {
|
||||
const flag = value => value ? 1 : 0;
|
||||
const profile = name => name === "flat" ? 1 : name === "adaptive" ? 2 : -1;
|
||||
const scrollMethod = name => ({
|
||||
"no-scroll": 0,
|
||||
"two-finger": 1,
|
||||
"edge": 2,
|
||||
"on-button-down": 4
|
||||
})[name] ?? -1;
|
||||
const clickMethod = name => name === "button-areas" ? 1 : name === "clickfinger" ? 2 : -1;
|
||||
const lines = [];
|
||||
// -1 is a "default" choice: the key stays out so Mango's own default applies
|
||||
const add = (key, value) => {
|
||||
if (value !== -1 && value !== "")
|
||||
lines.push(`${key}=${value}`);
|
||||
};
|
||||
add("mouse_accel_speed", s.mouseAccelSpeed.toFixed(2));
|
||||
add("mouse_accel_profile", profile(s.mouseAccelProfile));
|
||||
add("mouse_natural_scrolling", flag(s.mouseNaturalScroll));
|
||||
add("mouse_left_handed", flag(s.mouseLeftHanded));
|
||||
add("mouse_middle_button_emulation", flag(s.mouseMiddleEmulation));
|
||||
add("axis_scroll_factor", s.mouseScrollFactor.toFixed(2));
|
||||
add("mouse_scroll_method", scrollMethod(s.mouseScrollMethod));
|
||||
add("tap_to_click", flag(s.touchpadTapToClick));
|
||||
add("tap_and_drag", flag(s.touchpadTapAndDrag));
|
||||
add("drag_lock", flag(s.touchpadDragLock));
|
||||
add("trackpad_accel_speed", s.touchpadAccelSpeed.toFixed(2));
|
||||
add("trackpad_accel_profile", profile(s.touchpadAccelProfile));
|
||||
add("trackpad_natural_scrolling", flag(s.touchpadNaturalScroll));
|
||||
add("trackpad_scroll_factor", s.touchpadScrollFactor.toFixed(2));
|
||||
add("trackpad_scroll_method", scrollMethod(s.touchpadScrollMethod));
|
||||
add("trackpad_click_method", clickMethod(s.touchpadClickMethod));
|
||||
add("trackpad_disable_while_typing", flag(s.touchpadDisableWhileTyping));
|
||||
add("trackpad_middle_button_emulation", flag(s.touchpadMiddleEmulation));
|
||||
add("trackpad_send_events_mode", s.touchpadDisableOnExternalMouse ? 2 : 0);
|
||||
|
||||
// Unconfigured keyboard keeps the user's own xkb_rules_* keys and the XKB defaults
|
||||
const keyboardConfigured = s.keyboardLayouts || s.keyboardVariants || s.keyboardModel || s.keyboardOptions || s.keyboardRepeatDelay > 0 || s.keyboardRepeatRate > 0 || s.keyboardNumlock;
|
||||
if (!keyboardConfigured)
|
||||
return lines;
|
||||
add("xkb_rules_layout", s.keyboardLayouts);
|
||||
add("xkb_rules_variant", s.keyboardVariants);
|
||||
add("xkb_rules_model", s.keyboardModel);
|
||||
add("xkb_rules_options", s.keyboardOptions);
|
||||
if (s.keyboardRepeatDelay > 0)
|
||||
add("repeat_delay", s.keyboardRepeatDelay);
|
||||
if (s.keyboardRepeatRate > 0)
|
||||
add("repeat_rate", s.keyboardRepeatRate);
|
||||
add(snake ? "numlock_on" : "numlockon", flag(s.keyboardNumlock));
|
||||
return lines;
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,7 @@ Singleton {
|
||||
"smartDockCapable": () => CompositorService.supportsSmartDock,
|
||||
"workspaceFollowFocusCapable": () => CompositorService.supportsWorkspaceFollowFocus,
|
||||
"isHyprlandOrNiri": () => CompositorService.isHyprland || CompositorService.isNiri,
|
||||
"isNiriOrMango": () => CompositorService.isNiri || CompositorService.isMango,
|
||||
"windowRulesCapable": () => CompositorService.supportsWindowRules,
|
||||
"layoutCapable": () => CompositorService.supportsLayoutConfig,
|
||||
"keybindsAvailable": () => KeybindsService.available,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { loadScript } from "./qml-script.mjs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import vm from "node:vm";
|
||||
@@ -11,7 +12,7 @@ vm.runInContext(readFileSync(new URL("../Common/ConfigIncludeResolve.js", import
|
||||
const plain = value => JSON.parse(JSON.stringify(value));
|
||||
|
||||
test("includeSpec returns null off the table", () => {
|
||||
assert.equal(resolve.includeSpec("input", "mango"), null, "input exists for niri and hyprland only");
|
||||
assert.equal(resolve.includeSpec("colors", "mango"), null, "colors is deployed by setup, never repaired");
|
||||
assert.equal(resolve.includeSpec("colors", "niri"), null, "unknown kind");
|
||||
assert.equal(resolve.includeSpec("outputs", "sway"), null, "unknown compositor");
|
||||
assert.equal(resolve.includeSpec("outputs", ""), null, "empty compositor");
|
||||
@@ -34,7 +35,8 @@ test("includePaths joins the compositor directory, with hypr for hyprland", () =
|
||||
assert.deepEqual(plain(mango.fragmentFiles), ["/home/u/.config/mango/dms/cursor.conf"]);
|
||||
const binds = resolve.includePaths("binds", "hyprland", "/home/u/.config");
|
||||
assert.deepEqual(plain(binds.fragmentFiles), ["/home/u/.config/hypr/dms/binds.lua", "/home/u/.config/hypr/dms/binds-user.lua"]);
|
||||
assert.equal(resolve.includePaths("input", "mango", "/home/u/.config"), null, "no spec, no paths");
|
||||
assert.deepEqual(plain(resolve.includePaths("input", "mango", "/home/u/.config").fragmentFiles), ["/home/u/.config/mango/dms/input.conf"]);
|
||||
assert.equal(resolve.includePaths("colors", "mango", "/home/u/.config"), null, "no spec, no paths");
|
||||
});
|
||||
|
||||
test("resolveIncludeArgs spells mango as mangowc and names the fragment", () => {
|
||||
@@ -42,11 +44,12 @@ test("resolveIncludeArgs spells mango as mangowc and names the fragment", () =>
|
||||
assert.deepEqual(plain(resolve.resolveIncludeArgs("layout", "hyprland")), ["hyprland", "layout.lua"]);
|
||||
assert.deepEqual(plain(resolve.resolveIncludeArgs("input", "hyprland")), ["hyprland", "input.lua"]);
|
||||
assert.deepEqual(plain(resolve.resolveIncludeArgs("windowrules", "mango")), ["mangowc", "windowrules.conf"]);
|
||||
assert.equal(resolve.resolveIncludeArgs("input", "mango"), null, "no spec, no args");
|
||||
assert.deepEqual(plain(resolve.resolveIncludeArgs("input", "mango")), ["mangowc", "input.conf"]);
|
||||
assert.equal(resolve.resolveIncludeArgs("colors", "mango"), null, "no spec, no args");
|
||||
});
|
||||
|
||||
test("repairScriptFor returns an empty script off the table", () => {
|
||||
assert.equal(resolve.repairScriptFor("input", "mango", "/home/u/.config", "/x"), "");
|
||||
assert.equal(resolve.repairScriptFor("colors", "mango", "/home/u/.config", "/x"), "");
|
||||
assert.equal(resolve.repairScriptFor("nope", "niri", "/home/u/.config", "/x"), "");
|
||||
});
|
||||
|
||||
@@ -79,3 +82,32 @@ test("repair preserves config and fragments, backs up, and adds live includes on
|
||||
assert.equal(readFileSync(paths.configFile, "utf8"), repaired);
|
||||
}
|
||||
});
|
||||
|
||||
test("mango input fragment maps choices onto libinput numbers and spells numlock per dialect", () => {
|
||||
const SPEC = loadScript(new URL("../Common/settings/SettingsSpec.js", import.meta.url)).SPEC;
|
||||
const source = readFileSync(new URL("../Services/MangoService.qml", import.meta.url), "utf8");
|
||||
const found = source.match(/^ function mangoInputLines\([^)]*\) \{\n[\s\S]*?^ \}/m);
|
||||
assert.ok(found, "mangoInputLines not found in MangoService.qml");
|
||||
const scope = vm.createContext({});
|
||||
vm.runInContext(found[0], scope);
|
||||
const defaults = Object.fromEntries(Object.keys(SPEC).filter(key => /^(mouse|touchpad|keyboard)[A-Z]/.test(key)).map(key => [key, SPEC[key].def]));
|
||||
const lines = (overrides, snake = false) => [...scope.mangoInputLines(Object.assign({}, defaults, overrides), snake)];
|
||||
const value = (list, key) => list.find(line => line.startsWith(key + "="))?.slice(key.length + 1);
|
||||
|
||||
const stock = lines({});
|
||||
assert.equal(value(stock, "trackpad_natural_scrolling"), "1");
|
||||
assert.equal(value(stock, "mouse_accel_profile"), undefined, "default profile leaves the key to Mango");
|
||||
assert.ok(!stock.some(line => /^(xkb_rules_|repeat_|numlock)/.test(line)), "unconfigured keyboard writes nothing");
|
||||
|
||||
const chosen = lines({ mouseScrollMethod: "no-scroll", touchpadAccelProfile: "adaptive", touchpadScrollMethod: "on-button-down", touchpadClickMethod: "clickfinger", touchpadDisableOnExternalMouse: true, keyboardLayouts: "us,de", keyboardRepeatDelay: 300 });
|
||||
assert.equal(value(chosen, "mouse_scroll_method"), "0");
|
||||
assert.equal(value(chosen, "trackpad_accel_profile"), "2");
|
||||
assert.equal(value(chosen, "trackpad_scroll_method"), "4");
|
||||
assert.equal(value(chosen, "trackpad_click_method"), "2");
|
||||
assert.equal(value(chosen, "trackpad_send_events_mode"), "2");
|
||||
assert.equal(value(chosen, "xkb_rules_layout"), "us,de");
|
||||
assert.equal(value(chosen, "xkb_rules_variant"), undefined, "empty fields stay out");
|
||||
assert.equal(value(chosen, "repeat_delay"), "300");
|
||||
assert.equal(value(chosen, "numlockon"), "0");
|
||||
assert.equal(value(lines({ keyboardNumlock: true }, true), "numlock_on"), "1");
|
||||
});
|
||||
@@ -487,6 +487,10 @@ def find_settings_components(content, filename, wrappers, tab_meta, hub_meta):
|
||||
condition_key = "isAqueous"
|
||||
elif all(c in visible_raw for c in ("CompositorService.isNiri", "CompositorService.isHyprland", "CompositorService.isMango")):
|
||||
condition_key = "windowRulesCapable"
|
||||
elif all(c in visible_raw for c in ("CompositorService.isNiri", "CompositorService.isHyprland")):
|
||||
condition_key = "isHyprlandOrNiri"
|
||||
elif all(c in visible_raw for c in ("CompositorService.isNiri", "CompositorService.isMango")):
|
||||
condition_key = "isNiriOrMango"
|
||||
elif "CompositorService.isNiri" in visible_raw:
|
||||
condition_key = "isNiri"
|
||||
elif "CompositorService.isHyprland" in visible_raw:
|
||||
|
||||
@@ -251,24 +251,18 @@
|
||||
"keywords": [
|
||||
"background",
|
||||
"blur",
|
||||
"every",
|
||||
"frosted",
|
||||
"glass",
|
||||
"interface",
|
||||
"just",
|
||||
"passes",
|
||||
"radius",
|
||||
"reach",
|
||||
"sets",
|
||||
"shell",
|
||||
"size",
|
||||
"strength",
|
||||
"style",
|
||||
"transparency",
|
||||
"window"
|
||||
"transparency"
|
||||
],
|
||||
"page": "theme_surfaces",
|
||||
"description": "Sets the compositor's blur for every window, not just the shell"
|
||||
"page": "theme_surfaces"
|
||||
},
|
||||
{
|
||||
"section": "wallpaperBackgroundColorMode",
|
||||
@@ -1229,7 +1223,7 @@
|
||||
"wallpaper"
|
||||
],
|
||||
"page": "personalization",
|
||||
"conditionKey": "isNiri"
|
||||
"conditionKey": "isHyprlandOrNiri"
|
||||
},
|
||||
{
|
||||
"section": "notificationHistory",
|
||||
@@ -2704,7 +2698,7 @@
|
||||
"xray"
|
||||
],
|
||||
"page": "theme_surfaces",
|
||||
"conditionKey": "isNiri"
|
||||
"conditionKey": "isHyprlandOrNiri"
|
||||
},
|
||||
{
|
||||
"section": "_hub_personalization",
|
||||
@@ -13040,7 +13034,7 @@
|
||||
"toast"
|
||||
],
|
||||
"icon": "notifications",
|
||||
"conditionKey": "isNiri"
|
||||
"conditionKey": "isNiriOrMango"
|
||||
},
|
||||
{
|
||||
"section": "hyprlandLayoutGapsOutOverride",
|
||||
@@ -14319,7 +14313,7 @@
|
||||
"touchpad"
|
||||
],
|
||||
"parentLabel": "Input",
|
||||
"conditionKey": "isNiri"
|
||||
"conditionKey": "isNiriOrMango"
|
||||
},
|
||||
{
|
||||
"section": "touchpadDragLock",
|
||||
@@ -14367,7 +14361,7 @@
|
||||
"touchpad"
|
||||
],
|
||||
"parentLabel": "Input",
|
||||
"conditionKey": "isNiri"
|
||||
"conditionKey": "isNiriOrMango"
|
||||
},
|
||||
{
|
||||
"section": "touchpadMiddleEmulation",
|
||||
@@ -14420,23 +14414,6 @@
|
||||
"description": "Speed, scrolling, tap to click",
|
||||
"conditionKey": "pointerCapable"
|
||||
},
|
||||
{
|
||||
"section": "mangoTrackpadNaturalScrolling",
|
||||
"label": "Natural scrolling",
|
||||
"tabIndex": 44,
|
||||
"category": "Mouse & touchpad",
|
||||
"keywords": [
|
||||
"input",
|
||||
"invert",
|
||||
"mouse",
|
||||
"natural",
|
||||
"scrolling",
|
||||
"touchpad",
|
||||
"trackpad"
|
||||
],
|
||||
"parentLabel": "Input",
|
||||
"conditionKey": "pointerCapable"
|
||||
},
|
||||
{
|
||||
"section": "mouseNaturalScroll",
|
||||
"label": "Natural scrolling",
|
||||
@@ -14597,24 +14574,6 @@
|
||||
"parentLabel": "Input",
|
||||
"conditionKey": "pointerCapable"
|
||||
},
|
||||
{
|
||||
"section": "mangoTouchpadSettings",
|
||||
"label": "Touchpad",
|
||||
"tabIndex": 44,
|
||||
"category": "Mouse & touchpad",
|
||||
"keywords": [
|
||||
"input",
|
||||
"invert",
|
||||
"mouse",
|
||||
"natural",
|
||||
"scrolling",
|
||||
"touchpad",
|
||||
"trackpad"
|
||||
],
|
||||
"parentLabel": "Input",
|
||||
"icon": "trackpad_input_2",
|
||||
"conditionKey": "isMango"
|
||||
},
|
||||
{
|
||||
"section": "touchpadSettings",
|
||||
"label": "Touchpad",
|
||||
|
||||
Reference in new issue
Block a user