hold power for a device menu; usb-c state shown, not faked
This commit is contained in:
parent
bf8bc013ef
commit
f7eba9f658
6 changed files with 248 additions and 1 deletions
187
surfaces/quickshell/modules/souveraine/windowSheet/PowerMenu.qml
Normal file
187
surfaces/quickshell/modules/souveraine/windowSheet/PowerMenu.qml
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
// The device's own verbs, on a held power button.
|
||||
//
|
||||
// The counterpart to WindowSheet: that sheet is about a window, this is about
|
||||
// the machine. Casey, 2026-08-05 — "when we hold the power button ... we'd be
|
||||
// given another context overlay", and "that whole power usb state probably
|
||||
// belongs in the power switch options".
|
||||
//
|
||||
// ## Why the USB state lives here
|
||||
//
|
||||
// The USB-C port is not a preference, it is what the device currently *is*:
|
||||
// host or device, sourcing power or sinking it, and what is on the other end.
|
||||
// It changes the meaning of every peripheral attached, which makes it
|
||||
// proprioception and puts it in sessiond beside panel, lock and idle. This
|
||||
// surface is the view over that, not a second owner of it.
|
||||
//
|
||||
// ## What is honest here today
|
||||
//
|
||||
// The data role is real and readable — `/sys/class/usb_role/…/role` reports
|
||||
// `device`, and `pmi8998-charger` reports `online=1`, so "attached, drawing
|
||||
// power" is a measured fact rather than a guess. **Switching** the role is not
|
||||
// offered yet: the sysfs node is root-owned, so the flip has to be a sessiond
|
||||
// verb, and a button that cannot do the thing it names is the exact failure
|
||||
// this project keeps writing down. It is shown as state, and it says so.
|
||||
//
|
||||
// There is no `/sys/class/typec/` on this device — the tcpm driver is not
|
||||
// bound — so the *power* role genuinely is not knowable here yet. The row
|
||||
// reports what the charger says and claims nothing more.
|
||||
//
|
||||
// ## Why it is not gated on the lock
|
||||
//
|
||||
// Powering off is something you do to a locked phone. A menu that disappeared
|
||||
// when locked would send the user back to the hardware button they are already
|
||||
// holding. It shows device verbs and never session content, so disclosure is
|
||||
// unaffected.
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import "." as SheetLocal
|
||||
|
||||
Scope {
|
||||
id: scope
|
||||
|
||||
property bool menuOpen: false
|
||||
|
||||
// Measured, not assumed. Re-read each time the menu opens rather than
|
||||
// polled: the port's state only matters while someone is looking at it,
|
||||
// and a timer here would be a background reader of a file nobody needs.
|
||||
property string dataRole: "unknown"
|
||||
property bool chargerOnline: false
|
||||
|
||||
function refresh() {
|
||||
roleProc.running = true;
|
||||
chargerProc.running = true;
|
||||
}
|
||||
|
||||
function open() {
|
||||
scope.refresh();
|
||||
scope.menuOpen = true;
|
||||
}
|
||||
|
||||
function dismiss() {
|
||||
scope.menuOpen = false;
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "powerMenu"
|
||||
|
||||
function open(): void {
|
||||
scope.open();
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
scope.dismiss();
|
||||
}
|
||||
|
||||
function state(): string {
|
||||
return scope.dataRole + " charger_online=" + scope.chargerOnline;
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: roleProc
|
||||
command: ["cat", "/sys/class/usb_role/a600000.usb-role-switch/role"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: scope.dataRole = text.trim() || "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: chargerProc
|
||||
command: ["cat", "/sys/class/power_supply/pmi8998-charger/online"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: scope.chargerOnline = text.trim() === "1"
|
||||
}
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
PanelWindow {
|
||||
id: win
|
||||
required property var modelData
|
||||
screen: win.modelData
|
||||
|
||||
anchors { top: true; left: true; right: true; bottom: true }
|
||||
color: "transparent"
|
||||
visible: scope.menuOpen
|
||||
WlrLayershell.namespace: "souveraine:powermenu"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "#99000000"
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: scope.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 30
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: 28
|
||||
|
||||
SheetLocal.SheetButton {
|
||||
icon: "lock"
|
||||
label: "Lock"
|
||||
onTapped: {
|
||||
scope.dismiss();
|
||||
Session.lock();
|
||||
}
|
||||
}
|
||||
|
||||
SheetLocal.SheetButton {
|
||||
icon: "restart_alt"
|
||||
label: "Restart"
|
||||
holdLabel: "hold to restart"
|
||||
holdMs: 1000
|
||||
// Hold, not tap. A restart reached by a single tap on a
|
||||
// menu you opened by holding a button is one slip away
|
||||
// from losing whatever was open.
|
||||
onHeld: {
|
||||
scope.dismiss();
|
||||
Session.reboot();
|
||||
}
|
||||
}
|
||||
|
||||
SheetLocal.SheetButton {
|
||||
icon: "power_settings_new"
|
||||
label: "Power off"
|
||||
holdLabel: "hold to power off"
|
||||
holdMs: 1000
|
||||
onHeld: {
|
||||
scope.dismiss();
|
||||
Session.poweroff();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The port, as measured. Not a control yet — see the header.
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.maximumWidth: win.width * 0.8
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
color: "#c8ffffff"
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
text: "USB-C: " + scope.dataRole
|
||||
+ (scope.chargerOnline ? " · charger attached" : " · no charger")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
PowerMenu 1.0 PowerMenu.qml
|
||||
SheetButton 1.0 SheetButton.qml
|
||||
WindowSheet 1.0 WindowSheet.qml
|
||||
|
|
|
|||
Loading…
Reference in a new issue