aurora0e2345d
aurora: eject only unmounts the clicked partition, separate power-off
+30 −14
2 files changed
- M .config/quickshell/aurora/Devices.qml +20 −12
- M .config/quickshell/aurora/SysPage.qml +10 −2
| @@ -35,24 +35,32 @@ Singleton { | |||
| 35 | 35 | return false | |
| 36 | 36 | } | |
| 37 | 37 | function xdgOpen(path) { Quickshell.execDetached(["xdg-open", path]) } | |
| 38 | − | // unmount every filesystem of the disk, lock its LUKS containers, power it off | |
| 38 | + | // unmount this filesystem and lock its LUKS container; the disk stays powered, so it can be mounted again | |
| 39 | 39 | function eject(key) { | |
| 40 | 40 | if (key.indexOf("mtp://") === 0) { | |
| 41 | 41 | run(["gio", "mount", "-u", key], "phone unmounted, safe to unplug", null) | |
| 42 | 42 | return | |
| 43 | 43 | } | |
| 44 | − | var disk = "", steps = [] | |
| 45 | − | for (var i = 0; i < drives.length; i++) if (drives[i].key === key) disk = drives[i].disk | |
| 46 | − | if (disk === "") return | |
| 47 | − | for (i = 0; i < drives.length; i++) { | |
| 48 | − | var d = drives[i] | |
| 49 | − | if (d.disk !== disk) continue | |
| 50 | − | if (d.mount !== "") steps.push("u:" + d.dev) | |
| 51 | − | if (d.luksParent !== "") steps.push("l:" + d.luksParent) | |
| 52 | − | } | |
| 53 | − | steps.push((disk.indexOf("/dev/loop") === 0 ? "d:" : "p:") + disk) // an image: detach it | |
| 44 | + | var d = null, steps = [] | |
| 45 | + | for (var i = 0; i < drives.length; i++) if (drives[i].key === key) d = drives[i] | |
| 46 | + | if (!d) return | |
| 47 | + | if (d.mount !== "") steps.push("u:" + d.dev) | |
| 48 | + | if (d.luksParent !== "") steps.push("l:" + d.luksParent) | |
| 49 | + | if (steps.length === 0) return | |
| 54 | 50 | run(["sh", "-c", 'for a; do case $a in u:*) udisksctl unmount -b "${a#u:}" ;; l:*) udisksctl lock -b "${a#l:}" ;;' | |
| 55 | − | + ' p:*) udisksctl power-off -b "${a#p:}" ;; d:*) udisksctl loop-delete -b "${a#d:}" ;; esac >/dev/null || exit; done', "sh"].concat(steps), | |
| 51 | + | + ' esac >/dev/null || exit; done', "sh"].concat(steps), | |
| 52 | + | title(d) + (d.luksParent !== "" ? " locked" : " unmounted"), null) | |
| 53 | + | } | |
| 54 | + | // anything on the disk still mounted or unlocked | |
| 55 | + | function diskBusy(disk) { | |
| 56 | + | for (var i = 0; i < drives.length; i++) | |
| 57 | + | if (drives[i].disk === disk && (drives[i].mount !== "" || drives[i].luksParent !== "")) return true | |
| 58 | + | return false | |
| 59 | + | } | |
| 60 | + | // power the disk off (an image: detach it). It disappears until replugged. | |
| 61 | + | function powerOff(disk) { | |
| 62 | + | if (diskBusy(disk)) return | |
| 63 | + | run(["udisksctl", disk.indexOf("/dev/loop") === 0 ? "loop-delete" : "power-off", "-b", disk], | |
| 56 | 64 | "safe to unplug " + disk, null) | |
| 57 | 65 | } | |
| 58 | 66 | ||
| @@ -52,15 +52,18 @@ Flickable { | |||
| 52 | 52 | } | |
| 53 | 53 | } | |
| 54 | 54 | ||
| 55 | − | // removable drives + phones (Devices): mount / open / eject | |
| 55 | + | // removable drives + phones (Devices): mount / open / unmount, power-off once the disk is idle | |
| 56 | 56 | SectionLabel { text: "removable"; Layout.topMargin: 6; visible: Devices.drives.length + Devices.phones.length > 0 } | |
| 57 | 57 | Repeater { | |
| 58 | 58 | model: Devices.drives.concat(Devices.phones) | |
| 59 | 59 | delegate: ListRow { | |
| 60 | 60 | id: dr | |
| 61 | 61 | required property var modelData | |
| 62 | + | required property int index | |
| 62 | 63 | readonly property bool phone: modelData.uri !== undefined | |
| 63 | 64 | readonly property bool mounted: phone ? modelData.mounted : modelData.mount !== "" | |
| 65 | + | // first row of its disk carries the power-off button | |
| 66 | + | readonly property bool firstOfDisk: !phone && (index === 0 || Devices.drives[index - 1].disk !== modelData.disk) | |
| 64 | 67 | icon: phone ? Theme.iPhone : (modelData.locked ? Theme.iLock : Theme.iUsb) | |
| 65 | 68 | title: phone ? modelData.name : Devices.title(modelData) | |
| 66 | 69 | subtitle: phone ? (mounted ? modelData.path : "not mounted") | |
| @@ -73,9 +76,14 @@ Flickable { | |||
| 73 | 76 | } | |
| 74 | 77 | IconBtn { | |
| 75 | 78 | icon: Theme.iEject | |
| 76 | − | visible: dr.mounted || !dr.phone | |
| 79 | + | visible: dr.mounted || (!dr.phone && dr.modelData.luksParent !== "") | |
| 77 | 80 | onClicked: Devices.eject(dr.modelData.key) | |
| 78 | 81 | } | |
| 82 | + | IconBtn { | |
| 83 | + | icon: Theme.iPower | |
| 84 | + | visible: dr.firstOfDisk && !Devices.diskBusy(dr.modelData.disk) | |
| 85 | + | onClicked: Devices.powerOff(dr.modelData.disk) | |
| 86 | + | } | |
| 79 | 87 | } | |
| 80 | 88 | } | |
| 81 | 89 | ||