using System; using Engine.Core; using SDL3; namespace Engine.Integration.SDL3; public class Sdl3Window : Behaviour, IWindow, IDisposable, IEnterUniverse, IExitUniverse { public Event OnStateChanged { get; } = new(); public Event OnFocusStateChanged { get; } = new(); public Event OnModeChanged { get; } = new(); public Event OnShowStateChanged { get; } = new(); public Event OnResizeModeChanged { get; } = new(); public Event OnTitleChanged { get; } = new(); public Event OnTargetFpsChanged { get; } = new(); public Event OnSizeChanged { get; } = new(); public Event OnBackgroundColorChanged { get; } = new(); public IntPtr Window { get; private set; } = IntPtr.Zero; public WindowState State { get => field; set { if (value == field) return; switch (value) { case WindowState.Open: OpenWindow(); break; case WindowState.Closed: default: CloseWindow(); break; } WindowState previousState = field; field = value; OnStateChanged.Invoke(this, new(previousState)); } } = WindowState.Open; public WindowFocusState FocusState { get => field; internal set { if (value == field) return; WindowFocusState previousFocusState = field; field = value; OnFocusStateChanged.Invoke(this, new(previousFocusState)); } } = WindowFocusState.Unfocused; public WindowMode Mode { get => field; set { if (value == field) return; if (value is WindowMode.Windowed or WindowMode.BorderlessWindowed) { SDL.SetWindowFullscreen(Window, false); SDL.SyncWindow(Window); } if (Window != IntPtr.Zero) switch (value) { case WindowMode.Windowed: SDL.SetWindowBordered(Window, true); break; case WindowMode.BorderlessWindowed: SDL.SetWindowBordered(Window, false); break; case WindowMode.BorderlessFullscreen: SDL.SetWindowFullscreen(Window, true); break; case WindowMode.Fullscreen: SDL.SetWindowFullscreen(Window, true); break; } SDL.SyncWindow(Window); WindowMode previousMode = field; field = value; OnModeChanged.Invoke(this, new(previousMode)); } } = WindowMode.Windowed; public WindowShowState ShowState { get => field; set { if (value == field) return; if (Window != IntPtr.Zero) switch (value) { case WindowShowState.Normal: SDL.RestoreWindow(Window); break; case WindowShowState.Minimized: SDL.MinimizeWindow(Window); break; case WindowShowState.Maximized: SDL.MaximizeWindow(Window); break; } WindowShowState previousShowState = field; field = value; OnShowStateChanged.Invoke(this, new(previousShowState)); } } = WindowShowState.Normal; public WindowResizeMode ResizeMode { get => field; set { if (value == field) return; if (Window != IntPtr.Zero) switch (value) { case WindowResizeMode.Fixed: SDL.SetWindowResizable(Window, false); break; case WindowResizeMode.Resizable: case WindowResizeMode.AspectLocked: SDL.SetWindowResizable(Window, true); break; } WindowResizeMode previousResizeMode = field; field = value; OnResizeModeChanged.Invoke(this, new(previousResizeMode)); } } = WindowResizeMode.Fixed; public ColorRGB BackgroundColor { get => field; set { if (value == field) return; ColorRGB previousBackgroundColor = field; field = value; OnBackgroundColorChanged.Invoke(this, new(previousBackgroundColor)); } } = new ColorRGB(35, 20, 35); public string Title { get => field; set { if (value == field) return; string previousTitle = field; field = value; OnTitleChanged.Invoke(this, new(previousTitle)); } } = nameof(Sdl3Window); public Vector2DInt Size { get => field; set { if (value == field) return; if (value.Max(Vector2DInt.One) == Vector2DInt.One) throw new Exception($"Window size can not be smaller than 1x1"); Vector2DInt previousSize = field; field = value; OnSizeChanged.Invoke(this, new(previousSize)); } } = Vector2DInt.Zero; public void ExitUniverse(IUniverse universe) => CloseWindow(); public void EnterUniverse(IUniverse universe) { if (State == WindowState.Open) OpenWindow(); } private void OpenWindow() { if (!UniverseObject.IsInUniverse) throw new Exception($"{UniverseObject.Name} is not in a universe. The window can not be opened."); SDL.WindowFlags windowFlags = 0; switch (Mode) { case WindowMode.Windowed: break; case WindowMode.Fullscreen: windowFlags |= SDL.WindowFlags.Fullscreen; break; case WindowMode.BorderlessWindowed: windowFlags |= SDL.WindowFlags.Borderless; break; case WindowMode.BorderlessFullscreen: windowFlags |= SDL.WindowFlags.Fullscreen | SDL.WindowFlags.Borderless; break; } if (!SDL.Init(SDL.InitFlags.Video)) throw new Exception($"SDL_Init failed: {SDL.GetError()}"); Window = SDL.CreateWindow(Title, Size.X, Size.Y, windowFlags | SDL.WindowFlags.OpenGL); if (Window == IntPtr.Zero) throw new Exception($"SDL_CreateWindowAndRenderer failed: {SDL.GetError()}"); } private void CloseWindow() { if (Window != IntPtr.Zero) SDL.DestroyWindow(Window); Window = IntPtr.Zero; } public void Dispose() { CloseWindow(); GC.SuppressFinalize(this); } }