1
0
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:
bbedward
2025-10-08 12:03:50 -04:00
parent 1ed4abd347
commit 27f9b3cd0b
27 changed files with 1739 additions and 1792 deletions

62
Common/DankSocket.qml Normal file
View File

@@ -0,0 +1,62 @@
import QtQuick
import Quickshell.Io
Item {
id: root
property alias path: socket.path
property alias parser: socket.parser
property bool connected: false
property int reconnectBaseMs: 400
property int reconnectMaxMs: 15000
property int _reconnectAttempt: 0
signal connectionStateChanged()
onConnectedChanged: {
socket.connected = connected
}
Socket {
id: socket
onConnectionStateChanged: {
root.connectionStateChanged()
if (connected) {
root._reconnectAttempt = 0
return
}
if (root.connected) {
root._scheduleReconnect()
}
}
}
Timer {
id: reconnectTimer
interval: 0
repeat: false
onTriggered: {
socket.connected = false
Qt.callLater(() => socket.connected = true)
}
}
function send(data) {
const json = typeof data === "string" ? data : JSON.stringify(data)
const message = json.endsWith("\n") ? json : json + "\n"
socket.write(message)
socket.flush()
}
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++
}
}

View File

@@ -17,7 +17,7 @@ DankModal {
networkData = data
networkInfoModalVisible = true
open()
NetworkService.fetchNetworkInfo(ssid)
NetworkManagerService.fetchNetworkInfo(ssid)
}
function hideDialog() {
@@ -101,7 +101,7 @@ DankModal {
id: detailsText
width: parent.width
text: NetworkService.networkInfoDetails && NetworkService.networkInfoDetails.replace(/\\n/g, '\n') || "No information available"
text: NetworkManagerService.networkInfoDetails && NetworkManagerService.networkInfoDetails.replace(/\\n/g, '\n') || "No information available"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
wrapMode: Text.WordWrap

View File

@@ -9,44 +9,64 @@ DankModal {
property string wifiPasswordSSID: ""
property string wifiPasswordInput: ""
property string wifiUsernameInput: ""
property bool requiresEnterprise: false
function show(ssid) {
wifiPasswordSSID = ssid
wifiPasswordInput = ""
wifiUsernameInput = ""
const network = NetworkManagerService.wifiNetworks.find(n => n.ssid === ssid)
requiresEnterprise = network?.enterprise || false
open()
Qt.callLater(() => {
if (contentLoader.item && contentLoader.item.passwordInput)
if (contentLoader.item) {
if (requiresEnterprise && contentLoader.item.usernameInput) {
contentLoader.item.usernameInput.forceActiveFocus()
} else if (contentLoader.item.passwordInput) {
contentLoader.item.passwordInput.forceActiveFocus()
}
}
})
}
shouldBeVisible: false
width: 420
height: 230
height: requiresEnterprise ? 310 : 230
onShouldBeVisibleChanged: () => {
if (!shouldBeVisible)
if (!shouldBeVisible) {
wifiPasswordInput = ""
wifiUsernameInput = ""
}
}
onOpened: {
Qt.callLater(() => {
if (contentLoader.item && contentLoader.item.passwordInput)
if (contentLoader.item) {
if (requiresEnterprise && contentLoader.item.usernameInput) {
contentLoader.item.usernameInput.forceActiveFocus()
} else if (contentLoader.item.passwordInput) {
contentLoader.item.passwordInput.forceActiveFocus()
}
}
})
}
onBackgroundClicked: () => {
close()
wifiPasswordInput = ""
wifiUsernameInput = ""
}
Connections {
target: NetworkService
target: NetworkManagerService
function onPasswordDialogShouldReopenChanged() {
if (NetworkService.passwordDialogShouldReopen && NetworkService.connectingSSID !== "") {
wifiPasswordSSID = NetworkService.connectingSSID
if (NetworkManagerService.passwordDialogShouldReopen && NetworkManagerService.connectingSSID !== "") {
wifiPasswordSSID = NetworkManagerService.connectingSSID
wifiPasswordInput = ""
open()
NetworkService.passwordDialogShouldReopen = false
NetworkManagerService.passwordDialogShouldReopen = false
}
}
}
@@ -55,6 +75,7 @@ DankModal {
FocusScope {
id: wifiContent
property alias usernameInput: usernameInput
property alias passwordInput: passwordInput
anchors.fill: parent
@@ -62,6 +83,7 @@ DankModal {
Keys.onEscapePressed: event => {
close()
wifiPasswordInput = ""
wifiUsernameInput = ""
event.accepted = true
}
@@ -85,7 +107,7 @@ DankModal {
}
StyledText {
text: `Enter password for "${wifiPasswordSSID}"`
text: requiresEnterprise ? `Enter credentials for "${wifiPasswordSSID}"` : `Enter password for "${wifiPasswordSSID}"`
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceTextMedium
width: parent.width
@@ -100,6 +122,44 @@ DankModal {
onClicked: () => {
close()
wifiPasswordInput = ""
wifiUsernameInput = ""
}
}
}
Rectangle {
width: parent.width
height: 50
radius: Theme.cornerRadius
color: Theme.surfaceHover
border.color: usernameInput.activeFocus ? Theme.primary : Theme.outlineStrong
border.width: usernameInput.activeFocus ? 2 : 1
visible: requiresEnterprise
MouseArea {
anchors.fill: parent
onClicked: () => {
usernameInput.forceActiveFocus()
}
}
DankTextField {
id: usernameInput
anchors.fill: parent
font.pixelSize: Theme.fontSizeMedium
textColor: Theme.surfaceText
text: wifiUsernameInput
placeholderText: "Username"
backgroundColor: "transparent"
enabled: root.shouldBeVisible
onTextEdited: () => {
wifiUsernameInput = text
}
onAccepted: () => {
if (passwordInput) {
passwordInput.forceActiveFocus()
}
}
}
}
@@ -127,21 +187,24 @@ DankModal {
textColor: Theme.surfaceText
text: wifiPasswordInput
echoMode: showPasswordCheckbox.checked ? TextInput.Normal : TextInput.Password
placeholderText: ""
placeholderText: requiresEnterprise ? "Password" : ""
backgroundColor: "transparent"
focus: true
focus: !requiresEnterprise
enabled: root.shouldBeVisible
onTextEdited: () => {
wifiPasswordInput = text
}
onAccepted: () => {
NetworkService.connectToWifi(wifiPasswordSSID, passwordInput.text)
const username = requiresEnterprise ? usernameInput.text : ""
NetworkManagerService.connectToWifi(wifiPasswordSSID, passwordInput.text, username)
close()
wifiPasswordInput = ""
wifiUsernameInput = ""
passwordInput.text = ""
if (requiresEnterprise) usernameInput.text = ""
}
Component.onCompleted: () => {
if (root.shouldBeVisible)
if (root.shouldBeVisible && !requiresEnterprise)
focusDelayTimer.start()
}
@@ -151,10 +214,15 @@ DankModal {
interval: 100
repeat: false
onTriggered: () => {
if (root.shouldBeVisible)
if (root.shouldBeVisible) {
if (requiresEnterprise && usernameInput) {
usernameInput.forceActiveFocus()
} else {
passwordInput.forceActiveFocus()
}
}
}
}
Connections {
target: root
@@ -244,6 +312,7 @@ DankModal {
onClicked: () => {
close()
wifiPasswordInput = ""
wifiUsernameInput = ""
}
}
}
@@ -253,7 +322,7 @@ DankModal {
height: 36
radius: Theme.cornerRadius
color: connectArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
enabled: passwordInput.text.length > 0
enabled: requiresEnterprise ? (usernameInput.text.length > 0 && passwordInput.text.length > 0) : passwordInput.text.length > 0
opacity: enabled ? 1 : 0.5
StyledText {
@@ -274,10 +343,13 @@ DankModal {
cursorShape: Qt.PointingHandCursor
enabled: parent.enabled
onClicked: () => {
NetworkService.connectToWifi(wifiPasswordSSID, passwordInput.text)
const username = requiresEnterprise ? usernameInput.text : ""
NetworkManagerService.connectToWifi(wifiPasswordSSID, passwordInput.text, username)
close()
wifiPasswordInput = ""
wifiUsernameInput = ""
passwordInput.text = ""
if (requiresEnterprise) usernameInput.text = ""
}
}

View File

@@ -1,5 +1,6 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
@@ -8,6 +9,10 @@ import qs.Modules.Plugins
PluginComponent {
id: root
Ref {
service: VpnService
}
ccWidgetIcon: VpnService.isBusy ? "sync" : (VpnService.connected ? "vpn_lock" : "vpn_key_off")
ccWidgetPrimaryText: "VPN"
ccWidgetSecondaryText: {

View File

@@ -48,7 +48,10 @@ Item {
const builtinId = root.expandedSection
let builtinInstance = null
if (builtinId === "builtin_vpn" && widgetModel?.vpnBuiltinInstance) {
if (builtinId === "builtin_vpn") {
if (widgetModel?.vpnLoader) {
widgetModel.vpnLoader.active = true
}
builtinInstance = widgetModel.vpnBuiltinInstance
}

View File

@@ -126,7 +126,15 @@ Column {
return builtinPluginWidgetComponent
} else if (id.startsWith("plugin_")) {
return pluginWidgetComponent
} else if (id === "wifi" || id === "bluetooth" || id === "audioOutput" || id === "audioInput") {
} else if (id === "wifi") {
if (!DMSService.dmsAvailable) {
return errorPillComponent
}
if (DMSService.dmsAvailable && !DMSService.capabilities.includes("network")) {
return errorPillComponent
}
return compoundPillComponent
} else if (id === "bluetooth" || id === "audioOutput" || id === "audioInput") {
return compoundPillComponent
} else if (id === "volumeSlider") {
return audioSliderComponent
@@ -175,6 +183,22 @@ Column {
}
}
Component {
id: errorPillComponent
ErrorPill {
property var widgetData: parent.widgetData || {}
width: parent.width
height: 60
primaryMessage: {
if (!DMSService.dmsAvailable) {
return qsTr("DMS_SOCKET not available")
}
return qsTr("NM not supported")
}
secondaryMessage: qsTr("update dms for NM integration.")
}
}
Component {
id: compoundPillComponent
CompoundPill {
@@ -187,13 +211,13 @@ Column {
switch (widgetData.id || "") {
case "wifi":
{
if (NetworkService.wifiToggling)
if (NetworkManagerService.wifiToggling)
return "sync"
if (NetworkService.networkStatus === "ethernet")
if (NetworkManagerService.networkStatus === "ethernet")
return "settings_ethernet"
if (NetworkService.networkStatus === "wifi")
return NetworkService.wifiSignalIcon
if (NetworkService.wifiEnabled)
if (NetworkManagerService.networkStatus === "wifi")
return NetworkManagerService.wifiSignalIcon
if (NetworkManagerService.wifiEnabled)
return "wifi_off"
return "wifi_off"
}
@@ -246,13 +270,13 @@ Column {
switch (widgetData.id || "") {
case "wifi":
{
if (NetworkService.wifiToggling)
return NetworkService.wifiEnabled ? "Disabling WiFi..." : "Enabling WiFi..."
if (NetworkService.networkStatus === "ethernet")
if (NetworkManagerService.wifiToggling)
return NetworkManagerService.wifiEnabled ? "Disabling WiFi..." : "Enabling WiFi..."
if (NetworkManagerService.networkStatus === "ethernet")
return "Ethernet"
if (NetworkService.networkStatus === "wifi" && NetworkService.currentWifiSSID)
return NetworkService.currentWifiSSID
if (NetworkService.wifiEnabled)
if (NetworkManagerService.networkStatus === "wifi" && NetworkManagerService.currentWifiSSID)
return NetworkManagerService.currentWifiSSID
if (NetworkManagerService.wifiEnabled)
return "Not connected"
return "WiFi off"
}
@@ -278,13 +302,13 @@ Column {
switch (widgetData.id || "") {
case "wifi":
{
if (NetworkService.wifiToggling)
if (NetworkManagerService.wifiToggling)
return "Please wait..."
if (NetworkService.networkStatus === "ethernet")
if (NetworkManagerService.networkStatus === "ethernet")
return "Connected"
if (NetworkService.networkStatus === "wifi")
return NetworkService.wifiSignalStrength > 0 ? NetworkService.wifiSignalStrength + "%" : "Connected"
if (NetworkService.wifiEnabled)
if (NetworkManagerService.networkStatus === "wifi")
return NetworkManagerService.wifiSignalStrength > 0 ? NetworkManagerService.wifiSignalStrength + "%" : "Connected"
if (NetworkManagerService.wifiEnabled)
return "Select network"
return ""
}
@@ -332,13 +356,13 @@ Column {
switch (widgetData.id || "") {
case "wifi":
{
if (NetworkService.wifiToggling)
if (NetworkManagerService.wifiToggling)
return false
if (NetworkService.networkStatus === "ethernet")
if (NetworkManagerService.networkStatus === "ethernet")
return true
if (NetworkService.networkStatus === "wifi")
if (NetworkManagerService.networkStatus === "wifi")
return true
return NetworkService.wifiEnabled
return NetworkManagerService.wifiEnabled
}
case "bluetooth":
return !!(BluetoothService.available && BluetoothService.adapter && BluetoothService.adapter.enabled)
@@ -723,12 +747,16 @@ Column {
width: parent.width
height: 60
property var builtinInstance: {
property var builtinInstance: null
Component.onCompleted: {
const id = widgetData.id || ""
if (id === "builtin_vpn") {
return root.model?.vpnBuiltinInstance
if (root.model?.vpnLoader) {
root.model.vpnLoader.active = true
}
builtinInstance = Qt.binding(() => root.model?.vpnBuiltinInstance)
}
return null
}
sourceComponent: {

View File

@@ -73,13 +73,13 @@ DankPopout {
onShouldBeVisibleChanged: {
if (shouldBeVisible) {
Qt.callLater(() => {
NetworkService.autoRefreshEnabled = NetworkService.wifiEnabled
NetworkManagerService.autoRefreshEnabled = NetworkManagerService.wifiEnabled
if (UserInfoService)
UserInfoService.getUptime()
})
} else {
Qt.callLater(() => {
NetworkService.autoRefreshEnabled = false
NetworkManagerService.autoRefreshEnabled = false
if (BluetoothService.adapter && BluetoothService.adapter.discovering)
BluetoothService.adapter.discovering = false
editMode = false

View File

@@ -8,10 +8,10 @@ import qs.Modals
Rectangle {
implicitHeight: {
if (NetworkService.wifiToggling) {
if (NetworkManagerService.wifiToggling) {
return headerRow.height + wifiToggleContent.height + Theme.spacingM
}
if (NetworkService.wifiEnabled) {
if (NetworkManagerService.wifiEnabled) {
return headerRow.height + wifiContent.height + Theme.spacingM
}
return headerRow.height + wifiOffContent.height + Theme.spacingM
@@ -22,14 +22,11 @@ Rectangle {
border.width: 0
Component.onCompleted: {
NetworkService.addRef()
if (NetworkService.wifiEnabled) {
NetworkService.scanWifi()
}
NetworkManagerService.addRef()
}
Component.onDestruction: {
NetworkService.removeRef()
NetworkManagerService.removeRef()
}
Row {
@@ -59,16 +56,31 @@ Rectangle {
DankButtonGroup {
id: preferenceControls
anchors.verticalCenter: parent.verticalCenter
visible: NetworkService.ethernetConnected && NetworkService.wifiConnected
visible: NetworkManagerService.ethernetConnected
property int currentPreferenceIndex: NetworkService.userPreference === "ethernet" ? 0 : 1
property int currentPreferenceIndex: {
const pref = NetworkManagerService.userPreference
const status = NetworkManagerService.networkStatus
let index = 1
if (pref === "ethernet") {
index = 0
} else if (pref === "wifi") {
index = 1
} else {
index = status === "ethernet" ? 0 : 1
}
return index
}
model: ["Ethernet", "WiFi"]
currentIndex: currentPreferenceIndex
selectionMode: "single"
onSelectionChanged: (index, selected) => {
if (!selected) return
NetworkService.setNetworkPreference(index === 0 ? "ethernet" : "wifi")
console.log("NetworkDetail: Setting preference to", index === 0 ? "ethernet" : "wifi")
NetworkManagerService.setNetworkPreference(index === 0 ? "ethernet" : "wifi")
}
}
}
@@ -80,7 +92,7 @@ Rectangle {
anchors.right: parent.right
anchors.margins: Theme.spacingM
anchors.topMargin: Theme.spacingM
visible: NetworkService.wifiToggling
visible: NetworkManagerService.wifiToggling
height: visible ? 80 : 0
Column {
@@ -94,7 +106,7 @@ Rectangle {
color: Theme.primary
RotationAnimation on rotation {
running: NetworkService.wifiToggling
running: NetworkManagerService.wifiToggling
loops: Animation.Infinite
from: 0
to: 360
@@ -104,7 +116,7 @@ Rectangle {
StyledText {
anchors.horizontalCenter: parent.horizontalCenter
text: NetworkService.wifiEnabled ? "Disabling WiFi..." : "Enabling WiFi..."
text: NetworkManagerService.wifiEnabled ? "Disabling WiFi..." : "Enabling WiFi..."
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
horizontalAlignment: Text.AlignHCenter
@@ -119,7 +131,7 @@ Rectangle {
anchors.right: parent.right
anchors.margins: Theme.spacingM
anchors.topMargin: Theme.spacingM
visible: !NetworkService.wifiEnabled && !NetworkService.wifiToggling
visible: !NetworkManagerService.wifiEnabled && !NetworkManagerService.wifiToggling
height: visible ? 120 : 0
Column {
@@ -165,7 +177,7 @@ Rectangle {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: NetworkService.toggleWifiRadio()
onClicked: NetworkManagerService.toggleWifiRadio()
}
}
@@ -180,7 +192,7 @@ Rectangle {
anchors.bottom: parent.bottom
anchors.margins: Theme.spacingM
anchors.topMargin: Theme.spacingM
visible: NetworkService.wifiInterface && NetworkService.wifiEnabled && !NetworkService.wifiToggling
visible: NetworkManagerService.wifiInterface && NetworkManagerService.wifiEnabled && !NetworkManagerService.wifiToggling
contentHeight: wifiColumn.height
clip: true
@@ -192,7 +204,7 @@ Rectangle {
Item {
width: parent.width
height: 200
visible: NetworkService.wifiInterface && NetworkService.wifiNetworks?.length < 1 && !NetworkService.wifiToggling
visible: NetworkManagerService.wifiInterface && NetworkManagerService.wifiNetworks?.length < 1 && !NetworkManagerService.wifiToggling && NetworkManagerService.isScanning
DankIcon {
anchors.centerIn: parent
@@ -201,7 +213,7 @@ Rectangle {
color: Qt.rgba(Theme.surfaceText.r || 0.8, Theme.surfaceText.g || 0.8, Theme.surfaceText.b || 0.8, 0.3)
RotationAnimation on rotation {
running: true
running: NetworkManagerService.isScanning
loops: Animation.Infinite
from: 0
to: 360
@@ -211,14 +223,18 @@ Rectangle {
}
Repeater {
model: {
let networks = [...NetworkService.wifiNetworks]
networks.sort((a, b) => {
if (a.ssid === NetworkService.currentWifiSSID) return -1
if (b.ssid === NetworkService.currentWifiSSID) return 1
model: sortedNetworks
property var sortedNetworks: {
const ssid = NetworkManagerService.currentWifiSSID
const networks = NetworkManagerService.wifiNetworks
let sorted = [...networks]
sorted.sort((a, b) => {
if (a.ssid === ssid) return -1
if (b.ssid === ssid) return 1
return b.signal - a.signal
})
return networks
return sorted
}
delegate: Rectangle {
required property var modelData
@@ -228,7 +244,7 @@ Rectangle {
height: 50
radius: Theme.cornerRadius
color: networkMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest
border.color: modelData.ssid === NetworkService.currentWifiSSID ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.color: modelData.ssid === NetworkManagerService.currentWifiSSID ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: 0
Row {
@@ -245,7 +261,7 @@ Rectangle {
return "wifi_1_bar"
}
size: Theme.iconSize - 4
color: modelData.ssid === NetworkService.currentWifiSSID ? Theme.primary : Theme.surfaceText
color: modelData.ssid === NetworkManagerService.currentWifiSSID ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
@@ -257,7 +273,7 @@ Rectangle {
text: modelData.ssid || "Unknown Network"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: modelData.ssid === NetworkService.currentWifiSSID ? Font.Medium : Font.Normal
font.weight: modelData.ssid === NetworkManagerService.currentWifiSSID ? Font.Medium : Font.Normal
elide: Text.ElideRight
width: parent.width
}
@@ -266,7 +282,7 @@ Rectangle {
spacing: Theme.spacingXS
StyledText {
text: modelData.ssid === NetworkService.currentWifiSSID ? "Connected" : (modelData.secured ? "Secured" : "Open")
text: modelData.ssid === NetworkManagerService.currentWifiSSID ? "Connected" : (modelData.secured ? "Secured" : "Open")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
@@ -300,7 +316,7 @@ Rectangle {
} else {
networkContextMenu.currentSSID = modelData.ssid
networkContextMenu.currentSecured = modelData.secured
networkContextMenu.currentConnected = modelData.ssid === NetworkService.currentWifiSSID
networkContextMenu.currentConnected = modelData.ssid === NetworkManagerService.currentWifiSSID
networkContextMenu.currentSaved = modelData.saved
networkContextMenu.currentSignal = modelData.signal
networkContextMenu.popup(optionsButton, -networkContextMenu.width + optionsButton.width, optionsButton.height + Theme.spacingXS)
@@ -315,11 +331,11 @@ Rectangle {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: function(event) {
if (modelData.ssid !== NetworkService.currentWifiSSID) {
if (modelData.ssid !== NetworkManagerService.currentWifiSSID) {
if (modelData.secured && !modelData.saved) {
wifiPasswordModal.show(modelData.ssid)
} else {
NetworkService.connectToWifi(modelData.ssid)
NetworkManagerService.connectToWifi(modelData.ssid)
}
}
event.accepted = true
@@ -368,12 +384,12 @@ Rectangle {
onTriggered: {
if (networkContextMenu.currentConnected) {
NetworkService.disconnectWifi()
NetworkManagerService.disconnectWifi()
} else {
if (networkContextMenu.currentSecured && !networkContextMenu.currentSaved) {
wifiPasswordModal.show(networkContextMenu.currentSSID)
} else {
NetworkService.connectToWifi(networkContextMenu.currentSSID)
NetworkManagerService.connectToWifi(networkContextMenu.currentSSID)
}
}
}
@@ -397,7 +413,7 @@ Rectangle {
}
onTriggered: {
let networkData = NetworkService.getNetworkInfo(networkContextMenu.currentSSID)
let networkData = NetworkManagerService.getNetworkInfo(networkContextMenu.currentSSID)
networkInfoModal.showNetworkInfo(networkContextMenu.currentSSID, networkData)
}
}
@@ -421,7 +437,7 @@ Rectangle {
}
onTriggered: {
NetworkService.forgetWifiNetwork(networkContextMenu.currentSSID)
NetworkManagerService.forgetWifiNetwork(networkContextMenu.currentSSID)
}
}
}

View File

@@ -7,7 +7,30 @@ import "../utils/widgets.js" as WidgetUtils
QtObject {
id: root
property var vpnBuiltinInstance: VpnWidget {}
property var vpnBuiltinInstance: null
property var vpnLoader: Loader {
active: false
sourceComponent: Component {
VpnWidget {}
}
onItemChanged: {
root.vpnBuiltinInstance = item
}
Connections {
target: SettingsData
function onControlCenterWidgetsChanged() {
const widgets = SettingsData.controlCenterWidgets || []
const hasVpnWidget = widgets.some(w => w.id === "builtin_vpn")
if (!hasVpnWidget && vpnLoader.active) {
console.log("VpnWidget: No VPN widget in control center, deactivating loader")
vpnLoader.active = false
}
}
}
}
readonly property var coreWidgetDefinitions: [{
"id": "nightMode",
@@ -44,8 +67,8 @@ QtObject {
"description": "Wi-Fi and Ethernet connection",
"icon": "wifi",
"type": "connection",
"enabled": NetworkService.wifiAvailable,
"warning": !NetworkService.wifiAvailable ? "Wi-Fi not available" : undefined
"enabled": NetworkManagerService.wifiAvailable,
"warning": !NetworkManagerService.wifiAvailable ? "Wi-Fi not available" : undefined
}, {
"id": "bluetooth",
"text": "Bluetooth",

View File

@@ -0,0 +1,51 @@
import QtQuick
import qs.Common
import qs.Widgets
StyledRect {
id: root
property string primaryMessage: ""
property string secondaryMessage: ""
radius: Theme.cornerRadius
color: Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.1)
border.color: Theme.warning
border.width: 1
Row {
anchors.fill: parent
anchors.margins: Theme.spacingM
spacing: Theme.spacingXS
DankIcon {
name: "warning"
size: 16
color: Theme.warning
anchors.top: parent.top
anchors.topMargin: 2
}
Column {
width: parent.width - 16 - parent.spacing
spacing: Theme.spacingXS
StyledText {
width: parent.width
text: root.primaryMessage
font.pixelSize: Theme.fontSizeSmall
color: Theme.warning
font.weight: Font.Medium
wrapMode: Text.WordWrap
}
StyledText {
width: parent.width
text: root.secondaryMessage
font.pixelSize: Theme.fontSizeSmall
color: Theme.warning
visible: text.length > 0
}
}
}
}

View File

@@ -1,78 +0,0 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.ControlCenter.Widgets
CompoundPill {
id: root
isActive: {
if (NetworkService.wifiToggling) {
return false
}
if (NetworkService.networkStatus === "ethernet") {
return true
}
if (NetworkService.networkStatus === "wifi") {
return true
}
return NetworkService.wifiEnabled
}
iconName: {
if (NetworkService.wifiToggling) {
return "sync"
}
if (NetworkService.networkStatus === "ethernet") {
return "settings_ethernet"
}
if (NetworkService.networkStatus === "wifi") {
return NetworkService.wifiSignalIcon
}
if (NetworkService.wifiEnabled) {
return "wifi_off"
}
return "wifi_off"
}
primaryText: {
if (NetworkService.wifiToggling) {
return NetworkService.wifiEnabled ? "Disabling WiFi..." : "Enabling WiFi..."
}
if (NetworkService.networkStatus === "ethernet") {
return "Ethernet"
}
if (NetworkService.networkStatus === "wifi" && NetworkService.currentWifiSSID) {
return NetworkService.currentWifiSSID
}
if (NetworkService.wifiEnabled) {
return "Not connected"
}
return "WiFi off"
}
secondaryText: {
if (NetworkService.wifiToggling) {
return "Please wait..."
}
if (NetworkService.networkStatus === "ethernet") {
return "Connected"
}
if (NetworkService.networkStatus === "wifi") {
return NetworkService.wifiSignalStrength > 0 ? NetworkService.wifiSignalStrength + "%" : "Connected"
}
if (NetworkService.wifiEnabled) {
return "Select network"
}
return ""
}
onToggled: {
if (NetworkService.networkStatus !== "ethernet" && !NetworkService.wifiToggling) {
NetworkService.toggleWifiRadio()
}
}
}

View File

@@ -1003,8 +1003,8 @@ Item {
}
controlCenterLoader.item.triggerScreen = barWindow.screen
controlCenterLoader.item.toggle()
if (controlCenterLoader.item.shouldBeVisible && NetworkService.wifiEnabled) {
NetworkService.scanWifi()
if (controlCenterLoader.item.shouldBeVisible && NetworkManagerService.wifiEnabled) {
NetworkManagerService.scanWifi()
}
}
}

View File

@@ -13,6 +13,10 @@ import qs.Widgets
DankPopout {
id: root
Ref {
service: VpnService
}
property var triggerScreen: null
function setTriggerPosition(x, y, width, section, screen) {

View File

@@ -42,26 +42,26 @@ Rectangle {
DankIcon {
name: {
if (NetworkService.wifiToggling) {
if (NetworkManagerService.wifiToggling) {
return "sync"
}
if (NetworkService.networkStatus === "ethernet") {
if (NetworkManagerService.networkStatus === "ethernet") {
return "lan"
}
return NetworkService.wifiSignalIcon
return NetworkManagerService.wifiSignalIcon
}
size: Theme.barIconSize(barThickness)
color: {
if (NetworkService.wifiToggling) {
if (NetworkManagerService.wifiToggling) {
return Theme.primary
}
return NetworkService.networkStatus !== "disconnected" ? Theme.primary : Theme.outlineButton
return NetworkManagerService.networkStatus !== "disconnected" ? Theme.primary : Theme.outlineButton
}
anchors.horizontalCenter: parent.horizontalCenter
visible: root.showNetworkIcon
visible: root.showNetworkIcon && NetworkManagerService.networkAvailable
}
DankIcon {
@@ -141,26 +141,26 @@ Rectangle {
id: networkIcon
name: {
if (NetworkService.wifiToggling) {
if (NetworkManagerService.wifiToggling) {
return "sync";
}
if (NetworkService.networkStatus === "ethernet") {
if (NetworkManagerService.networkStatus === "ethernet") {
return "lan";
}
return NetworkService.wifiSignalIcon;
return NetworkManagerService.wifiSignalIcon;
}
size: Theme.barIconSize(barThickness)
color: {
if (NetworkService.wifiToggling) {
if (NetworkManagerService.wifiToggling) {
return Theme.primary;
}
return NetworkService.networkStatus !== "disconnected" ? Theme.primary : Theme.outlineButton;
return NetworkManagerService.networkStatus !== "disconnected" ? Theme.primary : Theme.outlineButton;
}
anchors.verticalCenter: parent.verticalCenter
visible: root.showNetworkIcon
visible: root.showNetworkIcon && NetworkManagerService.networkAvailable
}

View File

@@ -7,6 +7,10 @@ import qs.Widgets
Rectangle {
id: root
Ref {
service: VpnService
}
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property int widgetThickness: 28

View File

@@ -7,118 +7,55 @@ import qs.Services
Item {
id: root
property string sid: Quickshell.env("XDG_SESSION_ID") || "self"
property string sessionPath: ""
function activate() {
loader.activeAsync = true
}
Component.onCompleted: {
getSessionPath.running = true
if (SessionService.loginctlAvailable) {
if (SessionService.locked || SessionService.lockedHint) {
console.log("Lock: Session locked on startup")
loader.activeAsync = true
}
}
Component.onDestruction: {
lockStateMonitor.running = false
}
Connections {
target: IdleService
function onLockRequested() {
console.log("Lock: Received lock request from IdleService")
activate()
SessionService.lockSession()
}
}
Process {
id: getSessionPath
command: ["gdbus", "call", "--system", "--dest", "org.freedesktop.login1", "--object-path", "/org/freedesktop/login1", "--method", "org.freedesktop.login1.Manager.GetSession", sid]
running: false
Connections {
target: SessionService
stdout: StdioCollector {
onStreamFinished: {
const match = text.match(/objectpath '([^']+)'/)
if (match) {
root.sessionPath = match[1]
console.log("Found session path:", root.sessionPath)
checkCurrentLockState.running = true
lockStateMonitor.running = true
} else {
console.warn("Could not determine session path")
}
}
}
onExited: (exitCode, exitStatus) => {
if (exitCode !== 0) {
console.warn("Failed to get session path, exit code:", exitCode)
}
}
}
Process {
id: checkCurrentLockState
command: root.sessionPath ? ["gdbus", "call", "--system", "--dest", "org.freedesktop.login1", "--object-path", root.sessionPath, "--method", "org.freedesktop.DBus.Properties.Get", "org.freedesktop.login1.Session", "LockedHint"] : []
running: false
stdout: StdioCollector {
onStreamFinished: {
if (text.includes("true")) {
console.log("Session is locked on startup, activating lock screen")
function onSessionLocked() {
console.log("Lock: Lock signal received -> show lock")
loader.activeAsync = true
}
}
}
onExited: (exitCode, exitStatus) => {
if (exitCode !== 0) {
console.warn("Failed to check initial lock state, exit code:", exitCode)
}
}
}
Process {
id: lockStateMonitor
command: root.sessionPath ? ["gdbus", "monitor", "--system", "--dest", "org.freedesktop.login1"] : []
running: false
stdout: SplitParser {
splitMarker: "\n"
onRead: line => {
if (line.includes(root.sessionPath)) {
if (line.includes("org.freedesktop.login1.Session.Lock")) {
console.log("login1: Lock signal received -> show lock")
loader.activeAsync = true
return
}
if (line.includes("org.freedesktop.login1.Session.Unlock")) {
console.log("login1: Unlock signal received -> hide lock")
function onSessionUnlocked() {
console.log("Lock: Unlock signal received -> hide lock")
loader.active = false
return
}
if (line.includes("LockedHint") && line.includes("true")) {
console.log("login1: LockedHint=true -> show lock")
function onLoginctlStateChanged() {
if (SessionService.lockedHint && !loader.active) {
console.log("Lock: LockedHint=true -> show lock")
loader.activeAsync = true
return
}
if (line.includes("LockedHint") && line.includes("false")) {
console.log("login1: LockedHint=false -> hide lock")
} else if (!SessionService.locked && !SessionService.lockedHint && loader.active) {
console.log("Lock: LockedHint=false -> hide lock")
loader.active = false
return
}
}
if (line.includes("PrepareForSleep") &&
line.includes("true") &&
SessionData.lockBeforeSuspend) {
loader.activeAsync = true
}
}
}
onExited: (exitCode, exitStatus) => {
if (exitCode !== 0) {
console.warn("gdbus monitor failed, exit code:", exitCode)
function onPrepareForSleep() {
if (SessionService.preparingForSleep && SessionData.lockBeforeSuspend) {
console.log("Lock: PrepareForSleep -> lock before suspend")
loader.activeAsync = true
}
}
}
@@ -160,7 +97,7 @@ Item {
function lock() {
console.log("Lock screen requested via IPC")
loader.activeAsync = true
SessionService.lockSession()
}
function demo() {
@@ -169,7 +106,7 @@ Item {
}
function isLocked(): bool {
return loader.active
return SessionService.locked || loader.active
}
}
}

View File

@@ -906,20 +906,20 @@ Item {
height: 24
color: Qt.rgba(255, 255, 255, 0.2)
anchors.verticalCenter: parent.verticalCenter
visible: WeatherService.weather.available && (NetworkService.networkStatus !== "disconnected" || BluetoothService.enabled || (AudioService.sink && AudioService.sink.audio) || BatteryService.batteryAvailable)
visible: WeatherService.weather.available && (NetworkManagerService.networkStatus !== "disconnected" || BluetoothService.enabled || (AudioService.sink && AudioService.sink.audio) || BatteryService.batteryAvailable)
}
Row {
spacing: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
visible: NetworkService.networkStatus !== "disconnected" || (BluetoothService.available && BluetoothService.enabled) || (AudioService.sink && AudioService.sink.audio)
visible: NetworkManagerService.networkStatus !== "disconnected" || (BluetoothService.available && BluetoothService.enabled) || (AudioService.sink && AudioService.sink.audio)
DankIcon {
name: NetworkService.networkStatus === "ethernet" ? "lan" : NetworkService.wifiSignalIcon
name: NetworkManagerService.networkStatus === "ethernet" ? "lan" : NetworkManagerService.wifiSignalIcon
size: Theme.iconSize - 2
color: NetworkService.networkStatus !== "disconnected" ? "white" : Qt.rgba(255, 255, 255, 0.5)
color: NetworkManagerService.networkStatus !== "disconnected" ? "white" : Qt.rgba(255, 255, 255, 0.5)
anchors.verticalCenter: parent.verticalCenter
visible: NetworkService.networkStatus !== "disconnected"
visible: NetworkManagerService.networkStatus !== "disconnected"
}
DankIcon {
@@ -955,7 +955,7 @@ Item {
height: 24
color: Qt.rgba(255, 255, 255, 0.2)
anchors.verticalCenter: parent.verticalCenter
visible: BatteryService.batteryAvailable && (NetworkService.networkStatus !== "disconnected" || BluetoothService.enabled || (AudioService.sink && AudioService.sink.audio))
visible: BatteryService.batteryAvailable && (NetworkManagerService.networkStatus !== "disconnected" || BluetoothService.enabled || (AudioService.sink && AudioService.sink.audio))
}
Row {

View File

@@ -69,7 +69,7 @@ DankFlickable {
}
StyledText {
text: `Up ${UserInfoService.uptime} Boot: ${DgopService.bootTime}`
text: `${UserInfoService.uptime} Boot: ${DgopService.bootTime}`
font.pixelSize: Theme.fontSizeSmall
font.family: SettingsData.monoFontFamily
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)

View File

@@ -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) {

View 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

View File

@@ -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
}

View File

@@ -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) {
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 = ""
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
} 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"

View File

@@ -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)
}
})
}
}

View File

@@ -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()

File diff suppressed because it is too large Load Diff

View File

@@ -174,6 +174,13 @@
"reference": "",
"comment": ""
},
{
"term": "Apps Icon",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Apps are ordered by usage frequency, then last used, then alphabetically.",
"translation": "",
@@ -440,6 +447,13 @@
"reference": "",
"comment": ""
},
{
"term": "Choose Launcher Logo Color",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Choose icon",
"translation": "",
@@ -447,6 +461,13 @@
"reference": "",
"comment": ""
},
{
"term": "Choose the logo displayed on the launcher button in DankBar",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Clear",
"translation": "",
@@ -594,6 +615,13 @@
"reference": "",
"comment": ""
},
{
"term": "Custom",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Custom Location",
"translation": "",
@@ -629,6 +657,13 @@
"reference": "",
"comment": ""
},
{
"term": "DMS_SOCKET not available",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Daily at:",
"translation": "",
@@ -657,6 +692,13 @@
"reference": "",
"comment": ""
},
{
"term": "DankBar Font Scale",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Date Format",
"translation": "",
@@ -1112,6 +1154,13 @@
"reference": "",
"comment": ""
},
{
"term": "Invert on mode change",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Kill Process",
"translation": "",
@@ -1148,7 +1197,14 @@
"comment": ""
},
{
"term": "Launcher Button",
"term": "Launch on dGPU",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Launcher Button Logo",
"translation": "",
"context": "",
"reference": "",
@@ -1329,6 +1385,13 @@
"reference": "",
"comment": ""
},
{
"term": "NM not supported",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Named Workspace Icons",
"translation": "",
@@ -1539,6 +1602,13 @@
"reference": "",
"comment": ""
},
{
"term": "OS Logo",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Office",
"translation": "",
@@ -1784,6 +1854,13 @@
"reference": "",
"comment": ""
},
{
"term": "Scale DankBar font sizes independently",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Scale all font sizes",
"translation": "",
@@ -1826,6 +1903,13 @@
"reference": "",
"comment": ""
},
{
"term": "Select Launcher Logo",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Select a color from the palette or use custom sliders",
"translation": "",
@@ -1840,6 +1924,13 @@
"reference": "",
"comment": ""
},
{
"term": "Select an image file...",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Select which transitions to include in randomization",
"translation": "",
@@ -1938,6 +2029,13 @@
"reference": "",
"comment": ""
},
{
"term": "Size Offset",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Spacing",
"translation": "",
@@ -2183,13 +2281,6 @@
"reference": "",
"comment": ""
},
{
"term": "Use OS Logo",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Use%",
"translation": "",
@@ -2344,6 +2435,13 @@
"reference": "",
"comment": ""
},
{
"term": "update dms for NM integration.",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "• Install only from trusted sources",
"translation": "",