diff --git a/GlitchyEngine/src/Application.bf b/GlitchyEngine/src/Application.bf index 46820cf..e880451 100644 --- a/GlitchyEngine/src/Application.bf +++ b/GlitchyEngine/src/Application.bf @@ -1,11 +1,12 @@ using GlitchyEngine.Platform.Windows; +using GlitchyEngine.Events; namespace GlitchyEngine { public class Application { private Window _window ~ delete _; - private bool _running; + private bool _running = true; public bool IsRunning => _running; @@ -13,16 +14,29 @@ namespace GlitchyEngine { // Todo: make Platform independent _window = new WindowsWindow(WindowDescription()); + _window.EventCallback = new => OnEvent; } public void Run() { - _running = true; - while(_running) { _window.Update(); } } + + public void OnEvent(Event e) + { + EventDispatcher dispatcher = scope .(e); + dispatcher.Dispatch(scope => OnWindowClose); + + Log.EngineLogger.Trace("{}", e); + } + + public bool OnWindowClose(WindowCloseEvent e) + { + _running = false; + return true; + } } } diff --git a/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf b/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf index 91027b3..e94f7c4 100644 --- a/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf +++ b/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf @@ -4,28 +4,11 @@ using DirectX.Windows; using DirectX.Windows.Winuser; using DirectX.Windows.Kernel32; using DirectX.Windows.WindowMessages; +using GlitchyEngine.Events; +using System.Diagnostics; 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; @@ -33,31 +16,78 @@ namespace GlitchyEngine.Platform.Windows private WindowClassExW _windowClass; - private WindowRectangle _rect; + private WindowRectangle _clientRect; + + private MinMaxInfo _minMaxInfo; + + private String _title ~ delete _; + + public override int32 MinWidth + { + get => _minMaxInfo.MinimumTrackingSize.x; + set => _minMaxInfo.MinimumTrackingSize.x = value; + } + public override int32 MinHeight + { + get => _minMaxInfo.MinimumTrackingSize.y; + set => _minMaxInfo.MinimumTrackingSize.y = value; + } + + public override int32 MaxWidth + { + get => _minMaxInfo.MaximumTrackingSize.x; + set => _minMaxInfo.MaximumTrackingSize.x = value; + } + + public override int32 MaxHeight + { + get => _minMaxInfo.MaximumTrackingSize.y; + set => _minMaxInfo.MaximumTrackingSize.y = value; + } public override int32 Width { - get => _rect.Width; + get => _clientRect.Width; set { - _rect.Width = value; + _clientRect.Width = value; ApplyRectangle(); } } public override int32 Height { - get => _rect.Height; + get => _clientRect.Height; set { - _rect.Height = value; + _clientRect.Height = value; ApplyRectangle(); } } - private String _title; + public override int32 PositionX + { + get => _clientRect.X; + + set + { + _clientRect.X = value; + ApplyRectangle(); + } + } + + public override int32 PositionY + { + get => _clientRect.Y; + + set + { + _clientRect.Y = value; + ApplyRectangle(); + } + } public override StringView Title { @@ -80,14 +110,12 @@ namespace GlitchyEngine.Platform.Windows public this(WindowDescription desc) { + _minMaxInfo.MaximumTrackingSize.x = int32.MaxValue; + _minMaxInfo.MaximumTrackingSize.y = int32.MaxValue; + Init(desc); } - public ~this() - { - - } - private void Init(WindowDescription desc) { Log.EngineLogger.Trace("Creating window \"{}\" ({}, {})...", desc.Title, desc.Width, desc.Height); @@ -97,7 +125,7 @@ namespace GlitchyEngine.Platform.Windows _windowClass = .(); _windowClass.Style = .HorizontalRedrawOnChange | .VerticalRedrawOnChange; _windowClass.WindowProcedure = => MessageHandler; - _windowClass.HInstance = _instanceHandle; + _windowClass.HInstance = (.)_instanceHandle; if(desc.Icon.Ptr != null) _windowClass.Icon = LoadImageW(0, desc.Icon.ToScopedNativeWChar!(), .Icon, 0, 0, .LoadFromFile); @@ -115,7 +143,7 @@ namespace GlitchyEngine.Platform.Windows } _windowHandle = CreateWindowExW(.None, _windowClass.ClassName, desc.Title.ToScopedNativeWChar!(), .WS_OVERLAPPEDWINDOW | .WS_VISIBLE, - CW_USEDEFAULT, CW_USEDEFAULT, desc.Width, desc.Height, 0, 0, _instanceHandle, null); + CW_USEDEFAULT, CW_USEDEFAULT, desc.Width, desc.Height, 0, 0, (.)_instanceHandle, null); void* myPtr = Internal.UnsafeCastToPtr(this); @@ -126,13 +154,189 @@ namespace GlitchyEngine.Platform.Windows Log.EngineLogger.Trace("Created window \"{}\" ({}, {})", Title, Width, Height); } + private bool _isResizingOrMoving; + private bool _isMinimized; + 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); + if(window == null) + { + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } + switch(uMsg) { + //// + //// Sizing and Moving + //// + + // Window min/max size requested + case WM_GETMINMAXINFO: + { + MinMaxInfo *info = (.)(void*)lParam; + info.MinimumTrackingSize = window._minMaxInfo.MinimumTrackingSize; + info.MaximumTrackingSize = window._minMaxInfo.MaximumTrackingSize; + } + // Window size changed + case WM_SIZE: + { + if(wParam == (.)ResizingType.Minimized) + { + window._isMinimized = true; + } + else if (window._isMinimized) + { + window._isMinimized = false; + } + else + { + SplitHighAndLowOrder!(lParam, out window._clientRect.Width, out window._clientRect.Height); + + WindowResizeEvent event = scope WindowResizeEvent(window._clientRect.Width, window._clientRect.Height, window._isResizingOrMoving); + window._eventCallback(event); + } + } + // Window position changed + case WM_MOVE: + { + if(wParam == (.)ResizingType.Minimized) + { + window._isMinimized = true; + } + else if (window._isMinimized) + { + window._isMinimized = false; + } + else + { + SplitHighAndLowOrder!(lParam, out window._clientRect.X, out window._clientRect.Y); + + var event = scope WindowMoveEvent(window._clientRect.X, window._clientRect.Y, window._isResizingOrMoving); + window._eventCallback(event); + } + } + // Window resizing/moving started + case WM_ENTERSIZEMOVE: + window._isResizingOrMoving = true; + // Window resizing/moving ended + case WM_EXITSIZEMOVE: + { + window._isResizingOrMoving = false; + + var resEvent = scope WindowResizeEvent(window._clientRect.Width, window._clientRect.Height, false); + window._eventCallback(resEvent); + var moveEvent = scope WindowMoveEvent(window._clientRect.X, window._clientRect.Y, false); + window._eventCallback(moveEvent); + } + + //// + //// Keyboard input + //// + + case WM_KEYDOWN: + { + var event = scope KeyPressedEvent((int32)wParam, (int32)lParam & 0xFFFF); + window._eventCallback(event); + } + case WM_KEYUP: + { + var event = scope KeyReleasedEvent((int32)wParam); + window._eventCallback(event); + } + + //// + //// Mouse input + //// + + // Left mouse button + case WM_LBUTTONDOWN: + { + var event = scope MouseButtonPressedEvent(.LeftButton); + window._eventCallback(event); + } + case WM_LBUTTONUP: + { + var event = scope MouseButtonReleasedEvent(.LeftButton); + window._eventCallback(event); + } + // Right mouse button + case WM_RBUTTONDOWN: + { + var event = scope MouseButtonPressedEvent(.RightButton); + window._eventCallback(event); + } + case WM_RBUTTONUP: + { + var event = scope MouseButtonReleasedEvent(.RightButton); + window._eventCallback(event); + } + // Middle mouse button + case WM_MBUTTONDOWN: + { + var event = scope MouseButtonPressedEvent(.MiddleButton); + window._eventCallback(event); + } + case WM_MBUTTONUP: + { + var event = scope MouseButtonReleasedEvent(.MiddleButton); + window._eventCallback(event); + } + // X button + case WM_XBUTTONDOWN: + { + MouseButton button = .None; + if(HighOrder!((int64)wParam) == 1) + button = .XButton1; + else + button = .XButton2; + + var event = scope MouseButtonPressedEvent(button); + window._eventCallback(event); + } + case WM_XBUTTONUP: + { + MouseButton button = .None; + if(HighOrder!((int64)wParam) == 1) + button = .XButton1; + else + button = .XButton2; + + var event = scope MouseButtonReleasedEvent(button); + window._eventCallback(event); + } + // Vertical scrolling + case WM_MOUSEWHEEL: + { + int32 rotation = (int16)HighOrder!((int64)wParam) / WHEEL_DELTA; + + var event = scope MouseScrolledEvent(0, rotation); + window._eventCallback(event); + } + // Horizontal scrolling + case WM_MOUSEHWHEEL: + { + int32 rotation = (int16)HighOrder!((int64)wParam) / WHEEL_DELTA; + + var event = scope MouseScrolledEvent(rotation, 0); + window._eventCallback(event); + } + case WM_MOUSEMOVE: + { + SplitHighAndLowOrder!(lParam, let x, let y); + + var event = scope MouseMovedEvent(x, y); + window._eventCallback(event); + } + + // Todo: DirectInput + + //// + //// Application + //// + // Window title changed case WM_SETTEXT: { @@ -140,12 +344,21 @@ namespace GlitchyEngine.Platform.Windows delete window._title; window._title = null; } + case WM_SYSCOMMAND: + { + /* Remove beeping sound when ALT + some key is pressed. */ + if ( wParam == SC_KEYMENU ) + { + return 0; + } + } // Window closing - case WM_CLOSE, WM_DESTROY: + case WM_CLOSE: { - // Todo: fire closing event! PostQuitMessage(0); - Environment.Exit(0); + + var event = scope WindowCloseEvent(); + window._eventCallback(event); } } @@ -166,15 +379,15 @@ namespace GlitchyEngine.Platform.Windows { GetWindowRect(_windowHandle, let rectangle); - _rect.X = rectangle.Left; - _rect.Y = rectangle.Top; - _rect.Width = rectangle.Right - rectangle.Left; - _rect.Height = rectangle.Bottom - rectangle.Top; + _clientRect.X = rectangle.Left; + _clientRect.Y = rectangle.Top; + _clientRect.Width = rectangle.Right - rectangle.Left; + _clientRect.Height = rectangle.Bottom - rectangle.Top; } private void ApplyRectangle() { - SetWindowPos(_windowHandle, 0, _rect.X, _rect.Y, _rect.Width, _rect.Height, 0); + SetWindowPos(_windowHandle, 0, _clientRect.X, _clientRect.Y, _clientRect.Width, _clientRect.Height, 0); } /** diff --git a/GlitchyEngine/src/Window.bf b/GlitchyEngine/src/Window.bf index 9f17e8f..b0e7dc8 100644 --- a/GlitchyEngine/src/Window.bf +++ b/GlitchyEngine/src/Window.bf @@ -1,4 +1,6 @@ using System; +using GlitchyEngine.Events; + namespace GlitchyEngine { public struct WindowDescription @@ -28,7 +30,27 @@ namespace GlitchyEngine // Todo: interface? public abstract class Window { - // Todo: callback/events? + public delegate void EventCallback(Event e); + + protected EventCallback _eventCallback ~ delete _; + + /** + * Gets or Sets the minimum width of the window. + */ + public abstract int32 MinWidth {get; set;} + /** + * Gets or Sets the minimum height of the window. + */ + public abstract int32 MinHeight {get; set;} + + /** + * Gets or Sets the maximum width of the window. + */ + public abstract int32 MaxWidth {get; set;} + /** + * Gets or Sets the maximum height of the window. + */ + public abstract int32 MaxHeight {get; set;} /** * Gets or Sets the width of the window. @@ -38,6 +60,15 @@ namespace GlitchyEngine * Gets or Sets the height of the window. */ public abstract int32 Height {get; set;} + /** + * Gets or Sets the x-coordinate of the upper-left corner of the client area of the window. + */ + public abstract int32 PositionX {get; set;} + /** + * Gets or Sets the y-coordinate of the upper-left corner of the client area of the window. + */ + public abstract int32 PositionY {get; set;} + /** * Gets or Sets the windows title. */ @@ -48,6 +79,12 @@ namespace GlitchyEngine */ public abstract bool IsVSync {get; set;} + public EventCallback EventCallback + { + get => _eventCallback; + set => _eventCallback = value; + } + public abstract void Update(); } } diff --git a/GlitchyEngine/src/WindowRectangle.bf b/GlitchyEngine/src/WindowRectangle.bf new file mode 100644 index 0000000..c19298b --- /dev/null +++ b/GlitchyEngine/src/WindowRectangle.bf @@ -0,0 +1,23 @@ +using System; + +namespace GlitchyEngine +{ + [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; + } + } +}