diff --git a/Flow.Launcher.Infrastructure/MonitorInfo.cs b/Flow.Launcher.Infrastructure/MonitorInfo.cs
new file mode 100644
index 000000000..3221708c1
--- /dev/null
+++ b/Flow.Launcher.Infrastructure/MonitorInfo.cs
@@ -0,0 +1,123 @@
+using System.Collections.Generic;
+using System;
+using System.Runtime.InteropServices;
+using System.Windows;
+using Windows.Win32;
+using Windows.Win32.Foundation;
+using Windows.Win32.Graphics.Gdi;
+using Windows.Win32.UI.WindowsAndMessaging;
+
+namespace Flow.Launcher.Infrastructure;
+
+///
+/// Contains full information about a display monitor.
+/// Codes are edited from: .
+///
+internal class MonitorInfo
+{
+ ///
+ /// Gets the display monitors (including invisible pseudo-monitors associated with the mirroring drivers).
+ ///
+ /// A list of display monitors
+ public static unsafe IList GetDisplayMonitors()
+ {
+ var monitorCount = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CMONITORS);
+ var list = new List(monitorCount);
+ var callback = new MONITORENUMPROC((HMONITOR monitor, HDC deviceContext, RECT* rect, LPARAM data) =>
+ {
+ list.Add(new MonitorInfo(monitor, rect));
+ return true;
+ });
+ var dwData = new LPARAM();
+ var hdc = new HDC();
+ bool ok = PInvoke.EnumDisplayMonitors(hdc, (RECT?)null, callback, dwData);
+ if (!ok)
+ {
+ Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
+ }
+ return list;
+ }
+
+ ///
+ /// Gets the display monitor that is nearest to a given window.
+ ///
+ /// Window handle
+ /// The display monitor that is nearest to a given window, or null if no monitor is found.
+ public static unsafe MonitorInfo GetNearestDisplayMonitor(HWND hwnd)
+ {
+ var nearestMonitor = PInvoke.MonitorFromWindow(hwnd, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST);
+ MonitorInfo nearestMonitorInfo = null;
+ var callback = new MONITORENUMPROC((HMONITOR monitor, HDC deviceContext, RECT* rect, LPARAM data) =>
+ {
+ if (monitor == nearestMonitor)
+ {
+ nearestMonitorInfo = new MonitorInfo(monitor, rect);
+ return false;
+ }
+ return true;
+ });
+ var dwData = new LPARAM();
+ var hdc = new HDC();
+ bool ok = PInvoke.EnumDisplayMonitors(hdc, (RECT?)null, callback, dwData);
+ if (!ok)
+ {
+ Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
+ }
+ return nearestMonitorInfo;
+ }
+
+ private readonly HMONITOR _monitor;
+
+ internal unsafe MonitorInfo(HMONITOR monitor, RECT* rect)
+ {
+ RectMonitor =
+ new Rect(new Point(rect->left, rect->top),
+ new Point(rect->right, rect->bottom));
+ _monitor = monitor;
+ var info = new MONITORINFOEXW() { monitorInfo = new MONITORINFO() { cbSize = (uint)sizeof(MONITORINFOEXW) } };
+ GetMonitorInfo(monitor, ref info);
+ RectWork =
+ new Rect(new Point(info.monitorInfo.rcWork.left, info.monitorInfo.rcWork.top),
+ new Point(info.monitorInfo.rcWork.right, info.monitorInfo.rcWork.bottom));
+ Name = new string(info.szDevice.AsSpan()).Replace("\0", "").Trim();
+ }
+
+ ///
+ /// Gets the name of the display.
+ ///
+ public string Name { get; }
+
+ ///
+ /// Gets the display monitor rectangle, expressed in virtual-screen coordinates.
+ ///
+ ///
+ /// If the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
+ ///
+ public Rect RectMonitor { get; }
+
+ ///
+ /// Gets the work area rectangle of the display monitor, expressed in virtual-screen coordinates.
+ ///
+ ///
+ /// If the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
+ ///
+ public Rect RectWork { get; }
+
+ ///
+ /// Gets if the monitor is the the primary display monitor.
+ ///
+ public bool IsPrimary => _monitor == PInvoke.MonitorFromWindow(new(IntPtr.Zero), MONITOR_FROM_FLAGS.MONITOR_DEFAULTTOPRIMARY);
+
+ ///
+ public override string ToString() => $"{Name} {RectMonitor.Width}x{RectMonitor.Height}";
+
+ private static unsafe bool GetMonitorInfo(HMONITOR hMonitor, ref MONITORINFOEXW lpmi)
+ {
+ fixed (MONITORINFOEXW* lpmiLocal = &lpmi)
+ {
+ var lpmiBase = (MONITORINFO*)lpmiLocal;
+ var __result = PInvoke.GetMonitorInfo(hMonitor, lpmiBase);
+ return __result;
+ }
+ }
+}
diff --git a/Flow.Launcher.Infrastructure/NativeMethods.txt b/Flow.Launcher.Infrastructure/NativeMethods.txt
index e7256e8c4..5d72239f3 100644
--- a/Flow.Launcher.Infrastructure/NativeMethods.txt
+++ b/Flow.Launcher.Infrastructure/NativeMethods.txt
@@ -20,4 +20,31 @@ EnumWindows
DwmSetWindowAttribute
DWM_SYSTEMBACKDROP_TYPE
-DWM_WINDOW_CORNER_PREFERENCE
\ No newline at end of file
+DWM_WINDOW_CORNER_PREFERENCE
+
+MAX_PATH
+SystemParametersInfo
+
+SetForegroundWindow
+
+GetWindowLong
+SetWindowLong
+GetForegroundWindow
+GetDesktopWindow
+GetShellWindow
+GetWindowRect
+GetClassName
+FindWindowEx
+WINDOW_STYLE
+
+SetLastError
+WINDOW_EX_STYLE
+
+GetSystemMetrics
+EnumDisplayMonitors
+MonitorFromWindow
+GetMonitorInfo
+MONITORINFOEXW
+
+WM_ENTERSIZEMOVE
+WM_EXITSIZEMOVE
\ No newline at end of file
diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs
index 08452b721..9cc4be0c5 100644
--- a/Flow.Launcher.Infrastructure/Win32Helper.cs
+++ b/Flow.Launcher.Infrastructure/Win32Helper.cs
@@ -1,9 +1,13 @@
using System;
+using System.ComponentModel;
using System.Runtime.InteropServices;
-using System.Windows.Interop;
using System.Windows;
+using System.Windows.Interop;
+using System.Windows.Media;
using Windows.Win32;
+using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Dwm;
+using Windows.Win32.UI.WindowsAndMessaging;
using Flow.Launcher.Infrastructure.UserSettings;
namespace Flow.Launcher.Infrastructure
@@ -14,20 +18,17 @@ namespace Flow.Launcher.Infrastructure
public static bool IsBackdropSupported()
{
- // Windows 11 (22000) 이상에서만 Mica 및 Acrylic 효과 지원
+ // Mica and Acrylic only supported Windows 11 22000+
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
Environment.OSVersion.Version.Build >= 22000;
}
public static unsafe bool DWMSetCloakForWindow(Window window, bool cloak)
{
- var windowHelper = new WindowInteropHelper(window);
- windowHelper.EnsureHandle();
-
var cloaked = cloak ? 1 : 0;
return PInvoke.DwmSetWindowAttribute(
- new(windowHelper.Handle),
+ GetWindowHandle(window),
DWMWINDOWATTRIBUTE.DWMWA_CLOAK,
&cloaked,
(uint)Marshal.SizeOf()).Succeeded;
@@ -35,9 +36,6 @@ namespace Flow.Launcher.Infrastructure
public static unsafe bool DWMSetBackdropForWindow(Window window, BackdropTypes backdrop)
{
- var windowHelper = new WindowInteropHelper(window);
- windowHelper.EnsureHandle();
-
var backdropType = backdrop switch
{
BackdropTypes.Acrylic => DWM_SYSTEMBACKDROP_TYPE.DWMSBT_TRANSIENTWINDOW,
@@ -47,7 +45,7 @@ namespace Flow.Launcher.Infrastructure
};
return PInvoke.DwmSetWindowAttribute(
- new(windowHelper.Handle),
+ GetWindowHandle(window),
DWMWINDOWATTRIBUTE.DWMWA_SYSTEMBACKDROP_TYPE,
&backdropType,
(uint)Marshal.SizeOf()).Succeeded;
@@ -55,13 +53,10 @@ namespace Flow.Launcher.Infrastructure
public static unsafe bool DWMSetDarkModeForWindow(Window window, bool useDarkMode)
{
- var windowHelper = new WindowInteropHelper(window);
- windowHelper.EnsureHandle();
-
var darkMode = useDarkMode ? 1 : 0;
return PInvoke.DwmSetWindowAttribute(
- new(windowHelper.Handle),
+ GetWindowHandle(window),
DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE,
&darkMode,
(uint)Marshal.SizeOf()).Succeeded;
@@ -75,9 +70,6 @@ namespace Flow.Launcher.Infrastructure
///
public static unsafe bool DWMSetCornerPreferenceForWindow(Window window, string cornerType)
{
- var windowHelper = new WindowInteropHelper(window);
- windowHelper.EnsureHandle();
-
var preference = cornerType switch
{
"DoNotRound" => DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_DONOTROUND,
@@ -88,12 +80,246 @@ namespace Flow.Launcher.Infrastructure
};
return PInvoke.DwmSetWindowAttribute(
- new(windowHelper.Handle),
+ GetWindowHandle(window),
DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE,
&preference,
(uint)Marshal.SizeOf()).Succeeded;
}
#endregion
+
+ #region Wallpaper
+
+ public static unsafe string GetWallpaperPath()
+ {
+ var wallpaperPtr = stackalloc char[(int)PInvoke.MAX_PATH];
+ PInvoke.SystemParametersInfo(SYSTEM_PARAMETERS_INFO_ACTION.SPI_GETDESKWALLPAPER, PInvoke.MAX_PATH,
+ wallpaperPtr,
+ 0);
+ var wallpaper = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(wallpaperPtr);
+
+ return wallpaper.ToString();
+ }
+
+ #endregion
+
+ #region Window Foreground
+
+ public static nint GetForegroundWindow()
+ {
+ return PInvoke.GetForegroundWindow().Value;
+ }
+
+ public static bool SetForegroundWindow(Window window)
+ {
+ return PInvoke.SetForegroundWindow(GetWindowHandle(window));
+ }
+
+ public static bool SetForegroundWindow(nint handle)
+ {
+ return PInvoke.SetForegroundWindow(new(handle));
+ }
+
+ #endregion
+
+ #region Task Switching
+
+ ///
+ /// Hide windows in the Alt+Tab window list
+ ///
+ /// To hide a window
+ public static void HideFromAltTab(Window window)
+ {
+ var hwnd = GetWindowHandle(window);
+
+ var exStyle = GetCurrentWindowStyle(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
+
+ // Add TOOLWINDOW style, remove APPWINDOW style
+ var newExStyle = ((uint)exStyle | (uint)WINDOW_EX_STYLE.WS_EX_TOOLWINDOW) & ~(uint)WINDOW_EX_STYLE.WS_EX_APPWINDOW;
+
+ SetWindowLong(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)newExStyle);
+ }
+
+ ///
+ /// Restore window display in the Alt+Tab window list.
+ ///
+ /// To restore the displayed window
+ public static void ShowInAltTab(Window window)
+ {
+ var hwnd = GetWindowHandle(window);
+
+ var exStyle = GetCurrentWindowStyle(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
+
+ // Remove the TOOLWINDOW style and add the APPWINDOW style.
+ var newExStyle = ((uint)exStyle & ~(uint)WINDOW_EX_STYLE.WS_EX_TOOLWINDOW) | (uint)WINDOW_EX_STYLE.WS_EX_APPWINDOW;
+
+ SetWindowLong(GetWindowHandle(window), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)newExStyle);
+ }
+
+ ///
+ /// Disable windows toolbar's control box
+ /// This will also disable system menu with Alt+Space hotkey
+ ///
+ public static void DisableControlBox(Window window)
+ {
+ var hwnd = GetWindowHandle(window);
+
+ var style = GetCurrentWindowStyle(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE);
+
+ style &= ~(int)WINDOW_STYLE.WS_SYSMENU;
+
+ SetWindowLong(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE, style);
+ }
+
+ private static int GetCurrentWindowStyle(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex)
+ {
+ var style = PInvoke.GetWindowLong(hWnd, nIndex);
+ if (style == 0 && Marshal.GetLastPInvokeError() != 0)
+ {
+ throw new Win32Exception(Marshal.GetLastPInvokeError());
+ }
+ return style;
+ }
+
+ private static int SetWindowLong(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex, int dwNewLong)
+ {
+ PInvoke.SetLastError(WIN32_ERROR.NO_ERROR); // Clear any existing error
+
+ var result = PInvoke.SetWindowLong(hWnd, nIndex, dwNewLong);
+ if (result == 0 && Marshal.GetLastPInvokeError() != 0)
+ {
+ throw new Win32Exception(Marshal.GetLastPInvokeError());
+ }
+
+ return result;
+ }
+
+ #endregion
+
+ #region Window Fullscreen
+
+ private const string WINDOW_CLASS_CONSOLE = "ConsoleWindowClass";
+ private const string WINDOW_CLASS_WINTAB = "Flip3D";
+ private const string WINDOW_CLASS_PROGMAN = "Progman";
+ private const string WINDOW_CLASS_WORKERW = "WorkerW";
+
+ private static HWND _hwnd_shell;
+ private static HWND HWND_SHELL =>
+ _hwnd_shell != HWND.Null ? _hwnd_shell : _hwnd_shell = PInvoke.GetShellWindow();
+
+ private static HWND _hwnd_desktop;
+ private static HWND HWND_DESKTOP =>
+ _hwnd_desktop != HWND.Null ? _hwnd_desktop : _hwnd_desktop = PInvoke.GetDesktopWindow();
+
+ public static unsafe bool IsForegroundWindowFullscreen()
+ {
+ // Get current active window
+ var hWnd = PInvoke.GetForegroundWindow();
+ if (hWnd.Equals(HWND.Null))
+ {
+ return false;
+ }
+
+ // If current active window is desktop or shell, exit early
+ if (hWnd.Equals(HWND_DESKTOP) || hWnd.Equals(HWND_SHELL))
+ {
+ return false;
+ }
+
+ string windowClass;
+ const int capacity = 256;
+ Span buffer = stackalloc char[capacity];
+ int validLength;
+ fixed (char* pBuffer = buffer)
+ {
+ validLength = PInvoke.GetClassName(hWnd, pBuffer, capacity);
+ }
+
+ windowClass = buffer[..validLength].ToString();
+
+ // For Win+Tab (Flip3D)
+ if (windowClass == WINDOW_CLASS_WINTAB)
+ {
+ return false;
+ }
+
+ PInvoke.GetWindowRect(hWnd, out var appBounds);
+
+ // For console (ConsoleWindowClass), we have to check for negative dimensions
+ if (windowClass == WINDOW_CLASS_CONSOLE)
+ {
+ return appBounds.top < 0 && appBounds.bottom < 0;
+ }
+
+ // For desktop (Progman or WorkerW, depends on the system), we have to check
+ if (windowClass is WINDOW_CLASS_PROGMAN or WINDOW_CLASS_WORKERW)
+ {
+ var hWndDesktop = PInvoke.FindWindowEx(hWnd, HWND.Null, "SHELLDLL_DefView", null);
+ hWndDesktop = PInvoke.FindWindowEx(hWndDesktop, HWND.Null, "SysListView32", "FolderView");
+ if (hWndDesktop.Value != IntPtr.Zero)
+ {
+ return false;
+ }
+ }
+
+ var monitorInfo = MonitorInfo.GetNearestDisplayMonitor(hWnd);
+ return (appBounds.bottom - appBounds.top) == monitorInfo.RectMonitor.Height &&
+ (appBounds.right - appBounds.left) == monitorInfo.RectMonitor.Width;
+ }
+
+ #endregion
+
+ #region Pixel to DIP
+
+ ///
+ /// Transforms pixels to Device Independent Pixels used by WPF
+ ///
+ /// current window, required to get presentation source
+ /// horizontal position in pixels
+ /// vertical position in pixels
+ /// point containing device independent pixels
+ public static Point TransformPixelsToDIP(Visual visual, double unitX, double unitY)
+ {
+ Matrix matrix;
+ var source = PresentationSource.FromVisual(visual);
+ if (source is not null)
+ {
+ matrix = source.CompositionTarget.TransformFromDevice;
+ }
+ else
+ {
+ using var src = new HwndSource(new HwndSourceParameters());
+ matrix = src.CompositionTarget.TransformFromDevice;
+ }
+
+ return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY));
+ }
+
+ #endregion
+
+ #region WndProc
+
+ public static bool WM_ENTERSIZEMOVE(int msg)
+ {
+ return msg == (int)PInvoke.WM_ENTERSIZEMOVE;
+ }
+
+ public static bool WM_EXITSIZEMOVE(int msg)
+ {
+ return msg == (int)PInvoke.WM_EXITSIZEMOVE;
+ }
+
+ #endregion
+
+ #region Window Handle
+
+ internal static HWND GetWindowHandle(Window window)
+ {
+ var windowHelper = new WindowInteropHelper(window);
+ windowHelper.EnsureHandle();
+ return new(windowHelper.Handle);
+ }
+
+ #endregion
}
}
diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj
index 1e305d3d9..91cab9fa0 100644
--- a/Flow.Launcher/Flow.Launcher.csproj
+++ b/Flow.Launcher/Flow.Launcher.csproj
@@ -94,10 +94,6 @@
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
diff --git a/Flow.Launcher/Helper/DWMDropShadow.cs b/Flow.Launcher/Helper/DWMDropShadow.cs
deleted file mode 100644
index 58817d70e..000000000
--- a/Flow.Launcher/Helper/DWMDropShadow.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using System;
-using System.Windows;
-using System.Windows.Interop;
-using Windows.Win32;
-using Windows.Win32.Foundation;
-using Windows.Win32.Graphics.Dwm;
-using Windows.Win32.UI.Controls;
-
-namespace Flow.Launcher.Helper;
-
-public class DwmDropShadow
-{
-
- ///
- /// Drops a standard shadow to a WPF Window, even if the window isborderless. Only works with DWM (Vista and Seven).
- /// This method is much more efficient than setting AllowsTransparency to true and using the DropShadow effect,
- /// as AllowsTransparency involves a huge permormance issue (hardware acceleration is turned off for all the window).
- ///
- /// Window to which the shadow will be applied
- public static void DropShadowToWindow(Window window)
- {
- if (!DropShadow(window))
- {
- window.SourceInitialized += window_SourceInitialized;
- }
- }
-
- private static void window_SourceInitialized(object sender, EventArgs e) //fixed typo
- {
- Window window = (Window)sender;
-
- DropShadow(window);
-
- window.SourceInitialized -= window_SourceInitialized;
- }
-
- ///
- /// The actual method that makes API calls to drop the shadow to the window
- ///
- /// Window to which the shadow will be applied
- /// True if the method succeeded, false if not
- private static unsafe bool DropShadow(Window window)
- {
- try
- {
- WindowInteropHelper helper = new WindowInteropHelper(window);
- int val = 2;
- var ret1 = PInvoke.DwmSetWindowAttribute(new (helper.Handle), DWMWINDOWATTRIBUTE.DWMWA_NCRENDERING_POLICY, &val, 4);
-
- if (ret1 == HRESULT.S_OK)
- {
- var m = new MARGINS { cyBottomHeight = 0, cxLeftWidth = 0, cxRightWidth = 0, cyTopHeight = 0 };
- var ret2 = PInvoke.DwmExtendFrameIntoClientArea(new(helper.Handle), &m);
- return ret2 == HRESULT.S_OK;
- }
-
- return false;
- }
- catch (Exception)
- {
- // Probably dwmapi.dll not found (incompatible OS)
- return false;
- }
- }
-
-}
diff --git a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs
index a3bd83a97..151ce97dd 100644
--- a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs
+++ b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs
@@ -2,19 +2,16 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
+using Flow.Launcher.Infrastructure;
using Microsoft.Win32;
-using Windows.Win32;
-using Windows.Win32.UI.WindowsAndMessaging;
namespace Flow.Launcher.Helper;
public static class WallpaperPathRetrieval
{
- private static readonly int MAX_PATH = 260;
private static readonly int MAX_CACHE_SIZE = 3;
private static readonly Dictionary<(string, DateTime), ImageBrush> wallpaperCache = new();
@@ -29,7 +26,7 @@ public static class WallpaperPathRetrieval
try
{
- var wallpaperPath = GetWallpaperPath();
+ var wallpaperPath = Win32Helper.GetWallpaperPath();
if (wallpaperPath is not null && File.Exists(wallpaperPath))
{
// Since the wallpaper file name can be the same (TranscodedWallpaper),
@@ -78,17 +75,6 @@ public static class WallpaperPathRetrieval
}
}
- private static unsafe string GetWallpaperPath()
- {
- var wallpaperPtr = stackalloc char[MAX_PATH];
- PInvoke.SystemParametersInfo(SYSTEM_PARAMETERS_INFO_ACTION.SPI_GETDESKWALLPAPER, (uint)MAX_PATH,
- wallpaperPtr,
- 0);
- var wallpaper = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(wallpaperPtr);
-
- return wallpaper.ToString();
- }
-
private static Color GetWallpaperColor()
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Colors", true);
diff --git a/Flow.Launcher/Helper/WindowsInteropHelper.cs b/Flow.Launcher/Helper/WindowsInteropHelper.cs
deleted file mode 100644
index 3e57948a5..000000000
--- a/Flow.Launcher/Helper/WindowsInteropHelper.cs
+++ /dev/null
@@ -1,211 +0,0 @@
-using System;
-using System.ComponentModel;
-using System.Drawing;
-using System.Runtime.InteropServices;
-using System.Windows;
-using System.Windows.Forms;
-using System.Windows.Interop;
-using System.Windows.Media;
-using Windows.Win32;
-using Windows.Win32.Foundation;
-using Windows.Win32.UI.WindowsAndMessaging;
-using Point = System.Windows.Point;
-
-namespace Flow.Launcher.Helper;
-
-public class WindowsInteropHelper
-{
- private static HWND _hwnd_shell;
- private static HWND _hwnd_desktop;
-
- //Accessors for shell and desktop handlers
- //Will set the variables once and then will return them
- private static HWND HWND_SHELL
- {
- get
- {
- return _hwnd_shell != HWND.Null ? _hwnd_shell : _hwnd_shell = PInvoke.GetShellWindow();
- }
- }
-
- private static HWND HWND_DESKTOP
- {
- get
- {
- return _hwnd_desktop != HWND.Null ? _hwnd_desktop : _hwnd_desktop = PInvoke.GetDesktopWindow();
- }
- }
-
- const string WINDOW_CLASS_CONSOLE = "ConsoleWindowClass";
- const string WINDOW_CLASS_WINTAB = "Flip3D";
- const string WINDOW_CLASS_PROGMAN = "Progman";
- const string WINDOW_CLASS_WORKERW = "WorkerW";
-
- public unsafe static bool IsWindowFullscreen()
- {
- //get current active window
- var hWnd = PInvoke.GetForegroundWindow();
-
- if (hWnd.Equals(HWND.Null))
- {
- return false;
- }
-
- //if current active window is desktop or shell, exit early
- if (hWnd.Equals(HWND_DESKTOP) || hWnd.Equals(HWND_SHELL))
- {
- return false;
- }
-
- string windowClass;
- const int capacity = 256;
- Span buffer = stackalloc char[capacity];
- int validLength;
- fixed (char* pBuffer = buffer)
- {
- validLength = PInvoke.GetClassName(hWnd, pBuffer, capacity);
- }
-
- windowClass = buffer[..validLength].ToString();
-
-
- //for Win+Tab (Flip3D)
- if (windowClass == WINDOW_CLASS_WINTAB)
- {
- return false;
- }
-
- PInvoke.GetWindowRect(hWnd, out var appBounds);
-
- //for console (ConsoleWindowClass), we have to check for negative dimensions
- if (windowClass == WINDOW_CLASS_CONSOLE)
- {
- return appBounds.top < 0 && appBounds.bottom < 0;
- }
-
- //for desktop (Progman or WorkerW, depends on the system), we have to check
- if (windowClass is WINDOW_CLASS_PROGMAN or WINDOW_CLASS_WORKERW)
- {
- var hWndDesktop = PInvoke.FindWindowEx(hWnd, HWND.Null, "SHELLDLL_DefView", null);
- hWndDesktop = PInvoke.FindWindowEx(hWndDesktop, HWND.Null, "SysListView32", "FolderView");
- if (hWndDesktop.Value != (IntPtr.Zero))
- {
- return false;
- }
- }
-
- Rectangle screenBounds = Screen.FromHandle(hWnd).Bounds;
- return (appBounds.bottom - appBounds.top) == screenBounds.Height &&
- (appBounds.right - appBounds.left) == screenBounds.Width;
- }
-
- ///
- /// disable windows toolbar's control box
- /// this will also disable system menu with Alt+Space hotkey
- ///
- public static void DisableControlBox(Window win)
- {
- var hwnd = new HWND(new WindowInteropHelper(win).Handle);
-
- var style = PInvoke.GetWindowLong(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE);
-
- if (style == 0)
- {
- throw new Win32Exception(Marshal.GetLastPInvokeError());
- }
-
- style &= ~(int)WINDOW_STYLE.WS_SYSMENU;
-
- var previousStyle = PInvoke.SetWindowLong(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE,
- style);
-
- if (previousStyle == 0)
- {
- throw new Win32Exception(Marshal.GetLastPInvokeError());
- }
- }
-
- ///
- /// Transforms pixels to Device Independent Pixels used by WPF
- ///
- /// current window, required to get presentation source
- /// horizontal position in pixels
- /// vertical position in pixels
- /// point containing device independent pixels
- public static Point TransformPixelsToDIP(Visual visual, double unitX, double unitY)
- {
- Matrix matrix;
- var source = PresentationSource.FromVisual(visual);
- if (source is not null)
- {
- matrix = source.CompositionTarget.TransformFromDevice;
- }
- else
- {
- using var src = new HwndSource(new HwndSourceParameters());
- matrix = src.CompositionTarget.TransformFromDevice;
- }
-
- return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY));
- }
-
- #region Alt Tab
-
- private static int SetWindowLong(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex, int dwNewLong)
- {
- PInvoke.SetLastError(WIN32_ERROR.NO_ERROR); // Clear any existing error
-
- var result = PInvoke.SetWindowLong(hWnd, nIndex, dwNewLong);
- if (result == 0 && Marshal.GetLastPInvokeError() != 0)
- {
- throw new Win32Exception(Marshal.GetLastPInvokeError());
- }
-
- return result;
- }
-
- ///
- /// Hide windows in the Alt+Tab window list
- ///
- /// To hide a window
- public static void HideFromAltTab(Window window)
- {
- var exStyle = GetCurrentWindowStyle(window);
-
- // Add TOOLWINDOW style, remove APPWINDOW style
- var newExStyle = ((uint)exStyle | (uint)WINDOW_EX_STYLE.WS_EX_TOOLWINDOW) & ~(uint)WINDOW_EX_STYLE.WS_EX_APPWINDOW;
-
- SetWindowLong(new(new WindowInteropHelper(window).Handle), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)newExStyle);
- }
-
- ///
- /// Restore window display in the Alt+Tab window list.
- ///
- /// To restore the displayed window
- public static void ShowInAltTab(Window window)
- {
- var exStyle = GetCurrentWindowStyle(window);
-
- // Remove the TOOLWINDOW style and add the APPWINDOW style.
- var newExStyle = ((uint)exStyle & ~(uint)WINDOW_EX_STYLE.WS_EX_TOOLWINDOW) | (uint)WINDOW_EX_STYLE.WS_EX_APPWINDOW;
-
- SetWindowLong(new(new WindowInteropHelper(window).Handle), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)newExStyle);
- }
-
- ///
- /// To obtain the current overridden style of a window.
- ///
- /// To obtain the style dialog window
- /// current extension style value
- private static int GetCurrentWindowStyle(Window window)
- {
- var style = PInvoke.GetWindowLong(new(new WindowInteropHelper(window).Handle), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
- if (style == 0 && Marshal.GetLastPInvokeError() != 0)
- {
- throw new Win32Exception(Marshal.GetLastPInvokeError());
- }
- return style;
- }
-
- #endregion
-}
diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs
index 5aba73aa3..1e8ba1369 100644
--- a/Flow.Launcher/MainWindow.xaml.cs
+++ b/Flow.Launcher/MainWindow.xaml.cs
@@ -8,7 +8,6 @@ using System.Windows.Controls;
using System.Windows.Forms;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Core.Resource;
-using Flow.Launcher.Helper;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.ViewModel;
using Screen = System.Windows.Forms.Screen;
@@ -26,10 +25,10 @@ using System.Media;
using DataObject = System.Windows.DataObject;
using System.Windows.Media;
using System.Windows.Interop;
-using Windows.Win32;
using Window = System.Windows.Window;
using System.Linq;
using System.Windows.Shapes;
+using CommunityToolkit.Mvvm.DependencyInjection;
namespace Flow.Launcher
{
@@ -78,14 +77,13 @@ namespace Flow.Launcher
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
- if (msg == PInvoke.WM_ENTERSIZEMOVE)
+ if (Win32Helper.WM_ENTERSIZEMOVE(msg))
{
_initialWidth = (int)Width;
_initialHeight = (int)Height;
handled = true;
}
-
- if (msg == PInvoke.WM_EXITSIZEMOVE)
+ else if (Win32Helper.WM_EXITSIZEMOVE(msg))
{
if (_initialHeight != (int)Height)
{
@@ -168,7 +166,8 @@ namespace Flow.Launcher
private void OnSourceInitialized(object sender, EventArgs e)
{
- WindowsInteropHelper.HideFromAltTab(this);
+ Win32Helper.HideFromAltTab(this);
+ Win32Helper.DisableControlBox(this);
}
private void OnInitialized(object sender, EventArgs e)
@@ -184,13 +183,12 @@ namespace Flow.Launcher
// Show notify icon when flowlauncher is hidden
InitializeNotifyIcon();
InitializeColorScheme();
- WindowsInteropHelper.DisableControlBox(this);
InitProgressbarAnimation();
// Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910
InitializePosition();
InitializePosition();
// Refresh frame
- await ThemeManager.Instance.RefreshFrameAsync();
+ await Ioc.Default.GetRequiredService().RefreshFrameAsync();
PreviewReset();
// Since the default main window visibility is visible, so we need set focus during startup
QueryTextBox.Focus();
@@ -314,9 +312,9 @@ namespace Flow.Launcher
Top = 10;
break;
case SearchWindowAligns.Custom:
- Left = WindowsInteropHelper.TransformPixelsToDIP(this,
+ Left = Win32Helper.TransformPixelsToDIP(this,
screen.WorkingArea.X + _settings.CustomWindowLeft, 0).X;
- Top = WindowsInteropHelper.TransformPixelsToDIP(this, 0,
+ Top = Win32Helper.TransformPixelsToDIP(this, 0,
screen.WorkingArea.Y + _settings.CustomWindowTop).Y;
break;
}
@@ -377,7 +375,7 @@ namespace Flow.Launcher
open.Click += (o, e) => _viewModel.ToggleFlowLauncher();
gamemode.Click += (o, e) => _viewModel.ToggleGameMode();
- positionreset.Click += (o, e) => PositionReset();
+ positionreset.Click += (o, e) => _ = PositionResetAsync();
settings.Click += (o, e) => App.API.OpenSettingDialog();
exit.Click += (o, e) => Close();
@@ -403,7 +401,7 @@ namespace Flow.Launcher
// Get context menu handle and bring it to the foreground
if (PresentationSource.FromVisual(contextMenu) is HwndSource hwndSource)
{
- PInvoke.SetForegroundWindow(new(hwndSource.Handle));
+ Win32Helper.SetForegroundWindow(hwndSource.Handle);
}
contextMenu.Focus();
@@ -422,13 +420,13 @@ namespace Flow.Launcher
}
}
- private void OpenWelcomeWindow()
+ private static void OpenWelcomeWindow()
{
var WelcomeWindow = new WelcomeWindow();
WelcomeWindow.Show();
}
- private async void PositionReset()
+ private async Task PositionResetAsync()
{
_viewModel.Show();
await Task.Delay(300); // If don't give a time, Positioning will be weird.
@@ -846,7 +844,7 @@ namespace Flow.Launcher
screen = Screen.PrimaryScreen;
break;
case SearchWindowScreens.Focus:
- var foregroundWindowHandle = PInvoke.GetForegroundWindow().Value;
+ var foregroundWindowHandle = Win32Helper.GetForegroundWindow();
screen = Screen.FromHandle(foregroundWindowHandle);
break;
case SearchWindowScreens.Custom:
@@ -865,38 +863,38 @@ namespace Flow.Launcher
public double HorizonCenter(Screen screen)
{
- var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
- var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
+ var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
+ var dip2 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
var left = (dip2.X - ActualWidth) / 2 + dip1.X;
return left;
}
public double VerticalCenter(Screen screen)
{
- var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
- var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
+ var dip1 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
+ var dip2 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
var top = (dip2.Y - QueryTextBox.ActualHeight) / 4 + dip1.Y;
return top;
}
public double HorizonRight(Screen screen)
{
- var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
- var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
+ var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
+ var dip2 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
var left = (dip1.X + dip2.X - ActualWidth) - 10;
return left;
}
public double HorizonLeft(Screen screen)
{
- var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
+ var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
var left = dip1.X + 10;
return left;
}
///
/// Register up and down key
- /// todo: any way to put this in xaml ?
+ /// todo: Put this in xaml?
///
private void OnKeyDown(object sender, KeyEventArgs e)
{
diff --git a/Flow.Launcher/Msg.xaml.cs b/Flow.Launcher/Msg.xaml.cs
index 0bb02bbc5..94184ff63 100644
--- a/Flow.Launcher/Msg.xaml.cs
+++ b/Flow.Launcher/Msg.xaml.cs
@@ -1,10 +1,9 @@
-using System;
+using System;
using System.IO;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media.Animation;
-using Flow.Launcher.Helper;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Image;
@@ -12,14 +11,14 @@ namespace Flow.Launcher
{
public partial class Msg : Window
{
- Storyboard fadeOutStoryboard = new Storyboard();
+ private readonly Storyboard fadeOutStoryboard = new();
private bool closing;
public Msg()
{
InitializeComponent();
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
- var dipWorkingArea = WindowsInteropHelper.TransformPixelsToDIP(this,
+ var dipWorkingArea = Win32Helper.TransformPixelsToDIP(this,
screen.WorkingArea.Width,
screen.WorkingArea.Height);
Left = dipWorkingArea.X - Width;
@@ -82,13 +81,13 @@ namespace Flow.Launcher
Show();
await Dispatcher.InvokeAsync(async () =>
- {
- if (!closing)
- {
- closing = true;
- await Dispatcher.InvokeAsync(fadeOutStoryboard.Begin);
- }
- });
+ {
+ if (!closing)
+ {
+ closing = true;
+ await Dispatcher.InvokeAsync(fadeOutStoryboard.Begin);
+ }
+ });
}
}
}
diff --git a/Flow.Launcher/NativeMethods.txt b/Flow.Launcher/NativeMethods.txt
deleted file mode 100644
index 88eeeca6e..000000000
--- a/Flow.Launcher/NativeMethods.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-DwmSetWindowAttribute
-DwmExtendFrameIntoClientArea
-SystemParametersInfo
-SetForegroundWindow
-
-GetWindowLong
-SetWindowLong
-GetForegroundWindow
-GetDesktopWindow
-GetShellWindow
-GetWindowRect
-GetClassName
-FindWindowEx
-WINDOW_STYLE
-
-WM_ENTERSIZEMOVE
-WM_EXITSIZEMOVE
-
-SetLastError
-WINDOW_EX_STYLE
\ No newline at end of file
diff --git a/Flow.Launcher/SettingWindow.xaml.cs b/Flow.Launcher/SettingWindow.xaml.cs
index 30b51f992..d7f8489af 100644
--- a/Flow.Launcher/SettingWindow.xaml.cs
+++ b/Flow.Launcher/SettingWindow.xaml.cs
@@ -4,7 +4,7 @@ using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Interop;
using CommunityToolkit.Mvvm.DependencyInjection;
-using Flow.Launcher.Helper;
+using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.SettingPages.Views;
@@ -143,8 +143,8 @@ public partial class SettingWindow
private double WindowLeft()
{
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
- var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
- var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
+ var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
+ var dip2 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
var left = (dip2.X - ActualWidth) / 2 + dip1.X;
return left;
}
@@ -152,8 +152,8 @@ public partial class SettingWindow
private double WindowTop()
{
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
- var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
- var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
+ var dip1 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
+ var dip2 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
var top = (dip2.Y - ActualHeight) / 2 + dip1.Y - 20;
return top;
}
diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs
index 476479405..7d719a442 100644
--- a/Flow.Launcher/ViewModel/MainViewModel.cs
+++ b/Flow.Launcher/ViewModel/MainViewModel.cs
@@ -6,7 +6,6 @@ using System.Threading.Tasks;
using System.Windows;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Core.Resource;
-using Flow.Launcher.Helper;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Infrastructure.Storage;
@@ -27,7 +26,6 @@ using Flow.Launcher.Infrastructure.Image;
using System.Windows.Media;
using CommunityToolkit.Mvvm.DependencyInjection;
using System.Windows.Threading;
-using System.Windows.Interop;
namespace Flow.Launcher.ViewModel
{
@@ -1484,7 +1482,7 @@ namespace Flow.Launcher.ViewModel
///
public bool ShouldIgnoreHotkeys()
{
- return Settings.IgnoreHotkeysOnFullscreen && WindowsInteropHelper.IsWindowFullscreen() || GameModeStatus;
+ return Settings.IgnoreHotkeysOnFullscreen && Win32Helper.IsForegroundWindowFullscreen() || GameModeStatus;
}
#endregion