Improve code quality

This commit is contained in:
Jack251970 2025-03-16 22:24:06 +08:00
parent 1ca17aa2db
commit 2cd1683741
12 changed files with 434 additions and 378 deletions

View file

@ -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;
/// <summary>
/// Contains full information about a display monitor.
/// Codes are edited from: <see href="https://github.com/Jack251970/DesktopWidgets3">.
/// </summary>
internal class MonitorInfo
{
/// <summary>
/// Gets the display monitors (including invisible pseudo-monitors associated with the mirroring drivers).
/// </summary>
/// <returns>A list of display monitors</returns>
public static unsafe IList<MonitorInfo> GetDisplayMonitors()
{
var monitorCount = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CMONITORS);
var list = new List<MonitorInfo>(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;
}
/// <summary>
/// Gets the display monitor that is nearest to a given window.
/// </summary>
/// <param name="hwnd">Window handle</param>
/// <returns>The display monitor that is nearest to a given window, or null if no monitor is found.</returns>
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();
}
/// <summary>
/// Gets the name of the display.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the display monitor rectangle, expressed in virtual-screen coordinates.
/// </summary>
/// <remarks>
/// <note>If the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.</note>
/// </remarks>
public Rect RectMonitor { get; }
/// <summary>
/// Gets the work area rectangle of the display monitor, expressed in virtual-screen coordinates.
/// </summary>
/// <remarks>
/// <note>If the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.</note>
/// </remarks>
public Rect RectWork { get; }
/// <summary>
/// Gets if the monitor is the the primary display monitor.
/// </summary>
public bool IsPrimary => _monitor == PInvoke.MonitorFromWindow(new(IntPtr.Zero), MONITOR_FROM_FLAGS.MONITOR_DEFAULTTOPRIMARY);
/// <inheritdoc />
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;
}
}
}

View file

@ -20,4 +20,31 @@ EnumWindows
DwmSetWindowAttribute
DWM_SYSTEMBACKDROP_TYPE
DWM_WINDOW_CORNER_PREFERENCE
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

View file

@ -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<int>()).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<int>()).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<int>()).Succeeded;
@ -75,9 +70,6 @@ namespace Flow.Launcher.Infrastructure
/// <returns></returns>
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<int>()).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
/// <summary>
/// Hide windows in the Alt+Tab window list
/// </summary>
/// <param name="window">To hide a window</param>
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);
}
/// <summary>
/// Restore window display in the Alt+Tab window list.
/// </summary>
/// <param name="window">To restore the displayed window</param>
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);
}
/// <summary>
/// Disable windows toolbar's control box
/// This will also disable system menu with Alt+Space hotkey
/// </summary>
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<char> 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
/// <summary>
/// Transforms pixels to Device Independent Pixels used by WPF
/// </summary>
/// <param name="visual">current window, required to get presentation source</param>
/// <param name="unitX">horizontal position in pixels</param>
/// <param name="unitY">vertical position in pixels</param>
/// <returns>point containing device independent pixels</returns>
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
}
}

View file

@ -94,10 +94,6 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<!-- ModernWpfUI v0.9.5 introduced WinRT changes that causes Notification platform unavailable error on some machines -->
<!-- https://github.com/Flow-Launcher/Flow.Launcher/issues/1772#issuecomment-1502440801 -->
<PackageReference Include="ModernWpfUI" Version="0.9.4" />

View file

@ -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
{
/// <summary>
/// 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).
/// </summary>
/// <param name="window">Window to which the shadow will be applied</param>
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;
}
/// <summary>
/// The actual method that makes API calls to drop the shadow to the window
/// </summary>
/// <param name="window">Window to which the shadow will be applied</param>
/// <returns>True if the method succeeded, false if not</returns>
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;
}
}
}

View file

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

View file

@ -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<char> 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;
}
/// <summary>
/// disable windows toolbar's control box
/// this will also disable system menu with Alt+Space hotkey
/// </summary>
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());
}
}
/// <summary>
/// Transforms pixels to Device Independent Pixels used by WPF
/// </summary>
/// <param name="visual">current window, required to get presentation source</param>
/// <param name="unitX">horizontal position in pixels</param>
/// <param name="unitY">vertical position in pixels</param>
/// <returns>point containing device independent pixels</returns>
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;
}
/// <summary>
/// Hide windows in the Alt+Tab window list
/// </summary>
/// <param name="window">To hide a window</param>
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);
}
/// <summary>
/// Restore window display in the Alt+Tab window list.
/// </summary>
/// <param name="window">To restore the displayed window</param>
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);
}
/// <summary>
/// To obtain the current overridden style of a window.
/// </summary>
/// <param name="window">To obtain the style dialog window</param>
/// <returns>current extension style value</returns>
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
}

View file

@ -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<Theme>().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;
}
/// <summary>
/// Register up and down key
/// todo: any way to put this in xaml ?
/// todo: Put this in xaml?
/// </summary>
private void OnKeyDown(object sender, KeyEventArgs e)
{

View file

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

View file

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

View file

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

View file

@ -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
/// </summary>
public bool ShouldIgnoreHotkeys()
{
return Settings.IgnoreHotkeysOnFullscreen && WindowsInteropHelper.IsWindowFullscreen() || GameModeStatus;
return Settings.IgnoreHotkeysOnFullscreen && Win32Helper.IsForegroundWindowFullscreen() || GameModeStatus;
}
#endregion