mirror of
https://github.com/kangyu-california/PersistentWindows.git
synced 2025-05-10 20:45:38 +02:00
Compare commits
22 commits
Author | SHA1 | Date | |
---|---|---|---|
|
ae8da16834 | ||
|
6745982d60 | ||
|
cd29b29282 | ||
|
3a473bdc10 | ||
|
3d2ed7395c | ||
|
3884c4446f | ||
|
11d0c30ddb | ||
|
f7eb0abe43 | ||
|
cad1ec393f | ||
|
4df563f1bb | ||
|
e8f06c1368 | ||
|
2c06fe1684 | ||
|
9e85ee5810 | ||
|
4998a104ea | ||
|
1fd82f35c8 | ||
|
e7b141e095 | ||
|
304c654e80 | ||
|
9661f8bd79 | ||
|
5d26d86e28 | ||
|
f05d569387 | ||
|
f66dc3afd8 | ||
|
5f666b20d1 |
6 changed files with 96 additions and 57 deletions
1
Help.md
1
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:
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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<IntPtr, string> windowProcessName = new Dictionary<IntPtr, string>();
|
||||
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<ApplicationDisplayMetrics>();
|
||||
var appPos = deadAppPos[kid].LastOrDefault<ApplicationDisplayMetrics>();
|
||||
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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.*")]
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue