mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 21:42:51 -05:00
native NetworkManager + all native dbus bindings via dms
- Scrap janky NetworkService in favor of, dms' native NM integration socket - Scrap all gdbus usage in favor of native dbus bindings in dms (loginctl, freedesktop) It means that - some features won't work if running without dms wrapper. But the trade off is certainly worth it, in the long-run for efficiency improvements.
This commit is contained in:
@@ -6,11 +6,13 @@ import QtCore
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property bool dmsAvailable: false
|
||||
property var capabilities: []
|
||||
property var availablePlugins: []
|
||||
property var installedPlugins: []
|
||||
property bool isConnected: false
|
||||
@@ -18,14 +20,15 @@ Singleton {
|
||||
|
||||
readonly property string socketPath: Quickshell.env("DMS_SOCKET")
|
||||
|
||||
property int nextRequestId: 1
|
||||
property var pendingRequests: ({})
|
||||
property int requestIdCounter: 0
|
||||
|
||||
signal pluginsListReceived(var plugins)
|
||||
signal installedPluginsReceived(var plugins)
|
||||
signal searchResultsReceived(var plugins)
|
||||
signal operationSuccess(string message)
|
||||
signal operationError(string error)
|
||||
signal connectionStateChanged()
|
||||
|
||||
Component.onCompleted: {
|
||||
if (socketPath && socketPath.length > 0) {
|
||||
@@ -60,7 +63,7 @@ Singleton {
|
||||
socket.connected = true
|
||||
}
|
||||
|
||||
Socket {
|
||||
DankSocket {
|
||||
id: socket
|
||||
path: root.socketPath
|
||||
connected: false
|
||||
@@ -69,9 +72,11 @@ Singleton {
|
||||
if (connected) {
|
||||
root.isConnected = true
|
||||
root.isConnecting = false
|
||||
root.connectionStateChanged()
|
||||
} else {
|
||||
root.isConnected = false
|
||||
root.isConnecting = false
|
||||
root.connectionStateChanged()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +88,12 @@ Singleton {
|
||||
|
||||
try {
|
||||
const response = JSON.parse(line)
|
||||
|
||||
if (response.capabilities) {
|
||||
root.capabilities = response.capabilities
|
||||
return
|
||||
}
|
||||
|
||||
handleResponse(response)
|
||||
} catch (e) {
|
||||
console.warn("DMSService: Failed to parse response:", line, e)
|
||||
@@ -101,7 +112,8 @@ Singleton {
|
||||
return
|
||||
}
|
||||
|
||||
const id = nextRequestId++
|
||||
requestIdCounter++
|
||||
const id = Date.now() + requestIdCounter
|
||||
const request = {
|
||||
"id": id,
|
||||
"method": method
|
||||
@@ -115,11 +127,21 @@ Singleton {
|
||||
pendingRequests[id] = callback
|
||||
}
|
||||
|
||||
const json = JSON.stringify(request) + "\n"
|
||||
socket.write(json)
|
||||
socket.send(request)
|
||||
}
|
||||
|
||||
property var networkUpdateCallback: null
|
||||
|
||||
function handleResponse(response) {
|
||||
if (response.id === undefined && response.result) {
|
||||
if (response.result.type === "state_changed" && response.result.data) {
|
||||
if (networkUpdateCallback) {
|
||||
networkUpdateCallback(response.result.data)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const callback = pendingRequests[response.id]
|
||||
|
||||
if (callback) {
|
||||
|
||||
564
Services/NetworkManagerService.qml
Normal file
564
Services/NetworkManagerService.qml
Normal file
@@ -0,0 +1,564 @@
|
||||
pragma Singleton
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property bool networkAvailable: false
|
||||
|
||||
property string networkStatus: "disconnected"
|
||||
property string primaryConnection: ""
|
||||
|
||||
property string ethernetIP: ""
|
||||
property string ethernetInterface: ""
|
||||
property bool ethernetConnected: false
|
||||
property string ethernetConnectionUuid: ""
|
||||
|
||||
property string wifiIP: ""
|
||||
property string wifiInterface: ""
|
||||
property bool wifiConnected: false
|
||||
property bool wifiEnabled: true
|
||||
property string wifiConnectionUuid: ""
|
||||
property string wifiDevicePath: ""
|
||||
property string activeAccessPointPath: ""
|
||||
|
||||
property string currentWifiSSID: ""
|
||||
property int wifiSignalStrength: 0
|
||||
property var wifiNetworks: []
|
||||
property var savedConnections: []
|
||||
property var ssidToConnectionName: ({})
|
||||
property var wifiSignalIcon: {
|
||||
if (!wifiConnected || networkStatus !== "wifi") {
|
||||
return "wifi_off"
|
||||
}
|
||||
if (wifiSignalStrength >= 50) {
|
||||
return "wifi"
|
||||
}
|
||||
if (wifiSignalStrength >= 25) {
|
||||
return "wifi_2_bar"
|
||||
}
|
||||
return "wifi_1_bar"
|
||||
}
|
||||
|
||||
property string userPreference: "auto"
|
||||
property bool isConnecting: false
|
||||
property string connectingSSID: ""
|
||||
property string connectionError: ""
|
||||
|
||||
property bool isScanning: false
|
||||
property bool autoScan: false
|
||||
|
||||
property bool wifiAvailable: true
|
||||
property bool wifiToggling: false
|
||||
property bool changingPreference: false
|
||||
property string targetPreference: ""
|
||||
property var savedWifiNetworks: []
|
||||
property string connectionStatus: ""
|
||||
property string lastConnectionError: ""
|
||||
property bool passwordDialogShouldReopen: false
|
||||
property bool autoRefreshEnabled: false
|
||||
property string wifiPassword: ""
|
||||
property string forgetSSID: ""
|
||||
|
||||
property string networkInfoSSID: ""
|
||||
property string networkInfoDetails: ""
|
||||
property bool networkInfoLoading: false
|
||||
|
||||
property int refCount: 0
|
||||
property bool stateInitialized: false
|
||||
|
||||
signal networksUpdated
|
||||
signal connectionChanged
|
||||
|
||||
property var dmsService: null
|
||||
property bool subscriptionConnected: false
|
||||
|
||||
readonly property string socketPath: Quickshell.env("DMS_SOCKET")
|
||||
|
||||
Component.onCompleted: {
|
||||
root.userPreference = SettingsData.networkPreference
|
||||
Qt.callLater(initializeDMSConnection)
|
||||
}
|
||||
|
||||
DankSocket {
|
||||
id: subscriptionSocket
|
||||
path: root.socketPath
|
||||
connected: networkAvailable
|
||||
|
||||
onConnectionStateChanged: {
|
||||
root.subscriptionConnected = connected
|
||||
if (connected) {
|
||||
console.log("NetworkManagerService: Subscription socket connected")
|
||||
}
|
||||
}
|
||||
|
||||
parser: SplitParser {
|
||||
onRead: line => {
|
||||
if (!line || line.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const response = JSON.parse(line)
|
||||
|
||||
if (response.capabilities) {
|
||||
console.log("NetworkManagerService: Subscription socket received capabilities")
|
||||
Qt.callLater(() => sendSubscribeRequest())
|
||||
return
|
||||
}
|
||||
|
||||
if (response.result && response.result.type === "state_changed" && response.result.data) {
|
||||
const networksCount = response.result.data.wifiNetworks?.length ?? "null"
|
||||
console.log("NetworkManagerService: Subscription update received, networks:", networksCount)
|
||||
updateState(response.result.data)
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("NetworkManagerService: Failed to parse subscription response:", line, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sendSubscribeRequest() {
|
||||
subscriptionSocket.send({
|
||||
"id": 1,
|
||||
"method": "network.subscribe"
|
||||
})
|
||||
console.log("NetworkManagerService: Sent network.subscribe request")
|
||||
}
|
||||
|
||||
function initializeDMSConnection() {
|
||||
try {
|
||||
console.log("NetworkManagerService: Initializing DMS connection...")
|
||||
dmsService = Qt.createQmlObject('import QtQuick; import qs.Services; QtObject { property var service: DMSService }', root)
|
||||
if (dmsService && dmsService.service) {
|
||||
console.log("NetworkManagerService: DMS service reference created")
|
||||
checkCapabilities()
|
||||
dmsService.service.connectionStateChanged.connect(onDMSConnectionStateChanged)
|
||||
dmsService.service.capabilitiesChanged.connect(onDMSCapabilitiesChanged)
|
||||
console.log("NetworkManagerService: Callbacks registered, isConnected:", dmsService.service.isConnected, "capabilities:", JSON.stringify(dmsService.service.capabilities))
|
||||
} else {
|
||||
console.warn("NetworkManagerService: Failed to get DMS service reference")
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("NetworkManagerService: Failed to initialize DMS connection:", e)
|
||||
}
|
||||
}
|
||||
|
||||
function checkCapabilities() {
|
||||
if (dmsService && dmsService.service && dmsService.service.isConnected) {
|
||||
onDMSConnected()
|
||||
}
|
||||
}
|
||||
|
||||
function onDMSConnectionStateChanged() {
|
||||
if (dmsService && dmsService.service && dmsService.service.isConnected) {
|
||||
onDMSConnected()
|
||||
}
|
||||
}
|
||||
|
||||
function onDMSCapabilitiesChanged() {
|
||||
console.log("NetworkManagerService: onDMSCapabilitiesChanged called, capabilities:", dmsService ? JSON.stringify(dmsService.service.capabilities) : "no service")
|
||||
if (dmsService && dmsService.service && dmsService.service.capabilities.includes("network")) {
|
||||
console.log("NetworkManagerService: Network capability detected!")
|
||||
networkAvailable = true
|
||||
if (dmsService.service.isConnected && !stateInitialized) {
|
||||
console.log("NetworkManagerService: DMS is connected, fetching state and starting subscription socket...")
|
||||
stateInitialized = true
|
||||
getState()
|
||||
subscriptionSocket.connected = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onDMSConnected() {
|
||||
console.log("NetworkManagerService: onDMSConnected called")
|
||||
if (dmsService && dmsService.service && dmsService.service.capabilities && dmsService.service.capabilities.length > 0) {
|
||||
console.log("NetworkManagerService: Capabilities:", JSON.stringify(dmsService.service.capabilities))
|
||||
networkAvailable = dmsService.service.capabilities.includes("network")
|
||||
console.log("NetworkManagerService: Network available:", networkAvailable)
|
||||
|
||||
if (networkAvailable && !stateInitialized) {
|
||||
console.log("NetworkManagerService: Requesting network state and starting subscription socket...")
|
||||
stateInitialized = true
|
||||
getState()
|
||||
subscriptionSocket.connected = true
|
||||
}
|
||||
} else {
|
||||
console.log("NetworkManagerService: No capabilities yet or service not ready")
|
||||
}
|
||||
}
|
||||
|
||||
function addRef() {
|
||||
refCount++
|
||||
if (refCount === 1 && networkAvailable) {
|
||||
startAutoScan()
|
||||
}
|
||||
}
|
||||
|
||||
function removeRef() {
|
||||
refCount = Math.max(0, refCount - 1)
|
||||
if (refCount === 0) {
|
||||
stopAutoScan()
|
||||
}
|
||||
}
|
||||
|
||||
property bool initialStateFetched: false
|
||||
|
||||
function getState() {
|
||||
if (!networkAvailable || !dmsService || !dmsService.service) return
|
||||
|
||||
dmsService.service.sendRequest("network.getState", null, response => {
|
||||
if (response.result) {
|
||||
updateState(response.result)
|
||||
if (!initialStateFetched && response.result.wifiEnabled && (!response.result.wifiNetworks || response.result.wifiNetworks.length === 0)) {
|
||||
console.log("NetworkManagerService: Initial state has no networks, triggering scan")
|
||||
initialStateFetched = true
|
||||
Qt.callLater(() => scanWifi())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function updateState(state) {
|
||||
networkStatus = state.networkStatus || "disconnected"
|
||||
primaryConnection = state.primaryConnection || ""
|
||||
|
||||
ethernetIP = state.ethernetIP || ""
|
||||
ethernetInterface = state.ethernetDevice || ""
|
||||
ethernetConnected = state.ethernetConnected || false
|
||||
ethernetConnectionUuid = state.ethernetConnectionUuid || ""
|
||||
|
||||
wifiIP = state.wifiIP || ""
|
||||
wifiInterface = state.wifiDevice || ""
|
||||
wifiConnected = state.wifiConnected || false
|
||||
wifiEnabled = state.wifiEnabled !== undefined ? state.wifiEnabled : true
|
||||
wifiConnectionUuid = state.wifiConnectionUuid || ""
|
||||
wifiDevicePath = state.wifiDevicePath || ""
|
||||
activeAccessPointPath = state.activeAccessPointPath || ""
|
||||
|
||||
currentWifiSSID = state.wifiSSID || ""
|
||||
wifiSignalStrength = state.wifiSignal || 0
|
||||
|
||||
if (state.wifiNetworks) {
|
||||
wifiNetworks = state.wifiNetworks
|
||||
|
||||
const saved = []
|
||||
const mapping = {}
|
||||
for (const network of state.wifiNetworks) {
|
||||
if (network.saved) {
|
||||
saved.push({
|
||||
ssid: network.ssid,
|
||||
saved: true
|
||||
})
|
||||
mapping[network.ssid] = network.ssid
|
||||
}
|
||||
}
|
||||
savedConnections = saved
|
||||
savedWifiNetworks = saved
|
||||
ssidToConnectionName = mapping
|
||||
|
||||
networksUpdated()
|
||||
}
|
||||
|
||||
userPreference = state.preference || "auto"
|
||||
isConnecting = state.isConnecting || false
|
||||
connectingSSID = state.connectingSSID || ""
|
||||
connectionError = state.lastError || ""
|
||||
lastConnectionError = state.lastError || ""
|
||||
|
||||
connectionChanged()
|
||||
}
|
||||
|
||||
function scanWifi() {
|
||||
if (!networkAvailable || isScanning || !wifiEnabled || !dmsService || !dmsService.service) return
|
||||
|
||||
console.log("NetworkManagerService: Starting WiFi scan...")
|
||||
isScanning = true
|
||||
dmsService.service.sendRequest("network.wifi.scan", null, response => {
|
||||
isScanning = false
|
||||
if (response.error) {
|
||||
console.warn("NetworkManagerService: WiFi scan failed:", response.error)
|
||||
} else {
|
||||
console.log("NetworkManagerService: Scan completed, requesting fresh state...")
|
||||
Qt.callLater(() => getState())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function scanWifiNetworks() {
|
||||
scanWifi()
|
||||
}
|
||||
|
||||
function connectToWifi(ssid, password = "", username = "") {
|
||||
if (!networkAvailable || isConnecting || !dmsService || !dmsService.service) return
|
||||
|
||||
isConnecting = true
|
||||
connectingSSID = ssid
|
||||
connectionError = ""
|
||||
connectionStatus = "connecting"
|
||||
|
||||
const params = { ssid: ssid }
|
||||
if (password) params.password = password
|
||||
if (username) params.username = username
|
||||
|
||||
dmsService.service.sendRequest("network.wifi.connect", params, response => {
|
||||
if (response.error) {
|
||||
connectionError = response.error
|
||||
lastConnectionError = response.error
|
||||
connectionStatus = response.error.includes("password") || response.error.includes("authentication")
|
||||
? "invalid_password"
|
||||
: "failed"
|
||||
|
||||
if (connectionStatus === "invalid_password") {
|
||||
passwordDialogShouldReopen = true
|
||||
ToastService.showError(`Invalid password for ${ssid}`)
|
||||
} else {
|
||||
ToastService.showError(`Failed to connect to ${ssid}`)
|
||||
}
|
||||
} else {
|
||||
connectionError = ""
|
||||
connectionStatus = "connected"
|
||||
ToastService.showInfo(`Connected to ${ssid}`)
|
||||
|
||||
if (userPreference === "wifi" || userPreference === "auto") {
|
||||
setConnectionPriority("wifi")
|
||||
}
|
||||
}
|
||||
|
||||
isConnecting = false
|
||||
connectingSSID = ""
|
||||
})
|
||||
}
|
||||
|
||||
function disconnectWifi() {
|
||||
if (!networkAvailable || !wifiInterface || !dmsService || !dmsService.service) return
|
||||
|
||||
dmsService.service.sendRequest("network.wifi.disconnect", null, response => {
|
||||
if (response.error) {
|
||||
ToastService.showError("Failed to disconnect WiFi")
|
||||
} else {
|
||||
ToastService.showInfo("Disconnected from WiFi")
|
||||
currentWifiSSID = ""
|
||||
connectionStatus = ""
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function forgetWifiNetwork(ssid) {
|
||||
if (!networkAvailable || !dmsService || !dmsService.service) return
|
||||
|
||||
forgetSSID = ssid
|
||||
dmsService.service.sendRequest("network.wifi.forget", { ssid: ssid }, response => {
|
||||
if (response.error) {
|
||||
console.warn("Failed to forget network:", response.error)
|
||||
} else {
|
||||
ToastService.showInfo(`Forgot network ${ssid}`)
|
||||
|
||||
savedConnections = savedConnections.filter(s => s.ssid !== ssid)
|
||||
savedWifiNetworks = savedWifiNetworks.filter(s => s.ssid !== ssid)
|
||||
|
||||
const updated = [...wifiNetworks]
|
||||
for (const network of updated) {
|
||||
if (network.ssid === ssid) {
|
||||
network.saved = false
|
||||
if (network.connected) {
|
||||
network.connected = false
|
||||
currentWifiSSID = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
wifiNetworks = updated
|
||||
networksUpdated()
|
||||
}
|
||||
forgetSSID = ""
|
||||
})
|
||||
}
|
||||
|
||||
function toggleWifiRadio() {
|
||||
if (!networkAvailable || wifiToggling || !dmsService || !dmsService.service) return
|
||||
|
||||
wifiToggling = true
|
||||
dmsService.service.sendRequest("network.wifi.toggle", null, response => {
|
||||
wifiToggling = false
|
||||
|
||||
if (response.error) {
|
||||
console.warn("Failed to toggle WiFi:", response.error)
|
||||
} else if (response.result) {
|
||||
wifiEnabled = response.result.enabled
|
||||
ToastService.showInfo(wifiEnabled ? "WiFi enabled" : "WiFi disabled")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function enableWifiDevice() {
|
||||
if (!networkAvailable || !dmsService || !dmsService.service) return
|
||||
|
||||
dmsService.service.sendRequest("network.wifi.enable", null, response => {
|
||||
if (response.error) {
|
||||
ToastService.showError("Failed to enable WiFi")
|
||||
} else {
|
||||
ToastService.showInfo("WiFi enabled")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function setNetworkPreference(preference) {
|
||||
if (!networkAvailable || !dmsService || !dmsService.service) return
|
||||
|
||||
userPreference = preference
|
||||
changingPreference = true
|
||||
targetPreference = preference
|
||||
SettingsData.setNetworkPreference(preference)
|
||||
|
||||
dmsService.service.sendRequest("network.preference.set", { preference: preference }, response => {
|
||||
changingPreference = false
|
||||
targetPreference = ""
|
||||
|
||||
if (response.error) {
|
||||
console.warn("Failed to set network preference:", response.error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function setConnectionPriority(type) {
|
||||
if (type === "wifi") {
|
||||
setNetworkPreference("wifi")
|
||||
} else if (type === "ethernet") {
|
||||
setNetworkPreference("ethernet")
|
||||
}
|
||||
}
|
||||
|
||||
function connectToWifiAndSetPreference(ssid, password, username = "") {
|
||||
connectToWifi(ssid, password, username)
|
||||
setNetworkPreference("wifi")
|
||||
}
|
||||
|
||||
function toggleNetworkConnection(type) {
|
||||
if (!networkAvailable || !dmsService || !dmsService.service) return
|
||||
|
||||
if (type === "ethernet") {
|
||||
if (networkStatus === "ethernet") {
|
||||
dmsService.service.sendRequest("network.ethernet.disconnect", null, null)
|
||||
} else {
|
||||
dmsService.service.sendRequest("network.ethernet.connect", null, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function startAutoScan() {
|
||||
autoScan = true
|
||||
autoRefreshEnabled = true
|
||||
if (networkAvailable && wifiEnabled) {
|
||||
scanWifi()
|
||||
}
|
||||
}
|
||||
|
||||
function stopAutoScan() {
|
||||
autoScan = false
|
||||
autoRefreshEnabled = false
|
||||
}
|
||||
|
||||
function fetchNetworkInfo(ssid) {
|
||||
if (!networkAvailable || !dmsService || !dmsService.service) return
|
||||
|
||||
networkInfoSSID = ssid
|
||||
networkInfoLoading = true
|
||||
networkInfoDetails = "Loading network information..."
|
||||
|
||||
dmsService.service.sendRequest("network.info", { ssid: ssid }, response => {
|
||||
networkInfoLoading = false
|
||||
|
||||
if (response.error) {
|
||||
networkInfoDetails = "Failed to fetch network information"
|
||||
} else if (response.result) {
|
||||
formatNetworkInfo(response.result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function formatNetworkInfo(info) {
|
||||
let details = ""
|
||||
|
||||
if (!info || !info.bands || info.bands.length === 0) {
|
||||
details = "Network information not found or network not available."
|
||||
} else {
|
||||
for (const band of info.bands) {
|
||||
const freqGHz = band.frequency / 1000
|
||||
let bandName = "Unknown"
|
||||
if (band.frequency >= 2400 && band.frequency <= 2500) {
|
||||
bandName = "2.4 GHz"
|
||||
} else if (band.frequency >= 5000 && band.frequency <= 6000) {
|
||||
bandName = "5 GHz"
|
||||
} else if (band.frequency >= 6000) {
|
||||
bandName = "6 GHz"
|
||||
}
|
||||
|
||||
const statusPrefix = band.connected ? "● " : " "
|
||||
const statusSuffix = band.connected ? " (Connected)" : ""
|
||||
|
||||
details += statusPrefix + bandName + statusSuffix + " - " + band.signal + "%\\n"
|
||||
details += " Channel " + band.channel + " (" + freqGHz.toFixed(1) + " GHz) • " + band.rate + " Mbit/s\\n"
|
||||
details += " BSSID: " + band.bssid + "\\n"
|
||||
details += " Mode: " + band.mode + "\\n"
|
||||
details += " Security: " + (band.secured ? "Secured" : "Open") + "\\n"
|
||||
if (band.saved) {
|
||||
details += " Status: Saved network\\n"
|
||||
}
|
||||
details += "\\n"
|
||||
}
|
||||
}
|
||||
|
||||
networkInfoDetails = details
|
||||
}
|
||||
|
||||
function getNetworkInfo(ssid) {
|
||||
const network = wifiNetworks.find(n => n.ssid === ssid)
|
||||
if (!network) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
"ssid": network.ssid,
|
||||
"signal": network.signal,
|
||||
"secured": network.secured,
|
||||
"saved": network.saved,
|
||||
"connected": network.connected,
|
||||
"bssid": network.bssid
|
||||
}
|
||||
}
|
||||
|
||||
function refreshNetworkState() {
|
||||
if (networkAvailable) {
|
||||
getState()
|
||||
}
|
||||
}
|
||||
|
||||
function splitNmcliFields(line) {
|
||||
const parts = []
|
||||
let cur = ""
|
||||
let escape = false
|
||||
for (var i = 0; i < line.length; i++) {
|
||||
const ch = line[i]
|
||||
if (escape) {
|
||||
cur += ch
|
||||
escape = false
|
||||
} else if (ch === '\\') {
|
||||
escape = true
|
||||
} else if (ch === ':') {
|
||||
parts.push(cur)
|
||||
cur = ""
|
||||
} else {
|
||||
cur += ch
|
||||
}
|
||||
}
|
||||
parts.push(cur)
|
||||
return parts
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -36,26 +36,10 @@ Singleton {
|
||||
property bool matugenSuppression: false
|
||||
property bool configGenerationPending: false
|
||||
|
||||
property bool _wantSockets: true
|
||||
property int _reconnectAttempt: 0
|
||||
|
||||
readonly property int _reconnectBaseMs: 400
|
||||
readonly property int _reconnectMaxMs: 15000
|
||||
|
||||
signal windowUrgentChanged()
|
||||
|
||||
Component.onCompleted: fetchOutputs()
|
||||
|
||||
Timer {
|
||||
id: reconnectTimer
|
||||
interval: 0
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
root._wantSockets = false
|
||||
Qt.callLater(() => root._wantSockets = true)
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: suppressToastTimer
|
||||
interval: 3000
|
||||
@@ -150,20 +134,15 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
Socket {
|
||||
DankSocket {
|
||||
id: eventStreamSocket
|
||||
path: root.socketPath
|
||||
connected: CompositorService.isNiri && root._wantSockets
|
||||
connected: CompositorService.isNiri
|
||||
|
||||
onConnectionStateChanged: {
|
||||
if (connected) {
|
||||
_reconnectAttempt = 0
|
||||
write('"EventStream"\n')
|
||||
send('"EventStream"')
|
||||
fetchOutputs()
|
||||
return
|
||||
}
|
||||
if (CompositorService.isNiri) {
|
||||
_scheduleReconnect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,20 +158,10 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
Socket {
|
||||
DankSocket {
|
||||
id: requestSocket
|
||||
path: root.socketPath
|
||||
connected: CompositorService.isNiri && root._wantSockets
|
||||
|
||||
onConnectionStateChanged: {
|
||||
if (connected) {
|
||||
_reconnectAttempt = 0
|
||||
return
|
||||
}
|
||||
if (CompositorService.isNiri) {
|
||||
_scheduleReconnect()
|
||||
}
|
||||
}
|
||||
connected: CompositorService.isNiri
|
||||
}
|
||||
|
||||
function fetchOutputs() {
|
||||
@@ -200,16 +169,6 @@ Singleton {
|
||||
outputsProcess.running = true
|
||||
}
|
||||
|
||||
function _scheduleReconnect() {
|
||||
const pow = Math.min(_reconnectAttempt, 10)
|
||||
const base = Math.min(_reconnectBaseMs * Math.pow(2, pow), _reconnectMaxMs)
|
||||
const jitter = Math.floor(Math.random() * Math.floor(base / 4))
|
||||
reconnectTimer.interval = base + jitter
|
||||
reconnectTimer.restart()
|
||||
_reconnectAttempt++
|
||||
console.warn("NiriService: scheduling reconnect in ~", reconnectTimer.interval, "ms (attempt", _reconnectAttempt, ")")
|
||||
}
|
||||
|
||||
function sortWindowsByLayout(windowList) {
|
||||
return [...windowList].sort((a, b) => {
|
||||
const aWorkspace = workspaces[a.workspace_id]
|
||||
@@ -500,7 +459,7 @@ Singleton {
|
||||
|
||||
function send(request) {
|
||||
if (!CompositorService.isNiri || !requestSocket.connected) return false
|
||||
requestSocket.write(JSON.stringify(request) + "\n")
|
||||
requestSocket.send(request)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -13,12 +13,30 @@ Singleton {
|
||||
property string systemProfileImage: ""
|
||||
property string profileImage: ""
|
||||
property bool settingsPortalAvailable: false
|
||||
property int systemColorScheme: 0 // 0=default, 1=prefer-dark, 2=prefer-light
|
||||
property int systemColorScheme: 0
|
||||
|
||||
property var dmsService: null
|
||||
property bool freedeskAvailable: false
|
||||
|
||||
function init() {}
|
||||
|
||||
function getSystemProfileImage() {
|
||||
systemProfileCheckProcess.running = true
|
||||
if (!freedeskAvailable || !dmsService || !dmsService.service) return
|
||||
|
||||
const username = Quickshell.env("USER")
|
||||
if (!username) return
|
||||
|
||||
dmsService.service.sendRequest("freedesktop.accounts.getUserIconFile", { username: username }, response => {
|
||||
if (response.result && response.result.success) {
|
||||
const iconFile = response.result.value || ""
|
||||
if (iconFile && iconFile !== "" && iconFile !== "/var/lib/AccountsService/icons/") {
|
||||
systemProfileImage = iconFile
|
||||
if (!profileImage || profileImage === "") {
|
||||
profileImage = iconFile
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getUserProfileImage(username) {
|
||||
@@ -30,23 +48,20 @@ Singleton {
|
||||
profileImage = ""
|
||||
return
|
||||
}
|
||||
userProfileCheckProcess.command = [
|
||||
"bash", "-c",
|
||||
`uid=$(id -u ${username} 2>/dev/null) && [ -n "$uid" ] && dbus-send --system --print-reply --dest=org.freedesktop.Accounts /org/freedesktop/Accounts/User$uid org.freedesktop.DBus.Properties.Get string:org.freedesktop.Accounts.User string:IconFile 2>/dev/null | grep -oP 'string "\\K[^"]+' || echo ""`
|
||||
]
|
||||
userProfileCheckProcess.running = true
|
||||
}
|
||||
if (!freedeskAvailable || !dmsService || !dmsService.service) return
|
||||
|
||||
function getGreeterUserProfileImage(username) {
|
||||
if (!username) {
|
||||
profileImage = ""
|
||||
return
|
||||
}
|
||||
userProfileCheckProcess.command = [
|
||||
"bash", "-c",
|
||||
`uid=$(id -u ${username} 2>/dev/null) && [ -n "$uid" ] && dbus-send --system --print-reply --dest=org.freedesktop.Accounts /org/freedesktop/Accounts/User$uid org.freedesktop.DBus.Properties.Get string:org.freedesktop.Accounts.User string:IconFile 2>/dev/null | grep -oP 'string "\\K[^"]+' || echo ""`
|
||||
]
|
||||
userProfileCheckProcess.running = true
|
||||
dmsService.service.sendRequest("freedesktop.accounts.getUserIconFile", { username: username }, response => {
|
||||
if (response.result && response.result.success) {
|
||||
const icon = response.result.value || ""
|
||||
if (icon && icon !== "" && icon !== "/var/lib/AccountsService/icons/") {
|
||||
profileImage = icon
|
||||
} else {
|
||||
profileImage = ""
|
||||
}
|
||||
} else {
|
||||
profileImage = ""
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function setProfileImage(imagePath) {
|
||||
@@ -61,7 +76,23 @@ Singleton {
|
||||
}
|
||||
|
||||
function getSystemColorScheme() {
|
||||
systemColorSchemeCheckProcess.running = true
|
||||
if (!freedeskAvailable || !dmsService || !dmsService.service) return
|
||||
|
||||
dmsService.service.sendRequest("freedesktop.settings.getColorScheme", null, response => {
|
||||
if (response.result) {
|
||||
systemColorScheme = response.result.value || 0
|
||||
|
||||
if (typeof Theme !== "undefined") {
|
||||
const shouldBeLightMode = (systemColorScheme === 2)
|
||||
if (Theme.isLightMode !== shouldBeLightMode) {
|
||||
Theme.isLightMode = shouldBeLightMode
|
||||
if (typeof SessionData !== "undefined") {
|
||||
SessionData.setLightMode(shouldBeLightMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function setLightMode(isLightMode) {
|
||||
@@ -71,91 +102,118 @@ Singleton {
|
||||
}
|
||||
|
||||
function setSystemColorScheme(isLightMode) {
|
||||
if (!settingsPortalAvailable) {
|
||||
return
|
||||
}
|
||||
if (!settingsPortalAvailable) return
|
||||
|
||||
const colorScheme = isLightMode ? "default" : "prefer-dark"
|
||||
const script = `gsettings set org.gnome.desktop.interface color-scheme '${colorScheme}'`
|
||||
|
||||
systemColorSchemeSetProcess.command = ["bash", "-c", script]
|
||||
systemColorSchemeSetProcess.running = true
|
||||
}
|
||||
|
||||
function setSystemProfileImage(imagePath) {
|
||||
if (!accountsServiceAvailable) {
|
||||
return
|
||||
}
|
||||
|
||||
const path = imagePath || ""
|
||||
const script = `dbus-send --system --print-reply --dest=org.freedesktop.Accounts /org/freedesktop/Accounts/User$(id -u) org.freedesktop.Accounts.User.SetIconFile string:'${path}'`
|
||||
|
||||
systemProfileSetProcess.command = ["bash", "-c", script]
|
||||
systemProfileSetProcess.running = true
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
checkAccountsService()
|
||||
checkSettingsPortal()
|
||||
}
|
||||
|
||||
function checkAccountsService() {
|
||||
accountsServiceCheckProcess.running = true
|
||||
}
|
||||
|
||||
function checkSettingsPortal() {
|
||||
settingsPortalCheckProcess.running = true
|
||||
colorSchemeSetProcess.command = ["gsettings", "set", "org.gnome.desktop.interface", "color-scheme", colorScheme]
|
||||
colorSchemeSetProcess.running = true
|
||||
}
|
||||
|
||||
Process {
|
||||
id: accountsServiceCheckProcess
|
||||
command: ["bash", "-c", "dbus-send --system --print-reply --dest=org.freedesktop.Accounts /org/freedesktop/Accounts org.freedesktop.Accounts.FindUserByName string:\"$USER\""]
|
||||
running: false
|
||||
|
||||
onExited: exitCode => {
|
||||
root.accountsServiceAvailable = (exitCode === 0)
|
||||
if (root.accountsServiceAvailable) {
|
||||
root.getSystemProfileImage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: systemProfileCheckProcess
|
||||
command: ["bash", "-c", "dbus-send --system --print-reply --dest=org.freedesktop.Accounts /org/freedesktop/Accounts/User$(id -u) org.freedesktop.DBus.Properties.Get string:org.freedesktop.Accounts.User string:IconFile"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const match = text.match(/string\s+"([^"]+)"/)
|
||||
if (match && match[1] && match[1] !== "" && match[1] !== "/var/lib/AccountsService/icons/") {
|
||||
root.systemProfileImage = match[1]
|
||||
|
||||
if (!root.profileImage || root.profileImage === "") {
|
||||
root.profileImage = root.systemProfileImage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: exitCode => {
|
||||
if (exitCode !== 0) {
|
||||
root.systemProfileImage = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: systemProfileSetProcess
|
||||
id: colorSchemeSetProcess
|
||||
running: false
|
||||
|
||||
onExited: exitCode => {
|
||||
if (exitCode === 0) {
|
||||
root.getSystemProfileImage()
|
||||
Qt.callLater(() => getSystemColorScheme())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setSystemProfileImage(imagePath) {
|
||||
if (!accountsServiceAvailable || !freedeskAvailable || !dmsService || !dmsService.service) return
|
||||
|
||||
dmsService.service.sendRequest("freedesktop.accounts.setIconFile", { path: imagePath || "" }, response => {
|
||||
if (response.error) {
|
||||
console.warn("PortalService: Failed to set icon file:", response.error)
|
||||
} else {
|
||||
Qt.callLater(() => getSystemProfileImage())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Qt.callLater(initializeDMSConnection)
|
||||
}
|
||||
|
||||
function initializeDMSConnection() {
|
||||
try {
|
||||
dmsService = Qt.createQmlObject('import QtQuick; import qs.Services; QtObject { property var service: DMSService }', root)
|
||||
if (dmsService && dmsService.service) {
|
||||
dmsService.service.connectionStateChanged.connect(onDMSConnectionStateChanged)
|
||||
dmsService.service.capabilitiesChanged.connect(onDMSCapabilitiesChanged)
|
||||
if (dmsService.service.isConnected) {
|
||||
onDMSConnected()
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("PortalService: Failed to initialize DMS connection:", e)
|
||||
}
|
||||
}
|
||||
|
||||
function onDMSConnectionStateChanged() {
|
||||
if (dmsService && dmsService.service && dmsService.service.isConnected) {
|
||||
onDMSConnected()
|
||||
}
|
||||
}
|
||||
|
||||
function onDMSCapabilitiesChanged() {
|
||||
if (dmsService && dmsService.service && dmsService.service.capabilities.includes("freedesktop")) {
|
||||
freedeskAvailable = true
|
||||
checkAccountsService()
|
||||
checkSettingsPortal()
|
||||
}
|
||||
}
|
||||
|
||||
function onDMSConnected() {
|
||||
if (dmsService && dmsService.service && dmsService.service.capabilities && dmsService.service.capabilities.length > 0) {
|
||||
freedeskAvailable = dmsService.service.capabilities.includes("freedesktop")
|
||||
if (freedeskAvailable) {
|
||||
checkAccountsService()
|
||||
checkSettingsPortal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkAccountsService() {
|
||||
if (!freedeskAvailable || !dmsService || !dmsService.service) return
|
||||
|
||||
dmsService.service.sendRequest("freedesktop.getState", null, response => {
|
||||
if (response.result && response.result.accounts) {
|
||||
accountsServiceAvailable = response.result.accounts.available || false
|
||||
if (accountsServiceAvailable) {
|
||||
getSystemProfileImage()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function checkSettingsPortal() {
|
||||
if (!freedeskAvailable || !dmsService || !dmsService.service) return
|
||||
|
||||
dmsService.service.sendRequest("freedesktop.getState", null, response => {
|
||||
if (response.result && response.result.settings) {
|
||||
settingsPortalAvailable = response.result.settings.available || false
|
||||
if (settingsPortalAvailable) {
|
||||
getSystemColorScheme()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// For greeter use alternate method to get user profile image - since we dont run with dms
|
||||
function getGreeterUserProfileImage(username) {
|
||||
if (!username) {
|
||||
profileImage = ""
|
||||
return
|
||||
}
|
||||
userProfileCheckProcess.command = [
|
||||
"bash", "-c",
|
||||
`uid=$(id -u ${username} 2>/dev/null) && [ -n "$uid" ] && dbus-send --system --print-reply --dest=org.freedesktop.Accounts /org/freedesktop/Accounts/User$uid org.freedesktop.DBus.Properties.Get string:org.freedesktop.Accounts.User string:IconFile 2>/dev/null | grep -oP 'string "\\K[^"]+' || echo ""`
|
||||
]
|
||||
userProfileCheckProcess.running = true
|
||||
}
|
||||
|
||||
Process {
|
||||
id: userProfileCheckProcess
|
||||
command: []
|
||||
@@ -179,63 +237,6 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: settingsPortalCheckProcess
|
||||
command: ["gdbus", "call", "--session", "--dest", "org.freedesktop.portal.Desktop", "--object-path", "/org/freedesktop/portal/desktop", "--method", "org.freedesktop.portal.Settings.ReadOne", "org.freedesktop.appearance", "color-scheme"]
|
||||
running: false
|
||||
|
||||
onExited: exitCode => {
|
||||
root.settingsPortalAvailable = (exitCode === 0)
|
||||
if (root.settingsPortalAvailable) {
|
||||
root.getSystemColorScheme()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: systemColorSchemeCheckProcess
|
||||
command: ["gdbus", "call", "--session", "--dest", "org.freedesktop.portal.Desktop", "--object-path", "/org/freedesktop/portal/desktop", "--method", "org.freedesktop.portal.Settings.ReadOne", "org.freedesktop.appearance", "color-scheme"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const match = text.match(/uint32 (\d+)/)
|
||||
if (match && match[1]) {
|
||||
root.systemColorScheme = parseInt(match[1])
|
||||
|
||||
if (typeof Theme !== "undefined") {
|
||||
const shouldBeLightMode = (root.systemColorScheme === 2)
|
||||
if (Theme.isLightMode !== shouldBeLightMode) {
|
||||
Theme.isLightMode = shouldBeLightMode
|
||||
if (typeof SessionData !== "undefined") {
|
||||
SessionData.setLightMode(shouldBeLightMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: exitCode => {
|
||||
if (exitCode !== 0) {
|
||||
root.systemColorScheme = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: systemColorSchemeSetProcess
|
||||
running: false
|
||||
|
||||
onExited: exitCode => {
|
||||
if (exitCode === 0) {
|
||||
Qt.callLater(() => {
|
||||
root.getSystemColorScheme()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "profile"
|
||||
|
||||
|
||||
@@ -28,6 +28,30 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
property bool loginctlAvailable: false
|
||||
property string sessionId: ""
|
||||
property string sessionPath: ""
|
||||
property bool locked: false
|
||||
property bool active: false
|
||||
property bool idleHint: false
|
||||
property bool lockedHint: false
|
||||
property bool preparingForSleep: false
|
||||
property string sessionType: ""
|
||||
property string userName: ""
|
||||
property string seat: ""
|
||||
property string display: ""
|
||||
|
||||
signal sessionLocked()
|
||||
signal sessionUnlocked()
|
||||
signal prepareForSleep()
|
||||
signal loginctlStateChanged()
|
||||
|
||||
property var dmsService: null
|
||||
property bool subscriptionConnected: false
|
||||
property bool stateInitialized: false
|
||||
|
||||
readonly property string socketPath: Quickshell.env("DMS_SOCKET")
|
||||
|
||||
Timer {
|
||||
id: sessionInitTimer
|
||||
interval: 200
|
||||
@@ -38,6 +62,7 @@ Singleton {
|
||||
detectHibernateProcess.running = true
|
||||
detectPrimeRunProcess.running = true
|
||||
console.log("SessionService: Native inhibitor available:", nativeInhibitorAvailable)
|
||||
Qt.callLater(initializeDMSConnection)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,4 +268,155 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
DankSocket {
|
||||
id: subscriptionSocket
|
||||
path: root.socketPath
|
||||
connected: loginctlAvailable
|
||||
|
||||
onConnectionStateChanged: {
|
||||
root.subscriptionConnected = connected
|
||||
}
|
||||
|
||||
parser: SplitParser {
|
||||
onRead: line => {
|
||||
if (!line || line.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const response = JSON.parse(line)
|
||||
|
||||
if (response.capabilities) {
|
||||
Qt.callLater(() => sendSubscribeRequest())
|
||||
return
|
||||
}
|
||||
|
||||
if (response.result && response.result.type === "loginctl_event") {
|
||||
handleLoginctlEvent(response.result)
|
||||
} else if (response.result && response.result.type === "state_changed" && response.result.data) {
|
||||
updateLoginctlState(response.result.data)
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("SessionService: Failed to parse subscription response:", line, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sendSubscribeRequest() {
|
||||
subscriptionSocket.send({
|
||||
"id": 2,
|
||||
"method": "loginctl.subscribe"
|
||||
})
|
||||
}
|
||||
|
||||
function initializeDMSConnection() {
|
||||
try {
|
||||
dmsService = Qt.createQmlObject('import QtQuick; import qs.Services; QtObject { property var service: DMSService }', root)
|
||||
if (dmsService && dmsService.service) {
|
||||
checkCapabilities()
|
||||
dmsService.service.connectionStateChanged.connect(onDMSConnectionStateChanged)
|
||||
dmsService.service.capabilitiesChanged.connect(onDMSCapabilitiesChanged)
|
||||
} else {
|
||||
console.warn("SessionService: Failed to get DMS service reference")
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("SessionService: Failed to initialize DMS connection:", e)
|
||||
}
|
||||
}
|
||||
|
||||
function checkCapabilities() {
|
||||
if (dmsService && dmsService.service && dmsService.service.isConnected) {
|
||||
onDMSConnected()
|
||||
}
|
||||
}
|
||||
|
||||
function onDMSConnectionStateChanged() {
|
||||
if (dmsService && dmsService.service && dmsService.service.isConnected) {
|
||||
onDMSConnected()
|
||||
}
|
||||
}
|
||||
|
||||
function onDMSCapabilitiesChanged() {
|
||||
if (dmsService && dmsService.service && dmsService.service.capabilities.includes("loginctl")) {
|
||||
loginctlAvailable = true
|
||||
if (dmsService.service.isConnected && !stateInitialized) {
|
||||
stateInitialized = true
|
||||
getLoginctlState()
|
||||
subscriptionSocket.connected = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onDMSConnected() {
|
||||
if (dmsService && dmsService.service && dmsService.service.capabilities && dmsService.service.capabilities.length > 0) {
|
||||
loginctlAvailable = dmsService.service.capabilities.includes("loginctl")
|
||||
|
||||
if (loginctlAvailable && !stateInitialized) {
|
||||
stateInitialized = true
|
||||
getLoginctlState()
|
||||
subscriptionSocket.connected = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getLoginctlState() {
|
||||
if (!loginctlAvailable || !dmsService || !dmsService.service) return
|
||||
|
||||
dmsService.service.sendRequest("loginctl.getState", null, response => {
|
||||
if (response.result) {
|
||||
updateLoginctlState(response.result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function updateLoginctlState(state) {
|
||||
sessionId = state.sessionId || ""
|
||||
sessionPath = state.sessionPath || ""
|
||||
locked = state.locked || false
|
||||
active = state.active || false
|
||||
idleHint = state.idleHint || false
|
||||
lockedHint = state.lockedHint || false
|
||||
sessionType = state.sessionType || ""
|
||||
userName = state.userName || ""
|
||||
seat = state.seat || ""
|
||||
display = state.display || ""
|
||||
|
||||
const wasPreparing = preparingForSleep
|
||||
preparingForSleep = state.preparingForSleep || false
|
||||
|
||||
if (preparingForSleep && !wasPreparing) {
|
||||
prepareForSleep()
|
||||
}
|
||||
|
||||
loginctlStateChanged()
|
||||
}
|
||||
|
||||
function handleLoginctlEvent(event) {
|
||||
if (event.event === "Lock") {
|
||||
locked = true
|
||||
lockedHint = true
|
||||
sessionLocked()
|
||||
} else if (event.event === "Unlock") {
|
||||
locked = false
|
||||
lockedHint = false
|
||||
sessionUnlocked()
|
||||
} else if (event.event === "PrepareForSleep") {
|
||||
preparingForSleep = event.data?.sleeping || false
|
||||
if (preparingForSleep) {
|
||||
prepareForSleep()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function lockSession() {
|
||||
if (!loginctlAvailable || !dmsService || !dmsService.service) return
|
||||
|
||||
dmsService.service.sendRequest("loginctl.lock", null, response => {
|
||||
if (response.error) {
|
||||
console.warn("SessionService: Failed to lock session:", response.error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,20 @@ import Quickshell.Io
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property int refCount: 0
|
||||
|
||||
onRefCountChanged: {
|
||||
console.log("VpnService: refCount changed to", refCount)
|
||||
if (refCount > 0 && !nmMonitor.running) {
|
||||
console.log("VpnService: Starting nmMonitor")
|
||||
nmMonitor.running = true
|
||||
refreshAll()
|
||||
} else if (refCount === 0 && nmMonitor.running) {
|
||||
console.log("VpnService: Stopping nmMonitor")
|
||||
nmMonitor.running = false
|
||||
}
|
||||
}
|
||||
|
||||
// State
|
||||
property bool available: true
|
||||
property bool isBusy: false
|
||||
@@ -36,18 +50,6 @@ Singleton {
|
||||
|
||||
// Use implicit property notify signals (profilesChanged, activeUuidChanged, etc.)
|
||||
|
||||
Component.onCompleted: initialize()
|
||||
|
||||
Component.onDestruction: {
|
||||
nmMonitor.running = false
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
// Start monitoring NetworkManager for changes
|
||||
nmMonitor.running = true
|
||||
refreshAll()
|
||||
}
|
||||
|
||||
function refreshAll() {
|
||||
listProfiles()
|
||||
refreshActive()
|
||||
|
||||
Reference in New Issue
Block a user