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(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) { 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 colors = Span((.)pixels, 500 * 500); texture.SetData((.)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(scope (e) => OnImGuiRender(e)); /* dispatcher.Dispatch(scope (e) => OnWindowResize(e)); dispatcher.Dispatch(scope (e) => OnKeyPressed(e)); dispatcher.Dispatch(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); } }