diff --git a/Help.md b/Help.md index 67410e3..e7d93ad 100644 --- a/Help.md +++ b/Help.md @@ -60,7 +60,6 @@ * Dual Position Switching functionality: * Click the desktop window to bring the foreground window to its previous background position and Z-order. - * Shift + Click the desktop window to bring the foreground window to its *second* last background position. This is useful if the previous background position is mis-captured due to invoking start menu or other popups. * Ctrl + Click the desktop window to bring the foreground window to its previous Z-order while keeping the current location and size. * To cancel Dual Position Switching: diff --git a/Ninjacrab.PersistentWindows.Solution/Common/Models/ApplicationDisplayMetrics.cs b/Ninjacrab.PersistentWindows.Solution/Common/Models/ApplicationDisplayMetrics.cs index a18acd2..1c4acc4 100644 --- a/Ninjacrab.PersistentWindows.Solution/Common/Models/ApplicationDisplayMetrics.cs +++ b/Ninjacrab.PersistentWindows.Solution/Common/Models/ApplicationDisplayMetrics.cs @@ -26,6 +26,8 @@ namespace PersistentWindows.Common.Models public bool IsFullScreen { get; set; } public bool IsMinimized { get; set; } public bool IsInvisible { get; set; } + public long Style { get; set; } + public long ExtStyle { get; set; } // for restore window position to display session end time public DateTime CaptureTime { get; set; } diff --git a/Ninjacrab.PersistentWindows.Solution/Common/PersistentWindowProcessor.cs b/Ninjacrab.PersistentWindows.Solution/Common/PersistentWindowProcessor.cs index 20b9297..fbafe35 100644 --- a/Ninjacrab.PersistentWindows.Solution/Common/PersistentWindowProcessor.cs +++ b/Ninjacrab.PersistentWindows.Solution/Common/PersistentWindowProcessor.cs @@ -34,7 +34,7 @@ namespace PersistentWindows.Common public int UserForcedRestoreLatency = 0; private const int CaptureLatency = 3000; // delay in milliseconds from window OS move to capture private const int UserMoveLatency = 1000; // delay in milliseconds from user move/minimize/unminimize/maximize to capture, must < CaptureLatency - private const int ForegroundTimerLatency = UserMoveLatency / 5; + private const int ForegroundTimerLatency = UserMoveLatency / 4; private const int MaxUserMoves = 4; // max user window moves per capture cycle private const int MinWindowOsMoveEvents = 12; // threshold of window move events initiated by OS per capture cycle private const int MaxSnapshots = 38; // 0-9, a-z, ` (for redo last auto restore) and final one for undo last snapshot restore @@ -106,6 +106,7 @@ namespace PersistentWindows.Common private Timer restoreTimer; private Timer restoreFinishedTimer; public bool restoringFromMem = false; // automatic restore from memory or snapshot + private bool restoreSingleWindow = false; public bool restoringFromDB = false; // manual restore from DB public bool autoInitialRestoreFromDB = false; public bool restoringSnapshot = false; // implies restoringFromMem @@ -160,7 +161,7 @@ namespace PersistentWindows.Common private static Dictionary windowProcessName = new Dictionary(); private Process process; - private ProcessPriorityClass processPriority; + public ProcessPriorityClass processPriority; private string appDataFolder; public bool redirectAppDataFolder = false; @@ -482,6 +483,10 @@ namespace PersistentWindows.Common } else if (fullScreenGamingWindow == IntPtr.Zero) { + //create window event may be delayed + if (hwnd != IntPtr.Zero && !noRestoreWindows.Contains(hwnd)) + CaptureWindow(hwnd, 0, DateTime.Now, curDisplayKey); + StartCaptureTimer(); //speed up initial capture @@ -517,12 +522,12 @@ namespace PersistentWindows.Common if (!ctrl_key_pressed && !alt_key_pressed) { //restore window to previous background position - SwitchForeBackground(hwnd, secondBackGround:shift_key_pressed); + SwitchForeBackground(hwnd); } else if (ctrl_key_pressed && !alt_key_pressed) { //restore to previous background zorder with current size/pos - SwitchForeBackground(hwnd, strict_dps_check: false, updateBackgroundPos: true, secondBackGround:shift_key_pressed); + SwitchForeBackground(hwnd, strict_dps_check: false, updateBackgroundPos: true); } else if (!ctrl_key_pressed && alt_key_pressed && !shift_key_pressed) { @@ -593,8 +598,6 @@ namespace PersistentWindows.Common public bool Start(bool auto_restore_from_db, bool auto_restore_last_capture_at_startup) { process = Process.GetCurrentProcess(); - processPriority = process.PriorityClass; - string productName = System.Windows.Forms.Application.ProductName; appDataFolder = redirectAppDataFolder ? "." : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), productName); @@ -768,8 +771,6 @@ namespace PersistentWindows.Common Log.Event("Restore finished in pass {0} with {1} windows recovered for display setting {2}", restorePass, numWindowRestored, curDisplayKey); sessionActive = true; - WriteDataDump(); - if (!wasRestoringSnapshot && !wasRestoringFromDB) { if (!snapshotTakenTime.ContainsKey(curDisplayKey)) @@ -864,6 +865,7 @@ namespace PersistentWindows.Common (s, e) => { process.PriorityClass = ProcessPriorityClass.High; + EndDisplaySession(); WriteDataDump(); Log.Event("Session ending"); }; @@ -872,6 +874,9 @@ namespace PersistentWindows.Common this.displaySettingsChangingHandler = (s, e) => { + if (fastRestore) + process.PriorityClass = ProcessPriorityClass.High; + if (!freezeCapture) { lastDisplayChangeTime = DateTime.Now; @@ -894,9 +899,6 @@ namespace PersistentWindows.Common this.displaySettingsChangedHandler = (s, e) => { - if (fastRestore) - process.PriorityClass = ProcessPriorityClass.High; - string displayKey = GetDisplayKey(); Log.Event("Display settings changed {0}", displayKey); @@ -1486,23 +1488,33 @@ namespace PersistentWindows.Common if (!string.IsNullOrEmpty(className)) { - int title_match_cnt = 0; - IntPtr title_match_hid = IntPtr.Zero; int pos_match_cnt = 0; IntPtr pos_match_hid = IntPtr.Zero; int similar_pos_cnt = 0; int diff_size = int.MaxValue; IntPtr similar_pos_hid = IntPtr.Zero; + DateTime last_killed_time = new DateTime(0); + IntPtr last_killed_hid = IntPtr.Zero; + + long style = User32.GetWindowLong(hwnd, User32.GWL_STYLE); + long ext_style = User32.GetWindowLong(hwnd, User32.GWL_EXSTYLE); var deadAppPos = deadApps[curDisplayKey]; lock(captureLock) foreach (var kid in deadAppPos.Keys) { - var appPos = deadAppPos[kid].Last(); + var appPos = deadAppPos[kid].LastOrDefault(); + if (appPos == null) + continue; if (!procName.Equals(appPos.ProcessName)) continue; + if (appPos.Style != 0 && style != appPos.Style) + continue; + if (appPos.ExtStyle != 0 && ext_style != appPos.ExtStyle) + continue; + if (!className.Equals(appPos.ClassName)) { if (className.Length != appPos.ClassName.Length) @@ -1523,12 +1535,6 @@ namespace PersistentWindows.Common if (rect.Equals(r) && title.Equals(appPos.Title)) return kid; - if (title.Equals(appPos.Title)) - { - title_match_hid = kid; - ++title_match_cnt; - } - if (rect.Equals(r)) { pos_match_cnt++; @@ -1541,6 +1547,12 @@ namespace PersistentWindows.Common similar_pos_cnt++; similar_pos_hid = kid; } + + if (appPos.CaptureTime > last_killed_time) + { + last_killed_time = appPos.CaptureTime; + last_killed_hid = kid; + } } if (pos_match_cnt == 1) @@ -1552,40 +1564,47 @@ namespace PersistentWindows.Common return similar_pos_hid; } - /* - if (title_match_cnt == 1) - { - Log.Event($"matching window with same title"); - return title_match_hid; - } - */ if (!monitorApplications.ContainsKey(curDisplayKey)) return IntPtr.Zero; int proc_name_match_cnt = 0; int class_name_match_cnt = 0; + int class_name_mismatch_cnt = 0; foreach(var h in monitorApplications[curDisplayKey].Keys) { foreach (var dm in monitorApplications[curDisplayKey][h]) { + if (IsMinimized(hwnd) != dm.IsMinimized) + continue; + if (User32.IsWindowVisible(hwnd) == dm.IsInvisible) + continue; + if (dm.ProcessName == procName) + { proc_name_match_cnt++; - if (dm.ClassName == className) - class_name_match_cnt++; + if (dm.ClassName == className) + class_name_match_cnt++; + else + class_name_mismatch_cnt++; + } break; } } - //force match if hwnd is the first live window of the app + //force match last killed pos if hwnd is the first live window of the app if (proc_name_match_cnt == 0) - return similar_pos_hid; + return last_killed_hid; + //force match most closest pos if hwnd is the first sub window of the app if (proc_name_match_cnt == 1 && class_name_match_cnt == 0) return similar_pos_hid; //force match if hwnd-like window has multiple instantiations but has only one top-level matching candidate if (similar_pos_cnt == 1 && class_name_match_cnt > 0) return similar_pos_hid; + + if (class_name_match_cnt > 0 && class_name_mismatch_cnt == 0) + return last_killed_hid; } return IntPtr.Zero; @@ -1970,7 +1989,9 @@ namespace PersistentWindows.Common } // for matching new window with killed one - monitorApplications[display_config][hwnd].Last().ProcessName = windowProcessName[hwnd]; + var dm = monitorApplications[display_config][hwnd].Last(); + if (dm.SnapShotFlags == 0) + dm.CaptureTime = DateTime.Now; //for inheritence in LIFO stile deadApps[display_config][hwnd] = monitorApplications[display_config][hwnd]; @@ -2097,7 +2118,7 @@ namespace PersistentWindows.Common if (eventType == User32Events.EVENT_OBJECT_LOCATIONCHANGE) { - if ((remoteSession || restoreTimes >= MinRestoreTimes) && !restoringSnapshot) + if (((remoteSession && !restoreSingleWindow) || restoreTimes >= MinRestoreTimes) && !restoringSnapshot) { // restore is not finished as long as window location keeps changing CancelRestoreFinishedTimer(); @@ -2111,24 +2132,14 @@ namespace PersistentWindows.Common { case User32Events.EVENT_OBJECT_CREATE: { - if (idObject != 0) - // ignore non-window object (caret etc) - return; - if (restoringFromDB) return; if (freezeCapture || !monitorApplications.ContainsKey(curDisplayKey)) return; - /* - //try to inherit from killed window database - if (FindMatchingKilledWindow(hwnd) != IntPtr.Zero) - { - } - */ userMove = true; - StartCaptureTimer(UserMoveLatency * 4); + StartCaptureTimer(UserMoveLatency / 4); } break; @@ -2604,7 +2615,7 @@ namespace PersistentWindows.Common Log.Event("Bring foreground window {0} to bottom", GetWindowTitle(hwnd)); } - public void SwitchForeBackground(IntPtr hwnd, bool strict_dps_check = true, bool toForeground=false, bool updateBackgroundPos=false, bool secondBackGround = false) + public void SwitchForeBackground(IntPtr hwnd, bool strict_dps_check = true, bool toForeground=false, bool updateBackgroundPos=false) { if (hwnd == IntPtr.Zero || IsTaskBar(hwnd)) return; @@ -2629,8 +2640,6 @@ namespace PersistentWindows.Common if (toForeground && IsTaskBar(front_hwnd)) return; //already foreground - IntPtr firstBackgroundWindow = IntPtr.Zero; - for (; prevIndex >= 0; --prevIndex) { var metrics = monitorApplications[curDisplayKey][hwnd][prevIndex]; @@ -2639,8 +2648,16 @@ namespace PersistentWindows.Common continue; } + if (!toForeground) + { + RECT screenPosition = new RECT(); + User32.GetWindowRect(hwnd, ref screenPosition); + if (screenPosition.Equals(metrics.ScreenPosition)) + continue; + } + IntPtr prevZwnd = metrics.PrevZorderWindow; - if (prevZwnd != front_hwnd && (prevZwnd == IntPtr.Zero || prevZwnd != firstBackgroundWindow)) + if (prevZwnd != front_hwnd) { if (toForeground) { @@ -2649,12 +2666,6 @@ namespace PersistentWindows.Common } else { - if (secondBackGround) - { - firstBackgroundWindow = prevZwnd; - secondBackGround = false; - continue; - } if (IsTaskBar(front_hwnd) && IsTaskBar(prevZwnd)) return; //#266, ignore taskbar (as prev-zwindow) change due to window maximize @@ -3208,9 +3219,13 @@ namespace PersistentWindows.Common if (!restoringFromDB && IsResizableWindow(hwnd)) { Log.Trace($"restore {windowTitle[hwnd]} to last captured position"); + restoreSingleWindow = true; restoringFromMem = true; RestoreApplicationsOnCurrentDisplays(curDisplayKey, hwnd, prevDisplayMetrics.CaptureTime); + restoreSingleWindow = false; restoringFromMem = false; + userMove = true; + StartCaptureTimer(UserMoveLatency / 2); } } return true; @@ -3298,6 +3313,9 @@ namespace PersistentWindows.Common NeedUpdateWindowPlacement = false, ScreenPosition = screenPosition, + Style = User32.GetWindowLong(hwnd, User32.GWL_STYLE), + ExtStyle = User32.GetWindowLong(hwnd, User32.GWL_EXSTYLE), + IsTopMost = IsWindowTopMost(hwnd), NeedClearTopMost = false, @@ -3682,6 +3700,7 @@ namespace PersistentWindows.Common 0, 0, 0, UIntPtr.Zero); Log.Error("restore full screen window {0}", GetWindowTitle(hwnd)); + /* Thread.Sleep(3 * double_clck_interval); style = User32.GetWindowLong(hwnd, User32.GWL_STYLE); @@ -3706,6 +3725,7 @@ namespace PersistentWindows.Common } Log.Error("fail to restore full screen window {0}", GetWindowTitle(hwnd)); + */ } private void RestoreSnapWindow(IntPtr hwnd, RECT target_pos) diff --git a/Ninjacrab.PersistentWindows.Solution/SystrayShell/Program.cs b/Ninjacrab.PersistentWindows.Solution/SystrayShell/Program.cs index 37705b2..73168a9 100644 --- a/Ninjacrab.PersistentWindows.Solution/SystrayShell/Program.cs +++ b/Ninjacrab.PersistentWindows.Solution/SystrayShell/Program.cs @@ -56,6 +56,15 @@ if not errorlevel 1 goto wait_to_finish"; pwp = new PersistentWindowProcessor(); + var process = Process.GetCurrentProcess(); + pwp.processPriority = process.PriorityClass; + process.PriorityClass = ProcessPriorityClass.High; + var timer = new System.Threading.Timer(state => + { + process.PriorityClass = pwp.processPriority; + }); + timer.Change(10000, System.Threading.Timeout.Infinite); + bool splash = true; int delay_restart = 0; int relaunch_delay = 0; diff --git a/Ninjacrab.PersistentWindows.Solution/SystrayShell/Properties/AssemblyInfo.cs b/Ninjacrab.PersistentWindows.Solution/SystrayShell/Properties/AssemblyInfo.cs index a465d6e..03b8722 100644 --- a/Ninjacrab.PersistentWindows.Solution/SystrayShell/Properties/AssemblyInfo.cs +++ b/Ninjacrab.PersistentWindows.Solution/SystrayShell/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("5.63.*")] +[assembly: AssemblyVersion("5.65.*")] diff --git a/Ninjacrab.PersistentWindows.Solution/SystrayShell/SystrayForm.cs b/Ninjacrab.PersistentWindows.Solution/SystrayShell/SystrayForm.cs index e7bd829..28c2a48 100644 --- a/Ninjacrab.PersistentWindows.Solution/SystrayShell/SystrayForm.cs +++ b/Ninjacrab.PersistentWindows.Solution/SystrayShell/SystrayForm.cs @@ -252,7 +252,16 @@ namespace PersistentWindows.SystrayShell if (!upgradeDownloaded.ContainsKey(latestVersion)) { - Process.Start(Program.ProjectUrl + "/releases"); + string url = Program.ProjectUrl + "/releases"; + var os_version = Environment.OSVersion; + if (os_version.Version.Major < 10) + Process.Start(url); + else if (os_version.Version.Build < 22000) + Process.Start(url); + /* windows 11 + else + Process.Start(new ProcessStartInfo(url)); + */ var src_file = $"{Program.ProjectUrl}/releases/download/{latestVersion}/{System.Windows.Forms.Application.ProductName}{latestVersion}.zip"; var dst_file = $"{Program.AppdataFolder}/upgrade.zip";