Make region selector shower instead of whole screen )

This commit is contained in:
2025-08-29 22:19:21 +02:00
parent 554ed6098a
commit 418ae9352d
97 changed files with 6329 additions and 6327 deletions

View File

@@ -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
}
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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

View File

@@ -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;
}
}
}