using Nerfed.Runtime.Graphics; using SDL2; namespace Nerfed.Runtime; /// /// Represents a window in the client operating system.
/// Every Game has a MainWindow automatically.
/// You can create additional Windows if you desire. They must be Claimed by the GraphicsDevice to be rendered to. ///
public class Window { public event Action OnResizedEvent; public event Action OnMovedEvent; public event Action OnCloseEvent; internal IntPtr Handle { get; } public ScreenMode ScreenMode { get; private set; } public uint Width { get; private set; } public uint Height { get; private set; } internal Texture SwapchainTexture { get; set; } public bool Claimed { get; internal set; } public SwapchainComposition SwapchainComposition { get; internal set; } public TextureFormat SwapchainFormat { get; internal set; } public (int, int) Position { get { SDL.SDL_GetWindowPosition(Handle, out int x, out int y); return (x, y); } } public string Title { get; private set;} private bool IsDisposed; private static readonly Dictionary windowsById = new Dictionary(); public Window(GraphicsDevice graphicsDevice, WindowCreateInfo windowCreateInfo) { SDL.SDL_WindowFlags flags = SDL.SDL_WindowFlags.SDL_WINDOW_SHOWN; if (graphicsDevice.Backend == BackendFlags.Vulkan) { flags |= SDL.SDL_WindowFlags.SDL_WINDOW_VULKAN; } else if (graphicsDevice.Backend == BackendFlags.Metal) { flags |= SDL.SDL_WindowFlags.SDL_WINDOW_METAL; } if (windowCreateInfo.ScreenMode == ScreenMode.Fullscreen) { flags |= SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN; } else if (windowCreateInfo.ScreenMode == ScreenMode.FullscreenBorderless) { flags |= SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP; } else if(windowCreateInfo.ScreenMode == ScreenMode.WindowedBorderless) { flags |= SDL.SDL_WindowFlags.SDL_WINDOW_BORDERLESS; } if (windowCreateInfo.SystemResizable) { flags |= SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE; } if (windowCreateInfo.StartMaximized) { flags |= SDL.SDL_WindowFlags.SDL_WINDOW_MAXIMIZED; } ScreenMode = windowCreateInfo.ScreenMode; SDL.SDL_GetDesktopDisplayMode(0, out SDL.SDL_DisplayMode displayMode); Handle = SDL.SDL_CreateWindow( windowCreateInfo.WindowTitle, SDL.SDL_WINDOWPOS_CENTERED, SDL.SDL_WINDOWPOS_CENTERED, windowCreateInfo.ScreenMode == ScreenMode.Windowed ? (int) windowCreateInfo.WindowWidth : displayMode.w, windowCreateInfo.ScreenMode == ScreenMode.Windowed ? (int) windowCreateInfo.WindowHeight : displayMode.h, flags ); /* Requested size might be different in fullscreen, so let's just get the area */ SDL.SDL_GetWindowSize(Handle, out int width, out int height); Width = (uint) width; Height = (uint) height; windowsById.Add(SDL.SDL_GetWindowID(Handle), this); } internal static void ProcessEvent(ref SDL.SDL_Event ev) { uint windowId = ev.window.windowID; if (!windowsById.TryGetValue(windowId, out Window window)) { Log.Error($"Received window event for unknown window id {windowsById}"); return; } switch (ev.window.windowEvent) { case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED: window.ProcessSizeChangedEvent(ref ev.window); break; case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_MOVED: window.ProcessMovedChangedEvent(ref ev.window); break; case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE: window.ProcessCloseEvent(ref ev.window); break; } } private void ProcessSizeChangedEvent(ref SDL.SDL_WindowEvent ev) { uint newWidth = (uint)ev.data1; uint newHeight = (uint)ev.data2; Width = newWidth; Height = newHeight; OnResizedEvent?.Invoke(this, Width, Height); } private void ProcessMovedChangedEvent(ref SDL.SDL_WindowEvent ev) { OnMovedEvent?.Invoke(this, ev.data1, ev.data2); } private void ProcessCloseEvent(ref SDL.SDL_WindowEvent ev) { OnCloseEvent?.Invoke(this); Engine.GraphicsDevice.UnclaimWindow(this); Dispose(); } /// /// Changes the ScreenMode of this window. /// public void SetScreenMode(ScreenMode screenMode) { SDL.SDL_WindowFlags windowFlag = 0; if (screenMode == ScreenMode.Fullscreen) { windowFlag = SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN; } else if (screenMode == ScreenMode.FullscreenBorderless) { windowFlag = SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP; } SDL.SDL_SetWindowFullscreen(Handle, (uint) windowFlag); if (screenMode == ScreenMode.Windowed) { SDL.SDL_SetWindowPosition(Handle, SDL.SDL_WINDOWPOS_CENTERED, SDL.SDL_WINDOWPOS_CENTERED); } ScreenMode = screenMode; } /// /// Resizes the window.
/// Note that you are responsible for recreating any graphics resources that need to change as a result of the size change. ///
/// /// public void SetSize(uint width, uint height) { SDL.SDL_SetWindowSize(Handle, (int) width, (int) height); Width = width; Height = height; if (ScreenMode == ScreenMode.Windowed) { SDL.SDL_SetWindowPosition(Handle, SDL.SDL_WINDOWPOS_CENTERED, SDL.SDL_WINDOWPOS_CENTERED); } } /// /// Sets the window position. /// public void SetPosition(int x, int y) { SDL.SDL_SetWindowPosition(Handle, x, y); } /// /// Sets the window title. /// public void SetTitle(string title) { SDL.SDL_SetWindowTitle(Handle, title); Title = title; } internal void Show() { SDL.SDL_ShowWindow(Handle); } protected virtual void Dispose(bool disposing) { if (!IsDisposed) { if (disposing) { // dispose managed state (managed objects) } windowsById.Remove(SDL.SDL_GetWindowID(Handle)); SDL.SDL_DestroyWindow(Handle); IsDisposed = true; } } ~Window() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: false); } public void Dispose() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: true); GC.SuppressFinalize(this); } }