using System.Runtime.InteropServices; namespace CursorLang.Core.Threading; /// /// The Win32 message loop — what the application has instead of a dispatcher. /// /// /// It lives in Core rather than in the agent because the things that need a pumping /// thread do: 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. /// public static class MessageLoop { /// /// Pumps messages until WM_QUIT and returns its exit code. /// /// /// A -1 from GetMessage means the window handle has already gone; going round /// again would spin forever, so the loop gives up instead. /// 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); } } /// Asks the loop on this thread to finish. public static void Quit(int exitCode = 0) => PostQuitMessage(exitCode); [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")] 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; } }