Better WindowClass handling, Added Clipboard

This commit is contained in:
Simon Lübeß
2024-09-15 18:45:30 +02:00
parent 2e3c0d0aa4
commit 4d7863dcb3
7 changed files with 726 additions and 83 deletions
+1
View File
@@ -82,5 +82,6 @@
<body> <body>
<div id="msg"></div> <div id="msg"></div>
<button id="btn" onclick="HandleClick();">Click Me!</button> <button id="btn" onclick="HandleClick();">Click Me!</button>
<input type="text" id="testInput" value="Hello World!"></input>
</body> </body>
</html> </html>
@@ -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";
}
}
}
+111 -28
View File
@@ -7,6 +7,8 @@ using GlitchyEngine.Math;
using GlitchyEngine.Events; using GlitchyEngine.Events;
using GlitchyEngine.ImGui; using GlitchyEngine.ImGui;
using ImGui; using ImGui;
using System.Text;
using GlitchyEngine.System;
namespace GlitchyEditor; namespace GlitchyEditor;
@@ -36,43 +38,101 @@ class UltralightLayer : Layer
dispatcher.Dispatch<MouseMovedEvent>(scope => MouseMoved); dispatcher.Dispatch<MouseMovedEvent>(scope => MouseMoved);
dispatcher.Dispatch<MouseButtonPressedEvent>(scope (e) => MousePressed(e, true)); dispatcher.Dispatch<MouseButtonPressedEvent>(scope (e) => MousePressed(e, true));
dispatcher.Dispatch<MouseButtonReleasedEvent>(scope (e) => MousePressed(e, false)); dispatcher.Dispatch<MouseButtonReleasedEvent>(scope (e) => MousePressed(e, false));
dispatcher.Dispatch<KeyPressedEvent>(scope (e) => KeyEvent(e, true, e.RepeatCount));
dispatcher.Dispatch<KeyReleasedEvent>(scope (e) => KeyEvent(e, false, 0));
dispatcher.Dispatch<KeyTypedEvent>(scope (e) => CharTypedEvent(e));
dispatcher.Dispatch<WindowCloseEvent>(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) private bool MouseMoved(MouseMovedEvent e)
{ {
if (e.Window == windoww) Log.EngineLogger.Warning($"{e.PositionX}");
{
Log.EngineLogger.Warning($"{e.PositionX}");
ULMouseEvent evt = ulCreateMouseEvent(.kMouseEventType_MouseMoved, e.PositionX, e.PositionY, .kMouseButton_None);
ulViewFireMouseEvent(view, evt);
ulDestroyMouseEvent(evt);
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) private bool MousePressed(MouseButtonEvent e, bool press)
{ {
if (e.Window == windoww) ULMouseButton button = .kMouseButton_None;
switch (e.MouseButton)
{ {
ULMouseButton button = .kMouseButton_None; case .LeftButton: button = .kMouseButton_Left;
case .RightButton: button = .kMouseButton_Right;
switch (e.MouseButton) case .MiddleButton: button = .kMouseButton_Middle;
{ default: button = .kMouseButton_None;
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);
} }
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) private static void OnAppUpdate(void* user_data)
@@ -170,6 +230,24 @@ class UltralightLayer : Layer
ULRenderer renderer; 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() private void NoApp()
{ {
ULConfig config = ulCreateConfig(); ULConfig config = ulCreateConfig();
@@ -180,9 +258,7 @@ class UltralightLayer : Layer
ulEnablePlatformFileSystem(fileSystemPath); ulEnablePlatformFileSystem(fileSystemPath);
ulDestroyString(fileSystemPath); ulDestroyString(fileSystemPath);
/*ULString styleSheet = ulCreateString("body { background: purple; }"); InitClipboard();
ulConfigSetUserStylesheet(config, styleSheet);
ulDestroyString(styleSheet);*/
renderer = ulCreateRenderer(config); renderer = ulCreateRenderer(config);
@@ -225,6 +301,9 @@ class UltralightLayer : Layer
private void Render() private void Render()
{ {
if (windoww == null)
return;
RenderCommand.Clear(windoww.SwapChain.BackBuffer, .Color | .Depth, .(0.7f, 0.2f, 0.2f), 1.0f, 0); 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); ulViewSetDOMReadyCallback(view, => OnDOMReady, null);
ulDestroyViewConfig(viewConfig); 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"); ULString url = ulCreateString("file:///app.html");
ulViewLoadURL(view, url); ulViewLoadURL(view, url);
ulDestroyString(url); ulDestroyString(url);
+4 -12
View File
@@ -4,7 +4,6 @@ namespace GlitchyEngine.Events
{ {
public class MouseMovedEvent : Event, IEvent public class MouseMovedEvent : Event, IEvent
{ {
private Window _window;
private int32 _mouseX, _mouseY; private int32 _mouseX, _mouseY;
public override EventType EventType => .MouseMoved; public override EventType EventType => .MouseMoved;
@@ -15,14 +14,11 @@ namespace GlitchyEngine.Events
public static EventType StaticType => .MouseMoved; public static EventType StaticType => .MouseMoved;
public Window Window => _window;
public int32 PositionX => _mouseX; public int32 PositionX => _mouseX;
public int32 PositionY => _mouseY; public int32 PositionY => _mouseY;
public this(Window window, int32 x, int32 y) public this(int32 x, int32 y)
{ {
_window = window;
_mouseX = x; _mouseX = x;
_mouseY = y; _mouseY = y;
} }
@@ -99,18 +95,14 @@ namespace GlitchyEngine.Events
public abstract class MouseButtonEvent : Event public abstract class MouseButtonEvent : Event
{ {
protected Window _window;
protected MouseButton _mouseButton; protected MouseButton _mouseButton;
public override EventCategory Category => .Input | .Mouse; public override EventCategory Category => .Input | .Mouse;
public Window Window => _window;
public MouseButton MouseButton => _mouseButton; public MouseButton MouseButton => _mouseButton;
protected this(Window window, MouseButton mouseButton) protected this(MouseButton mouseButton)
{ {
_window = window;
_mouseButton = mouseButton; _mouseButton = mouseButton;
} }
} }
@@ -123,7 +115,7 @@ namespace GlitchyEngine.Events
public static EventType StaticType => .MouseButtonPressed; 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) public override void ToString(String strBuffer)
{ {
@@ -140,7 +132,7 @@ namespace GlitchyEngine.Events
public static EventType StaticType => .MouseButtonReleased; 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) public override void ToString(String strBuffer)
{ {
@@ -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<char16> nativeText = .(nativeTextPtr, UTF16.CStrLen(nativeTextPtr) + 1);
Handle handle = GlobalAlloc(.Moveable, sizeof(char16) * nativeText.Length);
if (handle == 0)
break;
char16* clipboardDataPtr = (char16*)GlobalLock(handle);
Span<char16> 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
@@ -21,12 +21,13 @@ namespace GlitchyEngine
public extension Window public extension Window
{ {
const String WindowClassName = "GlitchyEngineWindow"; 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; internal Windows.HWnd _windowHandle;
private WindowClassExW _windowClass;
private WindowRectangle _clientRect; private WindowRectangle _clientRect;
private MinMaxInfo _minMaxInfo; private MinMaxInfo _minMaxInfo;
@@ -183,52 +184,65 @@ namespace GlitchyEngine
Log.EngineLogger.Error($"Failed to destroy window: Message ({(int)res}): {res}"); 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); Application.Instance.Windows.Remove(this);
} }
static ~this()
{
#unwarn
UnregisterClass(WindowClass.ClassName, _instanceHandle);
}
[LinkName(.C)] [LinkName(.C)]
static extern HWnd GetActiveWindow(); 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) private void Init(WindowDescription desc)
{ {
Debug.Profiler.ProfileFunction!(); Debug.Profiler.ProfileFunction!();
Log.EngineLogger.Trace($"Creating window \"{desc.Title}\" ({desc.Width}, {desc.Height})..."); Log.EngineLogger.Trace($"Creating window \"{desc.Title}\" ({desc.Width}, {desc.Height})...");
_instanceHandle = (.)GetModuleHandleW(null); if (WindowClass == default)
_windowClass = .();
_windowClass.Style = .HorizontalRedrawOnChange | .VerticalRedrawOnChange;
_windowClass.WindowProcedure = => MessageHandler;
_windowClass.HInstance = (.)_instanceHandle;
if (desc.Icon.Ptr != null)
{ {
Debug.Profiler.ProfileScope!("LoadIcon"); CreateWindowClass(desc.Icon);
_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");
} }
{ {
Debug.Profiler.ProfileScope!("CreateWindow"); 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); 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 // Determine whether or not the window is currently active
_isActive = GetActiveWindow() == _windowHandle; _isActive = GetActiveWindow() == _windowHandle;
SetIcon(desc.Icon);
} }
private bool _isResizingOrMoving; private bool _isResizingOrMoving;
@@ -252,7 +268,7 @@ namespace GlitchyEngine
[CLink] [CLink]
static extern IntBool IsWindowUnicode(HWND whnd); static extern IntBool IsWindowUnicode(HWND whnd);
public Result<void> SetIcon(StringView filePath) public override Result<void> SetIcon(StringView filePath)
{ {
HICON hIcon = LoadImageW(0, filePath.ToScopedNativeWChar!(), .Icon, 0, 0, .LoadFromFile); HICON hIcon = LoadImageW(0, filePath.ToScopedNativeWChar!(), .Icon, 0, 0, .LoadFromFile);
if (hIcon == 0) if (hIcon == 0)
@@ -386,34 +402,34 @@ namespace GlitchyEngine
// Left mouse button // Left mouse button
case WM_LBUTTONDOWN: case WM_LBUTTONDOWN:
{ {
var event = scope MouseButtonPressedEvent(window, .LeftButton); var event = scope MouseButtonPressedEvent(.LeftButton);
window._eventCallback(event); window._eventCallback(event);
} }
case WM_LBUTTONUP: case WM_LBUTTONUP:
{ {
var event = scope MouseButtonReleasedEvent(window, .LeftButton); var event = scope MouseButtonReleasedEvent(.LeftButton);
window._eventCallback(event); window._eventCallback(event);
} }
// Right mouse button // Right mouse button
case WM_RBUTTONDOWN: case WM_RBUTTONDOWN:
{ {
var event = scope MouseButtonPressedEvent(window, .RightButton); var event = scope MouseButtonPressedEvent(.RightButton);
window._eventCallback(event); window._eventCallback(event);
} }
case WM_RBUTTONUP: case WM_RBUTTONUP:
{ {
var event = scope MouseButtonReleasedEvent(window, .RightButton); var event = scope MouseButtonReleasedEvent(.RightButton);
window._eventCallback(event); window._eventCallback(event);
} }
// Middle mouse button // Middle mouse button
case WM_MBUTTONDOWN: case WM_MBUTTONDOWN:
{ {
var event = scope MouseButtonPressedEvent(window, .MiddleButton); var event = scope MouseButtonPressedEvent(.MiddleButton);
window._eventCallback(event); window._eventCallback(event);
} }
case WM_MBUTTONUP: case WM_MBUTTONUP:
{ {
var event = scope MouseButtonReleasedEvent(window, .MiddleButton); var event = scope MouseButtonReleasedEvent(.MiddleButton);
window._eventCallback(event); window._eventCallback(event);
} }
// X button // X button
@@ -425,7 +441,7 @@ namespace GlitchyEngine
else else
button = .XButton2; button = .XButton2;
var event = scope MouseButtonPressedEvent(window, button); var event = scope MouseButtonPressedEvent(button);
window._eventCallback(event); window._eventCallback(event);
} }
case WM_XBUTTONUP: case WM_XBUTTONUP:
@@ -436,7 +452,7 @@ namespace GlitchyEngine
else else
button = .XButton2; button = .XButton2;
var event = scope MouseButtonReleasedEvent(window, button); var event = scope MouseButtonReleasedEvent(button);
window._eventCallback(event); window._eventCallback(event);
} }
// Vertical scrolling // Vertical scrolling
@@ -459,10 +475,10 @@ namespace GlitchyEngine
{ {
SplitHighAndLowOrder!(lParam, let x, let y); SplitHighAndLowOrder!(lParam, let x, let y);
var event = scope MouseMovedEvent(window, x, y); var event = scope MouseMovedEvent(x, y);
window._eventCallback(event); window._eventCallback(event);
} }
case WM_INPUT: /*case WM_INPUT:
{ {
uint32 dataSize = ?; uint32 dataSize = ?;
GetRawInputData((.)lParam, RID_INPUT, null, &dataSize, sizeof(RAWINPUTHEADER)); GetRawInputData((.)lParam, RID_INPUT, null, &dataSize, sizeof(RAWINPUTHEADER));
@@ -483,7 +499,7 @@ namespace GlitchyEngine
} }
} }
} }
} }*/
// Todo: DirectInput // Todo: DirectInput
+15
View File
@@ -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);
}