quickshell: restore ii-stock config keys the fork dropped
Config.qml regained sidebar.width/widthExtended (their absence collapsed the right sidebar PanelWindow to 1px — 'unclickable' on the laptop), plus lock.dpmsTimeout, lock.unlockHook, and autoIdleInhibit with its Idle.qml property. Audio.qml's absent capture source is now a zero-volume stub so stock consumers stop dereferencing null, and the pactl status parse runs under LC_ALL=C. volumeMixer dialog content comes along for the sidebar. Findings logged in SouveraineOS/docs/DUMP-power-idle-lock-2026-07-15.md.
This commit is contained in:
parent
0f50a3e9c5
commit
72b01457d2
4 changed files with 119 additions and 4 deletions
|
|
@ -445,6 +445,8 @@ Singleton {
|
||||||
property JsonObject lock: JsonObject {
|
property JsonObject lock: JsonObject {
|
||||||
property bool useHyprlock: false
|
property bool useHyprlock: false
|
||||||
property bool launchOnStartup: false
|
property bool launchOnStartup: false
|
||||||
|
property int dpmsTimeout: 3
|
||||||
|
property string unlockHook: ""
|
||||||
// Souveraine: PIN keypad surface for touch panels (phone).
|
// Souveraine: PIN keypad surface for touch panels (phone).
|
||||||
// The OSK can't rise above a session lock, so the lock
|
// The OSK can't rise above a session lock, so the lock
|
||||||
// surface must carry its own input.
|
// surface must carry its own input.
|
||||||
|
|
@ -573,6 +575,8 @@ Singleton {
|
||||||
property int interval: 4
|
property int interval: 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property bool autoIdleInhibit: false
|
||||||
|
|
||||||
property JsonObject search: JsonObject {
|
property JsonObject search: JsonObject {
|
||||||
property int nonAppResultDelay: 30 // This prevents lagging when typing
|
property int nonAppResultDelay: 30 // This prevents lagging when typing
|
||||||
property string engineBaseUrl: "https://www.google.com/search?q="
|
property string engineBaseUrl: "https://www.google.com/search?q="
|
||||||
|
|
@ -605,6 +609,8 @@ Singleton {
|
||||||
|
|
||||||
property JsonObject sidebar: JsonObject {
|
property JsonObject sidebar: JsonObject {
|
||||||
property bool keepRightSidebarLoaded: true
|
property bool keepRightSidebarLoaded: true
|
||||||
|
property int width: 460 // Collapsed sidebar width in px
|
||||||
|
property int widthExtended: 750 // Extended (Ctrl+O) sidebar width in px
|
||||||
property JsonObject translator: JsonObject {
|
property JsonObject translator: JsonObject {
|
||||||
property bool enable: false
|
property bool enable: false
|
||||||
property int delay: 300 // Delay before sending request. Reduces (potential) rate limits and lag.
|
property int delay: 300 // Delay before sending request. Reduces (potential) rate limits and lag.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
// Native PulseAudio has one verified board endpoint today: the UCM Speaker
|
||||||
|
// route published as alsa_output.hw_0_0. ii's original dialog only renders
|
||||||
|
// PipeWire nodes, so it presented an empty application list as though no
|
||||||
|
// device existed. Keep the device state explicit until capture and alternate
|
||||||
|
// routes are real PulseAudio endpoints.
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
required property bool isSink
|
||||||
|
readonly property bool outputAvailable: Audio.ready && Audio.sink?.name.length > 0
|
||||||
|
readonly property bool inputAvailable: Audio.source?.audio !== undefined && Audio.source?.audio !== null
|
||||||
|
spacing: 16
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: deviceRow.implicitHeight + 24
|
||||||
|
radius: Appearance.rounding.small
|
||||||
|
color: Appearance.colors.colLayer2
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: deviceRow
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 12
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
|
MaterialSymbol {
|
||||||
|
text: root.isSink ? "speaker" : "mic"
|
||||||
|
iconSize: Appearance.font.pixelSize.hugeass
|
||||||
|
color: Appearance.colors.colOnLayer2
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.pixelSize: Appearance.font.pixelSize.normal
|
||||||
|
color: Appearance.colors.colOnLayer2
|
||||||
|
text: root.isSink
|
||||||
|
? (root.outputAvailable ? Audio.friendlyDeviceName(Audio.sink) : Translation.tr("Connecting to PulseAudio…"))
|
||||||
|
: (root.inputAvailable ? Audio.friendlyDeviceName(Audio.source) : Translation.tr("No microphone source yet"))
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||||
|
color: Appearance.m3colors.m3outline
|
||||||
|
text: root.isSink
|
||||||
|
? Translation.tr("Default output • Pixel 3 internal stereo speakers")
|
||||||
|
: Translation.tr("The handset DMIC route is still under capture validation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledSlider {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
visible: root.isSink && root.outputAvailable
|
||||||
|
value: Audio.sink?.audio?.volume ?? 0
|
||||||
|
onMoved: Audio.sink.audio.volume = value
|
||||||
|
configuration: StyledSlider.Configuration.M
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
visible: root.isSink && root.outputAvailable
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
color: Appearance.colors.colSubtext
|
||||||
|
text: Audio.sink?.audio?.muted
|
||||||
|
? Translation.tr("Muted")
|
||||||
|
: `${Math.round((Audio.sink?.audio?.volume ?? 0) * 100)}%`
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { Layout.fillHeight: true }
|
||||||
|
}
|
||||||
|
|
@ -48,8 +48,25 @@ Singleton {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A capture source is intentionally absent until the ADSP capture route
|
// A capture source is intentionally absent until the ADSP capture route
|
||||||
// produces real samples. The ii controls already handle this as optional.
|
// produces real samples. Stock ii consumers (e.g. QuickSliders.qml) do
|
||||||
property var source: null
|
// `Audio.source.audio.volume` with no null-guard — safe against ii's own
|
||||||
|
// PipeWire Audio, which always populates source as an object. Give this
|
||||||
|
// a real stub (mirroring sink's shape) instead of null so those
|
||||||
|
// consumers work unmodified; mic volume just reads 0 and setting it is
|
||||||
|
// a no-op until real capture exists.
|
||||||
|
property QtObject source: QtObject {
|
||||||
|
id: sourceNode
|
||||||
|
property string id: ""
|
||||||
|
property string name: ""
|
||||||
|
property string description: Translation.tr("Microphone")
|
||||||
|
property string nickname: description
|
||||||
|
property bool isSink: false
|
||||||
|
property var properties: ({})
|
||||||
|
property QtObject audio: QtObject {
|
||||||
|
property real volume: 0
|
||||||
|
property bool muted: false
|
||||||
|
}
|
||||||
|
}
|
||||||
readonly property list<var> outputDevices: sink.name.length > 0 ? [sink] : []
|
readonly property list<var> outputDevices: sink.name.length > 0 ? [sink] : []
|
||||||
readonly property list<var> inputDevices: []
|
readonly property list<var> inputDevices: []
|
||||||
readonly property list<var> outputAppNodes: []
|
readonly property list<var> outputAppNodes: []
|
||||||
|
|
@ -124,12 +141,13 @@ Singleton {
|
||||||
Process {
|
Process {
|
||||||
id: statusProcess
|
id: statusProcess
|
||||||
command: ["sh", "-c", "printf 'name='; pactl get-default-sink; printf 'volume='; pactl get-sink-volume @DEFAULT_SINK@; printf 'mute='; pactl get-sink-mute @DEFAULT_SINK@"]
|
command: ["sh", "-c", "printf 'name='; pactl get-default-sink; printf 'volume='; pactl get-sink-volume @DEFAULT_SINK@; printf 'mute='; pactl get-sink-mute @DEFAULT_SINK@"]
|
||||||
|
environment: ({ LANG: "C", LC_ALL: "C" })
|
||||||
stdout: StdioCollector {
|
stdout: StdioCollector {
|
||||||
onStreamFinished: {
|
onStreamFinished: {
|
||||||
const text = this.text
|
const text = this.text
|
||||||
const name = /^name=(.+)$/m.exec(text)?.[1]?.trim()
|
const name = /^name=(.+)$/m.exec(text)?.[1]?.trim()
|
||||||
const volume = /^volume=.*?(\d+)%/m.exec(text)?.[1]
|
const volume = /^volume=.*?(\d+)%/m.exec(text)?.[1]
|
||||||
const muted = /^mute=(yes|no)$/m.exec(text)?.[1]
|
const muted = /^mute=Mute:\s*(yes|no)$/m.exec(text)?.[1]
|
||||||
if (!name || volume === undefined || muted === undefined) {
|
if (!name || volume === undefined || muted === undefined) {
|
||||||
root.ready = false
|
root.ready = false
|
||||||
return
|
return
|
||||||
|
|
@ -137,7 +155,7 @@ Singleton {
|
||||||
|
|
||||||
root.syncing = true
|
root.syncing = true
|
||||||
sinkNode.name = name
|
sinkNode.name = name
|
||||||
sinkNode.description = name === "alsa_output.hw_0_0"
|
sinkNode.description = name.includes("hw_0_0")
|
||||||
? Translation.tr("Internal speakers") : name
|
? Translation.tr("Internal speakers") : name
|
||||||
sinkNode.audio.volume = Math.max(0, Math.min(root.hardMaxValue, Number(volume) / 100))
|
sinkNode.audio.volume = Math.max(0, Math.min(root.hardMaxValue, Number(volume) / 100))
|
||||||
sinkNode.audio.muted = muted === "yes"
|
sinkNode.audio.muted = muted === "yes"
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,14 @@ Singleton {
|
||||||
property alias inhibit: idleInhibitor.enabled
|
property alias inhibit: idleInhibitor.enabled
|
||||||
inhibit: false
|
inhibit: false
|
||||||
|
|
||||||
|
// ii-stock consumers (IdleInhibitorToggle.qml) read Idle.autoIdleInhibit to
|
||||||
|
// label the "Keep awake" toggle. Souveraine's idle path routes through
|
||||||
|
// hypridle, not a Wayland idle-inhibitor, so there's no separate "auto"
|
||||||
|
// mode — but the stock toggle expects the property to exist. Expose it as
|
||||||
|
// a plain bool so the toggle binds cleanly instead of throwing
|
||||||
|
// [undefined] -> bool at startup.
|
||||||
|
property bool autoIdleInhibit: false
|
||||||
|
|
||||||
// Pixel 3: the Wayland idle-inhibitor on an invisible 0x0 surface is
|
// Pixel 3: the Wayland idle-inhibitor on an invisible 0x0 surface is
|
||||||
// not honored by our compositor build, and screen-off is driven by
|
// not honored by our compositor build, and screen-off is driven by
|
||||||
// hypridle scripts anyway — so Keep System Awake stops/starts hypridle.
|
// hypridle scripts anyway — so Keep System Awake stops/starts hypridle.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue