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 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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// 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"
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue