mirror of
https://github.com/theupriser/steamify-cachyos.git
synced 2026-10-03 17:41:58 +02:00
feat: BIOS update in the app, with both warnings
bios_enable split into bios_prepare (download, checksum, fwupd check) and bios_flash; the terminal flow is unchanged. The app: check, warning 1, warning 2 (type UPDATE, or hold A/OK for 5 seconds), flash, restart.
This commit is contained in:
1 parent
396459b511
commit
7c48b3d806
4 files changed
+298
-85
No files matched your search
+148
-14
@@ -71,9 +71,9 @@ ApplicationWindow {
|
||||
kpin: { label: "Pin the kernel", hint: "Fixes rebooting after shutdown",
|
||||
body: "Newer CachyOS kernels make the Steam Machine reboot instead of shutting down. Untick once CachyOS fixes that.",
|
||||
changes: ["linux-cachyos from Steamify's release (signature checked)", "Kept in /var/cache/steamify/kernel", "Added to IgnorePkg"] },
|
||||
bios: { label: "Update BIOS", hint: "Runs in the terminal version (two warnings, typed confirmation)",
|
||||
body: "Installs Valve's newest Steam Machine BIOS, at your own risk. The app leaves this to the terminal version, which asks twice and wants UPDATE typed.",
|
||||
changes: ["Run steamify.sh in Konsole for this"] }
|
||||
bios: { label: "Update BIOS", hint: "",
|
||||
body: "Installs Valve's newest Steam Machine BIOS, at your own risk. It checks Valve's checksum and asks fwupd whether the file fits this machine, then warns you twice before anything is written.",
|
||||
changes: ["Valve's fremont-hw-support package (checksum checked)", "fwupd writes the BIOS during the next restart", "Keep the power on until the machine has fully started again"] }
|
||||
})
|
||||
|
||||
// --- State ---
|
||||
@@ -91,6 +91,20 @@ ApplicationWindow {
|
||||
property string runError: ""
|
||||
property bool wrongPassword: false
|
||||
property int doneCount: 0
|
||||
property string pending: "apply" // what the password is for: apply | bios
|
||||
property string sessionPassword: "" // kept between the BIOS steps only
|
||||
property var biosInfo: ({})
|
||||
property bool biosRun: false
|
||||
property int biosFocus: 0 // warning 1: 0 = Cancel, 1 = continue
|
||||
property real holdProgress: 0 // warning 2: hold A/OK for 5 s
|
||||
property bool holding: false
|
||||
readonly property var bios: status.bios || null
|
||||
function biosHint() {
|
||||
if (!bios) return "";
|
||||
if (!bios.newest) return "Now " + bios.current + ", newest unknown (offline?)";
|
||||
if (bios.current === bios.newest) return bios.current + " is up to date";
|
||||
return "Now " + bios.current + ", newest " + bios.newest + " · at your own risk";
|
||||
}
|
||||
ListModel { id: logModel }
|
||||
|
||||
readonly property var status: backend.status || ({})
|
||||
@@ -151,15 +165,39 @@ ApplicationWindow {
|
||||
screen = "applying";
|
||||
backend.apply(wantedIds(), want.gaming ? boot : "", reapply, password || "");
|
||||
}
|
||||
function askPassword(forWhat) {
|
||||
pending = forWhat;
|
||||
if (backend.needsPassword()) { wrongPassword = false; pw.text = ""; screen = "password"; pw.forceActiveFocus(); return true; }
|
||||
return false;
|
||||
}
|
||||
function onApplyPressed() {
|
||||
if (plan.length === 0) return;
|
||||
if (backend.needsPassword()) { wrongPassword = false; pw.text = ""; screen = "password"; pw.forceActiveFocus(); }
|
||||
else startApply("");
|
||||
if (!askPassword("apply")) startApply("");
|
||||
}
|
||||
function submitPassword() {
|
||||
if (!backend.checkPassword(pw.text)) { wrongPassword = true; pw.selectAll(); return; }
|
||||
var p = pw.text; pw.text = "";
|
||||
startApply(p);
|
||||
if (pending === "bios") { sessionPassword = p; biosCheck(); }
|
||||
else startApply(p);
|
||||
}
|
||||
// --- BIOS: check -> warning 1 -> warning 2 -> flash ---
|
||||
function startBios() {
|
||||
if (!bios || !bios.selectable) return;
|
||||
biosInfo = {}; sessionPassword = "";
|
||||
if (!askPassword("bios")) biosCheck();
|
||||
}
|
||||
function biosStep(action) {
|
||||
steps = {}; failed = []; restartNeeded = false; runError = ""; doneCount = 0; logModel.clear();
|
||||
plan = [{ id: "bios", action: action }];
|
||||
var s = {}; s.bios = "wait"; steps = s;
|
||||
biosRun = true; screen = "applying";
|
||||
}
|
||||
function biosCheck() { biosStep("check"); backend.biosPrepare(sessionPassword); }
|
||||
function biosCancel() { sessionPassword = ""; holdProgress = 0; holding = false; biosRun = false; syncFromStatus(); screen = "menu"; }
|
||||
function biosFlash() { holdProgress = 0; holding = false; biosStep("flash"); backend.biosFlash(sessionPassword); }
|
||||
Timer {
|
||||
interval: 50; repeat: true; running: screen === "bios2" && holding
|
||||
onTriggered: { holdProgress = Math.min(1, holdProgress + 0.01); if (holdProgress >= 1) biosFlash(); }
|
||||
}
|
||||
Connections {
|
||||
target: backend
|
||||
@@ -168,7 +206,9 @@ ApplicationWindow {
|
||||
if (ev.event === "start") { s[ev.id] = "run"; steps = s; }
|
||||
else if (ev.event === "done") { s[ev.id] = ev.ok ? "ok" : "fail"; steps = s; doneCount++; }
|
||||
else if (ev.event === "log") { logModel.append({ line: ev.line }); if (logModel.count > 400) logModel.remove(0); }
|
||||
else if (ev.event === "bios-ready") { biosInfo = ev; biosFocus = 0; holdProgress = 0; screen = "bios1"; }
|
||||
else if (ev.event === "finished") {
|
||||
if (!(biosRun && plan.length && plan[0].action === "check" && !ev.failed.length && !ev.error && !ev.nothing)) sessionPassword = "";
|
||||
failed = ev.failed || []; restartNeeded = !!ev.restart;
|
||||
runError = ev.error === "wrong-password" ? "The password didn't work." : (ev.error ? "sudo isn't available." : "");
|
||||
screen = "done";
|
||||
@@ -185,6 +225,7 @@ ApplicationWindow {
|
||||
else if (a === "accept") {
|
||||
if (sel === rows.length) { goReview(false); return; }
|
||||
var r = rows[sel];
|
||||
if (r.kind === "action" && r.id === "bios") { startBios(); return; }
|
||||
if (r.kind === "choice") boot = boot === "gamescope" ? "desktop" : "gamescope";
|
||||
else if (r.kind === "toggle") toggle(r.id);
|
||||
}
|
||||
@@ -199,21 +240,36 @@ ApplicationWindow {
|
||||
} else if (screen === "password") {
|
||||
if (a === "accept" || a === "apply") submitPassword();
|
||||
else if (a === "back") { pw.text = ""; screen = "review"; }
|
||||
} else if (screen === "bios1") {
|
||||
if (a === "left") biosFocus = 0;
|
||||
else if (a === "right") biosFocus = 1;
|
||||
else if (a === "accept") { if (biosFocus === 1) { holdProgress = 0; biosTyped.text = ""; screen = "bios2"; if (inputType === "keyboard") biosTyped.forceActiveFocus(); } else biosCancel(); }
|
||||
else if (a === "back") biosCancel();
|
||||
} else if (screen === "bios2") {
|
||||
if (a === "back") biosCancel();
|
||||
else if (a === "hold") holding = true;
|
||||
else if (a === "release") { holding = false; if (holdProgress < 1) holdProgress = 0; }
|
||||
} else if (screen === "done") {
|
||||
if (a === "accept" && restartNeeded) backend.restart();
|
||||
else if (a === "back" || a === "accept") { syncFromStatus(); screen = "menu"; }
|
||||
else if (a === "back" || a === "accept") { biosRun = false; syncFromStatus(); screen = "menu"; }
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: gamepad
|
||||
function onButton(b) {
|
||||
if (b === "a_up") { if (screen === "bios2") act("release"); return; }
|
||||
if (b.slice(-3) === "_up") return;
|
||||
inputType = "controller";
|
||||
if (b === "a" && screen === "bios2") { act("hold"); return; }
|
||||
var map = { a: "accept", b: "back", x: "apply", y: "reapply", start: "apply", up: "up", down: "down", left: "left", right: "right" };
|
||||
if (map[b]) act(map[b]);
|
||||
}
|
||||
}
|
||||
function keyAct(e) {
|
||||
var fromRemote = Date.now() - remoteAt < 400;
|
||||
if (screen === "bios2" && inputType !== "keyboard" && (e.key === Qt.Key_Return || e.key === Qt.Key_Enter)) {
|
||||
if (!e.isAutoRepeat) act("hold"); e.accepted = true; return;
|
||||
}
|
||||
if (!fromRemote) inputType = "keyboard";
|
||||
var k = e.key;
|
||||
if (k === Qt.Key_Up) act("up");
|
||||
@@ -266,6 +322,18 @@ ApplicationWindow {
|
||||
Text { id: ct; anchors.centerIn: parent; text: parent.text.toUpperCase(); color: parent.fg; font.family: t.body; font.pixelSize: 12; font.weight: Font.DemiBold; font.letterSpacing: 0.6 }
|
||||
}
|
||||
|
||||
component RiskList: Column {
|
||||
spacing: 8
|
||||
Repeater {
|
||||
model: ["A failed or interrupted BIOS update can leave the machine unable to start (bricked). Steamify, CachyOS and Valve take no responsibility for that.",
|
||||
"NEVER turn off the power, unplug the machine or press the power button while it updates, including the restart(s) afterwards.",
|
||||
"The screen can stay black for several minutes. Wait.",
|
||||
"Only on mains power, with all other programs closed."]
|
||||
Row { required property string modelData; spacing: 10; width: parent.width
|
||||
Rectangle { width: 6; height: 6; radius: 3; color: t.bad; y: 9 }
|
||||
Text { text: parent.modelData; width: parent.width - 16; wrapMode: Text.WordWrap; color: t.soft; font.family: t.body; font.pixelSize: 15; lineHeight: 1.3 } }
|
||||
}
|
||||
}
|
||||
// --- The 1280x720 stage, scaled to the window ---
|
||||
Item {
|
||||
id: stage
|
||||
@@ -274,6 +342,7 @@ ApplicationWindow {
|
||||
scale: Math.min(win.width / 1280, win.height / 720)
|
||||
focus: true
|
||||
Keys.onPressed: function (e) { if (!(screen === "password" && pw.activeFocus && e.key !== Qt.Key_Escape && e.key !== Qt.Key_Return && e.key !== Qt.Key_Enter)) keyAct(e); }
|
||||
Keys.onReleased: function (e) { if (screen === "bios2" && !e.isAutoRepeat && (e.key === Qt.Key_Return || e.key === Qt.Key_Enter)) act("release"); }
|
||||
|
||||
// Header
|
||||
Rectangle {
|
||||
@@ -283,7 +352,7 @@ ApplicationWindow {
|
||||
anchors.left: parent.left; anchors.leftMargin: 40; anchors.verticalCenter: parent.verticalCenter; spacing: 14
|
||||
Image { source: iconUrl; width: 40; height: 40; sourceSize: Qt.size(80, 80); anchors.verticalCenter: parent.verticalCenter }
|
||||
Text { text: "Steamify"; color: t.text; font.family: t.display; font.pixelSize: 28; font.weight: Font.Bold; anchors.verticalCenter: parent.verticalCenter }
|
||||
Text { text: screen === "menu" ? "v" + (status.version || "") : "/ " + ({review: "Review", password: "Password", applying: "Applying", done: "Done"})[screen]
|
||||
Text { text: screen === "menu" ? "v" + (status.version || "") : "/ " + ({review: "Review", password: "Password", applying: biosRun ? "BIOS update" : "Applying", done: "Done", bios1: "BIOS update", bios2: "BIOS update"})[screen]
|
||||
color: t.faint; font.family: screen === "menu" ? t.mono : t.body; font.pixelSize: screen === "menu" ? 13 : 15; anchors.verticalCenter: parent.verticalCenter }
|
||||
}
|
||||
Rectangle {
|
||||
@@ -356,9 +425,9 @@ ApplicationWindow {
|
||||
anchors.verticalCenter: parent.verticalCenter; spacing: 2
|
||||
x: row.modelData.parent ? 46 : 16
|
||||
width: controls.x - x - 16
|
||||
opacity: row.modelData.kind === "action" ? 0.6 : 1
|
||||
opacity: row.modelData.kind === "action" && !(bios && bios.selectable) ? 0.6 : 1
|
||||
Text { text: label(row.modelData); color: t.textHi; font.family: t.body; font.pixelSize: 17; font.weight: Font.DemiBold; elide: Text.ElideRight; width: parent.width }
|
||||
Text { text: (texts[row.modelData.id] && texts[row.modelData.id].hint) || row.modelData.hint; color: t.mute; font.family: t.body; font.pixelSize: 13; elide: Text.ElideRight; width: parent.width }
|
||||
Text { text: row.modelData.id === "bios" ? biosHint() : ((texts[row.modelData.id] && texts[row.modelData.id].hint) || row.modelData.hint); color: t.mute; font.family: t.body; font.pixelSize: 13; elide: Text.ElideRight; width: parent.width }
|
||||
}
|
||||
// Right: every control ends on the same edge
|
||||
Item {
|
||||
@@ -384,7 +453,7 @@ ApplicationWindow {
|
||||
// action
|
||||
Rectangle { visible: row.modelData.kind === "action"; anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter
|
||||
width: at.implicitWidth + 24; height: 30; radius: 8; color: t.warnBg
|
||||
Text { id: at; anchors.centerIn: parent; text: "In the terminal"; color: t.warn; font.family: t.body; font.pixelSize: 13; font.weight: Font.DemiBold } }
|
||||
Text { id: at; anchors.centerIn: parent; text: bios && bios.selectable ? "Update…" : (bios && bios.newest ? "Up to date" : "Unavailable"); color: t.warn; font.family: t.body; font.pixelSize: 13; font.weight: Font.DemiBold } }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -540,7 +609,7 @@ ApplicationWindow {
|
||||
anchors.top: header.bottom; anchors.bottom: parent.bottom; width: parent.width
|
||||
Column {
|
||||
x: 40; y: 32; width: 560; spacing: 18
|
||||
Text { text: "Applying your changes"; color: t.text; font.family: t.display; font.pixelSize: 34; font.weight: Font.DemiBold }
|
||||
Text { text: biosRun ? (plan.length && plan[0].action === "check" ? "Checking the BIOS update" : "Installing the BIOS update") : "Applying your changes"; color: t.text; font.family: t.display; font.pixelSize: 34; font.weight: Font.DemiBold }
|
||||
Rectangle { width: 560; height: 10; radius: 5; color: t.line
|
||||
Rectangle { height: 10; radius: 5; color: t.accent; width: plan.length ? parent.width * Math.min(1, (doneCount + 0.3) / plan.length) : 0; Behavior on width { NumberAnimation { duration: 300 } } } }
|
||||
Row { width: 560
|
||||
@@ -556,7 +625,7 @@ ApplicationWindow {
|
||||
border.width: parent.parent.st === "wait" || parent.parent.st === "run" ? 2 : 0; border.color: parent.parent.st === "run" ? t.accent : "#343f50"
|
||||
Text { anchors.centerIn: parent; text: parent.parent.parent.st === "ok" ? "✓" : (parent.parent.parent.st === "fail" ? "!" : ""); color: parent.parent.parent.st === "ok" ? t.good : t.bad; font.pixelSize: 13; font.weight: Font.Bold }
|
||||
RotationAnimator on rotation { running: parent.parent.parent.st === "run"; from: 0; to: 360; duration: 1000; loops: Animation.Infinite } }
|
||||
Text { text: ({ on: "Turn on ", off: "Turn off ", again: "Re-apply ", desktop: "Boot into ", gaming: "Boot into " })[parent.parent.modelData.action] + ((texts[parent.parent.modelData.id] || {}).label || parent.parent.modelData.id)
|
||||
Text { text: ({ on: "Turn on ", off: "Turn off ", again: "Re-apply ", desktop: "Boot into ", gaming: "Boot into ", check: "Download and check the ", flash: "Hand to fwupd: the " })[parent.parent.modelData.action] + ((texts[parent.parent.modelData.id] || {}).label || parent.parent.modelData.id)
|
||||
color: parent.parent.st === "wait" ? t.faint : t.textHi; font.family: t.body; font.pixelSize: 16; font.weight: Font.DemiBold; anchors.verticalCenter: parent.verticalCenter; width: 380; elide: Text.ElideRight }
|
||||
Text { text: ({ wait: "Waiting", run: "Working…", ok: "Done", fail: "Problem" })[parent.parent.st]; color: parent.parent.st === "ok" ? t.good : (parent.parent.st === "fail" ? t.bad : "#b8c3d1"); font.family: t.body; font.pixelSize: 13; anchors.verticalCenter: parent.verticalCenter }
|
||||
}
|
||||
@@ -573,6 +642,71 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
// ================= BIOS: warning 1 =================
|
||||
Item {
|
||||
visible: screen === "bios1"
|
||||
anchors.top: header.bottom; anchors.bottom: parent.bottom; width: parent.width
|
||||
Rectangle {
|
||||
x: 40; y: 28; width: 1200; height: parent.height - 28 - 64 - 24; radius: 16; color: "#1d1416"; border.width: 2; border.color: "#b33a3a"
|
||||
Row { anchors.fill: parent; anchors.margins: 32; spacing: 40
|
||||
Column { width: 640; spacing: 16
|
||||
Row { spacing: 12
|
||||
Rectangle { width: 44; height: 44; radius: 12; color: "#3a1c1f"; Text { anchors.centerIn: parent; text: "!"; color: t.bad; font.pixelSize: 26; font.weight: Font.Bold } }
|
||||
Text { text: "BIOS update – entirely at your own risk"; color: "#ffd9d3"; font.family: t.display; font.pixelSize: 30; font.weight: Font.DemiBold; anchors.verticalCenter: parent.verticalCenter } }
|
||||
RiskList { width: 640 }
|
||||
}
|
||||
Rectangle { width: 440; height: 250; radius: 14; color: "#161d27"
|
||||
Column { anchors.fill: parent; anchors.margins: 22; spacing: 12
|
||||
Repeater { model: [["Current BIOS", biosInfo.current || ""], ["New BIOS", biosInfo.newest || ""], ["Checksum", "OK (Valve's package)"],
|
||||
["Compatible", biosInfo.compatible === "yes" ? "Yes (checked by fwupd)" : "Not checked (dry run)"]]
|
||||
Row { required property var modelData; width: 396
|
||||
Text { text: parent.modelData[0]; color: t.faint; font.family: t.body; font.pixelSize: 15; width: 150 }
|
||||
Text { text: parent.modelData[1]; color: parent.modelData[1].indexOf("Not") === 0 ? t.warn : t.text; font.family: t.mono; font.pixelSize: 15; width: 246; horizontalAlignment: Text.AlignRight; elide: Text.ElideLeft } } }
|
||||
} }
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
anchors.bottom: parent.bottom; width: parent.width; height: 64; color: t.bar
|
||||
Rectangle { width: parent.width; height: 1; color: t.line }
|
||||
Text { anchors.left: parent.left; anchors.leftMargin: 40; anchors.verticalCenter: parent.verticalCenter; text: "Do you understand the risks and want to continue?"; color: t.soft; font.family: t.body; font.pixelSize: 15 }
|
||||
Row { anchors.right: parent.right; anchors.rightMargin: 40; anchors.verticalCenter: parent.verticalCenter; spacing: 12
|
||||
Btn { k: biosFocus === 0 ? g.ok : "◀"; text: "Cancel"; focusRing: biosFocus === 0; onClicked: biosCancel() }
|
||||
Btn { k: biosFocus === 1 ? g.ok : "▶"; text: "I understand, continue"; focusRing: biosFocus === 1; color: "#8a2c2c"
|
||||
onClicked: { biosFocus = 1; act("accept"); } } }
|
||||
}
|
||||
}
|
||||
|
||||
// ================= BIOS: warning 2 (last chance) =================
|
||||
Item {
|
||||
visible: screen === "bios2"
|
||||
anchors.top: header.bottom; anchors.bottom: parent.bottom; width: parent.width
|
||||
Rectangle {
|
||||
anchors.centerIn: parent; width: 760; height: b2.implicitHeight + 64; radius: 20; color: "#1d1416"; border.width: 2; border.color: "#b33a3a"
|
||||
Column { id: b2; anchors.fill: parent; anchors.margins: 32; spacing: 18
|
||||
Text { text: "Last chance: this flashes BIOS " + (biosInfo.newest || ""); color: "#ffd9d3"; font.family: t.display; font.pixelSize: 32; font.weight: Font.DemiBold }
|
||||
Text { width: parent.width; wrapMode: Text.WordWrap; color: t.soft; font.family: t.body; font.pixelSize: 16; lineHeight: 1.3
|
||||
text: "After this, keep the power on until the machine has fully started again. Don't touch it, even if the screen is black." }
|
||||
// keyboard: type UPDATE
|
||||
Column { visible: inputType === "keyboard"; width: parent.width; spacing: 8
|
||||
Text { text: "Type UPDATE (in capitals) and press Enter to flash the BIOS:"; color: t.text; font.family: t.body; font.pixelSize: 15; font.weight: Font.DemiBold }
|
||||
TextField { id: biosTyped; width: 320; height: 50; color: t.textHi; font.family: t.mono; font.pixelSize: 20; leftPadding: 14
|
||||
placeholderText: "UPDATE"; placeholderTextColor: "#6b5a5a"
|
||||
background: Rectangle { radius: 10; color: t.bg; border.width: 2; border.color: biosTyped.text === "UPDATE" ? t.bad : "#5a3a3a" }
|
||||
Keys.onReturnPressed: if (text === "UPDATE") biosFlash()
|
||||
Keys.onEnterPressed: if (text === "UPDATE") biosFlash()
|
||||
Keys.onEscapePressed: biosCancel() } }
|
||||
// controller / remote: hold A or OK
|
||||
Column { visible: inputType !== "keyboard"; width: parent.width; spacing: 10
|
||||
Text { text: "Hold " + g.ok + " for 5 seconds to flash the BIOS. Let go to stop."; color: t.text; font.family: t.body; font.pixelSize: 15; font.weight: Font.DemiBold }
|
||||
Rectangle { width: parent.width; height: 14; radius: 7; color: "#3a2224"
|
||||
Rectangle { height: 14; radius: 7; color: t.bad; width: parent.width * holdProgress } } }
|
||||
Row { spacing: 12
|
||||
Btn { k: g.back; text: "Cancel"; onClicked: biosCancel() }
|
||||
Text { visible: bios && bios.dryRun; text: "Dry run: nothing will be flashed."; color: t.warn; font.family: t.body; font.pixelSize: 14; anchors.verticalCenter: parent.verticalCenter } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ================= DONE =================
|
||||
Item {
|
||||
visible: screen === "done"
|
||||
@@ -584,7 +718,7 @@ ApplicationWindow {
|
||||
Text { anchors.centerIn: parent; text: failed.length || runError ? "!" : "✓"; color: failed.length || runError ? t.warn : t.good; font.pixelSize: 36; font.weight: Font.Bold } }
|
||||
Text { text: runError ? "Nothing changed" : (failed.length ? "Done, with a problem" : "All done"); color: t.text; font.family: t.display; font.pixelSize: 40; font.weight: Font.DemiBold; anchors.horizontalCenter: parent.horizontalCenter }
|
||||
Text { width: parent.width; horizontalAlignment: Text.AlignHCenter; wrapMode: Text.WordWrap; color: t.soft; font.family: t.body; font.pixelSize: 17
|
||||
text: runError ? runError : (plan.length + (plan.length === 1 ? " change" : " changes") + " applied." + (restartNeeded ? " Some take effect after a restart." : "")) }
|
||||
text: runError ? runError : (biosRun ? (failed.length ? "The BIOS was not changed." : (bios && bios.dryRun ? "Dry run: nothing was flashed. " : "BIOS " + (biosInfo.newest || "") + " is staged and is written during the restart. ") + (failed.length ? "" : "Keep the power on and don't touch the machine until it has fully started again, even if the screen stays black.")) : (plan.length + (plan.length === 1 ? " change" : " changes") + " applied." + (restartNeeded ? " Some take effect after a restart." : ""))) }
|
||||
Rectangle { visible: failed.length > 0; anchors.horizontalCenter: parent.horizontalCenter; width: ft.implicitWidth + 32; height: 44; radius: 12; color: t.warnBg
|
||||
Text { id: ft; anchors.centerIn: parent; color: "#f2c27a"; font.family: t.body; font.pixelSize: 14
|
||||
text: "Had a problem: " + failed.map(function (id) { return (texts[id] || {}).label || id; }).join(", ") + ". See the details." } }
|
||||
|
||||
+27
-12
@@ -81,7 +81,8 @@ def controller_devices():
|
||||
|
||||
|
||||
class Gamepad(QObject):
|
||||
"""Emits button("a"|"b"|"x"|"y"|"up"|...) from any controller."""
|
||||
"""Emits button("a"|"b"|"x"|"y"|"up"|...) from any controller, and
|
||||
"<name>_up" when a button is released."""
|
||||
button = Signal(str)
|
||||
|
||||
def __init__(self):
|
||||
@@ -119,8 +120,10 @@ class Gamepad(QObject):
|
||||
continue
|
||||
for off in range(0, len(data) - EVENT.size + 1, EVENT.size):
|
||||
_, _, typ, code, value = EVENT.unpack_from(data, off)
|
||||
if typ == EV_KEY and value == 1 and code in BUTTONS:
|
||||
self.button.emit(BUTTONS[code])
|
||||
if typ == EV_KEY and code in BUTTONS and value in (0, 1):
|
||||
# Presses as "a"; releases as "a_up" (the BIOS
|
||||
# confirmation is a 5-second hold).
|
||||
self.button.emit(BUTTONS[code] + ("" if value else "_up"))
|
||||
elif typ == EV_ABS and code in (ABS_HAT0X, ABS_HAT0Y) and value:
|
||||
self.button.emit({(ABS_HAT0X, -1): "left", (ABS_HAT0X, 1): "right",
|
||||
(ABS_HAT0Y, -1): "up", (ABS_HAT0Y, 1): "down"}[(code, value)])
|
||||
@@ -221,16 +224,9 @@ class Backend(QObject):
|
||||
shutil.rmtree(self._secret_dir, ignore_errors=True)
|
||||
self._secret_dir = None
|
||||
|
||||
@Slot("QVariant", str, bool, str)
|
||||
def apply(self, ids, boot, reapply, password):
|
||||
def _start(self, args, password):
|
||||
if self._busy:
|
||||
return
|
||||
args = [self._script, "--backend", "apply"]
|
||||
if reapply:
|
||||
args.append("--reapply")
|
||||
if boot:
|
||||
args += ["--boot", boot]
|
||||
args += list(ids)
|
||||
env = QProcessEnvironment.systemEnvironment()
|
||||
if password:
|
||||
self._secret_dir, askpass, bindir = self._make_askpass(password)
|
||||
@@ -240,7 +236,7 @@ class Backend(QObject):
|
||||
p.setProcessEnvironment(env)
|
||||
p.setProcessChannelMode(QProcess.MergedChannels)
|
||||
p.setProgram("bash")
|
||||
p.setArguments(args)
|
||||
p.setArguments([self._script, "--backend"] + args)
|
||||
self._buf = b""
|
||||
p.readyReadStandardOutput.connect(lambda: self._read(p))
|
||||
p.finished.connect(lambda *_: self._finished(p))
|
||||
@@ -249,6 +245,25 @@ class Backend(QObject):
|
||||
self.busyChanged.emit()
|
||||
p.start()
|
||||
|
||||
@Slot("QVariant", str, bool, str)
|
||||
def apply(self, ids, boot, reapply, password):
|
||||
args = ["apply"]
|
||||
if reapply:
|
||||
args.append("--reapply")
|
||||
if boot:
|
||||
args += ["--boot", boot]
|
||||
self._start(args + list(ids), password)
|
||||
|
||||
@Slot(str)
|
||||
def biosPrepare(self, password):
|
||||
"""Download, checksum, fwupd check: ends with a bios-ready event."""
|
||||
self._start(["bios-prepare"], password)
|
||||
|
||||
@Slot(str)
|
||||
def biosFlash(self, password):
|
||||
"""Only after both warnings in the app."""
|
||||
self._start(["bios-flash"], password)
|
||||
|
||||
def _read(self, p):
|
||||
self._buf += bytes(p.readAllStandardOutput())
|
||||
while b"\n" in self._buf:
|
||||
|
||||
Reference in new issue
Block a user