From 6de44d40b3b84895f70358bb480cfb4991d7bf1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Sun, 8 Nov 2020 14:17:48 +0100 Subject: [PATCH] Added basic window implementation and modified Application loop --- GlitchyEngine/src/Application.bf | 20 +- .../src/Platform/Windows/WindowsWindow.bf | 196 ++++++++++++++++++ GlitchyEngine/src/Program.bf | 4 +- GlitchyEngine/src/Window.bf | 53 +++++ 4 files changed, 271 insertions(+), 2 deletions(-) create mode 100644 GlitchyEngine/src/Platform/Windows/WindowsWindow.bf create mode 100644 GlitchyEngine/src/Window.bf diff --git a/GlitchyEngine/src/Application.bf b/GlitchyEngine/src/Application.bf index a3d8b34..46820cf 100644 --- a/GlitchyEngine/src/Application.bf +++ b/GlitchyEngine/src/Application.bf @@ -1,10 +1,28 @@ +using GlitchyEngine.Platform.Windows; + namespace GlitchyEngine { public class Application { + private Window _window ~ delete _; + private bool _running; + + public bool IsRunning => _running; + + public this() + { + // Todo: make Platform independent + _window = new WindowsWindow(WindowDescription()); + } + public void Run() { - while(true) {} + _running = true; + + while(_running) + { + _window.Update(); + } } } } diff --git a/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf b/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf new file mode 100644 index 0000000..91027b3 --- /dev/null +++ b/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf @@ -0,0 +1,196 @@ +using System; +using DirectX.Common; +using DirectX.Windows; +using DirectX.Windows.Winuser; +using DirectX.Windows.Kernel32; +using DirectX.Windows.WindowMessages; + +namespace GlitchyEngine.Platform.Windows +{ + [CRepr] + public struct WindowRectangle + { + public int32 X; + public int32 Y; + public int32 Width; + public int32 Height; + + public this() => this = default; + + public this(int32 x, int32 y, int32 width, int32 height) + { + X = x; + Y = y; + Width = width; + Height = height; + } + } + + public class WindowsWindow : Window + { + private Windows.HInstance _instanceHandle; + private Windows.HWnd _windowHandle; + + private WindowClassExW _windowClass; + + private WindowRectangle _rect; + + public override int32 Width + { + get => _rect.Width; + + set + { + _rect.Width = value; + ApplyRectangle(); + } + } + + public override int32 Height + { + get => _rect.Height; + + set + { + _rect.Height = value; + ApplyRectangle(); + } + } + + private String _title; + + public override StringView Title + { + get + { + if(_title == null) + LoadTitle(); + + return _title; + } + + set => SetWindowTextW(_windowHandle, value.ToScopedNativeWChar!()); + } + + public override bool IsVSync + { + get; + set; + } + + public this(WindowDescription desc) + { + Init(desc); + } + + public ~this() + { + + } + + private void Init(WindowDescription desc) + { + Log.EngineLogger.Trace("Creating window \"{}\" ({}, {})...", desc.Title, desc.Width, desc.Height); + + _instanceHandle = (.)GetModuleHandleW(null); + + _windowClass = .(); + _windowClass.Style = .HorizontalRedrawOnChange | .VerticalRedrawOnChange; + _windowClass.WindowProcedure = => MessageHandler; + _windowClass.HInstance = _instanceHandle; + + if(desc.Icon.Ptr != null) + _windowClass.Icon = LoadImageW(0, desc.Icon.ToScopedNativeWChar!(), .Icon, 0, 0, .LoadFromFile); + else + _windowClass.Icon = 0; + + _windowClass.Cursor = LoadCursorW(0, IDC_ARROW); + _windowClass.BackgroundBrush = (HBRUSH)SystemColor.WindowFrame; + _windowClass.ClassName = "GlitchyEngineWindow".ToScopedNativeWChar!(); + + if(RegisterClassExW(ref _windowClass) == 0) + { + Log.EngineLogger.Error("Failed to register window class.", (HResult)GetLastError()); + Runtime.FatalError("Failed to register window class"); + } + + _windowHandle = CreateWindowExW(.None, _windowClass.ClassName, desc.Title.ToScopedNativeWChar!(), .WS_OVERLAPPEDWINDOW | .WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, desc.Width, desc.Height, 0, 0, _instanceHandle, null); + + void* myPtr = Internal.UnsafeCastToPtr(this); + + SetWindowLongPtrW(_windowHandle, GWL_USERDATA, (int)myPtr); + + LoadWindowRectangle(); + + Log.EngineLogger.Trace("Created window \"{}\" ({}, {})", Title, Width, Height); + } + + private static LRESULT MessageHandler(HWND hwnd, uint32 uMsg, WPARAM wParam, LPARAM lParam) + { + void* windowPtr = (void*)GetWindowLongPtrW(hwnd, GWL_USERDATA); + WindowsWindow window = (WindowsWindow)Internal.UnsafeCastToObject(windowPtr); + + switch(uMsg) + { + // Window title changed + case WM_SETTEXT: + { + // Deletes and nulls _title so that it will be reloaded on the next call of Title.get + delete window._title; + window._title = null; + } + // Window closing + case WM_CLOSE, WM_DESTROY: + { + // Todo: fire closing event! + PostQuitMessage(0); + Environment.Exit(0); + } + } + + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } + + public override void Update() + { + Message message = .(); + while (PeekMessageW(&message, 0, 0, 0, .Remove)) + { + TranslateMessage(&message); + DispatchMessageW(&message); + } + } + + private void LoadWindowRectangle() + { + GetWindowRect(_windowHandle, let rectangle); + + _rect.X = rectangle.Left; + _rect.Y = rectangle.Top; + _rect.Width = rectangle.Right - rectangle.Left; + _rect.Height = rectangle.Bottom - rectangle.Top; + } + + private void ApplyRectangle() + { + SetWindowPos(_windowHandle, 0, _rect.X, _rect.Y, _rect.Width, _rect.Height, 0); + } + + /** + * Gets the window title via WinApi and stores it in _title. + */ + private void LoadTitle() + { + int32 length = GetWindowTextLengthW(_windowHandle) + 1; + + char16[] chars = scope char16[length]; + + Span titleSpan = .(chars, 0, length - 1); + + GetWindowTextW(_windowHandle, chars.CArray(), length); + + _title = new String(titleSpan); + } + } +} diff --git a/GlitchyEngine/src/Program.bf b/GlitchyEngine/src/Program.bf index 10ca6f2..600a1f8 100644 --- a/GlitchyEngine/src/Program.bf +++ b/GlitchyEngine/src/Program.bf @@ -1,6 +1,7 @@ using System; using GlitchLog; using System.Diagnostics; +using GlitchyEngine.Platform.Windows; namespace GlitchyEngine { @@ -13,13 +14,14 @@ namespace GlitchyEngine { Log.EngineLogger.Info("Initializing Application..."); - Stopwatch initWatch = .StartNew(); + Stopwatch initWatch = scope Stopwatch(); var app = CreateApplication(); initWatch.Stop(); Log.EngineLogger.Info("Application initialized ({}ms).", initWatch.ElapsedMilliseconds); + Log.EngineLogger.Info("Running App..."); app.Run(); diff --git a/GlitchyEngine/src/Window.bf b/GlitchyEngine/src/Window.bf new file mode 100644 index 0000000..9f17e8f --- /dev/null +++ b/GlitchyEngine/src/Window.bf @@ -0,0 +1,53 @@ +using System; +namespace GlitchyEngine +{ + public struct WindowDescription + { + public uint32 Width; + public uint32 Height; + public StringView Title; + public StringView Icon; + + public this() + { + Width = 1280; + Height = 720; + Title = "Glitchy Engine"; + Icon = .(); + } + + public this(uint32 width, uint32 height, StringView title, StringView icon = default) + { + Width = width; + Height = height; + Title = title; + Icon = icon; + } + } + + // Todo: interface? + public abstract class Window + { + // Todo: callback/events? + + /** + * Gets or Sets the width of the window. + */ + public abstract int32 Width {get; set;} + /** + * Gets or Sets the height of the window. + */ + public abstract int32 Height {get; set;} + /** + * Gets or Sets the windows title. + */ + public abstract StringView Title {get; set;} + + /** + * Gets or Sets whether or not the application uses VSync + */ + public abstract bool IsVSync {get; set;} + + public abstract void Update(); + } +}