feature-enhancment from #321, automatically inherit data from killed window

This commit is contained in:
Kang Yu 2024-06-16 18:03:02 -07:00
parent e5f7371829
commit 42a6165603
3 changed files with 99 additions and 61 deletions

View file

@ -5,14 +5,6 @@ using PersistentWindows.Common.Diagnostics;
namespace PersistentWindows.Common.Models namespace PersistentWindows.Common.Models
{ {
public class DeadAppPosition
{
public string ClassName { get; set; }
public string Title { get; set; }
public string ProcessPath { get; set; }
public RECT ScreenPosition { get; set; }
}
public class ApplicationDisplayMetrics public class ApplicationDisplayMetrics
{ {
// for LiteDB use only // for LiteDB use only

View file

@ -43,10 +43,12 @@ namespace PersistentWindows.Common
// window position database // window position database
private Dictionary<string, Dictionary<IntPtr, List<ApplicationDisplayMetrics>>> monitorApplications private Dictionary<string, Dictionary<IntPtr, List<ApplicationDisplayMetrics>>> monitorApplications
= new Dictionary<string, Dictionary<IntPtr, List<ApplicationDisplayMetrics>>>(); //in-memory database = new Dictionary<string, Dictionary<IntPtr, List<ApplicationDisplayMetrics>>>(); //in-memory database of live windows
private Dictionary<string, Dictionary<Int64, List<ApplicationDisplayMetrics>>> deadApps
= new Dictionary<string, Dictionary<Int64, List<ApplicationDisplayMetrics>>>(); //database of killed windows
private Int64 lastKilledWindowId = 0; //monotonically increasing unique id for every killed window
private string persistDbName = null; //on-disk database name private string persistDbName = null; //on-disk database name
private Dictionary<string, POINT> lastCursorPos = new Dictionary<string, POINT>(); private Dictionary<string, POINT> lastCursorPos = new Dictionary<string, POINT>();
private Dictionary<string, List<DeadAppPosition>> deadApps = new Dictionary<string, List<DeadAppPosition>>();
private HashSet<IntPtr> allUserMoveWindows = new HashSet<IntPtr>(); private HashSet<IntPtr> allUserMoveWindows = new HashSet<IntPtr>();
private HashSet<IntPtr> unResponsiveWindows = new HashSet<IntPtr>(); private HashSet<IntPtr> unResponsiveWindows = new HashSet<IntPtr>();
private HashSet<IntPtr> noRecordWindows = new HashSet<IntPtr>(); private HashSet<IntPtr> noRecordWindows = new HashSet<IntPtr>();
@ -1012,42 +1014,82 @@ namespace PersistentWindows.Common
User32.MoveWindow(hwnd, target_rect.Left + target_rect.Width / 4, target_rect.Top + target_rect.Height / 4, target_rect.Width / 2, target_rect.Height / 2, true); User32.MoveWindow(hwnd, target_rect.Left + target_rect.Width / 4, target_rect.Top + target_rect.Height / 4, target_rect.Width / 2, target_rect.Height / 2, true);
} }
public bool RecallLastKilledPosition(IntPtr hwnd) public bool RecallLastPositionKilledWindow(IntPtr hwnd)
{ {
if (deadApps.ContainsKey(curDisplayKey)) Int64 kid = FindMatchingKilledWindow(hwnd);
if (kid < 0)
return false;
var d = deadApps[curDisplayKey][kid].Last<ApplicationDisplayMetrics>();
var r = d.ScreenPosition;
User32.MoveWindow(hwnd, r.Left, r.Top, r.Width, r.Height, true);
User32.SetForegroundWindow(hwnd);
Log.Error("Recover last closing location \"{0}\"", GetWindowTitle(hwnd));
return true;
}
public void RecallLastPosition(IntPtr hwnd)
{
int cnt = monitorApplications[curDisplayKey][hwnd].Count;
if (cnt < 2)
return;
var d = monitorApplications[curDisplayKey][hwnd][cnt - 1];
var r = d.ScreenPosition;
User32.MoveWindow(hwnd, r.Left, r.Top, r.Width, r.Height, true);
User32.SetForegroundWindow(hwnd);
Log.Error("Restore last location \"{0}\"", GetWindowTitle(hwnd));
}
private void InheritKilledWindow(IntPtr hwnd, Int64 kid)
{
foreach (var display_key in deadApps.Keys)
{ {
var deadAppPos = deadApps[curDisplayKey]; if (deadApps[display_key].ContainsKey(kid))
string className = GetWindowClassName(hwnd);
if (!string.IsNullOrEmpty(className))
{ {
uint processId = 0; if (!monitorApplications.ContainsKey(display_key))
uint threadId = User32.GetWindowThreadProcessId(hwnd, out processId); monitorApplications[display_key] = new Dictionary<IntPtr, List<ApplicationDisplayMetrics>>();
string procPath = GetProcExePath(processId); monitorApplications[display_key][hwnd] = deadApps[display_key][kid];
string title = GetWindowTitle(hwnd); deadApps[display_key].Remove(kid);
int idx = deadAppPos.Count; }
foreach (var appPos in deadAppPos.Reverse<DeadAppPosition>()) }
{ }
--idx;
if (!className.Equals(appPos.ClassName)) private Int64 FindMatchingKilledWindow(IntPtr hwnd)
continue; {
if (!title.Equals(appPos.Title)) if (!deadApps.ContainsKey(curDisplayKey))
continue; return -1;
if (!procPath.Equals(appPos.ProcessPath))
continue;
// found match var deadAppPos = deadApps[curDisplayKey];
RECT r = appPos.ScreenPosition; string className = GetWindowClassName(hwnd);
User32.MoveWindow(hwnd, r.Left, r.Top, r.Width, r.Height, true); if (!string.IsNullOrEmpty(className))
User32.SetForegroundWindow(hwnd); {
Log.Error("Recover last closing location\"{0}\"", GetWindowTitle(hwnd)); string procName = windowProcessName[hwnd];
//deadApps[curDisplayKey].RemoveAt(idx); string title = GetWindowTitle(hwnd);
return true; foreach (var kid in deadAppPos.Keys)
} {
var appPos = deadAppPos[kid].Last<ApplicationDisplayMetrics>();
if (!className.Equals(appPos.ClassName))
continue;
if (!procName.Equals(appPos.ProcessName))
continue;
// match position first
RECT r = appPos.ScreenPosition;
RECT rect = new RECT();
User32.GetWindowRect(hwnd, ref rect);
if (rect.Equals(r))
return kid;
// lastly match title
if (title.Equals(appPos.Title))
return kid;
} }
} }
return false; return -1;
} }
private void FixOffScreenWindow(IntPtr hwnd) private void FixOffScreenWindow(IntPtr hwnd)
@ -1059,7 +1101,7 @@ namespace PersistentWindows.Common
return; return;
} }
if (RecallLastKilledPosition(hwnd)) if (RecallLastPositionKilledWindow(hwnd))
return; return;
RECT rect = new RECT(); RECT rect = new RECT();
@ -1374,51 +1416,51 @@ namespace PersistentWindows.Common
} }
noRestoreWindows.Remove(hwnd); noRestoreWindows.Remove(hwnd);
windowProcessName.Remove(hwnd);
debugWindows.Remove(hwnd); debugWindows.Remove(hwnd);
if (fullScreenGamingWindows.Contains(hwnd)) if (fullScreenGamingWindows.Contains(hwnd))
{ {
fullScreenGamingWindows.Remove(hwnd); fullScreenGamingWindows.Remove(hwnd);
exitFullScreenGaming = true; exitFullScreenGaming = true;
} }
windowTitle.Remove(hwnd);
dualPosSwitchWindows.Remove(hwnd); dualPosSwitchWindows.Remove(hwnd);
foreach (var key in monitorApplications.Keys) foreach (var display_config in monitorApplications.Keys)
{ {
if (!monitorApplications[key].ContainsKey(hwnd)) if (!monitorApplications[display_config].ContainsKey(hwnd))
continue; continue;
if (monitorApplications[key][hwnd].Count > 0) if (monitorApplications[display_config][hwnd].Count > 0)
{ {
// save window size of closed app to restore off-screen window later // save window size of closed app to restore off-screen window later
if (!deadApps.ContainsKey(key)) if (!deadApps.ContainsKey(display_config))
{ {
deadApps.Add(key, new List<DeadAppPosition>()); deadApps.Add(display_config, new Dictionary<Int64, List<ApplicationDisplayMetrics>>());
} }
var appPos = new DeadAppPosition();
var lastMetric = monitorApplications[key][hwnd].Last();
appPos.ClassName = lastMetric.ClassName;
appPos.Title = lastMetric.Title;
appPos.ScreenPosition = lastMetric.ScreenPosition;
string procPath = GetProcExePath(lastMetric.ProcessId);
appPos.ProcessPath = procPath;
deadApps[key].Add(appPos);
windowTitle.Remove((IntPtr)lastMetric.WindowId); // for matching new window with killed one
monitorApplications[display_config][hwnd].Last().ProcessName = windowProcessName[hwnd];
//limit list size deadApps[display_config][lastKilledWindowId] = monitorApplications[display_config][hwnd];
while (deadApps[key].Count > 50)
windowTitle.Remove((IntPtr)monitorApplications[display_config][hwnd].Last().WindowId);
//limit deadApp size
foreach (var kid in deadApps[display_config].Keys)
{ {
deadApps[key].RemoveAt(0); if (lastKilledWindowId - kid > 50)
deadApps[display_config].Remove(kid);
break;
} }
} }
monitorApplications[key].Remove(hwnd); monitorApplications[display_config].Remove(hwnd);
} }
bool found = windowTitle.Remove(hwnd); ++lastKilledWindowId;
windowProcessName.Remove(hwnd);
bool found = windowTitle.Remove(hwnd);
if (sessionActive && found) if (sessionActive && found)
{ {
StartCaptureTimer(); //update z-order StartCaptureTimer(); //update z-order
@ -2572,6 +2614,10 @@ namespace PersistentWindows.Common
if (noRestoreWindows.Contains(hwnd)) if (noRestoreWindows.Contains(hwnd))
return false; return false;
Int64 kid = FindMatchingKilledWindow(hwnd);
if (kid >= 0)
InheritKilledWindow(hwnd, kid);
//newly created window or new display setting //newly created window or new display setting
curDisplayMetrics.WindowId = (uint)realHwnd; curDisplayMetrics.WindowId = (uint)realHwnd;

View file

@ -655,7 +655,7 @@ namespace PersistentWindows.SystrayShell
static public void RecallLastKilledPosition() static public void RecallLastKilledPosition()
{ {
pwp.RecallLastKilledPosition(PersistentWindowProcessor.GetForegroundWindow()); pwp.RecallLastPosition(PersistentWindowProcessor.GetForegroundWindow());
} }
static public void CenterWindow() static public void CenterWindow()