From e57eb095672bc76bef7fca94772f7a7e6ad99e02 Mon Sep 17 00:00:00 2001 From: Franco Pellicciotti Date: Wed, 20 May 2026 21:44:59 -0400 Subject: [PATCH] Added one line inside the wake block to capture the time of the wake event: obj.wakeTimestamp = os.time() --- WindowManager.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/WindowManager.lua b/WindowManager.lua index 98e2148..57111df 100644 --- a/WindowManager.lua +++ b/WindowManager.lua @@ -38,6 +38,7 @@ obj.isMenuUpdating = false -- Safety guard to prevent concurrent IPC menu render obj.wakeTimer = nil obj.lastScreenCount = #hs.screen.allScreens() obj.lastSavedTime = "Never" +obj.wakeTimestamp = 0 -- Track wake time to isolate screen changes during system startup -- ========================================== -- INTERNAL UTILITIES @@ -232,7 +233,6 @@ end local timerMenu = hs.menubar.new() function updateMenu() - -- Safety Guard: Bail if update is already running from a previous thread block if obj.isMenuUpdating then return end obj.isMenuUpdating = true @@ -270,7 +270,6 @@ obj.powerWatcher = hs.caffeinate.watcher.new(function(event) if event == hs.caffeinate.watcher.systemWillSleep or event == hs.caffeinate.watcher.screensDidSleep or event == hs.caffeinate.watcher.screensDidLock then log("POWER: Sleep event.") obj.isTransitioning = true - -- Stop both timers entirely so they don't fire or stack requests while suspended if obj.autoSaveTimer then obj.autoSaveTimer:stop() end if obj.clockTimer then obj.clockTimer:stop() end elseif event == hs.caffeinate.watcher.systemDidWake or event == hs.caffeinate.watcher.screensDidWake or event == hs.caffeinate.watcher.screensDidUnlock then @@ -279,8 +278,8 @@ obj.powerWatcher = hs.caffeinate.watcher.new(function(event) obj.isMenuUpdating = false obj.isTransitioning = false obj.isRestoring = false + obj.wakeTimestamp = os.time() - -- Safely start timers up only after wakeup initialization if obj.autoSaveTimer then obj.autoSaveTimer:start() end if obj.clockTimer then obj.clockTimer:start() end if obj.wakeTimer then obj.wakeTimer:stop() end @@ -288,15 +287,15 @@ obj.powerWatcher = hs.caffeinate.watcher.new(function(event) local currentScreens = #hs.screen.allScreens() if currentScreens > 1 then + log("WAKE ACTIVATE: Multi-screen detected on wake. Scheduling layout restoration.") + obj.lastScreenCount = currentScreens -- Sync variable immediately so it knows we are docked obj.wakeTimer = hs.timer.doAfter(12, function() obj.isTransitioning = false obj.isRestoring = false - obj.lastScreenCount = currentScreens obj.restoreLayout() obj.wakeTimer = nil end) else - -- Only run rescue if the state actually dropped from multi-monitor down to single monitor during sleep if obj.lastScreenCount > 1 then log("WAKE ACTIVATE: Screen count dropped from " .. obj.lastScreenCount .. " to 1 during sleep. Running rescue.") obj.rescueWindowsToLaptop() @@ -314,6 +313,11 @@ hs.hotkey.bind({"shift", "cmd"}, "R", function() obj.restoreLayout() end) hs.hotkey.bind({"shift", "cmd", "ctrl"}, "L", obj.rescueWindowsToLaptop) obj.screenWatcher = hs.screen.watcher.new(function() + if (os.time() - obj.wakeTimestamp) < 10 then + log("DOCK EVENT: Dropped via wake isolation guard.") + return + end + if obj.isTransitioning or obj.isRestoring or obj.wakeTimer then log("DOCK EVENT: Ignored.") return @@ -364,7 +368,6 @@ function obj.showMenu() local min = math.floor(obj.saveCountdown / 60) local sec = obj.saveCountdown % 60 - -- 1. Base Core Operational Rows local choices = { { text = string.format("⏱️ Next Autosave: %d:%02d", min, sec), @@ -393,11 +396,9 @@ function obj.showMenu() } } - -- 2. Dynamically read layout file and append saved windows local data = hs.json.read(obj.layoutFile) if data and data.windows and #data.windows > 0 then for _, win in ipairs(data.windows) do - -- Verify if the application is currently running on the system server local isRunning = hs.application.get(win.bundleID) or hs.application.get(win.appName) local statusIndicator = isRunning and "🟢" or "🔴" @@ -438,7 +439,6 @@ function obj.showMenu() obj.instanceChooser:show() end --- Export module instance globally for direct IPC command routing WindowManager = obj return obj \ No newline at end of file