// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using static SDL.SDL3; namespace SDL.Tests { public sealed unsafe class MyWindow : IDisposable { private bool flash; private ObjectHandle objectHandle { get; } private SDL_Window* sdlWindowHandle; private SDL_Renderer* renderer; private readonly bool initSuccess; private const SDL_InitFlags init_flags = SDL_InitFlags.SDL_INIT_VIDEO | SDL_InitFlags.SDL_INIT_GAMEPAD; public MyWindow() { if (!SDL_InitSubSystem(init_flags)) throw new InvalidOperationException($"failed to initialise SDL. Error: {SDL_GetError()}"); initSuccess = true; objectHandle = new ObjectHandle(this, GCHandleType.Normal); } public void Setup() { SDL_SetGamepadEventsEnabled(true); SDL_SetEventFilter(&nativeFilter, objectHandle.Handle); if (OperatingSystem.IsWindows()) SDL_SetWindowsMessageHook(&wndProc, objectHandle.Handle); } [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] private static SDLBool wndProc(IntPtr userdata, MSG* message) { var handle = new ObjectHandle(userdata); if (handle.GetTarget(out var window)) { Console.WriteLine($"from {window}, message: {message->message}"); } return true; } // ReSharper disable once UseCollectionExpression [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] private static SDLBool nativeFilter(IntPtr userdata, SDL_Event* e) { var handle = new ObjectHandle(userdata); if (handle.GetTarget(out var window)) return window.handleEventFromFilter(e); return true; } public Action? EventFilter; private bool handleEventFromFilter(SDL_Event* e) { switch (e->Type) { case SDL_EventType.SDL_EVENT_KEY_UP: case SDL_EventType.SDL_EVENT_KEY_DOWN: handleKeyFromFilter(e->key); break; default: EventFilter?.Invoke(*e); break; } return true; } private void handleKeyFromFilter(SDL_KeyboardEvent e) { if (e.key == SDL_Keycode.SDLK_F) { flash = true; } } public void Create() { sdlWindowHandle = SDL_CreateWindow("hello"u8, 800, 600, SDL_WindowFlags.SDL_WINDOW_RESIZABLE | SDL_WindowFlags.SDL_WINDOW_HIGH_PIXEL_DENSITY); renderer = SDL_CreateRenderer(sdlWindowHandle, (Utf8String)null); } private void handleEvent(SDL_Event e) { switch (e.Type) { case SDL_EventType.SDL_EVENT_QUIT: run = false; break; case SDL_EventType.SDL_EVENT_KEY_DOWN: switch (e.key.key) { case SDL_Keycode.SDLK_R: bool old = SDL_GetWindowRelativeMouseMode(sdlWindowHandle); SDL_SetWindowRelativeMouseMode(sdlWindowHandle, !old); break; case SDL_Keycode.SDLK_V: string? text = SDL_GetClipboardText(); Console.WriteLine($"clipboard: {text}"); break; case SDL_Keycode.SDLK_F10: SDL_SetWindowFullscreen(sdlWindowHandle, false); break; case SDL_Keycode.SDLK_F11: SDL_SetWindowFullscreen(sdlWindowHandle, true); break; case SDL_Keycode.SDLK_J: { using var gamepads = SDL_GetGamepads(); if (gamepads == null || gamepads.Count == 0) break; var gamepad = SDL_OpenGamepad(gamepads[0]); int count; var bindings = SDL_GetGamepadBindings(gamepad, &count); for (int i = 0; i < count; i++) { var binding = *bindings[i]; Console.WriteLine(binding.input_type); Console.WriteLine(binding.output_type); Console.WriteLine(); } SDL_CloseGamepad(gamepad); break; } case SDL_Keycode.SDLK_F1: SDL_StartTextInput(sdlWindowHandle); break; case SDL_Keycode.SDLK_F2: SDL_StopTextInput(sdlWindowHandle); break; case SDL_Keycode.SDLK_M: SDL_Keymod mod = e.key.mod; Console.WriteLine(mod); break; } break; case SDL_EventType.SDL_EVENT_TEXT_INPUT: Console.WriteLine(e.text.GetText()); break; case SDL_EventType.SDL_EVENT_GAMEPAD_ADDED: Console.WriteLine($"gamepad added: {e.gdevice.which}"); break; case SDL_EventType.SDL_EVENT_PEN_PROXIMITY_IN: Console.WriteLine($"pen proximity in: {e.pproximity.which}"); break; } } private bool run = true; private const int events_per_peep = 64; private readonly SDL_Event[] events = new SDL_Event[events_per_peep]; private void pollEvents() { SDL_PumpEvents(); int eventsRead; do { eventsRead = SDL_PeepEvents(events, SDL_EventAction.SDL_GETEVENT, SDL_EventType.SDL_EVENT_FIRST, SDL_EventType.SDL_EVENT_LAST); for (int i = 0; i < eventsRead; i++) handleEvent(events[i]); } while (eventsRead == events_per_peep); } private float frame; public void Run() { while (run) { if (flash) { flash = false; Console.WriteLine("flash!"); } pollEvents(); SDL_SetRenderDrawColorFloat(renderer, SDL_sinf(frame) / 2 + 0.5f, SDL_cosf(frame) / 2 + 0.5f, 0.3f, 1.0f); SDL_RenderClear(renderer); SDL_RenderPresent(renderer); frame += 0.015f; Thread.Sleep(10); } } public void Dispose() { if (initSuccess) SDL_QuitSubSystem(init_flags); objectHandle.Dispose(); } } }