Make region selector shower instead of whole screen )
This commit is contained in:
@@ -2,102 +2,86 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using EveOPreview.Services.Interop;
|
||||
|
||||
namespace EveOPreview.Services.Implementation
|
||||
{
|
||||
class DwmThumbnail : IDwmThumbnail
|
||||
{
|
||||
#region Private fields
|
||||
private readonly IWindowManager _windowManager;
|
||||
private IntPtr _handle;
|
||||
private DWM_THUMBNAIL_PROPERTIES _properties;
|
||||
#endregion
|
||||
namespace EveOPreview.Services.Implementation {
|
||||
class DwmThumbnail : IDwmThumbnail {
|
||||
#region Private fields
|
||||
private readonly IWindowManager _windowManager;
|
||||
private IntPtr _handle;
|
||||
private DWM_THUMBNAIL_PROPERTIES _properties;
|
||||
#endregion
|
||||
|
||||
public DwmThumbnail(IWindowManager windowManager)
|
||||
{
|
||||
this._windowManager = windowManager;
|
||||
this._handle = IntPtr.Zero;
|
||||
}
|
||||
public DwmThumbnail(IWindowManager windowManager) {
|
||||
this._windowManager = windowManager;
|
||||
this._handle = IntPtr.Zero;
|
||||
}
|
||||
|
||||
public void Register(IntPtr destination, IntPtr source)
|
||||
{
|
||||
this._properties = new DWM_THUMBNAIL_PROPERTIES();
|
||||
this._properties.dwFlags = DWM_TNP_CONSTANTS.DWM_TNP_VISIBLE
|
||||
+ DWM_TNP_CONSTANTS.DWM_TNP_OPACITY
|
||||
+ DWM_TNP_CONSTANTS.DWM_TNP_RECTDESTINATION
|
||||
+ DWM_TNP_CONSTANTS.DWM_TNP_SOURCECLIENTAREAONLY;
|
||||
this._properties.opacity = 255;
|
||||
this._properties.fVisible = true;
|
||||
this._properties.fSourceClientAreaOnly = true;
|
||||
public void Register(IntPtr destination, IntPtr source) {
|
||||
this._properties = new DWM_THUMBNAIL_PROPERTIES();
|
||||
this._properties.dwFlags = DWM_TNP_CONSTANTS.DWM_TNP_VISIBLE + DWM_TNP_CONSTANTS.DWM_TNP_OPACITY +
|
||||
DWM_TNP_CONSTANTS.DWM_TNP_RECTDESTINATION +
|
||||
DWM_TNP_CONSTANTS.DWM_TNP_SOURCECLIENTAREAONLY;
|
||||
this._properties.opacity = 255;
|
||||
this._properties.fVisible = true;
|
||||
this._properties.fSourceClientAreaOnly = true;
|
||||
this._properties.rcSource = new RECT(0, 0, 0, 0); // Initialize with empty source rect
|
||||
|
||||
if (!this._windowManager.IsCompositionEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!this._windowManager.IsCompositionEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
this._handle = DwmNativeMethods.DwmRegisterThumbnail(destination, source);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// This exception is raised if the source client is already closed
|
||||
// Can happen on a really slow CPU's that the window is still being
|
||||
// listed in the process list yet it already cannot be used as
|
||||
// a thumbnail source
|
||||
this._handle = IntPtr.Zero;
|
||||
}
|
||||
catch (COMException)
|
||||
{
|
||||
// This exception is raised if DWM is suddenly not available
|
||||
// (f.e. when switching between Windows user accounts)
|
||||
this._handle = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
try {
|
||||
this._handle = DwmNativeMethods.DwmRegisterThumbnail(destination, source);
|
||||
} catch (ArgumentException) {
|
||||
// This exception is raised if the source client is already closed
|
||||
// Can happen on a really slow CPU's that the window is still being
|
||||
// listed in the process list yet it already cannot be used as
|
||||
// a thumbnail source
|
||||
this._handle = IntPtr.Zero;
|
||||
} catch (COMException) {
|
||||
// This exception is raised if DWM is suddenly not available
|
||||
// (f.e. when switching between Windows user accounts)
|
||||
this._handle = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
public void Unregister()
|
||||
{
|
||||
if ((!this._windowManager.IsCompositionEnabled) || (this._handle == IntPtr.Zero))
|
||||
{
|
||||
return;
|
||||
}
|
||||
public void Unregister() {
|
||||
if ((!this._windowManager.IsCompositionEnabled) || (this._handle == IntPtr.Zero)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
DwmNativeMethods.DwmUnregisterThumbnail(this._handle);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
}
|
||||
catch (COMException)
|
||||
{
|
||||
// This exception is raised when DWM is not available for some reason
|
||||
}
|
||||
}
|
||||
try {
|
||||
DwmNativeMethods.DwmUnregisterThumbnail(this._handle);
|
||||
} catch (ArgumentException) {
|
||||
} catch (COMException) {
|
||||
// This exception is raised when DWM is not available for some reason
|
||||
}
|
||||
}
|
||||
|
||||
public void Move(int left, int top, int right, int bottom)
|
||||
{
|
||||
this._properties.rcDestination = new RECT(left, top, right, bottom);
|
||||
}
|
||||
public void Move(int left, int top, int right, int bottom) {
|
||||
this._properties.rcDestination = new RECT(left, top, right, bottom);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if ((!this._windowManager.IsCompositionEnabled) || (this._handle == IntPtr.Zero))
|
||||
{
|
||||
return;
|
||||
}
|
||||
public void SetSourceRegion(int left, int top, int right, int bottom) {
|
||||
this._properties.rcSource = new RECT(left, top, right, bottom);
|
||||
this._properties.dwFlags |= DWM_TNP_CONSTANTS.DWM_TNP_RECTSOURCE;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
DwmNativeMethods.DwmUpdateThumbnailProperties(this._handle, this._properties);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// This exception will be thrown if the EVE client disappears while this method is running
|
||||
}
|
||||
catch (COMException)
|
||||
{
|
||||
// This exception is raised when DWM is not available for some reason
|
||||
}
|
||||
}
|
||||
}
|
||||
public void ClearSourceRegion() {
|
||||
this._properties.dwFlags &= ~DWM_TNP_CONSTANTS.DWM_TNP_RECTSOURCE;
|
||||
}
|
||||
|
||||
public void Update() {
|
||||
if ((!this._windowManager.IsCompositionEnabled) || (this._handle == IntPtr.Zero)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
DwmNativeMethods.DwmUpdateThumbnailProperties(this._handle, this._properties);
|
||||
} catch (ArgumentException) {
|
||||
// This exception will be thrown if the EVE client disappears while this method is running
|
||||
} catch (COMException) {
|
||||
// This exception is raised when DWM is not available for some reason
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace EveOPreview.Services.Implementation
|
||||
{
|
||||
sealed class ProcessInfo : IProcessInfo
|
||||
{
|
||||
public ProcessInfo(IntPtr handle, string title)
|
||||
{
|
||||
this.Handle = handle;
|
||||
this.Title = title;
|
||||
}
|
||||
namespace EveOPreview.Services.Implementation {
|
||||
sealed class ProcessInfo : IProcessInfo {
|
||||
public ProcessInfo(IntPtr handle, string title) {
|
||||
this.Handle = handle;
|
||||
this.Title = title;
|
||||
}
|
||||
|
||||
public IntPtr Handle { get; }
|
||||
public string Title { get; }
|
||||
}
|
||||
public IntPtr Handle { get; }
|
||||
public string Title { get; }
|
||||
}
|
||||
}
|
||||
@@ -4,172 +4,148 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace EveOPreview.Services.Implementation
|
||||
{
|
||||
sealed class ProcessMonitor : IProcessMonitor
|
||||
{
|
||||
#region Private constants
|
||||
private const string DEFAULT_PROCESS_NAME = "Uwow-64";
|
||||
private const string CURRENT_PROCESS_NAME = "EVE-O Preview";
|
||||
#endregion
|
||||
namespace EveOPreview.Services.Implementation {
|
||||
sealed class ProcessMonitor : IProcessMonitor {
|
||||
#region Private constants
|
||||
private const string DEFAULT_PROCESS_NAME = "Uwow-64";
|
||||
private const string CURRENT_PROCESS_NAME = "EVE-O Preview";
|
||||
#endregion
|
||||
|
||||
#region Private fields
|
||||
private readonly IDictionary<IntPtr, string> _processCache;
|
||||
private IProcessInfo _currentProcessInfo;
|
||||
private readonly IThumbnailConfiguration _configuration;
|
||||
private readonly HashSet<int> _trackedPids; // Change to HashSet for multiple PIDs
|
||||
#endregion
|
||||
#region Private fields
|
||||
private readonly IDictionary<IntPtr, string> _processCache;
|
||||
private IProcessInfo _currentProcessInfo;
|
||||
private readonly IThumbnailConfiguration _configuration;
|
||||
private readonly HashSet<int> _trackedPids; // Change to HashSet for multiple PIDs
|
||||
#endregion
|
||||
|
||||
public ProcessMonitor(IThumbnailConfiguration configuration)
|
||||
{
|
||||
this._processCache = new Dictionary<IntPtr, string>(512);
|
||||
this._configuration = configuration;
|
||||
this._trackedPids = new HashSet<int>(); // Initialize empty set
|
||||
public ProcessMonitor(IThumbnailConfiguration configuration) {
|
||||
this._processCache = new Dictionary<IntPtr, string>(512);
|
||||
this._configuration = configuration;
|
||||
this._trackedPids = new HashSet<int>(); // Initialize empty set
|
||||
|
||||
// This field cannot be initialized properly in constructor
|
||||
// At the moment this code is executed the main application window is not yet initialized
|
||||
this._currentProcessInfo = new ProcessInfo(IntPtr.Zero, "");
|
||||
}
|
||||
// This field cannot be initialized properly in constructor
|
||||
// At the moment this code is executed the main application window is not yet initialized
|
||||
this._currentProcessInfo = new ProcessInfo(IntPtr.Zero, "");
|
||||
}
|
||||
|
||||
private bool IsMonitoredProcess(string processName, int processId)
|
||||
{
|
||||
// If we're tracking this specific PID, include it
|
||||
if (_trackedPids.Contains(processId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
private bool IsMonitoredProcess(string processName, int processId) {
|
||||
// If we're tracking this specific PID, include it
|
||||
if (_trackedPids.Contains(processId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Also include any processes that match executable tracking
|
||||
return _configuration.IsExecutableToPreview(processName);
|
||||
}
|
||||
// Also include any processes that match executable tracking
|
||||
return _configuration.IsExecutableToPreview(processName);
|
||||
}
|
||||
|
||||
private IProcessInfo GetCurrentProcessInfo()
|
||||
{
|
||||
var currentProcess = Process.GetCurrentProcess();
|
||||
return new ProcessInfo(currentProcess.MainWindowHandle, currentProcess.MainWindowTitle);
|
||||
}
|
||||
private IProcessInfo GetCurrentProcessInfo() {
|
||||
var currentProcess = Process.GetCurrentProcess();
|
||||
return new ProcessInfo(currentProcess.MainWindowHandle, currentProcess.MainWindowTitle);
|
||||
}
|
||||
|
||||
public IProcessInfo GetMainProcess()
|
||||
{
|
||||
if (this._currentProcessInfo.Handle == IntPtr.Zero)
|
||||
{
|
||||
var processInfo = this.GetCurrentProcessInfo();
|
||||
public IProcessInfo GetMainProcess() {
|
||||
if (this._currentProcessInfo.Handle == IntPtr.Zero) {
|
||||
var processInfo = this.GetCurrentProcessInfo();
|
||||
|
||||
// Are we initialized yet?
|
||||
if (processInfo.Title != "")
|
||||
{
|
||||
this._currentProcessInfo = processInfo;
|
||||
}
|
||||
}
|
||||
// Are we initialized yet?
|
||||
if (processInfo.Title != "") {
|
||||
this._currentProcessInfo = processInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return this._currentProcessInfo;
|
||||
}
|
||||
return this._currentProcessInfo;
|
||||
}
|
||||
|
||||
public ICollection<IProcessInfo> GetAllProcesses()
|
||||
{
|
||||
ICollection<IProcessInfo> result = new List<IProcessInfo>(this._processCache.Count);
|
||||
public ICollection<IProcessInfo> GetAllProcesses() {
|
||||
ICollection<IProcessInfo> result = new List<IProcessInfo>(this._processCache.Count);
|
||||
|
||||
// TODO Lock list here just in case
|
||||
foreach (KeyValuePair<IntPtr, string> entry in this._processCache)
|
||||
{
|
||||
result.Add(new ProcessInfo(entry.Key, entry.Value));
|
||||
}
|
||||
// TODO Lock list here just in case
|
||||
foreach (KeyValuePair<IntPtr, string> entry in this._processCache) {
|
||||
result.Add(new ProcessInfo(entry.Key, entry.Value));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void GetUpdatedProcesses(out ICollection<IProcessInfo> addedProcesses, out ICollection<IProcessInfo> updatedProcesses, out ICollection<IProcessInfo> removedProcesses)
|
||||
{
|
||||
addedProcesses = new List<IProcessInfo>(16);
|
||||
updatedProcesses = new List<IProcessInfo>(16);
|
||||
removedProcesses = new List<IProcessInfo>(16);
|
||||
public void GetUpdatedProcesses(out ICollection<IProcessInfo> addedProcesses,
|
||||
out ICollection<IProcessInfo> updatedProcesses,
|
||||
out ICollection<IProcessInfo> removedProcesses) {
|
||||
addedProcesses = new List<IProcessInfo>(16);
|
||||
updatedProcesses = new List<IProcessInfo>(16);
|
||||
removedProcesses = new List<IProcessInfo>(16);
|
||||
|
||||
IList<IntPtr> knownProcesses = new List<IntPtr>(this._processCache.Keys);
|
||||
foreach (Process process in Process.GetProcesses())
|
||||
{
|
||||
string processName = process.ProcessName;
|
||||
int processId = process.Id;
|
||||
IList<IntPtr> knownProcesses = new List<IntPtr>(this._processCache.Keys);
|
||||
foreach (Process process in Process.GetProcesses()) {
|
||||
string processName = process.ProcessName;
|
||||
int processId = process.Id;
|
||||
|
||||
if (!this.IsMonitoredProcess(processName, processId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!this.IsMonitoredProcess(processName, processId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IntPtr mainWindowHandle = process.MainWindowHandle;
|
||||
if (mainWindowHandle == IntPtr.Zero)
|
||||
{
|
||||
continue; // No need to monitor non-visual processes
|
||||
}
|
||||
IntPtr mainWindowHandle = process.MainWindowHandle;
|
||||
if (mainWindowHandle == IntPtr.Zero) {
|
||||
continue; // No need to monitor non-visual processes
|
||||
}
|
||||
|
||||
// Get all processes with same name and sort by PID
|
||||
var sameNameProcesses = Process.GetProcessesByName(processName)
|
||||
.Where(p => p.MainWindowHandle != IntPtr.Zero)
|
||||
.OrderBy(p => p.Id)
|
||||
.ToList();
|
||||
// Get all processes with same name and sort by PID
|
||||
var sameNameProcesses = Process.GetProcessesByName(processName)
|
||||
.Where(p => p.MainWindowHandle != IntPtr.Zero)
|
||||
.OrderBy(p => p.Id)
|
||||
.ToList();
|
||||
|
||||
// Find index of current process in sorted list
|
||||
int index = sameNameProcesses.FindIndex(p => p.Id == process.Id);
|
||||
string mainWindowTitle = $"{process.MainWindowTitle} ({index + 1})";
|
||||
// Find index of current process in sorted list
|
||||
int index = sameNameProcesses.FindIndex(p => p.Id == process.Id);
|
||||
string mainWindowTitle = $"{process.MainWindowTitle} ({index + 1})";
|
||||
|
||||
this._processCache.TryGetValue(mainWindowHandle, out string cachedTitle);
|
||||
this._processCache.TryGetValue(mainWindowHandle, out string cachedTitle);
|
||||
|
||||
if (cachedTitle == null)
|
||||
{
|
||||
// This is a new process in the list
|
||||
this._processCache.Add(mainWindowHandle, mainWindowTitle);
|
||||
addedProcesses.Add(new ProcessInfo(mainWindowHandle, mainWindowTitle));
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is an already known process
|
||||
if (cachedTitle != mainWindowTitle)
|
||||
{
|
||||
this._processCache[mainWindowHandle] = mainWindowTitle;
|
||||
updatedProcesses.Add(new ProcessInfo(mainWindowHandle, mainWindowTitle));
|
||||
}
|
||||
}
|
||||
if (cachedTitle == null) {
|
||||
// This is a new process in the list
|
||||
this._processCache.Add(mainWindowHandle, mainWindowTitle);
|
||||
addedProcesses.Add(new ProcessInfo(mainWindowHandle, mainWindowTitle));
|
||||
} else {
|
||||
// This is an already known process
|
||||
if (cachedTitle != mainWindowTitle) {
|
||||
this._processCache[mainWindowHandle] = mainWindowTitle;
|
||||
updatedProcesses.Add(new ProcessInfo(mainWindowHandle, mainWindowTitle));
|
||||
}
|
||||
}
|
||||
|
||||
knownProcesses.Remove(mainWindowHandle);
|
||||
}
|
||||
knownProcesses.Remove(mainWindowHandle);
|
||||
}
|
||||
|
||||
foreach (IntPtr index in knownProcesses)
|
||||
{
|
||||
string title = this._processCache[index];
|
||||
removedProcesses.Add(new ProcessInfo(index, title));
|
||||
this._processCache.Remove(index);
|
||||
}
|
||||
}
|
||||
foreach (IntPtr index in knownProcesses) {
|
||||
string title = this._processCache[index];
|
||||
removedProcesses.Add(new ProcessInfo(index, title));
|
||||
this._processCache.Remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
// Update to handle multiple PIDs
|
||||
public void SetTrackedPid(int pid)
|
||||
{
|
||||
if (pid == 0) // Special case: 0 means stop tracking all
|
||||
{
|
||||
this._trackedPids.Clear();
|
||||
return;
|
||||
}
|
||||
// Update to handle multiple PIDs
|
||||
public void SetTrackedPid(int pid) {
|
||||
if (pid == 0) // Special case: 0 means stop tracking all
|
||||
{
|
||||
this._trackedPids.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._trackedPids.Contains(pid))
|
||||
{
|
||||
this._trackedPids.Remove(pid); // Toggle off if already tracking
|
||||
}
|
||||
else
|
||||
{
|
||||
this._trackedPids.Add(pid); // Toggle on if not tracking
|
||||
}
|
||||
}
|
||||
if (this._trackedPids.Contains(pid)) {
|
||||
this._trackedPids.Remove(pid); // Toggle off if already tracking
|
||||
} else {
|
||||
this._trackedPids.Add(pid); // Toggle on if not tracking
|
||||
}
|
||||
}
|
||||
|
||||
// Update to return currently tracked PID (for compatibility)
|
||||
public int GetTrackedPid()
|
||||
{
|
||||
// Return the first tracked PID, or 0 if none
|
||||
return this._trackedPids.FirstOrDefault();
|
||||
}
|
||||
// Update to return currently tracked PID (for compatibility)
|
||||
public int GetTrackedPid() {
|
||||
// Return the first tracked PID, or 0 if none
|
||||
return this._trackedPids.FirstOrDefault();
|
||||
}
|
||||
|
||||
// Add method to get all tracked PIDs if needed
|
||||
public IReadOnlyCollection<int> GetTrackedPids()
|
||||
{
|
||||
return this._trackedPids;
|
||||
}
|
||||
}
|
||||
// Add method to get all tracked PIDs if needed
|
||||
public IReadOnlyCollection<int> GetTrackedPids() {
|
||||
return this._trackedPids;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,321 +5,278 @@ using System.Runtime.InteropServices;
|
||||
using EveOPreview.Configuration;
|
||||
using EveOPreview.Services.Interop;
|
||||
|
||||
namespace EveOPreview.Services.Implementation
|
||||
{
|
||||
public class WindowManager : IWindowManager
|
||||
{
|
||||
#region Private constants
|
||||
private const int WINDOW_SIZE_THRESHOLD = 300;
|
||||
private const int NO_ANIMATION = 0;
|
||||
#endregion
|
||||
namespace EveOPreview.Services.Implementation {
|
||||
public class WindowManager : IWindowManager {
|
||||
#region Private constants
|
||||
private const int WINDOW_SIZE_THRESHOLD = 300;
|
||||
private const int NO_ANIMATION = 0;
|
||||
#endregion
|
||||
|
||||
#region Private fields
|
||||
private readonly bool _enableWineCompatabilityMode;
|
||||
private string _bashLocation;
|
||||
private string _wmctrlLocation;
|
||||
private const string EXCEPTION_DUMP_FILE_NAME = "EVE-O-Preview.log";
|
||||
#endregion
|
||||
#region Private fields
|
||||
private readonly bool _enableWineCompatabilityMode;
|
||||
private string _bashLocation;
|
||||
private string _wmctrlLocation;
|
||||
private const string EXCEPTION_DUMP_FILE_NAME = "EVE-O-Preview.log";
|
||||
#endregion
|
||||
|
||||
|
||||
public WindowManager(IThumbnailConfiguration configuration)
|
||||
{
|
||||
public WindowManager(IThumbnailConfiguration configuration) {
|
||||
#if LINUX
|
||||
this._enableWineCompatabilityMode = configuration.EnableWineCompatibilityMode;
|
||||
this._bashLocation = FindLinuxBinLocation("bash");
|
||||
this._wmctrlLocation = FindLinuxBinLocation("wmctrl");
|
||||
this._enableWineCompatabilityMode = configuration.EnableWineCompatibilityMode;
|
||||
this._bashLocation = FindLinuxBinLocation("bash");
|
||||
this._wmctrlLocation = FindLinuxBinLocation("wmctrl");
|
||||
#endif
|
||||
// Composition is always enabled for Windows 8+
|
||||
this.IsCompositionEnabled =
|
||||
((Environment.OSVersion.Version.Major == 6) && (Environment.OSVersion.Version.Minor >= 2)) // Win 8 and Win 8.1
|
||||
|| (Environment.OSVersion.Version.Major >= 10) // Win 10
|
||||
|| DwmNativeMethods.DwmIsCompositionEnabled(); // In case of Win 7 an API call is requiredWin 7
|
||||
_animationParam.cbSize = (System.UInt32)Marshal.SizeOf(typeof(ANIMATIONINFO));
|
||||
}
|
||||
// Composition is always enabled for Windows 8+
|
||||
this.IsCompositionEnabled =
|
||||
((Environment.OSVersion.Version.Major == 6) &&
|
||||
(Environment.OSVersion.Version.Minor >= 2)) // Win 8 and Win 8.1
|
||||
|| (Environment.OSVersion.Version.Major >= 10) // Win 10
|
||||
|| DwmNativeMethods.DwmIsCompositionEnabled(); // In case of Win 7 an API call is requiredWin 7
|
||||
_animationParam.cbSize = (System.UInt32)Marshal.SizeOf(typeof(ANIMATIONINFO));
|
||||
}
|
||||
#if LINUX
|
||||
private string FindLinuxBinLocation(string command)
|
||||
{
|
||||
// Check common paths for command
|
||||
string[] paths = { "/run/host/usr/bin", "/bin", "/usr/bin" };
|
||||
foreach (var path in paths)
|
||||
{
|
||||
string locationToCheck = $"{path}/{command}";
|
||||
if (System.IO.File.Exists(locationToCheck))
|
||||
{
|
||||
string binLocation = System.IO.Path.GetDirectoryName(locationToCheck);
|
||||
string binLocationUnixStyle = binLocation.Replace("\\", "/");
|
||||
private string FindLinuxBinLocation(string command) {
|
||||
// Check common paths for command
|
||||
string[] paths = { "/run/host/usr/bin", "/bin", "/usr/bin" };
|
||||
foreach (var path in paths) {
|
||||
string locationToCheck = $"{path}/{command}";
|
||||
if (System.IO.File.Exists(locationToCheck)) {
|
||||
string binLocation = System.IO.Path.GetDirectoryName(locationToCheck);
|
||||
string binLocationUnixStyle = binLocation.Replace("\\", "/");
|
||||
|
||||
return binLocationUnixStyle;
|
||||
}
|
||||
}
|
||||
return binLocationUnixStyle;
|
||||
}
|
||||
}
|
||||
|
||||
WriteToLog($"[{DateTime.Now}] Error: {command} not found in expected locations.");
|
||||
return null;
|
||||
}
|
||||
WriteToLog($"[{DateTime.Now}] Error: {command} not found in expected locations.");
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
|
||||
private void WriteToLog(string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.IO.File.AppendAllText(EXCEPTION_DUMP_FILE_NAME, message + Environment.NewLine);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to write to log file: {ex.Message}");
|
||||
}
|
||||
}
|
||||
private void WriteToLog(string message) {
|
||||
try {
|
||||
System.IO.File.AppendAllText(EXCEPTION_DUMP_FILE_NAME, message + Environment.NewLine);
|
||||
} catch (Exception ex) {
|
||||
Console.WriteLine($"Failed to write to log file: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private int? _currentAnimationSetting = null;
|
||||
private ANIMATIONINFO _animationParam = new ANIMATIONINFO();
|
||||
private int? _currentAnimationSetting = null;
|
||||
private ANIMATIONINFO _animationParam = new ANIMATIONINFO();
|
||||
|
||||
public bool IsCompositionEnabled { get; }
|
||||
public bool IsCompositionEnabled { get; }
|
||||
|
||||
public IntPtr GetForegroundWindowHandle()
|
||||
{
|
||||
return User32NativeMethods.GetForegroundWindow();
|
||||
}
|
||||
public IntPtr GetForegroundWindowHandle() {
|
||||
return User32NativeMethods.GetForegroundWindow();
|
||||
}
|
||||
|
||||
private void TurnOffAnimation()
|
||||
{
|
||||
var currentAnimationSetup = User32NativeMethods.SystemParametersInfo(User32NativeMethods.SPI_GETANIMATION, (System.Int32)Marshal.SizeOf(typeof(ANIMATIONINFO)), ref _animationParam, 0);
|
||||
if (_currentAnimationSetting == null)
|
||||
{
|
||||
// Store the current Animation Setting
|
||||
_currentAnimationSetting = _animationParam.iMinAnimate;
|
||||
}
|
||||
private void TurnOffAnimation() {
|
||||
var currentAnimationSetup = User32NativeMethods.SystemParametersInfo(
|
||||
User32NativeMethods.SPI_GETANIMATION, (System.Int32)Marshal.SizeOf(typeof(ANIMATIONINFO)),
|
||||
ref _animationParam, 0);
|
||||
if (_currentAnimationSetting == null) {
|
||||
// Store the current Animation Setting
|
||||
_currentAnimationSetting = _animationParam.iMinAnimate;
|
||||
}
|
||||
|
||||
if (currentAnimationSetup != NO_ANIMATION)
|
||||
{
|
||||
// Turn off Animation
|
||||
_animationParam.iMinAnimate = NO_ANIMATION;
|
||||
var animationOffReturn = User32NativeMethods.SystemParametersInfo(User32NativeMethods.SPI_SETANIMATION, (System.Int32)Marshal.SizeOf(typeof(ANIMATIONINFO)), ref _animationParam, 0);
|
||||
}
|
||||
}
|
||||
if (currentAnimationSetup != NO_ANIMATION) {
|
||||
// Turn off Animation
|
||||
_animationParam.iMinAnimate = NO_ANIMATION;
|
||||
var animationOffReturn = User32NativeMethods.SystemParametersInfo(
|
||||
User32NativeMethods.SPI_SETANIMATION, (System.Int32)Marshal.SizeOf(typeof(ANIMATIONINFO)),
|
||||
ref _animationParam, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void RestoreAnimation()
|
||||
{
|
||||
var currentAnimationSetup = User32NativeMethods.SystemParametersInfo(User32NativeMethods.SPI_GETANIMATION, (System.Int32)Marshal.SizeOf(typeof(ANIMATIONINFO)), ref _animationParam, 0);
|
||||
// Restore current Animation Settings
|
||||
if (_animationParam.iMinAnimate != (int)_currentAnimationSetting)
|
||||
{
|
||||
_animationParam.iMinAnimate = (int)_currentAnimationSetting;
|
||||
var animationResetReturn = User32NativeMethods.SystemParametersInfo(User32NativeMethods.SPI_SETANIMATION, (System.Int32)Marshal.SizeOf(typeof(ANIMATIONINFO)), ref _animationParam, 0);
|
||||
}
|
||||
}
|
||||
private void RestoreAnimation() {
|
||||
var currentAnimationSetup = User32NativeMethods.SystemParametersInfo(
|
||||
User32NativeMethods.SPI_GETANIMATION, (System.Int32)Marshal.SizeOf(typeof(ANIMATIONINFO)),
|
||||
ref _animationParam, 0);
|
||||
// Restore current Animation Settings
|
||||
if (_animationParam.iMinAnimate != (int)_currentAnimationSetting) {
|
||||
_animationParam.iMinAnimate = (int)_currentAnimationSetting;
|
||||
var animationResetReturn = User32NativeMethods.SystemParametersInfo(
|
||||
User32NativeMethods.SPI_SETANIMATION, (System.Int32)Marshal.SizeOf(typeof(ANIMATIONINFO)),
|
||||
ref _animationParam, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// if building for LINUX the window handling is slightly different
|
||||
// if building for LINUX the window handling is slightly different
|
||||
#if LINUX
|
||||
private void WindowsActivateWindow(IntPtr handle)
|
||||
{
|
||||
User32NativeMethods.SetForegroundWindow(handle);
|
||||
User32NativeMethods.SetFocus(handle);
|
||||
private void WindowsActivateWindow(IntPtr handle) {
|
||||
User32NativeMethods.SetForegroundWindow(handle);
|
||||
User32NativeMethods.SetFocus(handle);
|
||||
|
||||
int style = User32NativeMethods.GetWindowLong(handle, InteropConstants.GWL_STYLE);
|
||||
int style = User32NativeMethods.GetWindowLong(handle, InteropConstants.GWL_STYLE);
|
||||
|
||||
if ((style & InteropConstants.WS_MINIMIZE) == InteropConstants.WS_MINIMIZE)
|
||||
{
|
||||
User32NativeMethods.ShowWindowAsync(handle, InteropConstants.SW_RESTORE);
|
||||
}
|
||||
}
|
||||
if ((style & InteropConstants.WS_MINIMIZE) == InteropConstants.WS_MINIMIZE) {
|
||||
User32NativeMethods.ShowWindowAsync(handle, InteropConstants.SW_RESTORE);
|
||||
}
|
||||
}
|
||||
|
||||
private void WineActivateWindow(string windowName)
|
||||
{
|
||||
// On Wine it is not possible to manipulate windows directly.
|
||||
// They are managed by native Window Manager
|
||||
// So a separate command-line utility is used
|
||||
if (string.IsNullOrEmpty(windowName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
private void WineActivateWindow(string windowName) {
|
||||
// On Wine it is not possible to manipulate windows directly.
|
||||
// They are managed by native Window Manager
|
||||
// So a separate command-line utility is used
|
||||
if (string.IsNullOrEmpty(windowName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
string cmd = "";
|
||||
try
|
||||
{
|
||||
try {
|
||||
// If we are in a flatpak, then use flatpak-spawn to run wmctrl outside the sandbox
|
||||
if (Environment.GetEnvironmentVariable("container") == "flatpak")
|
||||
{
|
||||
if (Environment.GetEnvironmentVariable("container") == "flatpak") {
|
||||
cmd = $"-c \"flatpak-spawn --host wmctrl -a \"\"" + windowName + "\"\"\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
cmd = $"-c \"{this._wmctrlLocation}/wmctrl -a \"\"" + windowName + "\"\"\"";
|
||||
}
|
||||
|
||||
// Configure and start the process
|
||||
var processStartInfo = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = $"{this._bashLocation}/bash",
|
||||
Arguments = cmd,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = false
|
||||
};
|
||||
// Configure and start the process
|
||||
var processStartInfo =
|
||||
new System.Diagnostics.ProcessStartInfo { FileName = $"{this._bashLocation}/bash", Arguments = cmd,
|
||||
UseShellExecute = false, CreateNoWindow = false };
|
||||
|
||||
using (var process = System.Diagnostics.Process.Start(processStartInfo))
|
||||
{
|
||||
process.WaitForExit();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteToLog($"[{DateTime.Now}] executing wmctrl - Exception: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void ActivateWindow(IntPtr handle, string windowName)
|
||||
{
|
||||
if (this._enableWineCompatabilityMode)
|
||||
{
|
||||
this.WineActivateWindow(windowName);
|
||||
using (var process = System.Diagnostics.Process.Start(processStartInfo)) {
|
||||
process.WaitForExit();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
WriteToLog($"[{DateTime.Now}] executing wmctrl - Exception: {ex.Message}");
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
public void ActivateWindow(IntPtr handle, string windowName) {
|
||||
if (this._enableWineCompatabilityMode) {
|
||||
this.WineActivateWindow(windowName);
|
||||
} else {
|
||||
this.WindowsActivateWindow(handle);
|
||||
}
|
||||
}
|
||||
|
||||
public void MinimizeWindow(IntPtr handle, bool enableAnimation)
|
||||
{
|
||||
if (enableAnimation)
|
||||
{
|
||||
User32NativeMethods.SendMessage(handle, InteropConstants.WM_SYSCOMMAND, InteropConstants.SC_MINIMIZE, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
WINDOWPLACEMENT param = new WINDOWPLACEMENT();
|
||||
param.length = Marshal.SizeOf(typeof(WINDOWPLACEMENT));
|
||||
User32NativeMethods.GetWindowPlacement(handle, ref param);
|
||||
param.showCmd = WINDOWPLACEMENT.SW_MINIMIZE;
|
||||
User32NativeMethods.SetWindowPlacement(handle, ref param);
|
||||
}
|
||||
}
|
||||
public void MinimizeWindow(IntPtr handle, bool enableAnimation) {
|
||||
if (enableAnimation) {
|
||||
User32NativeMethods.SendMessage(handle, InteropConstants.WM_SYSCOMMAND, InteropConstants.SC_MINIMIZE,
|
||||
0);
|
||||
} else {
|
||||
WINDOWPLACEMENT param = new WINDOWPLACEMENT();
|
||||
param.length = Marshal.SizeOf(typeof(WINDOWPLACEMENT));
|
||||
User32NativeMethods.GetWindowPlacement(handle, ref param);
|
||||
param.showCmd = WINDOWPLACEMENT.SW_MINIMIZE;
|
||||
User32NativeMethods.SetWindowPlacement(handle, ref param);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if WINDOWS
|
||||
public void ActivateWindow(IntPtr handle, AnimationStyle animation)
|
||||
{
|
||||
User32NativeMethods.SetForegroundWindow(handle);
|
||||
User32NativeMethods.SetFocus(handle);
|
||||
public void ActivateWindow(IntPtr handle, AnimationStyle animation) {
|
||||
User32NativeMethods.SetForegroundWindow(handle);
|
||||
User32NativeMethods.SetFocus(handle);
|
||||
|
||||
int style = User32NativeMethods.GetWindowLong(handle, InteropConstants.GWL_STYLE);
|
||||
int style = User32NativeMethods.GetWindowLong(handle, InteropConstants.GWL_STYLE);
|
||||
|
||||
if ((style & InteropConstants.WS_MINIMIZE) == InteropConstants.WS_MINIMIZE)
|
||||
{
|
||||
switch (animation)
|
||||
{
|
||||
case AnimationStyle.OriginalAnimation:
|
||||
User32NativeMethods.ShowWindowAsync(handle, InteropConstants.SW_RESTORE);
|
||||
break;
|
||||
case AnimationStyle.NoAnimation:
|
||||
TurnOffAnimation();
|
||||
User32NativeMethods.ShowWindowAsync(handle, InteropConstants.SW_RESTORE);
|
||||
RestoreAnimation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MinimizeWindow(IntPtr handle, AnimationStyle animation, bool enableAnimation)
|
||||
{
|
||||
if (enableAnimation)
|
||||
{
|
||||
switch (animation)
|
||||
{
|
||||
case AnimationStyle.OriginalAnimation:
|
||||
User32NativeMethods.SendMessage(handle, InteropConstants.WM_SYSCOMMAND, InteropConstants.SC_MINIMIZE, 0);
|
||||
break;
|
||||
case AnimationStyle.NoAnimation:
|
||||
TurnOffAnimation();
|
||||
User32NativeMethods.SendMessage(handle, InteropConstants.WM_SYSCOMMAND, InteropConstants.SC_MINIMIZE, 0);
|
||||
RestoreAnimation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (animation)
|
||||
{
|
||||
case AnimationStyle.OriginalAnimation:
|
||||
WINDOWPLACEMENT param = new WINDOWPLACEMENT();
|
||||
param.length = Marshal.SizeOf(typeof(WINDOWPLACEMENT));
|
||||
User32NativeMethods.GetWindowPlacement(handle, ref param);
|
||||
param.showCmd = WINDOWPLACEMENT.SW_MINIMIZE;
|
||||
User32NativeMethods.SetWindowPlacement(handle, ref param);
|
||||
break;
|
||||
case AnimationStyle.NoAnimation:
|
||||
TurnOffAnimation();
|
||||
User32NativeMethods.SendMessage(handle, InteropConstants.WM_SYSCOMMAND, InteropConstants.SC_MINIMIZE, 0);
|
||||
RestoreAnimation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public void MoveWindow(IntPtr handle, int left, int top, int width, int height)
|
||||
{
|
||||
User32NativeMethods.MoveWindow(handle, left, top, width, height, true);
|
||||
}
|
||||
|
||||
public void MaximizeWindow(IntPtr handle)
|
||||
{
|
||||
User32NativeMethods.ShowWindowAsync(handle, InteropConstants.SW_SHOWMAXIMIZED);
|
||||
if ((style & InteropConstants.WS_MINIMIZE) == InteropConstants.WS_MINIMIZE) {
|
||||
switch (animation) {
|
||||
case AnimationStyle.OriginalAnimation:
|
||||
User32NativeMethods.ShowWindowAsync(handle, InteropConstants.SW_RESTORE);
|
||||
break;
|
||||
case AnimationStyle.NoAnimation:
|
||||
TurnOffAnimation();
|
||||
User32NativeMethods.ShowWindowAsync(handle, InteropConstants.SW_RESTORE);
|
||||
RestoreAnimation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public (int Left, int Top, int Right, int Bottom) GetWindowPosition(IntPtr handle)
|
||||
{
|
||||
User32NativeMethods.GetWindowRect(handle, out RECT windowRectangle);
|
||||
public void MinimizeWindow(IntPtr handle, AnimationStyle animation, bool enableAnimation) {
|
||||
if (enableAnimation) {
|
||||
switch (animation) {
|
||||
case AnimationStyle.OriginalAnimation:
|
||||
User32NativeMethods.SendMessage(handle, InteropConstants.WM_SYSCOMMAND,
|
||||
InteropConstants.SC_MINIMIZE, 0);
|
||||
break;
|
||||
case AnimationStyle.NoAnimation:
|
||||
TurnOffAnimation();
|
||||
User32NativeMethods.SendMessage(handle, InteropConstants.WM_SYSCOMMAND,
|
||||
InteropConstants.SC_MINIMIZE, 0);
|
||||
RestoreAnimation();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (animation) {
|
||||
case AnimationStyle.OriginalAnimation:
|
||||
WINDOWPLACEMENT param = new WINDOWPLACEMENT();
|
||||
param.length = Marshal.SizeOf(typeof(WINDOWPLACEMENT));
|
||||
User32NativeMethods.GetWindowPlacement(handle, ref param);
|
||||
param.showCmd = WINDOWPLACEMENT.SW_MINIMIZE;
|
||||
User32NativeMethods.SetWindowPlacement(handle, ref param);
|
||||
break;
|
||||
case AnimationStyle.NoAnimation:
|
||||
TurnOffAnimation();
|
||||
User32NativeMethods.SendMessage(handle, InteropConstants.WM_SYSCOMMAND,
|
||||
InteropConstants.SC_MINIMIZE, 0);
|
||||
RestoreAnimation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return (windowRectangle.Left, windowRectangle.Top, windowRectangle.Right, windowRectangle.Bottom);
|
||||
}
|
||||
public void MoveWindow(IntPtr handle, int left, int top, int width, int height) {
|
||||
User32NativeMethods.MoveWindow(handle, left, top, width, height, true);
|
||||
}
|
||||
|
||||
public bool IsWindowMaximized(IntPtr handle)
|
||||
{
|
||||
return User32NativeMethods.IsZoomed(handle);
|
||||
}
|
||||
public void MaximizeWindow(IntPtr handle) {
|
||||
User32NativeMethods.ShowWindowAsync(handle, InteropConstants.SW_SHOWMAXIMIZED);
|
||||
}
|
||||
|
||||
public bool IsWindowMinimized(IntPtr handle)
|
||||
{
|
||||
return User32NativeMethods.IsIconic(handle);
|
||||
}
|
||||
public (int Left, int Top, int Right, int Bottom) GetWindowPosition(IntPtr handle) {
|
||||
User32NativeMethods.GetWindowRect(handle, out RECT windowRectangle);
|
||||
|
||||
public IDwmThumbnail GetLiveThumbnail(IntPtr destination, IntPtr source)
|
||||
{
|
||||
IDwmThumbnail thumbnail = new DwmThumbnail(this);
|
||||
thumbnail.Register(destination, source);
|
||||
return (windowRectangle.Left, windowRectangle.Top, windowRectangle.Right, windowRectangle.Bottom);
|
||||
}
|
||||
|
||||
return thumbnail;
|
||||
}
|
||||
public bool IsWindowMaximized(IntPtr handle) {
|
||||
return User32NativeMethods.IsZoomed(handle);
|
||||
}
|
||||
|
||||
public Image GetStaticThumbnail(IntPtr source)
|
||||
{
|
||||
var sourceContext = User32NativeMethods.GetDC(source);
|
||||
public bool IsWindowMinimized(IntPtr handle) {
|
||||
return User32NativeMethods.IsIconic(handle);
|
||||
}
|
||||
|
||||
User32NativeMethods.GetClientRect(source, out RECT windowRect);
|
||||
public IDwmThumbnail GetLiveThumbnail(IntPtr destination, IntPtr source) {
|
||||
IDwmThumbnail thumbnail = new DwmThumbnail(this);
|
||||
thumbnail.Register(destination, source);
|
||||
|
||||
var width = windowRect.Right - windowRect.Left;
|
||||
var height = windowRect.Bottom - windowRect.Top;
|
||||
return thumbnail;
|
||||
}
|
||||
|
||||
// Check if there is anything to make thumbnail of
|
||||
if ((width < WINDOW_SIZE_THRESHOLD) || (height < WINDOW_SIZE_THRESHOLD))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
public Image GetStaticThumbnail(IntPtr source) {
|
||||
var sourceContext = User32NativeMethods.GetDC(source);
|
||||
|
||||
var destContext = Gdi32NativeMethods.CreateCompatibleDC(sourceContext);
|
||||
var bitmap = Gdi32NativeMethods.CreateCompatibleBitmap(sourceContext, width, height);
|
||||
User32NativeMethods.GetClientRect(source, out RECT windowRect);
|
||||
|
||||
var oldBitmap = Gdi32NativeMethods.SelectObject(destContext, bitmap);
|
||||
Gdi32NativeMethods.BitBlt(destContext, 0, 0, width, height, sourceContext, 0, 0, Gdi32NativeMethods.SRCCOPY);
|
||||
Gdi32NativeMethods.SelectObject(destContext, oldBitmap);
|
||||
Gdi32NativeMethods.DeleteDC(destContext);
|
||||
User32NativeMethods.ReleaseDC(source, sourceContext);
|
||||
var width = windowRect.Right - windowRect.Left;
|
||||
var height = windowRect.Bottom - windowRect.Top;
|
||||
|
||||
Image image = Image.FromHbitmap(bitmap);
|
||||
Gdi32NativeMethods.DeleteObject(bitmap);
|
||||
// Check if there is anything to make thumbnail of
|
||||
if ((width < WINDOW_SIZE_THRESHOLD) || (height < WINDOW_SIZE_THRESHOLD)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
||||
var destContext = Gdi32NativeMethods.CreateCompatibleDC(sourceContext);
|
||||
var bitmap = Gdi32NativeMethods.CreateCompatibleBitmap(sourceContext, width, height);
|
||||
|
||||
var oldBitmap = Gdi32NativeMethods.SelectObject(destContext, bitmap);
|
||||
Gdi32NativeMethods.BitBlt(destContext, 0, 0, width, height, sourceContext, 0, 0,
|
||||
Gdi32NativeMethods.SRCCOPY);
|
||||
Gdi32NativeMethods.SelectObject(destContext, oldBitmap);
|
||||
Gdi32NativeMethods.DeleteDC(destContext);
|
||||
User32NativeMethods.ReleaseDC(source, sourceContext);
|
||||
|
||||
Image image = Image.FromHbitmap(bitmap);
|
||||
Gdi32NativeMethods.DeleteObject(bitmap);
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user