From c3b5390567b677e2e0ed8a2343bdbf0acc950ea5 Mon Sep 17 00:00:00 2001 From: IvanTheGeek <7623933+IvanTheGeek@users.noreply.github.com> Date: Thu, 30 Jul 2026 15:07:00 -0400 Subject: [PATCH 1/6] network applet: fix updateAccessPoints loop that never runs The loop compared the index against the accessPoints ARRAY rather than its length, so the body never executed. _accessPoints was left empty and the notify::strength handlers were never connected, silently stopping signal strength updates for that network. Co-Authored-By: Claude Opus 5 --- files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js index 850dddf51a..da3dae1142 100644 --- a/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js @@ -168,7 +168,7 @@ NMNetworkMenuItem.prototype = { accessPoints = sortAccessPoints(accessPoints); this.bestAP = accessPoints[0]; this._accessPoints = [ ]; - for (let i = 0; i < accessPoints; i++) { + for (let i = 0; i < accessPoints.length; i++) { let ap = accessPoints[i]; let apObj = { ap: ap, From c60fef7de9faabcaa9de0e1f764501c9e04b0c8f Mon Sep 17 00:00:00 2001 From: IvanTheGeek <7623933+IvanTheGeek@users.noreply.github.com> Date: Thu, 30 Jul 2026 15:07:00 -0400 Subject: [PATCH 2/6] network applet: fix splice() truncating the connection list Array.splice(pos) with no delete count removes everything from pos to the end of the array, so removing one connection dropped every connection after it from _connections. Co-Authored-By: Claude Opus 5 --- files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js index da3dae1142..ecbdbb8513 100644 --- a/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js @@ -2301,7 +2301,7 @@ CinnamonNetworkApplet.prototype = { _connectionRemoved: function(client, connection) { let pos = this._connections.indexOf(connection); if (pos != -1) - this._connections.splice(pos); + this._connections.splice(pos, 1); let section = connection._section; From 3b43974cddeddf9cb78ef35f5ebba3e7541dd7cb Mon Sep 17 00:00:00 2001 From: IvanTheGeek <7623933+IvanTheGeek@users.noreply.github.com> Date: Thu, 30 Jul 2026 15:07:00 -0400 Subject: [PATCH 3/6] network applet: fix VPN-over-wireless icon name and guard the access point set_applet_icon_symbolic_name() passes the name through unchanged and sets St.IconType.SYMBOLIC; St appends '-symbolic' itself during lookup. The name built here already carried the suffix, so it resolved to '...-secure-symbolic-symbolic', which does not exist, and the panel icon rendered as nothing while a VPN was active over wireless. Also guard active_access_point, which is null on a wireless device that is still activating. Co-Authored-By: Claude Opus 5 --- .../share/cinnamon/applets/network@cinnamon.org/applet.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js index ecbdbb8513..d1735be875 100644 --- a/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js @@ -2532,10 +2532,9 @@ CinnamonNetworkApplet.prototype = { let iconName = 'xsi-network-vpn'; if (mc._section == NMConnectionCategory.WIRELESS) { const dev = mc._primaryDevice; - if (dev) { - const ap = dev.device.active_access_point; - iconName = 'xsi-network-wireless-signal-' + signalToIcon(ap.strength) + '-secure-symbolic'; - } + const ap = dev ? dev.device.active_access_point : null; + if (ap) + iconName = 'xsi-network-wireless-signal-' + signalToIcon(ap.strength) + '-secure'; } this._setIcon(iconName); this.set_applet_tooltip(_("Connected to the VPN")); From 41c1098f4cd173d3752800acef1ace744596463f Mon Sep 17 00:00:00 2001 From: IvanTheGeek <7623933+IvanTheGeek@users.noreply.github.com> Date: Thu, 30 Jul 2026 15:07:00 -0400 Subject: [PATCH 4/6] network applet: return the state from _correctStateForTunnel The function fell through without a return when no tun/wireguard connection was active, yielding undefined instead of the state it was given. Co-Authored-By: Claude Opus 5 --- files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js index d1735be875..b5add7b244 100644 --- a/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js @@ -2604,6 +2604,8 @@ CinnamonNetworkApplet.prototype = { if (this._activeConnections.some(con => con.get_connection_type() === "wireguard")) { return NM.ConnectivityState.FULL; } + + return state; }, _proxyConnectivityCheckCallback(new_state) { From 091fb703af2eba4bd826e7927cf543b1949a07c8 Mon Sep 17 00:00:00 2001 From: IvanTheGeek <7623933+IvanTheGeek@users.noreply.github.com> Date: Thu, 30 Jul 2026 15:07:00 -0400 Subject: [PATCH 5/6] network applet: show the dot on an already-active VPN when the row is first built _updateConnectionItemView() takes 'active' as its third argument, but the first-build call omitted it, so a connection that was already up drew no dot until some later refresh happened to redraw the row. Co-Authored-By: Claude Opus 5 --- .../usr/share/cinnamon/applets/network@cinnamon.org/applet.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js index b5add7b244..ec61c56e60 100644 --- a/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js @@ -1041,7 +1041,8 @@ NMDeviceVPN.prototype = { for (let obj of this._connections) { if (!obj.item) { obj.item = new PopupMenu.PopupMenuItem(obj.name); - this._updateConnectionItemView(obj.item, obj.connection); + this._updateConnectionItemView(obj.item, obj.connection, + this._activeConnections.some(ac => ac.connection == obj.connection)); obj.item.connect('activate', Lang.bind(this, function() { let activeConnection = this._activeConnections.find(ac => ac.connection === obj.connection); if (activeConnection) { From 722dd35dd7de4b763bb0221a5f04b35710c1926b Mon Sep 17 00:00:00 2001 From: IvanTheGeek <7623933+IvanTheGeek@users.noreply.github.com> Date: Thu, 30 Jul 2026 15:07:14 -0400 Subject: [PATCH 6/6] network applet: support multiple simultaneous WireGuard connections NMDeviceWIREGUARD kept a single _activeConnection slot, but every WireGuard connection is handed the same pseudo-device and _syncActiveConnections called setActiveConnection() once per connection - so each activation overwrote the previous one. With two tunnels up the menu showed one as connected and drew the other as if it were down, and the section switch tore down only one of them and then flipped straight back on. This mirrors what commit 'nm-applet: make visible multiple active vpn connection' (PR #12930) did for NMDeviceVPN: keep a list, assign it once after the sync loop instead of per connection, and deactivate all of them. Rows become a switch each rather than a pick-one list, since WireGuard connections are independent of one another - this is the behaviour requested in the issue. Closes #12178 Co-Authored-By: Claude Opus 5 --- .../applets/network@cinnamon.org/applet.js | 75 +++++++++++++++---- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js index ec61c56e60..d2b4430b01 100644 --- a/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js @@ -1073,6 +1073,8 @@ NMDeviceWIREGUARD.prototype = { this.category = NMConnectionCategory.WIREGUARD; this._type = NM.SETTING_WIREGUARD_SETTING_NAME; + this._activeConnections = []; + NMDevice.prototype._init.call(this, client, null, [ ]); // Tests: @@ -1089,16 +1091,14 @@ NMDeviceWIREGUARD.prototype = { }, get connected() { - return !!this._activeConnection; + return this._activeConnections.length > 0; }, - setActiveConnection: function(activeConnection) { - if (activeConnection) { - activeConnection._type = NM.SETTING_WIREGUARD_SETTING_NAME; - } - NMDevice.prototype.setActiveConnection.call(this, activeConnection); + setActiveConnections: function(activeConnections) { + this._activeConnections = activeConnections || []; - this.emit('active-connection-changed'); + this._createSection(); + this.emit('active-connections-changed'); }, _shouldShowConnectionList: function() { @@ -1106,8 +1106,48 @@ NMDeviceWIREGUARD.prototype = { }, deactivate: function() { - if (this._activeConnection) - this._client.deactivate_connection(this._activeConnection, null); + for (let ac of this._activeConnections) + this._client.deactivate_connection(ac, null); + + this._activeConnections = []; + }, + + _clearSection: function() { + if (this.section && this.section.removeAll) + this.section.removeAll(); + + this._autoConnectionItem = null; + this._overflowItem = null; + + for (let i = 0; i < this._connections.length; i++) { + if (this._connections[i].item && this._connections[i].item.destroy) + this._connections[i].item.destroy(); + + this._connections[i].item = null; + } + }, + + /* A switch per tunnel: WireGuard connections are independent of one another, + so they are not a pick-one list. */ + _createSection: function() { + for (let obj of this._connections) { + let active = this._activeConnections.some(ac => ac.connection == obj.connection); + + if (!obj.item) { + obj.item = new PopupMenu.PopupSwitchMenuItem(obj.name, active); + obj.item.connect('toggled', (item, state) => { + let activeConnection = this._activeConnections.find(ac => ac.connection === obj.connection); + + if (!state && activeConnection) + this._client.deactivate_connection(activeConnection, null); + else if (state && !activeConnection) + this._client.activate_connection_async(obj.connection, this.device, null, null, null); + }); + this.section.addMenuItem(obj.item); + } else { + obj.item.setToggleState(active); + } + } }, statusLabel: null, @@ -1905,9 +1945,9 @@ CinnamonNetworkApplet.prototype = { device: new NMDeviceWIREGUARD(this._client), item: new NMWiredSectionTitleMenuItem(_("WIREGUARD Connections")) }; - this._devices.wireguard.device.connect('active-connection-changed', Lang.bind(this, function() { + this._devices.wireguard.device.connect('active-connections-changed', () => { this._devices.wireguard.item.updateForDevice(this._devices.wireguard.device); - })); + }); this._devices.wireguard.item.updateForDevice(this._devices.wireguard.device); this._devices.wireguard.section.addMenuItem(this._devices.wireguard.item); this._devices.wireguard.section.addMenuItem(this._devices.wireguard.device.section); @@ -2149,6 +2189,8 @@ CinnamonNetworkApplet.prototype = { if (active._primaryDevice) { if (active._type == NM.SETTING_VPN_SETTING_NAME) this._devices.vpn.device.setActiveConnections([]); + else if (active._type == NM.SETTING_WIREGUARD_SETTING_NAME) + this._devices.wireguard.device.setActiveConnections([]); else active._primaryDevice.setActiveConnection(null); @@ -2170,6 +2212,7 @@ CinnamonNetworkApplet.prototype = { let default_ip6 = null; let vpnConnections = []; + let wireguardConnections = []; for (let a of this._activeConnections) { if (!a._inited) { @@ -2246,17 +2289,21 @@ CinnamonNetworkApplet.prototype = { } if (a._primaryDevice) { - if (a._type == NM.SETTING_VPN_SETTING_NAME) { + if (a._type == NM.SETTING_VPN_SETTING_NAME) vpnConnections.push(a); - } else { + else if (a._type == NM.SETTING_WIREGUARD_SETTING_NAME) + wireguardConnections.push(a); + else a._primaryDevice.setActiveConnection(a); - } } } if (this._devices.vpn && this._devices.vpn.device) this._devices.vpn.device.setActiveConnections(vpnConnections); + if (this._devices.wireguard && this._devices.wireguard.device) + this._devices.wireguard.device.setActiveConnections(wireguardConnections); + this._mainConnection = activated || activating || default_ip4 || default_ip6 || null; },