diff --git a/GlitchyEditor/assets/app.html b/GlitchyEditor/assets/app.html index c7a9ef9..aa7d08e 100644 --- a/GlitchyEditor/assets/app.html +++ b/GlitchyEditor/assets/app.html @@ -82,5 +82,6 @@
+ \ No newline at end of file diff --git a/GlitchyEditor/src/Ultralight/ULKeyCodeExtension.bf b/GlitchyEditor/src/Ultralight/ULKeyCodeExtension.bf new file mode 100644 index 0000000..20a7f8f --- /dev/null +++ b/GlitchyEditor/src/Ultralight/ULKeyCodeExtension.bf @@ -0,0 +1,362 @@ +using Ultralight; +using GlitchyEngine; +using System; + +namespace Ultralight; + +extension ULKeyCode +{ + public static explicit operator ULKeyCode(Key key) + { + return (ULKeyCode)(int32)key; + } + + public String GetKeyIdentifier() + { + switch (this) + { + case .GK_BACK: + return "U+000008"; + case .GK_TAB: + return "U+000009"; + case .GK_CLEAR: + return "Clear"; + case .GK_RETURN: + return "Enter"; + case .GK_SHIFT: + return "Shift"; + case .GK_CONTROL: + return "Control"; + case .GK_MENU: + return "Alt"; + case .GK_PAUSE: + return "Pause"; + case .GK_CAPITAL: + return "CapsLock"; + case .GK_KANA: + return "KanaMode"; + //case .GK_HANGUL: + // return "HangulMode"; + case .GK_IME_ON: + return "Convert"; + case .GK_JUNJA: + return "JunjaMode"; + case .GK_FINAL: + return "FinalMode"; + case .GK_HANJA: + return "HanjaMode"; + //case .GK_KANJI: + // return "KanjiMode"; + case .GK_IME_OFF: + return "Nonconvert"; + case .GK_ESCAPE: + return "U+00001B"; + case .GK_CONVERT: + return "Convert"; + case .GK_NONCONVERT: + return "Nonconvert"; + case .GK_ACCEPT: + return "Accept"; + case .GK_MODECHANGE: + return "ModeChange"; + case .GK_SPACE: + return "U+000020"; + case .GK_PRIOR: + return "PageUp"; + case .GK_NEXT: + return "PageDown"; + case .GK_END: + return "End"; + case .GK_HOME: + return "Home"; + case .GK_LEFT: + return "Left"; + case .GK_UP: + return "Up"; + case .GK_RIGHT: + return "Right"; + case .GK_DOWN: + return "Down"; + case .GK_SELECT: + return "Select"; + case .GK_PRINT: + return "PrintScreen"; + case .GK_EXECUTE: + return "Execute"; + case .GK_SNAPSHOT: + return "PrintScreen"; + case .GK_INSERT: + return "Insert"; + case .GK_DELETE: + return "U+00007F"; + case .GK_HELP: + return "Help"; + case .GK_0: + return "U+000030"; + case .GK_1: + return "U+000031"; + case .GK_2: + return "U+000032"; + case .GK_3: + return "U+000033"; + case .GK_4: + return "U+000034"; + case .GK_5: + return "U+000035"; + case .GK_6: + return "U+000036"; + case .GK_7: + return "U+000037"; + case .GK_8: + return "U+000038"; + case .GK_9: + return "U+000039"; + case .GK_A: + return "U+000041"; + case .GK_B: + return "U+000042"; + case .GK_C: + return "U+000043"; + case .GK_D: + return "U+000044"; + case .GK_E: + return "U+000045"; + case .GK_F: + return "U+000046"; + case .GK_G: + return "U+000047"; + case .GK_H: + return "U+000048"; + case .GK_I: + return "U+000049"; + case .GK_J: + return "U+00004A"; + case .GK_K: + return "U+00004B"; + case .GK_L: + return "U+00004C"; + case .GK_M: + return "U+00004D"; + case .GK_N: + return "U+00004E"; + case .GK_O: + return "U+00004F"; + case .GK_P: + return "U+000050"; + case .GK_Q: + return "U+000051"; + case .GK_R: + return "U+000052"; + case .GK_S: + return "U+000053"; + case .GK_T: + return "U+000054"; + case .GK_U: + return "U+000055"; + case .GK_V: + return "U+000056"; + case .GK_W: + return "U+000057"; + case .GK_X: + return "U+000058"; + case .GK_Y: + return "U+000059"; + case .GK_Z: + return "U+00005A"; + case .GK_LWIN: + return "Win"; + case .GK_RWIN: + return "Win"; + case .GK_APPS: + return "Apps"; + case .GK_SLEEP: + return "Sleep"; + case .GK_NUMPAD0: + return "U+000030"; + case .GK_NUMPAD1: + return "U+000031"; + case .GK_NUMPAD2: + return "U+000032"; + case .GK_NUMPAD3: + return "U+000033"; + case .GK_NUMPAD4: + return "U+000034"; + case .GK_NUMPAD5: + return "U+000035"; + case .GK_NUMPAD6: + return "U+000036"; + case .GK_NUMPAD7: + return "U+000037"; + case .GK_NUMPAD8: + return "U+000038"; + case .GK_NUMPAD9: + return "U+000039"; + case .GK_MULTIPLY: + return "U+00002A"; + case .GK_ADD: + return "U+00002B"; + case .GK_SEPARATOR: + return "Separator"; + case .GK_SUBTRACT: + return "U+00002D"; + case .GK_DECIMAL: + return "U+00002E"; + case .GK_DIVIDE: + return "U+00002F"; + case .GK_F1: + return "F1"; + case .GK_F2: + return "F2"; + case .GK_F3: + return "F3"; + case .GK_F4: + return "F4"; + case .GK_F5: + return "F5"; + case .GK_F6: + return "F6"; + case .GK_F7: + return "F7"; + case .GK_F8: + return "F8"; + case .GK_F9: + return "F9"; + case .GK_F10: + return "F10"; + case .GK_F11: + return "F11"; + case .GK_F12: + return "F12"; + case .GK_F13: + return "F13"; + case .GK_F14: + return "F14"; + case .GK_F15: + return "F15"; + case .GK_F16: + return "F16"; + case .GK_F17: + return "F17"; + case .GK_F18: + return "F18"; + case .GK_F19: + return "F19"; + case .GK_F20: + return "F20"; + case .GK_F21: + return "F21"; + case .GK_F22: + return "F22"; + case .GK_F23: + return "F23"; + case .GK_F24: + return "F24"; + case .GK_NUMLOCK: + return "NumLock"; + case .GK_SCROLL: + return "Scroll"; + case .GK_LSHIFT: + return "Shift"; + case .GK_RSHIFT: + return "Shift"; + case .GK_LCONTROL: + return "Control"; + case .GK_RCONTROL: + return "Control"; + case .GK_LMENU: + return "Alt"; + case .GK_RMENU: + return "AltGraph"; + case .GK_BROWSER_BACK: + return "BrowserBack"; + case .GK_BROWSER_FORWARD: + return "BrowserForward"; + case .GK_BROWSER_REFRESH: + return "BrowserRefresh"; + case .GK_BROWSER_STOP: + return "BrowserStop"; + case .GK_BROWSER_SEARCH: + return "BrowserSearch"; + case .GK_BROWSER_FAVORITES: + return "BrowserFavorites"; + case .GK_BROWSER_HOME: + return "BrowserHome"; + case .GK_VOLUME_MUTE: + return "VolumeMute"; + case .GK_VOLUME_DOWN: + return "VolumeDown"; + case .GK_VOLUME_UP: + return "VolumeUp"; + case .GK_MEDIA_NEXT_TRACK: + return "MediaNextTrack"; + case .GK_MEDIA_PREV_TRACK: + return "MediaPreviousTrack"; + case .GK_MEDIA_STOP: + return "MediaStop"; + case .GK_MEDIA_PLAY_PAUSE: + return "MediaPlayPause"; + case .GK_MEDIA_LAUNCH_MAIL: + return "LaunchMail"; + case .GK_MEDIA_LAUNCH_MEDIA_SELECT: + return "SelectMedia"; + case .GK_MEDIA_LAUNCH_APP1: + return "LaunchApplication1"; + case .GK_MEDIA_LAUNCH_APP2: + return "LaunchApplication2"; + case .GK_OEM_1: + return "U+00003B"; + case .GK_OEM_PLUS: + return "U+00002B"; + case .GK_OEM_COMMA: + return "U+00002C"; + case .GK_OEM_MINUS: + return "U+00002D"; + case .GK_OEM_PERIOD: + return "U+00002E"; + case .GK_OEM_2: + return "U+00002F"; + case .GK_OEM_3: + return "U+000040"; + case .GK_OEM_4: + return "U+00005B"; + case .GK_OEM_5: + return "U+00005C"; + case .GK_OEM_6: + return "U+00005D"; + case .GK_OEM_7: + return "U+000027"; + case .GK_OEM_8: + return "U+00005E"; + case .GK_OEM_102: + return "U+00007C"; + case .GK_PROCESSKEY: + return "Process"; + case .GK_PACKET: + return "Packet"; + case .GK_OEM_ATTN: + return "Attn"; + case .GK_ATTN: + return "Attn"; + case .GK_CRSEL: + return "Crsel"; + case .GK_EXSEL: + return "Exsel"; + case .GK_EREOF: + return "EraseEof"; + case .GK_PLAY: + return "Play"; + case .GK_ZOOM: + return "Zoom"; + case .GK_NONAME: + return "NoName"; + case .GK_PA1: + return "PA1"; + case .GK_OEM_CLEAR: + return "Clear"; + case .GK_UNKNOWN: + return "Unknown"; + default: + return "Unknown"; + } + } +} \ No newline at end of file diff --git a/GlitchyEditor/src/UltralightLayer.bf b/GlitchyEditor/src/UltralightLayer.bf index bfe2e0b..fec6a08 100644 --- a/GlitchyEditor/src/UltralightLayer.bf +++ b/GlitchyEditor/src/UltralightLayer.bf @@ -7,6 +7,8 @@ using GlitchyEngine.Math; using GlitchyEngine.Events; using GlitchyEngine.ImGui; using ImGui; +using System.Text; +using GlitchyEngine.System; namespace GlitchyEditor; @@ -36,43 +38,101 @@ class UltralightLayer : Layer dispatcher.Dispatch(scope => MouseMoved); dispatcher.Dispatch(scope (e) => MousePressed(e, true)); dispatcher.Dispatch(scope (e) => MousePressed(e, false)); + dispatcher.Dispatch(scope (e) => KeyEvent(e, true, e.RepeatCount)); + dispatcher.Dispatch(scope (e) => KeyEvent(e, false, 0)); + dispatcher.Dispatch(scope (e) => CharTypedEvent(e)); + dispatcher.Dispatch(scope (e) => { + delete windoww; + windoww = null; + return true; + }); + } + + private bool CharTypedEvent(KeyTypedEvent e) + { + String s = scope String(); + s.Append(e.Char); + + ULString str = ulCreateString(s.CStr()); + + ULKeyEvent event = ulCreateKeyEvent(.kKeyEventType_Char, + 0, 0, 0, str, str, false, false, false); + + ulDestroyString(str); + + ulViewFireKeyEvent(view, event); + ulDestroyKeyEvent(event); + + return true; + } + + private bool KeyEvent(KeyEvent e, bool pressed, int repeatCount) + { + bool isRepeat = repeatCount > 1; + + ULKeyCode keyCode = (ULKeyCode)e.KeyCode; + + ULString str = ulCreateString(keyCode.GetKeyIdentifier()); + + ULKeyboardModifier modifier = 0; + + if (Input.IsKeyPressed(.Control)) + modifier |= .kMod_CtrlKey; + + if (Input.IsKeyPressed(.Alt)) + modifier |= .kMod_AltKey; + + if (Input.IsKeyPressed(.Shift)) + modifier |= .kMod_ShiftKey; + + if (Input.IsKeyPressed(.Home)) + modifier |= .kMod_MetaKey; + + ULKeyEvent event = ulCreateKeyEvent(pressed ? .kKeyEventType_RawKeyDown : .kKeyEventType_KeyUp, + (uint32)modifier, (int32)keyCode, 0, str, str, false, isRepeat, false); + + ulDestroyString(str); + + for (int i < repeatCount) + { + ulViewFireKeyEvent(view, event); + } + + ulDestroyKeyEvent(event); + + return true; } private bool MouseMoved(MouseMovedEvent e) { - if (e.Window == windoww) - { - Log.EngineLogger.Warning($"{e.PositionX}"); - ULMouseEvent evt = ulCreateMouseEvent(.kMouseEventType_MouseMoved, e.PositionX, e.PositionY, .kMouseButton_None); - ulViewFireMouseEvent(view, evt); - ulDestroyMouseEvent(evt); + Log.EngineLogger.Warning($"{e.PositionX}"); - cursorPosition = .(e.PositionX, e.PositionY); - } + ULMouseEvent evt = ulCreateMouseEvent(.kMouseEventType_MouseMoved, e.PositionX, e.PositionY, .kMouseButton_None); + ulViewFireMouseEvent(view, evt); + ulDestroyMouseEvent(evt); - return false; + cursorPosition = .(e.PositionX, e.PositionY); + + return true; } private bool MousePressed(MouseButtonEvent e, bool press) { - if (e.Window == windoww) + ULMouseButton button = .kMouseButton_None; + + switch (e.MouseButton) { - ULMouseButton button = .kMouseButton_None; - - switch (e.MouseButton) - { - case .LeftButton: button = .kMouseButton_Left; - case .RightButton: button = .kMouseButton_Right; - case .MiddleButton: button = .kMouseButton_Middle; - default: button = .kMouseButton_None; - } - - ULMouseEvent evt = ulCreateMouseEvent(press ? .kMouseEventType_MouseDown : .kMouseEventType_MouseUp, cursorPosition.X, cursorPosition.Y, button); - ulViewFireMouseEvent(view, evt); - ulDestroyMouseEvent(evt); + case .LeftButton: button = .kMouseButton_Left; + case .RightButton: button = .kMouseButton_Right; + case .MiddleButton: button = .kMouseButton_Middle; + default: button = .kMouseButton_None; } - return false; + ULMouseEvent evt = ulCreateMouseEvent(press ? .kMouseEventType_MouseDown : .kMouseEventType_MouseUp, cursorPosition.X, cursorPosition.Y, button); + ulViewFireMouseEvent(view, evt); + ulDestroyMouseEvent(evt); + + return true; } private static void OnAppUpdate(void* user_data) @@ -170,6 +230,24 @@ class UltralightLayer : Layer ULRenderer renderer; + private static void InitClipboard() + { + ULClipboard clipboard = .(); + clipboard.clear = () => Clipboard.Clear(); + + clipboard.read_plain_text = (s) => { + String text = scope .(); + Clipboard.Read(text); + ulStringAssignCString(s, text); + }; + + clipboard.write_plain_text = (s) => { + Clipboard.Set(StringView(ulStringGetData(s))); + }; + + ulPlatformSetClipboard(clipboard); + } + private void NoApp() { ULConfig config = ulCreateConfig(); @@ -180,9 +258,7 @@ class UltralightLayer : Layer ulEnablePlatformFileSystem(fileSystemPath); ulDestroyString(fileSystemPath); - /*ULString styleSheet = ulCreateString("body { background: purple; }"); - ulConfigSetUserStylesheet(config, styleSheet); - ulDestroyString(styleSheet);*/ + InitClipboard(); renderer = ulCreateRenderer(config); @@ -225,6 +301,9 @@ class UltralightLayer : Layer private void Render() { + if (windoww == null) + return; + RenderCommand.Clear(windoww.SwapChain.BackBuffer, .Color | .Depth, .(0.7f, 0.2f, 0.2f), 1.0f, 0); } @@ -238,7 +317,11 @@ class UltralightLayer : Layer ulViewSetDOMReadyCallback(view, => OnDOMReady, null); ulDestroyViewConfig(viewConfig); - + + ulViewSetFailLoadingCallback(view, (user_data, caller, frame_id, is_main_frame, url, description, error_domain, error_code) => { + Log.ClientLogger.Error("Errorrr"); + }, null); + ULString url = ulCreateString("file:///app.html"); ulViewLoadURL(view, url); ulDestroyString(url); diff --git a/GlitchyEngine/src/Events/MouseEvent.bf b/GlitchyEngine/src/Events/MouseEvent.bf index bbd68f6..c785594 100644 --- a/GlitchyEngine/src/Events/MouseEvent.bf +++ b/GlitchyEngine/src/Events/MouseEvent.bf @@ -4,7 +4,6 @@ namespace GlitchyEngine.Events { public class MouseMovedEvent : Event, IEvent { - private Window _window; private int32 _mouseX, _mouseY; public override EventType EventType => .MouseMoved; @@ -15,14 +14,11 @@ namespace GlitchyEngine.Events public static EventType StaticType => .MouseMoved; - public Window Window => _window; - public int32 PositionX => _mouseX; public int32 PositionY => _mouseY; - public this(Window window, int32 x, int32 y) + public this(int32 x, int32 y) { - _window = window; _mouseX = x; _mouseY = y; } @@ -99,18 +95,14 @@ namespace GlitchyEngine.Events public abstract class MouseButtonEvent : Event { - protected Window _window; protected MouseButton _mouseButton; public override EventCategory Category => .Input | .Mouse; - public Window Window => _window; - public MouseButton MouseButton => _mouseButton; - protected this(Window window, MouseButton mouseButton) + protected this(MouseButton mouseButton) { - _window = window; _mouseButton = mouseButton; } } @@ -123,7 +115,7 @@ namespace GlitchyEngine.Events public static EventType StaticType => .MouseButtonPressed; - public this(Window window, MouseButton mouseButton) : base(window, mouseButton) { } + public this(MouseButton mouseButton) : base(mouseButton) { } public override void ToString(String strBuffer) { @@ -140,7 +132,7 @@ namespace GlitchyEngine.Events public static EventType StaticType => .MouseButtonReleased; - public this(Window window, MouseButton mouseButton) : base(window, mouseButton) { } + public this(MouseButton mouseButton) : base(mouseButton) { } public override void ToString(String strBuffer) { diff --git a/GlitchyEngine/src/Platform/Windows/System/WindowsClipboard.bf b/GlitchyEngine/src/Platform/Windows/System/WindowsClipboard.bf new file mode 100644 index 0000000..ce6a92b --- /dev/null +++ b/GlitchyEngine/src/Platform/Windows/System/WindowsClipboard.bf @@ -0,0 +1,174 @@ +#if BF_PLATFORM_WINDOWS +using System; +using DirectX.Common; +using System.Text; + +using static System.Windows; + +namespace GlitchyEngine.System; + +extension Clipboard +{ + [CLink, CallingConvention(.Stdcall)] + private static extern IntBool OpenClipboard(HWnd hWndNewOwner); + [CLink, CallingConvention(.Stdcall)] + private static extern IntBool CloseClipboard(); + [CLink, CallingConvention(.Stdcall)] + private static extern IntBool EmptyClipboard(); + + [AllowDuplicates] + private enum ClipboardFormat : uint32 + { + Bitmap = 2, + Dib = 8, + DibV5 = 17, + Dif = 5, + DspBitmap = 0, + Dspenhmetafile = 0, + DspMetaFilepict = 0, + DspText = 0, + Enhmetafile = 14, + Gdiobjfirst = 0, + Gdiobjlast = 0, + Hdrop = 15, + Locale = 16, + Metafilepict = 3, + Oemtext = 7, + Ownerdisplay = 0, + Palette = 9, + Pendata = 10, + Privatefirst = 0, + Privatelast = 0, + Riff = 11, + Sylk = 4, + Text = 1, + Tiff = 6, + UnicodeText = 13, + Wave = 12 + } + + [CLink, CallingConvention(.Stdcall)] + private static extern Handle GetClipboardData(ClipboardFormat format); + [CLink, CallingConvention(.Stdcall)] + private static extern Handle SetClipboardData(ClipboardFormat format, Handle memory); + + [CLink, CallingConvention(.Stdcall)] + private static extern void* GlobalLock(Handle memory); + [CLink, CallingConvention(.Stdcall)] + private static extern IntBool GlobalUnlock(Handle memory); + + [AllowDuplicates] + private enum GlobalMemoryFlags : uint32 + { + Fixed = 0x0000, + Moveable = 0x0002, + ZeroInit = 0x0040, + /// Combines Fixed and ZeroInit + GPTR = 0x0040, + /// Combines Moveable and ZeroInit + GHND = 0x0042 + } + + [CLink, CallingConvention(.Stdcall)] + private static extern Handle GlobalAlloc(GlobalMemoryFlags uFlags, int dwBytes); + [CLink, CallingConvention(.Stdcall)] + private static extern Handle GlobalFree(Handle memory); + + public static mixin TryLogging(IntBool result) + { + if (!result) + { + HResult hresult = HResult.FromWin32((uint32)GetLastError()); + Log.EngineLogger.Error($"{hresult.Underlying}: {hresult}"); + return; + } + } + + public static mixin TryLoggingSilent(IntBool result) + { + if (!result) + { + HResult hresult = HResult.FromWin32((uint32)GetLastError()); + Log.EngineLogger.Error($"{hresult.Underlying}: {hresult}"); + } + } + + public override static void Clear() + { + TryLogging!(OpenClipboard(0)); + TryLogging!(EmptyClipboard()); + TryLogging!(CloseClipboard()); + } + + public override static void Read(String outBuffer) + { + TryLogging!(OpenClipboard(0)); + + do + { + Handle handle = GetClipboardData(.UnicodeText); + + if (handle == 0) + break; + + char16* clipboardData = (char16*)GlobalLock(handle); + + if (clipboardData != null) + { + outBuffer.Append(clipboardData); + + TryLoggingSilent!(GlobalUnlock(handle)); + } + else + { + HResult hresult = HResult.FromWin32((uint32)GetLastError()); + Log.EngineLogger.Error($"{hresult.Underlying}: {hresult}"); + } + } + + TryLogging!(CloseClipboard()); + } + + public override static void Set(StringView text) + { + TryLogging!(OpenClipboard(0)); + + do + { + char16* nativeTextPtr = text.ToScopedNativeWChar!(); + Span nativeText = .(nativeTextPtr, UTF16.CStrLen(nativeTextPtr) + 1); + + Handle handle = GlobalAlloc(.Moveable, sizeof(char16) * nativeText.Length); + + if (handle == 0) + break; + + char16* clipboardDataPtr = (char16*)GlobalLock(handle); + Span clipboardData = .(clipboardDataPtr, nativeText.Length); + + if (clipboardDataPtr == null) + { + HResult hresult = HResult.FromWin32((uint32)GetLastError()); + Log.EngineLogger.Error($"{hresult.Underlying}: {hresult}"); + break; + } + + nativeText.CopyTo(clipboardData); + TryLoggingSilent!(GlobalUnlock(handle)); + + TryLoggingSilent!(EmptyClipboard()); + + if (SetClipboardData(.UnicodeText, handle) == 0) + { + HResult hresult = HResult.FromWin32((uint32)GetLastError()); + Log.EngineLogger.Error($"{hresult.Underlying}: {hresult}"); + + GlobalFree(handle); + } + } + + TryLogging!(CloseClipboard()); + } +} + +#endif diff --git a/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf b/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf index 01e4438..a8e4c41 100644 --- a/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf +++ b/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf @@ -21,12 +21,13 @@ namespace GlitchyEngine public extension Window { const String WindowClassName = "GlitchyEngineWindow"; + static readonly char16[?] WindowClassNameW = WindowClassName.ToConstNativeW(); - private Windows.HInstance _instanceHandle; + private static WindowClassExW WindowClass; + + private static Windows.HInstance _instanceHandle; internal Windows.HWnd _windowHandle; - private WindowClassExW _windowClass; - private WindowRectangle _clientRect; private MinMaxInfo _minMaxInfo; @@ -183,52 +184,65 @@ namespace GlitchyEngine Log.EngineLogger.Error($"Failed to destroy window: Message ({(int)res}): {res}"); } - // TODO manage window classes somehow - UnregisterClass(WindowClassName.ToScopedNativeWChar!(), _instanceHandle); - Application.Instance.Windows.Remove(this); } + static ~this() + { +#unwarn + UnregisterClass(WindowClass.ClassName, _instanceHandle); + } + [LinkName(.C)] static extern HWnd GetActiveWindow(); + private void CreateWindowClass(StringView iconPath) + { + _instanceHandle = (.)GetModuleHandleW(null); + + WindowClass = .(); + WindowClass.Style = .HorizontalRedrawOnChange | .VerticalRedrawOnChange; + WindowClass.WindowProcedure = => MessageHandler; + WindowClass.HInstance = (.)_instanceHandle; + + if (iconPath.Ptr != null) + { + Debug.Profiler.ProfileScope!("LoadIcon"); + + WindowClass.Icon = LoadImageW(0, iconPath.ToScopedNativeWChar!(), .Icon, 0, 0, .LoadFromFile); + } + else + WindowClass.Icon = 0; + + WindowClass.Cursor = LoadCursorW(0, IDC_ARROW); + WindowClass.BackgroundBrush = (HBRUSH)SystemColor.WindowFrame; + +#unwarn + WindowClass.ClassName = &WindowClassNameW; + + if (RegisterClassExW(ref WindowClass) == 0) + { + uint32 lastError = GetLastError(); + Log.EngineLogger.Error($"Failed to register window class. Message({(int)lastError}): {(HResult)lastError}"); + Runtime.FatalError("Failed to register window class"); + } + } + private void Init(WindowDescription desc) { Debug.Profiler.ProfileFunction!(); 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) + if (WindowClass == default) { - Debug.Profiler.ProfileScope!("LoadIcon"); - - _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 = WindowClassName.ToScopedNativeWChar!(); - - if (RegisterClassExW(ref _windowClass) == 0) - { - uint32 lastError = GetLastError(); - Log.EngineLogger.Error($"Failed to register window class. Message({(int)lastError}): {(HResult)lastError}"); - //Runtime.FatalError("Failed to register window class"); + CreateWindowClass(desc.Icon); } { Debug.Profiler.ProfileScope!("CreateWindow"); - _windowHandle = CreateWindowExW(.None, _windowClass.ClassName, desc.Title.ToScopedNativeWChar!(), .WS_OVERLAPPEDWINDOW | .WS_VISIBLE, + _windowHandle = CreateWindowExW(.None, WindowClass.ClassName, desc.Title.ToScopedNativeWChar!(), .WS_OVERLAPPEDWINDOW | .WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, desc.Width, desc.Height, 0, 0, (.)_instanceHandle, null); } @@ -244,6 +258,8 @@ namespace GlitchyEngine // Determine whether or not the window is currently active _isActive = GetActiveWindow() == _windowHandle; + + SetIcon(desc.Icon); } private bool _isResizingOrMoving; @@ -252,7 +268,7 @@ namespace GlitchyEngine [CLink] static extern IntBool IsWindowUnicode(HWND whnd); - public Result SetIcon(StringView filePath) + public override Result SetIcon(StringView filePath) { HICON hIcon = LoadImageW(0, filePath.ToScopedNativeWChar!(), .Icon, 0, 0, .LoadFromFile); if (hIcon == 0) @@ -386,34 +402,34 @@ namespace GlitchyEngine // Left mouse button case WM_LBUTTONDOWN: { - var event = scope MouseButtonPressedEvent(window, .LeftButton); + var event = scope MouseButtonPressedEvent(.LeftButton); window._eventCallback(event); } case WM_LBUTTONUP: { - var event = scope MouseButtonReleasedEvent(window, .LeftButton); + var event = scope MouseButtonReleasedEvent(.LeftButton); window._eventCallback(event); } // Right mouse button case WM_RBUTTONDOWN: { - var event = scope MouseButtonPressedEvent(window, .RightButton); + var event = scope MouseButtonPressedEvent(.RightButton); window._eventCallback(event); } case WM_RBUTTONUP: { - var event = scope MouseButtonReleasedEvent(window, .RightButton); + var event = scope MouseButtonReleasedEvent(.RightButton); window._eventCallback(event); } // Middle mouse button case WM_MBUTTONDOWN: { - var event = scope MouseButtonPressedEvent(window, .MiddleButton); + var event = scope MouseButtonPressedEvent(.MiddleButton); window._eventCallback(event); } case WM_MBUTTONUP: { - var event = scope MouseButtonReleasedEvent(window, .MiddleButton); + var event = scope MouseButtonReleasedEvent(.MiddleButton); window._eventCallback(event); } // X button @@ -425,7 +441,7 @@ namespace GlitchyEngine else button = .XButton2; - var event = scope MouseButtonPressedEvent(window, button); + var event = scope MouseButtonPressedEvent(button); window._eventCallback(event); } case WM_XBUTTONUP: @@ -436,7 +452,7 @@ namespace GlitchyEngine else button = .XButton2; - var event = scope MouseButtonReleasedEvent(window, button); + var event = scope MouseButtonReleasedEvent(button); window._eventCallback(event); } // Vertical scrolling @@ -459,10 +475,10 @@ namespace GlitchyEngine { SplitHighAndLowOrder!(lParam, let x, let y); - var event = scope MouseMovedEvent(window, x, y); + var event = scope MouseMovedEvent(x, y); window._eventCallback(event); } - case WM_INPUT: + /*case WM_INPUT: { uint32 dataSize = ?; GetRawInputData((.)lParam, RID_INPUT, null, &dataSize, sizeof(RAWINPUTHEADER)); @@ -483,7 +499,7 @@ namespace GlitchyEngine } } } - } + }*/ // Todo: DirectInput diff --git a/GlitchyEngine/src/System/Clipboard.bf b/GlitchyEngine/src/System/Clipboard.bf new file mode 100644 index 0000000..3110fd9 --- /dev/null +++ b/GlitchyEngine/src/System/Clipboard.bf @@ -0,0 +1,15 @@ +using System; + +namespace GlitchyEngine.System; + +class Clipboard +{ + /// Clears the clipboard. + public static extern void Clear(); + + /// Reads a unicode text from the clipboard. + public static extern void Read(String outBuffer); + + /// Sets the content of the clipboard to the given unicode text. + public static extern void Set(StringView text); +}