commit 86e5dcd3185a02e628c2bf9f5a9058a671713d23 Author: Herwin Bozet Date: Sun Jan 19 23:08:37 2025 +0100 Initial commit Update .gitignore, ConsoleApp1.csproj, and 23 more files... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..319ca0a --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# IDEs +.vs/ +.idea/ + +# Build Artifacts +bin/ +obj/ +*.dll +*.exe + +# Trash +/packages/ +riderModule.iml +/_ReSharper.Caches/ diff --git a/ConsoleApp1/ConsoleApp1.csproj b/ConsoleApp1/ConsoleApp1.csproj new file mode 100644 index 0000000..4f6a2c9 --- /dev/null +++ b/ConsoleApp1/ConsoleApp1.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + enable + enable + true + true + + + + + + + diff --git a/ConsoleApp1/Program.cs b/ConsoleApp1/Program.cs new file mode 100644 index 0000000..6b4c0cf --- /dev/null +++ b/ConsoleApp1/Program.cs @@ -0,0 +1,15 @@ +using NibblePoker.Win32Wrappers; + +namespace ConsoleApp1; + +//https://getandplay.github.io/2019/05/08/Find%20the%20window%20you%20wanted%20by%20using%20EnumWindows/ + +class Program { + public static int Main(string[] args) { + + + + Console.WriteLine("Hello, World!"); + return 0; + } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/NibblePoker.Flemmotron.Commons/BasicExecutor.cs b/NibblePoker.Flemmotron.Commons/BasicExecutor.cs new file mode 100644 index 0000000..ee4031d --- /dev/null +++ b/NibblePoker.Flemmotron.Commons/BasicExecutor.cs @@ -0,0 +1,7 @@ +namespace NibblePoker.Flemmotron.Commons; + +public class BasicExecutor : IExecutor { + + + +} diff --git a/NibblePoker.Flemmotron.Commons/IExecutor.cs b/NibblePoker.Flemmotron.Commons/IExecutor.cs new file mode 100644 index 0000000..078b1fd --- /dev/null +++ b/NibblePoker.Flemmotron.Commons/IExecutor.cs @@ -0,0 +1,12 @@ +namespace NibblePoker.Flemmotron.Commons; + +public interface IExecutor { + + /*#region Properties + + public readonly List prerequisiteTasks; + + + #endregion*/ + +} diff --git a/NibblePoker.Flemmotron.Commons/NibblePoker.Flemmotron.Commons.csproj b/NibblePoker.Flemmotron.Commons/NibblePoker.Flemmotron.Commons.csproj new file mode 100644 index 0000000..3a63532 --- /dev/null +++ b/NibblePoker.Flemmotron.Commons/NibblePoker.Flemmotron.Commons.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/NibblePoker.Flemmotron.Commons/Utils/ProcessUtils.cs b/NibblePoker.Flemmotron.Commons/Utils/ProcessUtils.cs new file mode 100644 index 0000000..849d1f2 --- /dev/null +++ b/NibblePoker.Flemmotron.Commons/Utils/ProcessUtils.cs @@ -0,0 +1,27 @@ +namespace NibblePoker.Flemmotron.Commons.Utils; + +public class ProcessUtils { + public static IntPtr GetProcess() { + return IntPtr.Zero; + } + + /*[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool EnumWindows(EnumedWindow lpEnumFunc, ArrayList lParam); + + public static ArrayList GetWindows() + { + ArrayList windowHandles = new ArrayList(); + EnumedWindow callBackPtr = GetWindowHandle; + EnumWindows(callBackPtr, windowHandles); + + return windowHandles; + } + + private static bool GetWindowHandle(IntPtr windowHandle, ArrayList windowHandles) + { + windowHandles.Add(windowHandle); + return true; + }*/ + +} diff --git a/NibblePoker.Flemmotron.Commons/Utils/SendKeys.cs b/NibblePoker.Flemmotron.Commons/Utils/SendKeys.cs new file mode 100644 index 0000000..d4f9b47 --- /dev/null +++ b/NibblePoker.Flemmotron.Commons/Utils/SendKeys.cs @@ -0,0 +1,35 @@ +namespace NibblePoker.Flemmotron.Commons.Utils; + +public class SendKeys { + + /*public static async Task Main(string[] args) { + // Simulate a left mouse button click + var input = new INPUT[2]; + + // Mouse down + input[0] = new INPUT { + type = INPUT_MOUSE, + U = new InputUnion { + mi = new MOUSEINPUT { + dwFlags = MOUSEEVENTF_LEFTDOWN, + } + } + }; + + // Mouse up + input[1] = new INPUT { + type = INPUT_MOUSE, + U = new InputUnion { + mi = new MOUSEINPUT { + dwFlags = MOUSEEVENTF_LEFTUP, + } + } + }; + + // Send the input + Imports.SendInput((uint)input.Length, input, Marshal.SizeOf(typeof(INPUT))); + + // Optional delay + await Task.Delay(300); + }*/ +} diff --git a/NibblePoker.Flemmotron.Commons/Utils/WindowUtils.cs b/NibblePoker.Flemmotron.Commons/Utils/WindowUtils.cs new file mode 100644 index 0000000..d915822 --- /dev/null +++ b/NibblePoker.Flemmotron.Commons/Utils/WindowUtils.cs @@ -0,0 +1,22 @@ +namespace NibblePoker.Flemmotron.Commons.Utils; + +public class WindowUtils { + /*public static IReadOnlyList FindWindowByClassName(string className) + { + var windowList = new List(); + EnumWindows(OnWindowEnum, 0); + return windowList; + + bool OnWindowEnum(int hwnd, int lparam) + { + var lpString = new StringBuilder(512); + GetClassName(hwnd, lpString, lpString.Capacity); + if (lpString.ToString().Equals(className, StringComparison.InvariantCultureIgnoreCase)) + { + windowList.Add(hwnd); + } + + return true; + } + }*/ +} diff --git a/NibblePoker.Flemmotron.TestLib/Class1.cs b/NibblePoker.Flemmotron.TestLib/Class1.cs new file mode 100644 index 0000000..368a2cd --- /dev/null +++ b/NibblePoker.Flemmotron.TestLib/Class1.cs @@ -0,0 +1,3 @@ +namespace NibblePoker.Flemmotron.TestLib; + +public class Class1 { } diff --git a/NibblePoker.Flemmotron.TestLib/NibblePoker.Flemmotron.TestLib.csproj b/NibblePoker.Flemmotron.TestLib/NibblePoker.Flemmotron.TestLib.csproj new file mode 100644 index 0000000..3a63532 --- /dev/null +++ b/NibblePoker.Flemmotron.TestLib/NibblePoker.Flemmotron.TestLib.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/NibblePoker.Flemmotron.sln b/NibblePoker.Flemmotron.sln new file mode 100644 index 0000000..9cfe69c --- /dev/null +++ b/NibblePoker.Flemmotron.sln @@ -0,0 +1,40 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NibblePoker.Flemmotron.TestLib", "NibblePoker.Flemmotron.TestLib\NibblePoker.Flemmotron.TestLib.csproj", "{B72FCF67-410C-4AB9-93EB-138AA997025E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NibblePoker.Flemmotron.Commons", "NibblePoker.Flemmotron.Commons\NibblePoker.Flemmotron.Commons.csproj", "{9B454754-933F-49E3-9EA1-A161CDEB02D6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NibblePoker.Win32Bindings", "NibblePoker.Win32Bindings\NibblePoker.Win32Bindings.csproj", "{D59AA77B-F9F4-4EE7-857C-C0B95489655B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NibblePoker.Win32Wrappers", "NibblePoker.Win32Wrappers\NibblePoker.Win32Wrappers.csproj", "{FDA327CC-C600-4CCB-B3D9-2585B1C7C5E5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp1", "ConsoleApp1\ConsoleApp1.csproj", "{EA368610-B371-40AF-98C6-3AFB46D7209E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B72FCF67-410C-4AB9-93EB-138AA997025E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B72FCF67-410C-4AB9-93EB-138AA997025E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B72FCF67-410C-4AB9-93EB-138AA997025E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B72FCF67-410C-4AB9-93EB-138AA997025E}.Release|Any CPU.Build.0 = Release|Any CPU + {9B454754-933F-49E3-9EA1-A161CDEB02D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B454754-933F-49E3-9EA1-A161CDEB02D6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B454754-933F-49E3-9EA1-A161CDEB02D6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B454754-933F-49E3-9EA1-A161CDEB02D6}.Release|Any CPU.Build.0 = Release|Any CPU + {D59AA77B-F9F4-4EE7-857C-C0B95489655B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D59AA77B-F9F4-4EE7-857C-C0B95489655B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D59AA77B-F9F4-4EE7-857C-C0B95489655B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D59AA77B-F9F4-4EE7-857C-C0B95489655B}.Release|Any CPU.Build.0 = Release|Any CPU + {FDA327CC-C600-4CCB-B3D9-2585B1C7C5E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDA327CC-C600-4CCB-B3D9-2585B1C7C5E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDA327CC-C600-4CCB-B3D9-2585B1C7C5E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDA327CC-C600-4CCB-B3D9-2585B1C7C5E5}.Release|Any CPU.Build.0 = Release|Any CPU + {EA368610-B371-40AF-98C6-3AFB46D7209E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA368610-B371-40AF-98C6-3AFB46D7209E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA368610-B371-40AF-98C6-3AFB46D7209E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA368610-B371-40AF-98C6-3AFB46D7209E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/NibblePoker.Win32Bindings/Kernel32.cs b/NibblePoker.Win32Bindings/Kernel32.cs new file mode 100644 index 0000000..2649a0d --- /dev/null +++ b/NibblePoker.Win32Bindings/Kernel32.cs @@ -0,0 +1,16 @@ +using System.Runtime.InteropServices; + +namespace NibblePoker.Win32Bindings; + +// ReSharper disable UnusedMember.Global +public static class Kernel32 { + + /// https://learn.microsoft.com/en-us/windows/win32/api/utilapiset/nf-utilapiset-beep + /// + /// After some testing on a Windows 11 23H2 machine, it appears that the frequency limits are not enforced. + /// + [DllImport("kernel32.dll", CharSet = CharSet.None, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool Beep([In] uint dwFreq, [In] uint dwDuration); + +} diff --git a/NibblePoker.Win32Bindings/NibblePoker.Win32Bindings.csproj b/NibblePoker.Win32Bindings/NibblePoker.Win32Bindings.csproj new file mode 100644 index 0000000..a431a22 --- /dev/null +++ b/NibblePoker.Win32Bindings/NibblePoker.Win32Bindings.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/NibblePoker.Win32Bindings/User32.cs b/NibblePoker.Win32Bindings/User32.cs new file mode 100644 index 0000000..2b02332 --- /dev/null +++ b/NibblePoker.Win32Bindings/User32.cs @@ -0,0 +1,86 @@ +using System.Runtime.InteropServices; + +namespace NibblePoker.Win32Bindings; + +// ReSharper disable InconsistentNaming +// ReSharper disable UnusedMember.Global +public class User32 { + + #region MB_* flags + + public const uint MB_ABORTRETRYIGNORE = 0x00000002; + public const uint MB_CANCELTRYCONTINUE = 0x00000006; + public const uint MB_HELP = 0x00004000; + public const uint MB_OK = 0x00000000; + public const uint MB_OKCANCEL = 0x00000001; + public const uint MB_RETRYCANCEL = 0x00000005; + public const uint MB_YESNO = 0x00000004; + public const uint MB_YESNOCANCEL = 0x00000003; + + public const uint MB_ICONEXCLAMATION = 0x00000030; + public const uint MB_ICONWARNING = 0x00000030; + public const uint MB_ICONINFORMATION = 0x00000040; + public const uint MB_ICONASTERISK = 0x00000040; + public const uint MB_ICONQUESTION = 0x00000020; + public const uint MB_ICONSTOP = 0x00000010; + public const uint MB_ICONERROR = 0x00000010; + public const uint MB_ICONHAND = 0x00000010; + + public const uint MB_DEFBUTTON1 = 0x00000000; + public const uint MB_DEFBUTTON2 = 0x00000100; + public const uint MB_DEFBUTTON3 = 0x00000200; + public const uint MB_DEFBUTTON4 = 0x00000300; + + public const uint MB_APPLMODAL = 0x00000000; + public const uint MB_SYSTEMMODAL = 0x00001000; + public const uint MB_TASKMODAL = 0x00002000; + + public const uint MB_DEFAULT_DESKTOP_ONLY = 0x00020000; + public const uint MB_RIGHT = 0x00080000; + public const uint MB_RTLREADING = 0x00100000; + public const uint MB_SETFOREGROUND = 0x00010000; + public const uint MB_TOPMOST = 0x00040000; + public const uint MB_SERVICE_NOTIFICATION = 0x00200000; + + /// + /// Used by for the sound type that is propagated to internally. + /// + public const uint MB_FALLBACK = 0xFFFFFFFF; + + #endregion + + + #region MessageBox Returns + + public const int IDABORT = 3; + public const int IDCANCEL = 2; + public const int IDCONTINUE = 11; + public const int IDIGNORE = 5; + public const int IDNO = 7; + public const int IDOK = 1; + public const int IDRETRY = 4; + public const int IDTRYAGAIN = 10; + public const int IDYES = 6; + + #endregion + + /// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebeep + [DllImport("user32.dll", CharSet = CharSet.None, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool MessageBeep(uint uType); + + [DllImport("user32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.I4)] + public static extern int MessageBoxA(IntPtr hWnd, nint lpText, nint lpCaption, uint uType); + + [DllImport("user32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.I4)] + public static extern int MessageBoxW(IntPtr hWnd, nint lpText, nint lpCaption, uint uType); + + //[DllImport("user32.dll")] + //public static extern int GetClassName(int hWnd, StringBuilder lpString, int nMaxCount); + + //[DllImport("user32")] + //public static extern int GetWindowText(int hwnd, StringBuilder lptrString, int nMaxCount); + +} diff --git a/NibblePoker.Win32Bindings/WinUser/SendKeys/EEventTypes.cs b/NibblePoker.Win32Bindings/WinUser/SendKeys/EEventTypes.cs new file mode 100644 index 0000000..0683768 --- /dev/null +++ b/NibblePoker.Win32Bindings/WinUser/SendKeys/EEventTypes.cs @@ -0,0 +1,24 @@ +namespace NibblePoker.Win32Bindings.WinUser.SendKeys; + +public enum EEventTypes { + /// + /// The event is a mouse event. Use the mi structure of the union. + /// + /// + /// + INPUT_MOUSE = 0, + + /// + /// The event is a keyboard event. Use the ki structure of the union. + /// + /// + /// + INPUT_KEYBOARD = 1, + + /// + /// The event is a hardware event. Use the hi structure of the union. + /// + /// + /// + INPUT_HARDWARE = 2, +} diff --git a/NibblePoker.Win32Bindings/WinUser/SendKeys/EKeyEventFlags.cs b/NibblePoker.Win32Bindings/WinUser/SendKeys/EKeyEventFlags.cs new file mode 100644 index 0000000..f1eb2a5 --- /dev/null +++ b/NibblePoker.Win32Bindings/WinUser/SendKeys/EKeyEventFlags.cs @@ -0,0 +1,40 @@ +namespace NibblePoker.Win32Bindings.WinUser.SendKeys; + +// ReSharper disable InconsistentNaming +[Flags] +public enum EKeyEventFlags : uint { + // https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput + + /// + /// If specified, the wScan scan code consists of a sequence of two bytes, where the first byte has a value of 0xE0. + /// See Extended-Key Flag + /// for more info. + /// + /// + /// + KEYEVENTF_EXTENDEDKEY = 0x0001, + + /// + /// If specified, the key is being released. + /// If not specified, the key is being pressed. + /// + /// + /// + KEYEVENTF_KEYUP = 0x0002, + + /// + /// If specified, identifies the key and is ignored. + /// + /// + /// + KEYEVENTF_SCANCODE = 0x0004, + + /// + /// If specified, the system synthesizes a VK_PACKET keystroke. + /// The parameter must be zero. + /// This flag can only be combined with the flag. + /// + /// + /// + KEYEVENTF_UNICODE = 0x0008, +} diff --git a/NibblePoker.Win32Bindings/WinUser/SendKeys/EMouseEventFlags.cs b/NibblePoker.Win32Bindings/WinUser/SendKeys/EMouseEventFlags.cs new file mode 100644 index 0000000..97e6a7f --- /dev/null +++ b/NibblePoker.Win32Bindings/WinUser/SendKeys/EMouseEventFlags.cs @@ -0,0 +1,114 @@ +namespace NibblePoker.Win32Bindings.WinUser.SendKeys; + +// ReSharper disable InconsistentNaming +[Flags] +public enum EMouseEventFlags : uint { + // https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-MOUSEINPUT + + /// + /// Movement occurred. + /// + /// + /// + MOUSEEVENTF_MOVE = 0x0001, + + /// + /// The left button was pressed. + /// + /// + /// + MOUSEEVENTF_LEFTDOWN = 0x0002, + + /// + /// The left button was released. + /// + /// + /// + MOUSEEVENTF_LEFTUP = 0x0004, + + /// + /// The right button was pressed. + /// + /// + /// + MOUSEEVENTF_RIGHTDOWN = 0x0008, + + /// + /// The right button was released. + /// + /// + /// + MOUSEEVENTF_RIGHTUP = 0x0010, + + /// + /// The middle button was pressed. + /// + /// + /// + MOUSEEVENTF_MIDDLEDOWN = 0x0020, + + /// + /// The middle button was released. + /// + /// + /// + MOUSEEVENTF_MIDDLEUP = 0x0040, + + /// + /// An X button was pressed. + /// + /// + /// + MOUSEEVENTF_XDOWN = 0x0080, + + /// + /// An X button was released. + /// + /// + /// + MOUSEEVENTF_XUP = 0x0100, + + /// + /// The wheel was moved, if the mouse has a wheel. + /// The amount of movement is specified in mouseData. + /// + /// + /// + MOUSEEVENTF_WHEEL = 0x0800, + + /// + /// The wheel was moved horizontally, if the mouse has a wheel. + /// The amount of movement is specified in mouseData. + /// + /// Windows XP/2000: This value is not supported + /// + /// + MOUSEEVENTF_HWHEEL = 0x1000, + + /// + /// The WM_MOUSEMOVE + /// messages will not be coalesced. + /// The default behavior is to coalesce WM_MOUSEMOVE messages. + /// + /// Windows XP/2000: This value is not supported + /// + /// + MOUSEEVENTF_MOVE_NOCOALESCE = 0x2000, + + /// + /// Maps coordinates to the entire desktop. + /// Must be used with . + /// + /// + /// + MOUSEEVENTF_VIRTUALDESK = 0x4000, + + /// + /// The dx and dy members contain normalized absolute coordinates. + /// If the flag is not set, `dx` and `dy` contain relative data (the change in position since the last reported position). + /// This flag can be set, or not set, regardless of what kind of mouse or other pointing device, if any, is connected to the system. + /// + /// + /// + MOUSEEVENTF_ABSOLUTE = 0x8000, +} diff --git a/NibblePoker.Win32Bindings/WinUser/SendKeys/Imports.cs b/NibblePoker.Win32Bindings/WinUser/SendKeys/Imports.cs new file mode 100644 index 0000000..76a5c47 --- /dev/null +++ b/NibblePoker.Win32Bindings/WinUser/SendKeys/Imports.cs @@ -0,0 +1,8 @@ +using System.Runtime.InteropServices; + +namespace NibblePoker.Win32Bindings.WinUser.SendKeys; + +public class Imports { + [DllImport("user32.dll", CallingConvention = CallingConvention.Winapi)] + public static extern uint SendInput(uint nInputs, Structures.INPUT[] pInputs, int cbSize); +} diff --git a/NibblePoker.Win32Bindings/WinUser/SendKeys/Structures.cs b/NibblePoker.Win32Bindings/WinUser/SendKeys/Structures.cs new file mode 100644 index 0000000..d5f554d --- /dev/null +++ b/NibblePoker.Win32Bindings/WinUser/SendKeys/Structures.cs @@ -0,0 +1,44 @@ +using System.Runtime.InteropServices; + +namespace NibblePoker.Win32Bindings.WinUser.SendKeys; + +public class Structures { + [StructLayout(LayoutKind.Sequential)] + public struct INPUT { + public int type; + public InputUnion U; + } + + [StructLayout(LayoutKind.Explicit)] + public struct InputUnion { + [FieldOffset(0)] public MOUSEINPUT mi; + [FieldOffset(0)] public KEYBDINPUT ki; + [FieldOffset(0)] public HARDWAREINPUT hi; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MOUSEINPUT { + public int dx; + public int dy; + public int mouseData; + public uint dwFlags; + public uint time; + public IntPtr dwExtraInfo; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KEYBDINPUT { + public ushort wVk; + public ushort wScan; + public uint dwFlags; + public uint time; + public IntPtr dwExtraInfo; + } + + [StructLayout(LayoutKind.Sequential)] + public struct HARDWAREINPUT { + public uint uMsg; + public ushort wParamL; + public ushort wParamH; + } +} diff --git a/NibblePoker.Win32Wrappers/Beeps.cs b/NibblePoker.Win32Wrappers/Beeps.cs new file mode 100644 index 0000000..a4fcbe5 --- /dev/null +++ b/NibblePoker.Win32Wrappers/Beeps.cs @@ -0,0 +1,45 @@ +using System.ComponentModel; +using System.Runtime.InteropServices; +using NibblePoker.Win32Bindings; + +namespace NibblePoker.Win32Wrappers; + +// ReSharper disable MemberCanBePrivate.Global +public static class Beeps { + public enum SoundTypes : uint { + Fallback = User32.MB_FALLBACK, + CriticalStop = User32.MB_ICONERROR, + Asterisk = User32.MB_ICONINFORMATION, + Question = User32.MB_ICONQUESTION, // Doesn't work on W11 !! + Exclamation = User32.MB_ICONWARNING, + Default = User32.MB_OK, + } + + public static void Beep(uint frequency, uint durationMs) { + if(!Kernel32.Beep(frequency, durationMs)) { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } + + public static void Beep(uint type) { + if(!User32.MessageBeep(type)) { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } + + public static void Beep(SoundTypes type) { + Beep((uint)type); + } + + public static bool BeepSafe(uint frequency, uint durationMs) { + return Kernel32.Beep(frequency, durationMs); + } + + public static bool BeepSafe(uint type) { + return User32.MessageBeep(type); + } + + public static bool BeepSafe(SoundTypes type) { + return BeepSafe((uint)type); + } +} diff --git a/NibblePoker.Win32Wrappers/MessageBox.cs b/NibblePoker.Win32Wrappers/MessageBox.cs new file mode 100644 index 0000000..b60f947 --- /dev/null +++ b/NibblePoker.Win32Wrappers/MessageBox.cs @@ -0,0 +1,52 @@ +using System.ComponentModel; +using System.Runtime.InteropServices; +using static NibblePoker.Win32Bindings.User32; + +namespace NibblePoker.Win32Wrappers; + +public static class MessageBox { + // ReSharper disable once EnumUnderlyingTypeIsInt + public enum EResults : int { + Error = 0, + Abort = IDABORT, + Cancel = IDCANCEL, + Continue = IDCONTINUE, + Ignore = IDIGNORE, + No = IDNO, + Ok = IDOK, + Retry = IDRETRY, + TryAgain = IDTRYAGAIN, + Yes = IDYES, + } + + public static EResults Show(string title, string content, uint options = 0) { + nint ptrTitle = Marshal.StringToHGlobalUni(title); + nint ptrContent = Marshal.StringToHGlobalUni(content); + + try { + EResults mbResult = (EResults)MessageBoxW(IntPtr.Zero, ptrContent, ptrTitle, options); + + if(mbResult == EResults.Error) { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + // TODO: Check if the code is known ! + /*else { +#if DEBUG + Console.WriteLine("Mode=Debug"); +#else + Console.WriteLine("Mode=Release"); +#endif + }*/ + + return mbResult; + } finally { + Marshal.FreeHGlobal(ptrContent); + Marshal.FreeHGlobal(ptrTitle); + } + } + + /*public static int ShowSafe(string title, string content, uint options = 0) { + return 0; + }*/ +} diff --git a/NibblePoker.Win32Wrappers/NibblePoker.Win32Wrappers.csproj b/NibblePoker.Win32Wrappers/NibblePoker.Win32Wrappers.csproj new file mode 100644 index 0000000..7dfab9a --- /dev/null +++ b/NibblePoker.Win32Wrappers/NibblePoker.Win32Wrappers.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..1a2e243 Binary files /dev/null and b/icon.png differ