mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
461 lines
12 KiB
Beef
461 lines
12 KiB
Beef
using System;
|
|
using GlitchyEngine;
|
|
using Ultralight;
|
|
using Ultralight.CAPI;
|
|
using GlitchyEngine.Renderer;
|
|
using GlitchyEngine.Math;
|
|
using GlitchyEngine.Events;
|
|
using GlitchyEngine.ImGui;
|
|
using ImGui;
|
|
using System.Text;
|
|
using GlitchyEngine.System;
|
|
|
|
namespace GlitchyEditor;
|
|
|
|
class UltralightLayer : Layer
|
|
{
|
|
Window windoww ~ delete _;
|
|
|
|
int2 cursorPosition;
|
|
|
|
public this()
|
|
{
|
|
NoApp();
|
|
//RenderApp();
|
|
|
|
WindowDescription windowDesc = .Default;
|
|
windowDesc.Icon = "Resources/Textures/GlitchyEngineIcon.ico";
|
|
windowDesc.Title = "Test";
|
|
|
|
windoww = new Window(windowDesc);
|
|
windoww.EventCallback = new => EventHandler;
|
|
}
|
|
|
|
private void EventHandler(Event e)
|
|
{
|
|
EventDispatcher dispatcher = EventDispatcher(e);
|
|
|
|
dispatcher.Dispatch<MouseMovedEvent>(scope => MouseMoved);
|
|
dispatcher.Dispatch<MouseButtonPressedEvent>(scope (e) => MousePressed(e, true));
|
|
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)
|
|
{
|
|
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);
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool MousePressed(MouseButtonEvent e, bool press)
|
|
{
|
|
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);
|
|
|
|
return true;
|
|
}
|
|
|
|
private static void OnAppUpdate(void* user_data)
|
|
{
|
|
//UltralightLayer layer = (UltralightLayer)Internal.UnsafeCastToObject(user_data);
|
|
//layer.OnAppUpdate();
|
|
}
|
|
|
|
private static void OnClose(void* user_data, ULWindow window)
|
|
{
|
|
Log.ClientLogger.Info("OnClose...");
|
|
ulAppQuit(app);
|
|
}
|
|
|
|
private static void OnResize(void* user_data, ULWindow window, uint32 width, uint32 height)
|
|
{
|
|
Log.ClientLogger.Info($"OnResize: {width} {height}");
|
|
ulOverlayResize(overlay, width, height);
|
|
}
|
|
|
|
///
|
|
/// This callback is bound to a JavaScript function on the page.
|
|
///
|
|
static JSValueRef GetMessage(JSContextRef ctx, JSObjectRef fn, JSObjectRef thisObject, uint32 argumentCount, JSValueRef* arguments, JSValueRef* exception) {
|
|
///
|
|
/// Create a JavaScript String from a C-string, initialize it with our
|
|
/// welcome message.
|
|
///
|
|
JSStringRef str = JSStringCreateWithUTF8CString("Hello from Beef!");
|
|
|
|
///
|
|
/// Create a garbage-collected JSValue using the String we just created.
|
|
///
|
|
/// **Note**:
|
|
/// Both JSValueRef and JSObjectRef types are garbage-collected types. (And actually,
|
|
/// JSObjectRef is just a typedef of JSValueRef, they share definitions).
|
|
///
|
|
/// The garbage collector in JavaScriptCore periodically scans the entire stack to check if
|
|
/// there are any active JSValueRefs, and marks those with no references for destruction.
|
|
///
|
|
/// If you happen to store a JSValueRef/JSObjectRef in heap memory or in memory unreachable
|
|
/// by the stack-based garbage-collector, you should explicitly call JSValueProtect() and
|
|
/// JSValueUnprotect() on the reference to ensure it is kept alive.
|
|
///
|
|
JSValueRef value = JSValueMakeString(ctx, str);
|
|
|
|
///
|
|
/// Release the string we created earlier (we only Release what we Create).
|
|
///
|
|
JSStringRelease(str);
|
|
|
|
return value;
|
|
}
|
|
|
|
private static void OnDOMReady(void* user_data, ULView caller, uint64 frame_id, bool is_main_frame, ULString url)
|
|
{
|
|
Log.ClientLogger.Info("OnDOMReady");
|
|
///
|
|
/// Acquire the page's JavaScript execution context.
|
|
///
|
|
/// This locks the JavaScript context so we can modify it safely on this thread, we need to
|
|
/// unlock it when we're done via ulViewUnlockJSContext().
|
|
///
|
|
JSContextRef ctx = ulViewLockJSContext(view);
|
|
|
|
///
|
|
/// Create a JavaScript String containing the name of our callback.
|
|
///
|
|
JSStringRef name = JSStringCreateWithUTF8CString("GetMessage");
|
|
|
|
///
|
|
/// Create a garbage-collected JavaScript function that is bound to our native C callback
|
|
/// 'GetMessage()'.
|
|
///
|
|
JSObjectRef func = JSObjectMakeFunctionWithCallback(ctx, name, => GetMessage);
|
|
|
|
///
|
|
/// Store our function in the page's global JavaScript object so that it is accessible from the
|
|
/// page as 'GetMessage()'.
|
|
///
|
|
/// The global JavaScript object is also known as 'window' in JS.
|
|
///
|
|
JSObjectSetProperty(ctx, JSContextGetGlobalObject(ctx), name, func, 0, null);
|
|
|
|
///
|
|
/// Release the JavaScript String we created earlier.
|
|
///
|
|
JSStringRelease(name);
|
|
|
|
///
|
|
/// Unlock the JS context so other threads can modify JavaScript state.
|
|
///
|
|
ulViewUnlockJSContext(view);
|
|
}
|
|
|
|
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();
|
|
|
|
ulEnablePlatformFontLoader();
|
|
|
|
ULString fileSystemPath = ulCreateString(@"D:\Development\Projects\Beef\GlitchyEngine\GlitchyEditor\assets");
|
|
ulEnablePlatformFileSystem(fileSystemPath);
|
|
ulDestroyString(fileSystemPath);
|
|
|
|
InitClipboard();
|
|
|
|
renderer = ulCreateRenderer(config);
|
|
|
|
CreateView();
|
|
}
|
|
|
|
Texture2D texture ~ _.ReleaseRef();
|
|
|
|
private void CopybitmapToTexture(ULBitmap bitmap)
|
|
{
|
|
void* pixels = ulBitmapLockPixels(bitmap);
|
|
|
|
uint32 width = ulBitmapGetWidth(bitmap);
|
|
uint32 height = ulBitmapGetHeight(bitmap);
|
|
uint32 stride = ulBitmapGetRowBytes(bitmap);
|
|
|
|
Span<Color> colors = Span<Color>((.)pixels, 500 * 500);
|
|
|
|
texture.SetData<uint32>((.)pixels, 0, 0, width, height, 0, 0);
|
|
|
|
ulBitmapUnlockPixels(bitmap);
|
|
}
|
|
|
|
public override void Update(GameTime gameTime)
|
|
{
|
|
ulUpdate(renderer);
|
|
|
|
ulRender(renderer);
|
|
|
|
ULBitmapSurface surface = ulViewGetSurface(view);
|
|
|
|
if (surface != null && !ulIntRectIsEmpty(ulSurfaceGetDirtyBounds(surface)))
|
|
{
|
|
CopybitmapToTexture(ulBitmapSurfaceGetBitmap(surface));
|
|
ulSurfaceClearDirtyBounds(surface);
|
|
}
|
|
|
|
Render();
|
|
}
|
|
|
|
private void Render()
|
|
{
|
|
if (windoww == null)
|
|
return;
|
|
|
|
RenderCommand.Clear(windoww.SwapChain.BackBuffer, .Color | .Depth, .(0.7f, 0.2f, 0.2f), 1.0f, 0);
|
|
}
|
|
|
|
private void CreateView()
|
|
{
|
|
ULViewConfig viewConfig = ulCreateViewConfig();
|
|
ulViewConfigSetIsAccelerated(viewConfig, false);
|
|
|
|
view = ulCreateView(renderer, 500, 500, viewConfig, null);
|
|
|
|
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);
|
|
|
|
//surface = ulViewGetSurface(view);
|
|
|
|
// TODO: Dynamic
|
|
Texture2DDesc desc = .(500, 500, .B8G8R8A8_UNorm, usage: .Default, cpuAccess: .Write);
|
|
|
|
texture = new Texture2D(desc);
|
|
texture.[Friend]Identifier = .("TestTexture");
|
|
|
|
Application.Instance.ContentManager.ManageAsset(texture);
|
|
}
|
|
|
|
public override void OnEvent(GlitchyEngine.Events.Event event)
|
|
{
|
|
EventDispatcher dispatcher = EventDispatcher(event);
|
|
|
|
dispatcher.Dispatch<ImGuiRenderEvent>(scope (e) => OnImGuiRender(e));
|
|
|
|
/*
|
|
dispatcher.Dispatch<WindowResizeEvent>(scope (e) => OnWindowResize(e));
|
|
dispatcher.Dispatch<KeyPressedEvent>(scope (e) => OnKeyPressed(e));
|
|
dispatcher.Dispatch<MouseScrolledEvent>(scope (e) => OnMouseScrolled(e));*/
|
|
|
|
}
|
|
|
|
private bool OnImGuiRender(ImGuiRenderEvent event)
|
|
{
|
|
if (ImGui.Begin("Testlul"))
|
|
{
|
|
ImGui.Image(texture, .(500, 500));
|
|
|
|
}
|
|
ImGui.End();
|
|
|
|
return false;
|
|
}
|
|
|
|
static ULApp app;
|
|
static ULWindow window;
|
|
static ULOverlay overlay;
|
|
static ULView view;
|
|
|
|
private void RenderApp()
|
|
{
|
|
///
|
|
/// Create default settings/config
|
|
///
|
|
ULSettings settings = ulCreateSettings();
|
|
ulSettingsSetForceCPURenderer(settings, true);
|
|
ULConfig config = ulCreateConfig();
|
|
|
|
ULString fileSystemPath = ulCreateString(@"D:\Development\Projects\Beef\GlitchyEngine\GlitchyEditor\assets");
|
|
ulSettingsSetFileSystemPath(settings, fileSystemPath);
|
|
ulDestroyString(fileSystemPath);
|
|
|
|
///
|
|
/// Create our App
|
|
///
|
|
app = ulCreateApp(settings, config);
|
|
|
|
///
|
|
/// Register a callback to handle app update logic.
|
|
///
|
|
ulAppSetUpdateCallback(app, => OnAppUpdate, null);
|
|
|
|
///
|
|
/// Done using settings/config, make sure to destroy anything we create
|
|
///
|
|
ulDestroySettings(settings);
|
|
ulDestroyConfig(config);
|
|
|
|
///
|
|
/// Create our window, make it 500x500 with a titlebar and resize handles.
|
|
///
|
|
window = ulCreateWindow(ulAppGetMainMonitor(app), 500, 500, false,
|
|
(uint32)(ULWindowFlags.kWindowFlags_Titled | ULWindowFlags.kWindowFlags_Resizable));
|
|
|
|
///
|
|
/// Set our window title.
|
|
///
|
|
ulWindowSetTitle(window, "Ultralight Sample 6 - Intro to C API");
|
|
|
|
///
|
|
/// Register a callback to handle window close.
|
|
///
|
|
ulWindowSetCloseCallback(window, => OnClose, null);
|
|
|
|
///
|
|
/// Register a callback to handle window resize.
|
|
///
|
|
ulWindowSetResizeCallback(window, => OnResize, null);
|
|
|
|
///
|
|
/// Create an overlay same size as our window at 0,0 (top-left) origin. Overlays also create an
|
|
/// HTML view for us to display content in.
|
|
///
|
|
/// **Note**:
|
|
/// Ownership of the view remains with the overlay since we don't explicitly create it.
|
|
///
|
|
overlay = ulCreateOverlay(window, ulWindowGetWidth(window), ulWindowGetHeight(window), 0, 0);
|
|
|
|
///
|
|
/// Get the overlay's view.
|
|
///
|
|
view = ulOverlayGetView(overlay);
|
|
|
|
///
|
|
/// Register a callback to handle our view's DOMReady event. We will use this event to setup any
|
|
/// JavaScript <-> C bindings and initialize our page.
|
|
///
|
|
ulViewSetDOMReadyCallback(view, => OnDOMReady, null);
|
|
|
|
ulViewSetFailLoadingCallback(view, (user_data, caller, frame_id, is_main_frame, url, description, error_domain, error_code) => {
|
|
Log.ClientLogger.Error("Errorrr");
|
|
}, null);
|
|
|
|
///
|
|
/// Load a file from the FileSystem.
|
|
///
|
|
/// **IMPORTANT**: Make sure `file:///` has three (3) forward slashes.
|
|
///
|
|
/// **Note**: You can configure the base path for the FileSystem in the Settings we passed to
|
|
/// ulCreateApp earlier.
|
|
///
|
|
ULString url = ulCreateString("file:///app.html");
|
|
|
|
var v = ulStringGetData(url);
|
|
|
|
ulViewLoadURL(view, url);
|
|
ulDestroyString(url);
|
|
|
|
ulAppRun(app);
|
|
}
|
|
} |