GNOME's Wi-Fi panel has this and it is the most-used thing in it: the alternative is reading a passphrase out loud. Network & Devices now shows a scannable code for any saved network whose passphrase this user can read. The image contains the network password in machine-readable form, so most of the care here is about that rather than about QR codes. It is written under XDG_RUNTIME_DIR -- 0700, on tmpfs, gone at logout -- rather than /tmp, which is shared; the file is 0600; the passphrase is piped to qrencode on stdin rather than passed as an argument, because argv is world-readable through /proc for as long as the process runs; and it is never printed or included in an error message. Generated on demand, because producing a code for every saved network up front means writing images of passwords nobody asked to see. Enterprise networks are listed as not shareable rather than offered and broken: there is no passphrase to encode, so the code could not work. Two bugs the contract caught while being written. Semicolons in an SSID were not escaped -- the sed replacement had one backslash where its four neighbours have two, so sed dropped it, and an SSID containing a semicolon would have produced a QR code describing a different network. And nmcli's trailing newline landed inside the payload; it decoded here, but a newline in the middle of a WIFI: URI is not something every phone tolerates, and that failure would present as "the QR code just doesn't work on my phone". The contract stubs nmcli and qrencode, because the real ones would write this machine's actual Wi-Fi password into a fixture directory. It asserts the escaping, the absence of a newline, the file and directory modes, that no temporary payload survives, and that the passphrase never reaches argv. Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
193 lines
6.7 KiB
QML
193 lines
6.7 KiB
QML
// Network & Devices.
|
|
//
|
|
// Wi-Fi and Bluetooth are handled here rather than delegated. Everything goes
|
|
// through Quickshell.Networking and Quickshell.Bluetooth -- NetworkManager and
|
|
// BlueZ over DBus -- and nothing shells out to nmcli or bluetoothctl. That was
|
|
// the founding requirement for this desktop: never having to drop to a terminal
|
|
// to join a network.
|
|
//
|
|
// Scanning follows this page being on screen. Wi-Fi scanning and especially
|
|
// Bluetooth discovery hold the radio, and doing either for a list nobody is
|
|
// looking at is battery and airtime spent on nothing.
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Networking
|
|
import qs.config
|
|
import qs.services
|
|
|
|
SettingsPage {
|
|
id: root
|
|
|
|
|
|
title: "Network & Devices"
|
|
lede: Connectivity.activeNetwork
|
|
? "Connected to " + Connectivity.activeNetwork.name
|
|
: "Wi-Fi, Bluetooth, and the things Fedora owns."
|
|
|
|
// Drive the scanners only while this page is the one being shown.
|
|
Component.onCompleted: {
|
|
Connectivity.active = true;
|
|
if (!WifiShare.scanned)
|
|
WifiShare.refresh();
|
|
}
|
|
Component.onDestruction: Connectivity.active = false
|
|
|
|
SettingsCard {
|
|
title: "Wired"
|
|
visible: Connectivity.wiredDevice !== null
|
|
|
|
TextRow {
|
|
label: "Ethernet"
|
|
detail: Connectivity.wiredDevice ? Connectivity.wiredDevice.name : ""
|
|
value: Connectivity.wiredDevice && Connectivity.wiredDevice.connected ? "Connected" : "Not connected"
|
|
divider: false
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
title: "Wi-Fi"
|
|
// A Wi-Fi switch reading "On" above the words "No Wi-Fi adapter" is a
|
|
// contradiction; with no radio the card simply does not belong.
|
|
visible: Connectivity.wifiDevice !== null
|
|
subtitle: "Networks are re-scanned while this page is open."
|
|
|
|
SettingRow {
|
|
label: "Wi-Fi"
|
|
detail: Connectivity.wifiEnabled ? "On" : "Off"
|
|
controlWidth: 48
|
|
divider: Connectivity.wifiEnabled
|
|
|
|
SettingsToggle {
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
checked: Connectivity.wifiEnabled
|
|
enabled: Connectivity.wifiAvailable
|
|
onToggled: value => Networking.wifiEnabled = value
|
|
}
|
|
}
|
|
|
|
WifiPanel {
|
|
width: parent.width
|
|
visible: Connectivity.wifiEnabled
|
|
}
|
|
}
|
|
|
|
// Sharing a network by QR, the way GNOME's Wi-Fi panel does. The
|
|
// alternative is reading a passphrase out loud.
|
|
//
|
|
// The image holds the password in machine-readable form, so it is generated
|
|
// on demand rather than up front, and the helper writes it to tmpfs under
|
|
// XDG_RUNTIME_DIR instead of anywhere persistent.
|
|
SettingsCard {
|
|
visible: Connectivity.wifiDevice !== null && WifiShare.shareable.length > 0
|
|
title: "Share a network"
|
|
subtitle: WifiShare.sharing !== ""
|
|
? "Point a phone's camera at the code to join " + WifiShare.sharing + "."
|
|
: "Shows a QR code a phone can scan to join, without reading the password out."
|
|
|
|
Repeater {
|
|
model: WifiShare.shareable
|
|
|
|
ActionRow {
|
|
id: shareRow
|
|
required property var modelData
|
|
required property int index
|
|
|
|
label: shareRow.modelData.ssid
|
|
detail: WifiShare.sharing === shareRow.modelData.name
|
|
? "Showing a code below — anyone who can see the screen can join"
|
|
: "Saved network"
|
|
action: WifiShare.sharing === shareRow.modelData.name ? "Hide" : "Show code"
|
|
divider: shareRow.index < WifiShare.shareable.length - 1 || WifiShare.sharing !== ""
|
|
onTriggered: WifiShare.sharing === shareRow.modelData.name
|
|
? WifiShare.stopSharing()
|
|
: WifiShare.share(shareRow.modelData.name)
|
|
}
|
|
}
|
|
|
|
// Drawn at its natural size on a white plate: a QR code inverted or
|
|
// tinted to match a dark theme is unreliable to scan, and this one has
|
|
// exactly one job.
|
|
Item {
|
|
width: parent.width
|
|
visible: WifiShare.sharing !== "" && WifiShare.imagePath !== ""
|
|
implicitHeight: visible ? plate.height + 20 : 0
|
|
|
|
Rectangle {
|
|
id: plate
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
y: 10
|
|
width: 208
|
|
height: 208
|
|
radius: 10
|
|
color: "white"
|
|
|
|
Image {
|
|
anchors.centerIn: parent
|
|
width: 184
|
|
height: 184
|
|
smooth: false
|
|
fillMode: Image.PreserveAspectFit
|
|
cache: false
|
|
source: WifiShare.imagePath !== "" ? "file://" + WifiShare.imagePath : ""
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
title: "Bluetooth"
|
|
visible: Connectivity.adapter !== null
|
|
subtitle: "Discovery runs while this page is open."
|
|
|
|
SettingRow {
|
|
label: "Bluetooth"
|
|
detail: Connectivity.adapter
|
|
? (Connectivity.adapter.enabled ? "On" : "Off")
|
|
: "Unavailable"
|
|
controlWidth: 48
|
|
divider: !!(Connectivity.adapter && Connectivity.adapter.enabled)
|
|
|
|
SettingsToggle {
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
checked: !!(Connectivity.adapter && Connectivity.adapter.enabled)
|
|
enabled: Connectivity.adapter !== null
|
|
onToggled: value => {
|
|
if (Connectivity.adapter)
|
|
Connectivity.adapter.enabled = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
BluetoothPanel {
|
|
width: parent.width
|
|
visible: !!(Connectivity.adapter && Connectivity.adapter.enabled)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
title: "Owned by Fedora"
|
|
subtitle: "VPNs, per-connection routing, printers, and online accounts are configured by GNOME's panels, which are installed and searchable."
|
|
|
|
ActionRow {
|
|
label: "Network connections"
|
|
detail: "VPN, proxies, and per-connection settings"
|
|
action: "Open"
|
|
onTriggered: SystemSettings.openGnomePanel("network")
|
|
}
|
|
ActionRow {
|
|
label: "Printers"
|
|
action: "Open"
|
|
onTriggered: SystemSettings.openGnomePanel("printers")
|
|
}
|
|
ActionRow {
|
|
label: "Online accounts"
|
|
action: "Open"
|
|
divider: false
|
|
onTriggered: SystemSettings.openGnomePanel("online-accounts")
|
|
}
|
|
}
|
|
}
|