This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace CursorLang.Core.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// The Win32 message loop — what the application has instead of a dispatcher.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It lives in Core rather than in the agent because the things that need a pumping
|
||||
/// thread do: <see cref="MessageTimer"/> is used by the layout polling and by saving
|
||||
/// the settings, and both are Core's. The settings window has a loop of its own, run
|
||||
/// by WPF, and everything here works inside it just the same.
|
||||
/// </remarks>
|
||||
public static class MessageLoop
|
||||
{
|
||||
/// <summary>
|
||||
/// Pumps messages until <c>WM_QUIT</c> and returns its exit code.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A -1 from GetMessage means the window handle has already gone; going round
|
||||
/// again would spin forever, so the loop gives up instead.
|
||||
/// </remarks>
|
||||
public static int Run()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int result = GetMessage(out Message message, IntPtr.Zero, 0, 0);
|
||||
if (result is 0 or -1)
|
||||
{
|
||||
return result == 0 ? (int)message.wParam : 1;
|
||||
}
|
||||
|
||||
TranslateMessage(ref message);
|
||||
DispatchMessage(ref message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Asks the loop on this thread to finish.</summary>
|
||||
public static void Quit(int exitCode = 0) => PostQuitMessage(exitCode);
|
||||
|
||||
/// <summary>
|
||||
/// Runs everything already waiting in the queue and returns.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For the tests and for the rare place that has to let a posted message through
|
||||
/// without giving up control for good.
|
||||
/// </remarks>
|
||||
public static void DrainQueue()
|
||||
{
|
||||
while (PeekMessage(out Message message, IntPtr.Zero, 0, 0, PM_REMOVE))
|
||||
{
|
||||
if (message.message == WM_QUIT)
|
||||
{
|
||||
PostQuitMessage((int)message.wParam);
|
||||
return;
|
||||
}
|
||||
|
||||
TranslateMessage(ref message);
|
||||
DispatchMessage(ref message);
|
||||
}
|
||||
}
|
||||
|
||||
private const uint WM_QUIT = 0x0012;
|
||||
private const uint PM_REMOVE = 0x0001;
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetMessageW")]
|
||||
private static extern int GetMessage(out Message lpMsg, IntPtr hWnd, uint filterMin, uint filterMax);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "PeekMessageW")]
|
||||
private static extern bool PeekMessage(
|
||||
out Message lpMsg, IntPtr hWnd, uint filterMin, uint filterMax, uint removeMsg);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool TranslateMessage(ref Message lpMsg);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "DispatchMessageW")]
|
||||
private static extern IntPtr DispatchMessage(ref Message lpMsg);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern void PostQuitMessage(int exitCode);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct Message
|
||||
{
|
||||
public IntPtr hwnd;
|
||||
public uint message;
|
||||
public IntPtr wParam;
|
||||
public IntPtr lParam;
|
||||
public uint time;
|
||||
public int x;
|
||||
public int y;
|
||||
public uint lPrivate;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user