Nerfed/Nerfed.Runtime/Window/Window.cs

243 lines
6.3 KiB
C#
Raw Normal View History

using Nerfed.Runtime.Graphics;
using SDL2;
namespace Nerfed.Runtime;
/// <summary>
/// Represents a window in the client operating system. <br/>
/// Every Game has a MainWindow automatically. <br/>
/// You can create additional Windows if you desire. They must be Claimed by the GraphicsDevice to be rendered to.
/// </summary>
public class Window
{
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 System.Action<uint, uint> SizeChangeCallback = null;
private static readonly Dictionary<uint, Window> windowsById = new Dictionary<uint, Window>();
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.BorderlessFullscreen)
{
flags |= SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP;
}
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;
unsafe
{
byte* pixels = ImageUtils.GetPixelDataFromFile(Path.Combine(System.AppContext.BaseDirectory, "Assets", "Textures", "NerfedIcon.png"), out uint w, out uint h, out uint size);
nint icon = SDL.SDL_CreateRGBSurfaceFrom(new IntPtr(pixels), (int)w, (int)h, 8 * 4, (int)w * 4, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
SDL.SDL_SetWindowIcon(Handle, icon);
ImageUtils.FreePixelData(pixels);
}
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_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;
if (SizeChangeCallback != null)
{
SizeChangeCallback(newWidth, newHeight);
}
}
private void ProcessCloseEvent(ref SDL.SDL_WindowEvent ev)
{
Engine.GraphicsDevice.UnclaimWindow(this);
Dispose();
}
/// <summary>
/// Changes the ScreenMode of this window.
/// </summary>
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.BorderlessFullscreen)
{
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;
}
/// <summary>
/// Resizes the window. <br/>
/// Note that you are responsible for recreating any graphics resources that need to change as a result of the size change.
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
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);
}
}
/// <summary>
/// Sets the window position.
/// </summary>
public void SetPosition(int x, int y)
{
SDL.SDL_SetWindowPosition(Handle, x, y);
}
/// <summary>
/// Sets the window title.
/// </summary>
public void SetTitle(string title)
{
SDL.SDL_SetWindowTitle(Handle, title);
Title = title;
}
internal void Show()
{
SDL.SDL_ShowWindow(Handle);
}
/// <summary>
/// You can specify a method to run when the window size changes.
/// </summary>
public void RegisterSizeChangeCallback(System.Action<uint, uint> sizeChangeCallback)
{
SizeChangeCallback = sizeChangeCallback;
}
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);
}
}