Krolyxon
Commit 0355e4e

Branches

Refs →

aurora.configquickshellwal-controlNet.qml

198 lines · 6.3 KB · QML

1pragma Singleton
2import QtQuick
3import Quickshell
4import Quickshell.Io
5
6// Wi-Fi backend: thin wrapper over nmcli (NetworkManager).
7Singleton {
8 id: root
9
10 property bool active: false // NetPage sets this while visible
11 property bool wifiEnabled: true
12 property string wifiDevice: ""
13 property var raw: [] // [{inUse, ssid, signal, security}]
14 property var savedNames: []
15 property string askFor: "" // ssid currently asking for a password
16 property string message: ""
17 property bool busy: false
18 property bool scanning: false
19
20 property string lastSsid: ""
21 property bool lastWasNew: false
22
23 readonly property var networks: build(raw, savedNames)
24 readonly property string connected: {
25 for (var i = 0; i < raw.length; i++) if (raw[i].inUse) return raw[i].ssid
26 return ""
27 }
28
29 onActiveChanged: if (active) { askFor = ""; message = ""; rescan() }
30
31 // nmcli -t output: ':' separates fields, '\:' and '\\' are escapes
32 function parseTerse(line) {
33 var out = [], cur = ""
34 for (var i = 0; i < line.length; i++) {
35 var ch = line[i]
36 if (ch === "\\" && i + 1 < line.length) { cur += line[i + 1]; i++ }
37 else if (ch === ":") { out.push(cur); cur = "" }
38 else cur += ch
39 }
40 out.push(cur)
41 return out
42 }
43
44 function build(list, saved) {
45 var out = []
46 for (var i = 0; i < list.length; i++) {
47 var n = list[i]
48 out.push({
49 ssid: n.ssid, signal: n.signal, security: n.security, inUse: n.inUse,
50 saved: saved.indexOf(n.ssid) >= 0
51 })
52 }
53 out.sort(function (a, b) {
54 if (a.inUse !== b.inUse) return a.inUse ? -1 : 1
55 return b.signal - a.signal
56 })
57 return out
58 }
59
60 function refresh() {
61 radioProc.running = true
62 devProc.running = true
63 savedProc.running = true
64 listProc.running = true
65 }
66
67 function rescan() {
68 scanning = true
69 rescanProc.running = true
70 refresh()
71 }
72
73 // ---- actions ------------------------------------------------------
74 function run(args) {
75 if (busy) return
76 busy = true
77 runProc.command = ["sh", "-c",
78 'out=$(nmcli "$@" 2>&1); rc=$?; printf "%s\\n%d" "$out" "$rc"', "sh"].concat(args)
79 runProc.running = true
80 }
81
82 function finish(text) {
83 busy = false
84 var lines = text.split("\n")
85 var rc = parseInt(lines.pop())
86 var out = lines.join(" ").trim()
87 if (rc !== 0) {
88 message = out
89 // a failed first-time connect leaves a broken profile behind; drop it
90 if (lastWasNew && lastSsid !== "")
91 Quickshell.execDetached(["nmcli", "connection", "delete", "id", lastSsid])
92 } else {
93 message = ""
94 askFor = ""
95 }
96 lastWasNew = false
97 refresh()
98 }
99
100 function connectTo(entry) {
101 if (entry.inUse) return
102 if (entry.saved || entry.security === "" || entry.security === "--") connect(entry.ssid, "")
103 else askFor = (askFor === entry.ssid) ? "" : entry.ssid
104 }
105
106 function connect(ssid, password) {
107 message = ""
108 lastSsid = ssid
109 var isSaved = savedNames.indexOf(ssid) >= 0
110 if (isSaved && !password) {
111 lastWasNew = false
112 run(["connection", "up", "id", ssid])
113 } else {
114 lastWasNew = !isSaved
115 var cmd = ["--wait", "20", "device", "wifi", "connect", ssid]
116 if (password) cmd = cmd.concat(["password", password])
117 run(cmd)
118 }
119 }
120
121 function disconnect() { if (wifiDevice !== "") run(["device", "disconnect", wifiDevice]) }
122 function forget(ssid) { run(["connection", "delete", "id", ssid]) }
123 function setWifi(on) { run(["radio", "wifi", on ? "on" : "off"]) }
124
125 // ---- processes ----------------------------------------------------
126 Timer {
127 interval: 10000
128 running: root.active
129 repeat: true
130 onTriggered: root.refresh()
131 }
132
133 Process {
134 id: runProc
135 stdout: StdioCollector { onStreamFinished: root.finish(text) }
136 }
137
138 Process {
139 id: rescanProc
140 command: ["nmcli", "device", "wifi", "rescan"]
141 onExited: { root.scanning = false; listProc.running = true }
142 }
143
144 Process {
145 id: radioProc
146 command: ["nmcli", "radio", "wifi"]
147 stdout: StdioCollector { onStreamFinished: root.wifiEnabled = text.trim() === "enabled" }
148 }
149
150 Process {
151 id: devProc
152 command: ["nmcli", "-t", "-f", "DEVICE,TYPE,STATE", "device"]
153 stdout: StdioCollector {
154 onStreamFinished: {
155 var lines = text.split("\n")
156 for (var i = 0; i < lines.length; i++) {
157 var f = root.parseTerse(lines[i])
158 if (f.length >= 2 && f[1] === "wifi") { root.wifiDevice = f[0]; return }
159 }
160 }
161 }
162 }
163
164 Process {
165 id: savedProc
166 command: ["nmcli", "-t", "-f", "NAME,TYPE", "connection", "show"]
167 stdout: StdioCollector {
168 onStreamFinished: {
169 var out = [], lines = text.split("\n")
170 for (var i = 0; i < lines.length; i++) {
171 var f = root.parseTerse(lines[i])
172 if (f.length >= 2 && f[1].indexOf("wireless") >= 0) out.push(f[0])
173 }
174 root.savedNames = out
175 }
176 }
177 }
178
179 Process {
180 id: listProc
181 command: ["nmcli", "-t", "-f", "IN-USE,SSID,SIGNAL,SECURITY", "device", "wifi", "list"]
182 stdout: StdioCollector {
183 onStreamFinished: {
184 var best = {}, lines = text.split("\n")
185 for (var i = 0; i < lines.length; i++) {
186 var f = root.parseTerse(lines[i])
187 if (f.length < 4 || f[1] === "") continue
188 var e = { inUse: f[0] === "*", ssid: f[1], signal: parseInt(f[2]) || 0, security: f[3] }
189 var o = best[e.ssid]
190 if (!o || e.inUse || (!o.inUse && e.signal > o.signal)) best[e.ssid] = e
191 }
192 var out = []
193 for (var k in best) out.push(best[k])
194 root.raw = out
195 }
196 }
197 }
198}