Krolyxon
Commit b32fedf

Branches

Refs →

aurorab32fedf

Hyprland: uses aurora's powermenu instead of sysact

Author
krolyxon
Date
2026-10-19 21:39 +0530
Parent
667975a
Commit
b32fedfb9d67dc4e3c9f421a71687ad59dbac508

+237 −24

5 files changed

  1. M .config/hypr/source/keybinds.lua +1 −1
  2. A .config/quickshell/aurora/PowerMenu.qml +228 −0
  3. M .config/quickshell/aurora/Theme.qml +1 −0
  4. M .config/quickshell/aurora/shell.qml +7 −0
  5. D .local/bin/sysact +0 −23
M .config/hypr/source/keybinds.lua View
@@ -37,7 +37,7 @@ hl.bind(mainMod .. " + R", hl.dsp.exec_cmd(menu))
3737 -- bind = $mainMod SHIFT, P, pseudo, # dwindle
3838 hl.bind(mainMod .. " + Tab", hl.dsp.window.cycle_next({ next = false }), { repeating = true })
3939 hl.bind(mainMod .. " + grave", hl.dsp.exec_cmd("fuzzelunicode"))
40−hl.bind(mainMod .. " + escape", hl.dsp.exec_cmd("sysact"))
40+hl.bind(mainMod .. " + escape", hl.dsp.exec_cmd("qs -c aurora ipc call shell power"))
4141
4242 -- Applications
4343 hl.bind(mainMod .. " + W", hl.dsp.exec_cmd(browser))
A .config/quickshell/aurora/PowerMenu.qml View
@@ -0,0 +1,228 @@
1+import QtQuick
2+import QtQuick.Layouts
3+import Quickshell
4+import Quickshell.Wayland
5+import Quickshell.Hyprland
6+
7+// Session / power actions (was the fuzzel `sysact` script). Type to fuzzy filter, Up/Down/Tab pick,
8+// Enter runs, Esc closes. Destructive ones need a second Enter/click ("sure?").
9+PanelWindow {
10+ id: win
11+ property bool opened: false
12+ signal closeRequested()
13+
14+ visible: opened
15+ implicitWidth: 300
16+ // fixed to the full list so the centred window doesn't jump while filtering
17+ implicitHeight: 24 + 36 + actions.length * 32 + 22
18+ exclusiveZone: 0
19+ color: "transparent"
20+ WlrLayershell.layer: WlrLayer.Overlay
21+ WlrLayershell.namespace: "aurora-power"
22+ WlrLayershell.keyboardFocus: opened ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
23+
24+ HyprlandFocusGrab {
25+ windows: [win]
26+ active: win.opened
27+ onCleared: win.closeRequested()
28+ }
29+
30+ readonly property var actions: [
31+ { id: "lock", icon: Theme.iLock, label: "lock", confirm: false },
32+ { id: "leave", icon: Theme.iLogout, label: "leave Hyprland", confirm: true },
33+ { id: "renew", icon: Theme.iRefresh, label: "renew Hyprland", confirm: false },
34+ { id: "hibernate", icon: Theme.iHibernate, label: "hibernate", confirm: true },
35+ { id: "reboot", icon: Theme.iReboot, label: "reboot", confirm: true },
36+ { id: "shutdown", icon: Theme.iPower, label: "shutdown", confirm: true },
37+ { id: "sleep", icon: Theme.iSleep, label: "sleep", confirm: false },
38+ { id: "display", icon: Theme.iMonitor, label: "display off", confirm: false }
39+ ]
40+
41+ property int current: 0
42+ property string armed: ""
43+ Timer { id: disarm; interval: 3000; onTriggered: win.armed = "" }
44+
45+ onOpenedChanged: {
46+ if (!opened) return
47+ filter.text = ""
48+ current = 0
49+ armed = ""
50+ filter.forceActiveFocus()
51+ }
52+
53+ // fuzzel-style: query chars must appear in order; prefix > word start > contiguous > scattered
54+ function score(label, q) {
55+ if (q === "") return 1
56+ var s = label.toLowerCase()
57+ if (s.indexOf(q) === 0) return 1000
58+ var w = s.indexOf(" " + q)
59+ if (w >= 0) return 500
60+ if (s.indexOf(q) >= 0) return 300
61+ var pos = 0, gaps = 0
62+ for (var i = 0; i < q.length; i++) {
63+ var f = s.indexOf(q[i], pos)
64+ if (f < 0) return 0
65+ gaps += f - pos
66+ pos = f + 1
67+ }
68+ return 100 - gaps
69+ }
70+ readonly property var shown: {
71+ var q = filter.text.toLowerCase().trim(), out = []
72+ for (var i = 0; i < actions.length; i++) {
73+ var sc = score(actions[i].label, q)
74+ if (sc > 0) out.push({ a: actions[i], sc: sc, i: i })
75+ }
76+ out.sort(function (x, y) { return y.sc - x.sc || x.i - y.i })
77+ var res = []
78+ for (var j = 0; j < out.length; j++) res.push(out[j].a)
79+ return res
80+ }
81+
82+ function trigger(i) {
83+ var a = shown[i]
84+ if (!a) return
85+ current = i
86+ if (a.confirm && armed !== a.id) { armed = a.id; disarm.restart(); return }
87+ armed = ""
88+ closeRequested()
89+ run(a.id)
90+ }
91+ function run(id) {
92+ switch (id) {
93+ case "lock": Quickshell.execDetached(["loginctl", "lock-session"]); break
94+ case "leave": Hypr.logout(); break
95+ case "renew": Quickshell.execDetached(["hyprctl", "reload"]); break
96+ case "hibernate": Quickshell.execDetached(["systemctl", "hibernate", "-i"]); break
97+ case "reboot": Quickshell.execDetached(["systemctl", "reboot", "-i"]); break
98+ case "shutdown": Quickshell.execDetached(["systemctl", "poweroff", "-i"]); break
99+ case "sleep": Quickshell.execDetached(["systemctl", "suspend", "-i"]); break
100+ case "display": dpmsDelay.restart(); break // let the Enter key-up pass first, it would wake the screen
101+ }
102+ }
103+ Timer {
104+ id: dpmsDelay
105+ interval: 400
106+ onTriggered: Quickshell.execDetached(["hyprctl", "dispatch", 'hl.dsp.dpms({action="off"})'])
107+ }
108+
109+ Rectangle {
110+ anchors.fill: parent
111+ color: Theme.bgA
112+ border.width: 2
113+ border.color: Theme.accent
114+
115+ ColumnLayout {
116+ id: col
117+ anchors.fill: parent
118+ anchors.margins: 12
119+ spacing: 2
120+
121+ Rectangle {
122+ Layout.fillWidth: true
123+ Layout.bottomMargin: 6
124+ implicitHeight: 30
125+ color: Theme.surface
126+ border.width: 1
127+ border.color: filter.activeFocus ? Theme.accent : Theme.line
128+ Text {
129+ anchors.left: parent.left
130+ anchors.leftMargin: 10
131+ anchors.verticalCenter: parent.verticalCenter
132+ text: Theme.iPower
133+ color: Theme.dim
134+ font.family: Theme.font
135+ font.pixelSize: Theme.fontSize
136+ }
137+ TextInput {
138+ id: filter
139+ anchors.fill: parent
140+ anchors.leftMargin: 32
141+ anchors.rightMargin: 10
142+ verticalAlignment: TextInput.AlignVCenter
143+ color: Theme.fg
144+ selectionColor: Theme.accent
145+ selectedTextColor: Theme.bg
146+ font.family: Theme.font
147+ font.pixelSize: Theme.fontSize
148+ clip: true
149+ onTextChanged: { win.current = 0; win.armed = "" }
150+ Keys.onEscapePressed: win.closeRequested()
151+ Keys.onReturnPressed: win.trigger(win.current)
152+ Keys.onEnterPressed: win.trigger(win.current)
153+ Keys.onPressed: event => {
154+ var n = win.shown.length
155+ if (!n) return
156+ if (event.key === Qt.Key_Down || event.key === Qt.Key_Tab) {
157+ win.current = (win.current + 1) % n; win.armed = ""; event.accepted = true
158+ } else if (event.key === Qt.Key_Up || event.key === Qt.Key_Backtab) {
159+ win.current = (win.current + n - 1) % n; win.armed = ""; event.accepted = true
160+ }
161+ }
162+ }
163+ }
164+
165+ Repeater {
166+ model: win.shown
167+ delegate: Rectangle {
168+ id: row
169+ required property var modelData
170+ required property int index
171+ readonly property bool selected: win.current === index
172+ readonly property bool isArmed: win.armed === modelData.id
173+ Layout.fillWidth: true
174+ implicitHeight: 30
175+ color: isArmed ? Theme.err : (selected ? Theme.surfaceHi : (rm.containsMouse ? Theme.surface : "transparent"))
176+ border.width: selected ? 1 : 0
177+ border.color: isArmed ? Theme.err : Theme.accent
178+
179+ Rectangle { // selection marker, like ClipWindow
180+ visible: row.selected
181+ width: 2
182+ height: parent.height
183+ color: row.isArmed ? Theme.bg : Theme.accent
184+ }
185+ Text {
186+ id: ico
187+ anchors.left: parent.left
188+ anchors.leftMargin: 12
189+ anchors.verticalCenter: parent.verticalCenter
190+ width: 18
191+ text: row.modelData.icon
192+ color: row.isArmed ? Theme.bg : Theme.accent
193+ font.family: Theme.font
194+ font.pixelSize: Theme.fontSize + 2
195+ }
196+ Text {
197+ anchors.left: ico.right
198+ anchors.leftMargin: 10
199+ anchors.verticalCenter: parent.verticalCenter
200+ text: row.isArmed ? row.modelData.label + " sure?" : row.modelData.label
201+ color: row.isArmed ? Theme.bg : Theme.fg
202+ font.family: Theme.font
203+ font.pixelSize: Theme.fontSize
204+ }
205+ MouseArea {
206+ id: rm
207+ anchors.fill: parent
208+ hoverEnabled: true
209+ cursorShape: Qt.PointingHandCursor
210+ onEntered: if (win.current !== row.index) { win.current = row.index; win.armed = "" }
211+ onClicked: win.trigger(row.index)
212+ }
213+ }
214+ }
215+
216+ Item { Layout.fillHeight: true }
217+
218+ Text {
219+ Layout.fillWidth: true
220+ Layout.topMargin: 6
221+ text: win.shown.length ? "type filter enter run esc close" : "no match"
222+ color: win.shown.length ? Theme.dim : Theme.err
223+ font.family: Theme.font
224+ font.pixelSize: Theme.fontSize - 2
225+ }
226+ }
227+ }
228+}
M .config/quickshell/aurora/Theme.qml View
@@ -116,6 +116,7 @@ Singleton {
116116 readonly property string iSleep: cp(0xF04B2)
117117 readonly property string iReboot: cp(0xF0709)
118118 readonly property string iRefresh: cp(0xF0450)
119+ readonly property string iHibernate: cp(0xF0717)
119120 readonly property string iHeadphones: cp(0xF02CB)
120121 readonly property string iSpeaker: cp(0xF04C3)
121122 readonly property string iMusic: cp(0xF075A)
M .config/quickshell/aurora/shell.qml View
@@ -123,6 +123,12 @@ ShellRoot {
123123 onCloseRequested: root.closeOverlay()
124124 }
125125
126+ PowerMenu {
127+ screen: root.overlayScreen
128+ opened: root.overlay === "power"
129+ onCloseRequested: root.closeOverlay()
130+ }
131+
126132 NotifPopups { screen: root.focusedScr; suppressed: root.opened }
127133
128134 Osd { id: osd; screen: root.focusedScr; suppressed: root.opened }
@@ -151,6 +157,7 @@ ShellRoot {
151157 function cal(): void { root.toggleOverlay("cal") }
152158 function clip(): void { root.toggleOverlay("clip") }
153159 function wall(): void { root.toggleOverlay("wall") }
160+ function power(): void { root.toggleOverlay("power") }
154161 function wallrandom(): void { Wall.random() }
155162 function wallset(path: string): void { Wall.apply(path) } // absolute path (setwall resolves it)
156163 function rec(mode: string): void { root.close(); Rec.toggle(mode === "region" ? "region" : "screen") } // screen (default) | region
D .local/bin/sysact View
@@ -1,23 +0,0 @@
1−#!/bin/sh
2−
3−export WM="Hyprland"
4−case "$(readlink -f /sbin/init)" in
5− *systemd*) ctl='systemctl' ;;
6− *) ctl='loginctl' ;;
7−esac
8−
9−wmpid(){ # Get Hyprland process PID
10− pgrep -o hyprland
11−}
12−
13−case "$(printf " lock\n󰠚 leave $WM\n renew $WM\n󱣻 hibernate\n reboot\n shutdown\n sleep\n display off" | fuzzel -d -i -l 15 -p 'Action: ')" in
14− ' lock') hyprlock ;;
15− "󰠚 leave $WM") kill -TERM "$(wmpid)" ;;
16− " renew $WM") hyprctl reload ;;
17− '󱣻 hibernate') $ctl hibernate -i ;;
18− ' sleep') $ctl suspend -i ;;
19− ' reboot') $ctl reboot -i ;;
20− ' shutdown') $ctl poweroff -i ;;
21− ' display off') hyprctl dispatch dpms off ;;
22− *) exit 1 ;;
23−esac