176 lines
6.4 KiB
C#
176 lines
6.4 KiB
C#
using System.Runtime.InteropServices;
|
|
using CursorLang.Core.Interop;
|
|
|
|
namespace CursorLang.Agent.Interop;
|
|
|
|
/// <summary>
|
|
/// The Win32 pieces a window needs when there is no framework to make one:
|
|
/// the class, the window itself, the message loop.
|
|
/// </summary>
|
|
internal static class WindowNative
|
|
{
|
|
/// <summary>The window procedure. Windows keeps the only reference to it.</summary>
|
|
internal delegate IntPtr WindowProc(IntPtr hWnd, uint message, IntPtr wParam, IntPtr lParam);
|
|
|
|
internal const int WS_POPUP = unchecked((int)0x80000000);
|
|
|
|
internal const int WS_EX_LAYERED = 0x00080000;
|
|
internal const int WS_EX_TOOLWINDOW = 0x00000080;
|
|
internal const int WS_EX_NOACTIVATE = 0x08000000;
|
|
internal const int WS_EX_TRANSPARENT = 0x00000020;
|
|
internal const int WS_EX_TOPMOST = 0x00000008;
|
|
|
|
internal const int SW_HIDE = 0;
|
|
internal const int SW_SHOWNOACTIVATE = 4;
|
|
|
|
internal const uint WM_DESTROY = 0x0002;
|
|
internal const uint WM_CLOSE = 0x0010;
|
|
internal const uint WM_QUIT = 0x0012;
|
|
internal const uint WM_NULL = 0x0000;
|
|
internal const uint WM_ENDSESSION = 0x0016;
|
|
|
|
/// <summary>WM_APP and up belong to the application; the tray takes WM_APP + 1.</summary>
|
|
internal const uint WM_APP = 0x8000;
|
|
|
|
/// <summary>
|
|
/// Registers a window class. A class already there is not an error: the name is
|
|
/// unique per window kind, and a second agent in the same process would meet its
|
|
/// own registration.
|
|
/// </summary>
|
|
internal static void RegisterClass(string className, WindowProc windowProc)
|
|
{
|
|
var description = new WindowClass
|
|
{
|
|
cbSize = Marshal.SizeOf<WindowClass>(),
|
|
lpfnWndProc = windowProc,
|
|
hInstance = GetModuleHandle(null),
|
|
lpszClassName = className,
|
|
};
|
|
|
|
if (RegisterClassEx(ref description) == 0 &&
|
|
Marshal.GetLastWin32Error() != ErrorClassAlreadyExists)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"RegisterClassExW failed for '{className}': {Marshal.GetLastWin32Error()}");
|
|
}
|
|
}
|
|
|
|
private const int ErrorClassAlreadyExists = 1410;
|
|
|
|
/// <summary>Creates a window of a registered class. It is not shown.</summary>
|
|
internal static IntPtr CreateWindow(string className, string title, int style, int exStyle)
|
|
{
|
|
IntPtr window = CreateWindowEx(
|
|
exStyle, className, title, style,
|
|
0, 0, 0, 0,
|
|
IntPtr.Zero, IntPtr.Zero, GetModuleHandle(null), IntPtr.Zero);
|
|
|
|
if (window == IntPtr.Zero)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"CreateWindowExW failed for '{className}': {Marshal.GetLastWin32Error()}");
|
|
}
|
|
|
|
return window;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Puts a finished picture into a layered window, together with where it goes and
|
|
/// how see-through it is.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// One call replaces moving the window, resizing it, painting it and setting its
|
|
/// opacity, and it works while the window is still hidden. That is the point: the
|
|
/// content is ready before anyone can see the window, so it can never be shown
|
|
/// holding the picture of the previous time.
|
|
/// </remarks>
|
|
internal static bool SetContent(
|
|
IntPtr window, PopupWindowNative.Point at, Size size, IntPtr sourceDc, byte alpha)
|
|
{
|
|
var source = new PopupWindowNative.Point { X = 0, Y = 0 };
|
|
var blend = new BlendFunction
|
|
{
|
|
BlendOp = AC_SRC_OVER,
|
|
SourceConstantAlpha = alpha,
|
|
AlphaFormat = AC_SRC_ALPHA,
|
|
};
|
|
|
|
return UpdateLayeredWindow(
|
|
window, IntPtr.Zero, ref at, ref size, sourceDc, ref source, 0, ref blend, ULW_ALPHA);
|
|
}
|
|
|
|
private const byte AC_SRC_OVER = 0;
|
|
private const byte AC_SRC_ALPHA = 1;
|
|
private const uint ULW_ALPHA = 0x00000002;
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct BlendFunction
|
|
{
|
|
public byte BlendOp;
|
|
public byte BlendFlags;
|
|
public byte SourceConstantAlpha;
|
|
public byte AlphaFormat;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
internal struct Size
|
|
{
|
|
public int Width;
|
|
public int Height;
|
|
}
|
|
|
|
[DllImport("user32.dll", SetLastError = true)]
|
|
private static extern bool UpdateLayeredWindow(IntPtr hWnd, IntPtr hdcDst,
|
|
ref PopupWindowNative.Point pptDst, ref Size psize, IntPtr hdcSrc,
|
|
ref PopupWindowNative.Point pptSrc, uint crKey, ref BlendFunction pblend, uint dwFlags);
|
|
|
|
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
private static extern ushort RegisterClassEx(ref WindowClass lpwcx);
|
|
|
|
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "CreateWindowExW")]
|
|
private static extern IntPtr CreateWindowEx(int dwExStyle, string lpClassName, string lpWindowName,
|
|
int dwStyle, int x, int y, int nWidth, int nHeight,
|
|
IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);
|
|
|
|
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "DefWindowProcW")]
|
|
internal static extern IntPtr DefWindowProc(IntPtr hWnd, uint message, IntPtr wParam, IntPtr lParam);
|
|
|
|
[DllImport("user32.dll")]
|
|
internal static extern bool DestroyWindow(IntPtr hWnd);
|
|
|
|
[DllImport("user32.dll")]
|
|
internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
|
|
|
[DllImport("user32.dll")]
|
|
internal static extern bool IsWindowVisible(IntPtr hWnd);
|
|
|
|
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "PostMessageW")]
|
|
internal static extern bool PostMessage(IntPtr hWnd, uint message, IntPtr wParam, IntPtr lParam);
|
|
|
|
[DllImport("user32.dll")]
|
|
internal static extern void PostQuitMessage(int nExitCode);
|
|
|
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
|
private static extern IntPtr GetModuleHandle(string? lpModuleName);
|
|
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
private struct WindowClass
|
|
{
|
|
public int cbSize;
|
|
public uint style;
|
|
|
|
[MarshalAs(UnmanagedType.FunctionPtr)]
|
|
public WindowProc lpfnWndProc;
|
|
|
|
public int cbClsExtra;
|
|
public int cbWndExtra;
|
|
public IntPtr hInstance;
|
|
public IntPtr hIcon;
|
|
public IntPtr hCursor;
|
|
public IntPtr hbrBackground;
|
|
public string? lpszMenuName;
|
|
public string lpszClassName;
|
|
public IntPtr hIconSm;
|
|
}
|
|
}
|