diff --git a/program/MainWindow.cs b/program/MainWindow.cs index 2f91720..83c5453 100644 --- a/program/MainWindow.cs +++ b/program/MainWindow.cs @@ -5,6 +5,7 @@ using Gtk; using UI = Gtk.Builder.ObjectAttribute; using System.Runtime.InteropServices; +using System.Diagnostics; public class MainWindow : Window { @@ -556,16 +557,46 @@ private void OnCloseClicked(object sender, EventArgs e) { + if (KioskMode && !ConfirmPowerOff()) return; Shutdown(); } private void Shutdown() { + // _arduino.Disconnect(); + // _arduino.Dispose(); + // // _settingsWindow?.Destroy(); + // Application.Quit(); + // Release hardware before the OS starts killing things. + if (_mapWindow != null) _mapWindow.StopSession(); + WavePlayer.Stop(); + StopAutoRetry(); + if (_batteryTimerId != 0) { GLib.Source.Remove(_batteryTimerId); _batteryTimerId = 0; } _arduino.Disconnect(); _arduino.Dispose(); - // _settingsWindow?.Destroy(); + + if (!KioskMode) { Application.Quit(); return; } + + // CloseButton is the only label MainWindow itself owns on the splash page. + CloseButton.Label = _Language["Close.ShuttingDown"]; + CloseButton.Sensitive = false; + Option1Button.Sensitive = false; + Option2Button.Sensitive = false; + Option3Button.Sensitive = false; + SettingButton.Sensitive = false; + + // Return to the main loop so that repaint actually happens, then fire. + GLib.Timeout.Add(200, new GLib.TimeoutHandler(OnPowerOffTick)); + } + private bool OnPowerOffTick() +{ + if (!RequestPowerOff()) + { + Console.WriteLine("poweroff refused - quitting instead"); Application.Quit(); } + return false; // one shot +} private void OnNormalSessionClicked(object sender, EventArgs e) { @@ -613,4 +644,58 @@ { RootNoteBook.CurrentPage = PageSound; // back to disease list } + + // ---------- power ---------- + + // Same master switch the boot scripts read. On a dev box the flag is absent, + // so Close just exits the app instead of killing your machine. + private static bool KioskMode + { + get + { + string home = System.Environment.GetEnvironmentVariable("HOME"); + if (string.IsNullOrEmpty(home)) return false; + return System.IO.File.Exists( + System.IO.Path.Combine(home, "kiosk/enabled")); + } + } + + private bool ConfirmPowerOff() + { + var dlg = new MessageDialog(this, + DialogFlags.Modal | DialogFlags.DestroyWithParent, + MessageType.Question, ButtonsType.None, + "{0}", _Language["Close.ConfirmPowerOff"]); + + dlg.AddButton(_Language["Common.Cancel"], (int)ResponseType.Cancel); + dlg.AddButton(_Language["Common.PowerOff"], (int)ResponseType.Ok); + dlg.DefaultResponse = ResponseType.Cancel; + + var r = (ResponseType)dlg.Run(); + dlg.Destroy(); + return r == ResponseType.Ok; + } + + + private static bool RequestPowerOff() + { + if (Spawn("systemctl", "poweroff")) return true; // logind + polkit + if (Spawn("sudo", "-n /sbin/poweroff")) return true; // sudoers fallback + return false; + } + private static bool Spawn(string exe, string args) + { + try + { + var psi = new System.Diagnostics.ProcessStartInfo(exe, args); + psi.UseShellExecute = false; + var proc = System.Diagnostics.Process.Start(psi); + if (proc == null) return false; + if (!proc.WaitForExit(5000)) return true; // still running = request accepted + return proc.ExitCode == 0; + } + catch { return false; } + } + + } \ No newline at end of file