@@ -1,5 +1,7 @@ |
| 1 | //! Main application state and event loop. | 1 | //! Main application state and event loop. |
| 2 | | 2 | |
| | 3 | +use std::process::Child; |
| | 4 | + |
| 3 | use anyhow::Result; | 5 | use anyhow::Result; |
| 4 | use gartk_core::{Color, InputEvent, Key, Rect, Size, Theme}; | 6 | use gartk_core::{Color, InputEvent, Key, Rect, Size, Theme}; |
| 5 | use gartk_render::{copy_surface_to_window, Renderer}; | 7 | use gartk_render::{copy_surface_to_window, Renderer}; |
@@ -15,6 +17,7 @@ use crate::ui::{ |
| 15 | Button, ConfirmOverlay, ConfirmResult, DisplayPanel, DisplayPanelResult, Dropdown, | 17 | Button, ConfirmOverlay, ConfirmResult, DisplayPanel, DisplayPanelResult, Dropdown, |
| 16 | DropdownAction, EventResult, MonitorView, TextInput, | 18 | DropdownAction, EventResult, MonitorView, TextInput, |
| 17 | }; | 19 | }; |
| | 20 | +use crate::watchdog; |
| 18 | | 21 | |
| 19 | /// Window dimensions. | 22 | /// Window dimensions. |
| 20 | const WINDOW_WIDTH: u32 = 800; | 23 | const WINDOW_WIDTH: u32 = 800; |
@@ -55,6 +58,9 @@ pub struct App { |
| 55 | // Confirmation state for display changes | 58 | // Confirmation state for display changes |
| 56 | confirm_overlay: Option<ConfirmOverlay>, | 59 | confirm_overlay: Option<ConfirmOverlay>, |
| 57 | pre_change_config: Option<Vec<MonitorConfig>>, | 60 | pre_change_config: Option<Vec<MonitorConfig>>, |
| | 61 | + // Watchdog process for auto-revert (independent of main process) |
| | 62 | + #[allow(dead_code)] // Child kept alive for watchdog process |
| | 63 | + watchdog_child: Option<Child>, |
| 58 | } | 64 | } |
| 59 | | 65 | |
| 60 | impl App { | 66 | impl App { |
@@ -225,6 +231,7 @@ impl App { |
| 225 | save_as_input: None, | 231 | save_as_input: None, |
| 226 | confirm_overlay: None, | 232 | confirm_overlay: None, |
| 227 | pre_change_config: None, | 233 | pre_change_config: None, |
| | 234 | + watchdog_child: None, |
| 228 | }) | 235 | }) |
| 229 | } | 236 | } |
| 230 | | 237 | |
@@ -720,6 +727,22 @@ impl App { |
| 720 | return; | 727 | return; |
| 721 | } | 728 | } |
| 722 | | 729 | |
| | 730 | + // Start the watchdog process for auto-revert |
| | 731 | + // This is more robust than relying on the event loop, which may crash |
| | 732 | + // if the display change causes X connection issues |
| | 733 | + if let Some(ref pre_config) = self.pre_change_config { |
| | 734 | + match watchdog::start_watchdog(pre_config) { |
| | 735 | + Ok(child) => { |
| | 736 | + tracing::info!("started watchdog process for auto-revert"); |
| | 737 | + self.watchdog_child = Some(child); |
| | 738 | + } |
| | 739 | + Err(e) => { |
| | 740 | + tracing::error!("failed to start watchdog: {}", e); |
| | 741 | + // Continue anyway - we still have the in-process timeout |
| | 742 | + } |
| | 743 | + } |
| | 744 | + } |
| | 745 | + |
| 723 | // Show confirmation overlay | 746 | // Show confirmation overlay |
| 724 | let size = self.window.size(); | 747 | let size = self.window.size(); |
| 725 | let window_rect = Rect::new(0, 0, size.width, size.height); | 748 | let window_rect = Rect::new(0, 0, size.width, size.height); |
@@ -732,6 +755,10 @@ impl App { |
| 732 | self.confirm_overlay = None; | 755 | self.confirm_overlay = None; |
| 733 | self.pre_change_config = None; | 756 | self.pre_change_config = None; |
| 734 | | 757 | |
| | 758 | + // Cancel the watchdog by removing its config file |
| | 759 | + watchdog::cancel_watchdog(); |
| | 760 | + self.watchdog_child = None; |
| | 761 | + |
| 735 | // Update original_monitors to current state | 762 | // Update original_monitors to current state |
| 736 | let primary_name = self.monitor_view.primary_name().map(|s| s.to_string()); | 763 | let primary_name = self.monitor_view.primary_name().map(|s| s.to_string()); |
| 737 | self.original_monitors = self | 764 | self.original_monitors = self |
@@ -760,6 +787,10 @@ impl App { |
| 760 | fn revert_to_pre_change(&mut self) { | 787 | fn revert_to_pre_change(&mut self) { |
| 761 | self.confirm_overlay = None; | 788 | self.confirm_overlay = None; |
| 762 | | 789 | |
| | 790 | + // Cancel the watchdog - we're reverting manually |
| | 791 | + watchdog::cancel_watchdog(); |
| | 792 | + self.watchdog_child = None; |
| | 793 | + |
| 763 | if let Some(ref configs) = self.pre_change_config.take() { | 794 | if let Some(ref configs) = self.pre_change_config.take() { |
| 764 | if let Some(ref randr) = self.randr { | 795 | if let Some(ref randr) = self.randr { |
| 765 | // IMPORTANT: Prepare screen size BEFORE reverting | 796 | // IMPORTANT: Prepare screen size BEFORE reverting |