Watch
1
0
Fork
You've already forked souveraine
0

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:
Fimeg 2026-07-16 08:06:11 -04:00
commit 72b01457d2
4 changed files with 119 additions and 4 deletions

View file

@ -445,6 +445,8 @@ Singleton {
property JsonObject lock: JsonObject {
property bool useHyprlock: false
property bool launchOnStartup: false
property int dpmsTimeout: 3
property string unlockHook: ""
// Souveraine: PIN keypad surface for touch panels (phone).
// The OSK can't rise above a session lock, so the lock
// surface must carry its own input.
@ -573,6 +575,8 @@ Singleton {
property int interval: 4
}
property bool autoIdleInhibit: false
property JsonObject search: JsonObject {
property int nonAppResultDelay: 30 // This prevents lagging when typing
property string engineBaseUrl: "https://www.google.com/search?q="
@ -605,6 +609,8 @@ Singleton {
property JsonObject sidebar: JsonObject {
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 bool enable: false
property int delay: 300 // Delay before sending request. Reduces (potential) rate limits and lag.

View file

@ -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 }
}

View file

@ -48,8 +48,25 @@ Singleton {
}
// A capture source is intentionally absent until the ADSP capture route
// produces real samples. The ii controls already handle this as optional.
property var source: null
// produces real samples. Stock ii consumers (e.g. QuickSliders.qml) do
// `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> inputDevices: []
readonly property list<var> outputAppNodes: []
@ -124,12 +141,13 @@ Singleton {
Process {
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@"]
environment: ({ LANG: "C", LC_ALL: "C" })
stdout: StdioCollector {
onStreamFinished: {
const text = this.text
const name = /^name=(.+)$/m.exec(text)?.[1]?.trim()
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) {
root.ready = false
return
@ -137,7 +155,7 @@ Singleton {
root.syncing = true
sinkNode.name = name
sinkNode.description = name === "alsa_output.hw_0_0"
sinkNode.description = name.includes("hw_0_0")
? Translation.tr("Internal speakers") : name
sinkNode.audio.volume = Math.max(0, Math.min(root.hardMaxValue, Number(volume) / 100))
sinkNode.audio.muted = muted === "yes"

View file

@ -14,6 +14,14 @@ Singleton {
property alias inhibit: idleInhibitor.enabled
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
// not honored by our compositor build, and screen-off is driven by
// hypridle scripts anyway so Keep System Awake stops/starts hypridle.