From b620e73dc8c03a15dc78cc69d92b05ac56f496e1 Mon Sep 17 00:00:00 2001 From: Quentin Quadrat Date: Fri, 29 Nov 2024 19:38:55 +0100 Subject: [PATCH] Adding JS functions => gdscript methods bindings --- addons/gdcef/demos/HelloCEF/Control.gd | 20 +- addons/gdcef/demos/JS/Control.gd | 212 +++++++++++++++++ addons/gdcef/demos/JS/Control.tscn | 28 +++ .../demos/JS/character-management-ui.html | 220 ++++++++++++++++++ addons/gdcef/demos/JS/default_env.tres | 7 + addons/gdcef/demos/JS/icon.png | Bin 0 -> 4843 bytes addons/gdcef/demos/JS/project.godot | 25 ++ addons/gdcef/demos/JS/qq.html | 19 ++ addons/gdcef/demos/README.md | 6 + addons/gdcef/gdcef/SConstruct | 2 +- addons/gdcef/gdcef/src/gdbrowser.cpp | 88 ++++++- addons/gdcef/gdcef/src/gdbrowser.hpp | 31 ++- addons/gdcef/gdcef/src/gdcef.cpp | 3 +- addons/gdcef/gdcef/src/gdcef.hpp | 16 +- addons/gdcef/gdcef/src/godot_js_binder.cpp | 171 ++++++++++++++ addons/gdcef/gdcef/src/godot_js_binder.hpp | 74 ++++++ addons/gdcef/gdcef/src/register_types.cpp | 1 + .../render_process/src/render_process.cpp | 183 +++++++++------ .../render_process/src/render_process.hpp | 80 ++----- 19 files changed, 1033 insertions(+), 153 deletions(-) create mode 100644 addons/gdcef/demos/JS/Control.gd create mode 100644 addons/gdcef/demos/JS/Control.tscn create mode 100644 addons/gdcef/demos/JS/character-management-ui.html create mode 100644 addons/gdcef/demos/JS/default_env.tres create mode 100644 addons/gdcef/demos/JS/icon.png create mode 100644 addons/gdcef/demos/JS/project.godot create mode 100644 addons/gdcef/demos/JS/qq.html create mode 100644 addons/gdcef/gdcef/src/godot_js_binder.cpp create mode 100644 addons/gdcef/gdcef/src/godot_js_binder.hpp diff --git a/addons/gdcef/demos/HelloCEF/Control.gd b/addons/gdcef/demos/HelloCEF/Control.gd index 3db9081..a36efa9 100644 --- a/addons/gdcef/demos/HelloCEF/Control.gd +++ b/addons/gdcef/demos/HelloCEF/Control.gd @@ -181,16 +181,16 @@ func _ready(): # Left browser is displaying the first webpage with a 3D scene, we are # enabling webgl. Other default configuration are: - # {"frame_rate", 30} - # {"javascript", true} - # {"javascript_close_windows", false} - # {"javascript_access_clipboard", false} - # {"javascript_dom_paste", false} - # {"image_loading", true} - # {"databases", true} - # {"webgl", true} - var left = $CEF.create_browser(pages[4], $TextRectLeft, {}) - var right = $CEF.create_browser(pages[0], $TextRectRight, {}) + # {"frame_rate": 30} + # {"javascript": true} + # {"javascript_close_windows": false} + # {"javascript_access_clipboard": false} + # {"javascript_dom_paste": false} + # {"image_loading": true} + # {"databases": true} + # {"webgl": true} + var left = $CEF.create_browser(pages[4], $TextRectLeft, {"javascript": true, "webgl": true}) + var right = $CEF.create_browser(pages[0], $TextRectRight, {"javascript": true, "webgl": true}) left.name = "left" right.name = "right" diff --git a/addons/gdcef/demos/JS/Control.gd b/addons/gdcef/demos/JS/Control.gd new file mode 100644 index 0000000..96455d3 --- /dev/null +++ b/addons/gdcef/demos/JS/Control.gd @@ -0,0 +1,212 @@ +# ============================================================================== +# Basic application made in HTML/JS/CSS with interaction with Godot. +# ============================================================================== +extends Control + +# ============================================================================== +# CEF variables +# ============================================================================== +const BROWSER_NAME = "player_stats" +@onready var mouse_pressed: bool = false + +# ============================================================================== +# Variables for character stats +# ============================================================================== +@onready var player_name: String = "Anonymous" +@onready var weapon: String = "sword" +@onready var xp: int = 0 +@onready var level: int = 1 + +# ============================================================================== +# Initial character configuration +# ============================================================================== +func _ready(): + initialize_cef() + pass + +# ============================================================================== +# Change character's weapon +# ============================================================================== +func change_weapon(new_weapon: String): + print("Weapon changed to: ", new_weapon) + weapon = new_weapon + _update_character_stats() + pass + +# ============================================================================== +# Set character's name +# ============================================================================== +func set_character_name(new_name: String): + print("New name: ", new_name) + player_name = new_name + _update_character_stats() + pass + +# ============================================================================== +# Modify XP (can be positive or negative) +# ============================================================================== +func modify_xp(xp_change: int): + xp += xp_change + _level_up_check() + _update_character_stats() + pass + +# ============================================================================== +# Check for level up +# ============================================================================== +func _level_up_check(): + var previous_level = level + level = 1 + floor(xp / 100) # Simple progression example + + if level > previous_level: + print("Level up! New level: ", level) + pass + +# ============================================================================== +# Update character statistics +# ============================================================================== +func _update_character_stats(): + var character_info = { + "name": player_name, + "weapon": weapon, + "xp": xp, + "level": level + } + print("Character update: ", character_info) + pass + +# ============================================================================== +# Optional method to get complete character state +# ============================================================================== +func get_character_state() -> Dictionary: + return { + "name": player_name, + "weapon": weapon, + "xp": xp, + "level": level + } + +# ============================================================================== +# CEF Callback when a page has ended to load with success. +# TODO on page_unload ? +# ============================================================================== +func _on_page_loaded(browser): + print("The browser " + browser.name + " has loaded " + browser.get_url()) + browser.register_method(Callable(self, "change_weapon")) + browser.register_method(Callable(self, "set_character_name")) + browser.register_method(Callable(self, "modify_xp")) + pass + +# ============================================================================== +# Callback when a page has ended to load with failure. +# Display a load error message using a data: URI. +# ============================================================================== +func _on_page_failed_loading(_err_code, _err_msg, browser): + $AcceptDialog.title = "Alert!" + $AcceptDialog.dialog_text = "The browser " + browser.name + " did not load " + browser.get_url() + $AcceptDialog.popup_centered(Vector2(0, 0)) + $AcceptDialog.show() + pass + +# ============================================================================== +# Split the browser vertically to display two browsers (aka tabs) rendered in +# two separate textures. +# ============================================================================== +func initialize_cef(): + + ### CEF + + if !$CEF.initialize({"incognito": true, "locale": "en-US", + "remote_debugging_port": 7777, "remote_allow_origin": "*"}): + push_error("Failed initializing CEF") + get_tree().quit() + else: + push_warning("CEF version: " + $CEF.get_full_version()) + pass + + ### Browser + + var browser = $CEF.create_browser("", $TextureRect, {"javascript": true}) + browser.name = BROWSER_NAME + browser.connect("on_page_loaded", _on_page_loaded) + browser.connect("on_page_failed_loading", _on_page_failed_loading) + browser.resize($TextureRect.get_size()) + browser.load_data_uri(_load_html_file(), "text/html") + pass + +# ============================================================================== +# Load the HTML file containing the JavaScript code +# ============================================================================== +func _load_html_file(): + var file = FileAccess.open("res://character-management-ui.html", FileAccess.READ) + var content = file.get_as_text() + file.close() + return content + +# ============================================================================== +# Get the browser node interacting with the JavaScript code. +# ============================================================================== +func get_browser(): + var browser = $CEF.get_node(BROWSER_NAME) + if browser == null: + push_error("Failed getting Godot node '" + name + "'") + get_tree().quit() + return browser + +# ============================================================================== +# Get mouse events and broadcast them to CEF +# ============================================================================== +func _on_TextureRect_gui_input(event: InputEvent): + var current_browser = get_browser() + if event is InputEventMouseButton: + if event.button_index == MOUSE_BUTTON_WHEEL_UP: + current_browser.set_mouse_wheel_vertical(2) + elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN: + current_browser.set_mouse_wheel_vertical(-2) + elif event.button_index == MOUSE_BUTTON_LEFT: + mouse_pressed = event.pressed + if mouse_pressed: + current_browser.set_mouse_left_down() + else: + current_browser.set_mouse_left_up() + elif event.button_index == MOUSE_BUTTON_RIGHT: + mouse_pressed = event.pressed + if mouse_pressed: + current_browser.set_mouse_right_down() + else: + current_browser.set_mouse_right_up() + else: + mouse_pressed = event.pressed + if mouse_pressed: + current_browser.set_mouse_middle_down() + else: + current_browser.set_mouse_middle_up() + elif event is InputEventMouseMotion: + if mouse_pressed: + current_browser.set_mouse_left_down() + current_browser.set_mouse_moved(event.position.x, event.position.y) + pass + +# ============================================================================== +# Make the CEF browser reacts from keyboard events. +# ============================================================================== +func _input(event): + if event is InputEventKey: + get_browser().set_key_pressed( + event.unicode if event.unicode != 0 else event.keycode, + event.pressed, event.shift_pressed, event.alt_pressed, + event.is_command_or_control_pressed()) + pass + +# ============================================================================== +# Windows has resized +# ============================================================================== +func _on_texture_rect_resized(): + get_browser().resize($Panel/VBox/TextureRect.get_size()) + pass + +# ============================================================================== +# CEF is implicitly updated by this function. +# ============================================================================== +func _process(_delta): + pass diff --git a/addons/gdcef/demos/JS/Control.tscn b/addons/gdcef/demos/JS/Control.tscn new file mode 100644 index 0000000..8e6ca1c --- /dev/null +++ b/addons/gdcef/demos/JS/Control.tscn @@ -0,0 +1,28 @@ +[gd_scene load_steps=2 format=3 uid="uid://ckewd5tjvl4rj"] + +[ext_resource type="Script" path="res://Control.gd" id="2"] + +[node name="Control" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("2") + +[node name="TextureRect" type="TextureRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="CEF" type="GDCef" parent="."] + +[node name="Timer" type="Timer" parent="."] +wait_time = 10.0 +autostart = true + +[connection signal="gui_input" from="TextureRect" to="." method="_on_TextureRect_gui_input"] diff --git a/addons/gdcef/demos/JS/character-management-ui.html b/addons/gdcef/demos/JS/character-management-ui.html new file mode 100644 index 0000000..a6b2618 --- /dev/null +++ b/addons/gdcef/demos/JS/character-management-ui.html @@ -0,0 +1,220 @@ + + + + + + Character Management UI + + + + +
+
+

Character Management UI

+
+ +
+

Name of the character

+
+ + +
+
+ +
+

Weapon Selection

+
+ + + + + + +
+
+ +
+

XP Management

+
+ + + +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/addons/gdcef/demos/JS/default_env.tres b/addons/gdcef/demos/JS/default_env.tres new file mode 100644 index 0000000..dfc5189 --- /dev/null +++ b/addons/gdcef/demos/JS/default_env.tres @@ -0,0 +1,7 @@ +[gd_resource type="Environment" load_steps=2 format=3 uid="uid://btpjrvhyjvhj2"] + +[sub_resource type="Sky" id="1"] + +[resource] +background_mode = 2 +sky = SubResource("1") diff --git a/addons/gdcef/demos/JS/icon.png b/addons/gdcef/demos/JS/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1acbade95549969d28cb4f82c6982d6235409321 GIT binary patch literal 4843 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 MO4^Dw@-~tG1M5PK^Z)<= literal 0 HcmV?d00001 diff --git a/addons/gdcef/demos/JS/project.godot b/addons/gdcef/demos/JS/project.godot new file mode 100644 index 0000000..322cd70 --- /dev/null +++ b/addons/gdcef/demos/JS/project.godot @@ -0,0 +1,25 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="JS Bindings" +run/main_scene="res://Control.tscn" +config/features=PackedStringArray("4.3") +config/icon="res://icon.png" + +[physics] + +common/enable_pause_aware_picking=true + +[rendering] + +renderer/rendering_method="gl_compatibility" +environment/defaults/default_environment="res://default_env.tres" diff --git a/addons/gdcef/demos/JS/qq.html b/addons/gdcef/demos/JS/qq.html new file mode 100644 index 0000000..97f951b --- /dev/null +++ b/addons/gdcef/demos/JS/qq.html @@ -0,0 +1,19 @@ + + + + + + Character Management UI + + + + + + + + + \ No newline at end of file diff --git a/addons/gdcef/demos/README.md b/addons/gdcef/demos/README.md index 5e54933..878ca2a 100644 --- a/addons/gdcef/demos/README.md +++ b/addons/gdcef/demos/README.md @@ -33,3 +33,9 @@ A demo showing a 3D GUI with a single CEF browser tab showing a radio website. T This demo is based on the asset library: https://godotengine.org/asset-library/asset/127 ![Screenshot](3D/icon.png) + +### Demo 03: CEF browser in 2D with JS bindings + +A demo showing a 2D GUI made in HTML/CSS/JavaScript and loaded in a CEF browser. The demo shows how to bind Godot methods to JavaScript functions using JS Binder. + +![Screenshot](JS/icon.png) diff --git a/addons/gdcef/gdcef/SConstruct b/addons/gdcef/gdcef/SConstruct index b327692..73fd195 100644 --- a/addons/gdcef/gdcef/SConstruct +++ b/addons/gdcef/gdcef/SConstruct @@ -232,7 +232,7 @@ env.Append(CPPPATH=['src/']) env.Append(RPATH=[env['cef_artifacts_folder'][2:-2]]) # Compile the library -sources = ['src/helper_files.cpp', 'src/browser_io.cpp', 'src/gdcef.cpp', 'src/gdbrowser.cpp', 'src/register_types.cpp'] +sources = ['src/helper_files.cpp', 'src/browser_io.cpp', 'src/gdcef.cpp', 'src/gdbrowser.cpp', 'src/register_types.cpp', 'src/godot_js_binder.cpp'] # sources = Glob('src/*.cpp') library = env.SharedLibrary(target=target_path + '/' + target_library, source=sources) Default(library) diff --git a/addons/gdcef/gdcef/src/gdbrowser.cpp b/addons/gdcef/gdcef/src/gdbrowser.cpp index 0ec824f..76687ac 100644 --- a/addons/gdcef/gdcef/src/gdbrowser.cpp +++ b/addons/gdcef/gdcef/src/gdbrowser.cpp @@ -23,8 +23,8 @@ // SOFTWARE. //***************************************************************************** -//------------------------------------------------------------------------------ #include "gdbrowser.hpp" +#include "godot_js_binder.hpp" #include "helper_config.hpp" #include "helper_files.hpp" @@ -37,6 +37,10 @@ # include #endif +#ifndef CALL_GODOT_METHOD +# define CALL_GODOT_METHOD "callGodotMethod" +#endif + //------------------------------------------------------------------------------ // Visit the html content of the current page. class Visitor: public CefStringVisitor @@ -149,6 +153,8 @@ void GDBrowserView::_bind_methods() &GDBrowserView::getAudioStreamer); ClassDB::bind_method(D_METHOD("get_pixel_color", "x", "y"), &GDBrowserView::getPixelColor); + ClassDB::bind_method(D_METHOD("register_method"), + &GDBrowserView::registerGodotMethod); // Signals ADD_SIGNAL(MethodInfo("on_download_updated", @@ -886,4 +892,84 @@ void GDBrowserView::onDownloadUpdated( // Emit signal for Godot script emit_signal( "on_download_updated", godot::String(file.c_str()), percentage, this); +} + +//------------------------------------------------------------------------------ +void GDBrowserView::registerGodotMethod(const godot::Callable& callable) +{ + godot::String method_name = callable.get_method(); + + BROWSER_DEBUG("Registering gdscript method " + << method_name.utf8().get_data()); + if (!callable.is_valid()) + { + BROWSER_ERROR("Invalid callable provided"); + return; + } + + std::string key = method_name.utf8().get_data(); + m_js_bindings[key] = callable; +} + +//------------------------------------------------------------------------------ +bool GDBrowserView::onProcessMessageReceived( + CefRefPtr browser, + CefRefPtr frame, + CefProcessId source_process, + CefRefPtr message) +{ + BROWSER_DEBUG("Received message " << message->GetName().ToString()); + if (message->GetName() != CALL_GODOT_METHOD) + { + BROWSER_DEBUG("Not method " << CALL_GODOT_METHOD); + return false; + } + + if (message->GetArgumentList()->GetSize() < 1) + { + BROWSER_ERROR("Expected method name as first argument"); + return false; + } + + // Create the callable key + std::string key = message->GetArgumentList()->GetString(0).ToString(); + + // Does not exist ? + auto callable = m_js_bindings[key]; + if (!callable.is_valid()) + { + BROWSER_ERROR("Callable not found for method " << key); + return false; + } + + // Convert the message arguments to a Godot Array + godot::Array args; + auto message_args = message->GetArgumentList(); + for (size_t i = 1; i < message_args->GetSize(); ++i) + { + switch (message_args->GetType(i)) + { + case VTYPE_BOOL: + args.push_back(message_args->GetBool(i)); + break; + case VTYPE_INT: + args.push_back(message_args->GetInt(i)); + break; + case VTYPE_DOUBLE: + args.push_back(message_args->GetDouble(i)); + break; + case VTYPE_STRING: + args.push_back(godot::String( + message_args->GetString(i).ToString().c_str())); + break; + default: + // For unsupported types, pass as string + args.push_back(godot::String( + message_args->GetString(i).ToString().c_str())); + } + } + + // Call the function + callable.callv(args); + return true; } \ No newline at end of file diff --git a/addons/gdcef/gdcef/src/gdbrowser.hpp b/addons/gdcef/gdcef/src/gdbrowser.hpp index 4cb427e..8f66ec2 100644 --- a/addons/gdcef/gdcef/src/gdbrowser.hpp +++ b/addons/gdcef/gdcef/src/gdbrowser.hpp @@ -189,6 +189,19 @@ class GDBrowserView: public godot::Node return this; } + // --------------------------------------------------------------------- + //! \brief Called when a message is received from a different process. + // --------------------------------------------------------------------- + virtual bool + OnProcessMessageReceived(CefRefPtr browser, + CefRefPtr frame, + CefProcessId source_process, + CefRefPtr message) override + { + return m_owner.onProcessMessageReceived( + browser, frame, source_process, message); + } + private: // CefRenderHandler interfaces // --------------------------------------------------------------------- @@ -285,7 +298,7 @@ class GDBrowserView: public godot::Node { } - private: // CefBrowserProcessHandler interfaces + private: // CefLifeSpanHandler interfaces virtual bool OnBeforePopup(CefRefPtr browser, CefRefPtr frame, @@ -668,6 +681,11 @@ class GDBrowserView: public godot::Node // ------------------------------------------------------------------------- godot::Color getPixelColor(int x, int y) const; + // ------------------------------------------------------------------------- + //! \brief Register a Godot method in the JavaScript context + // ------------------------------------------------------------------------- + void registerGodotMethod(const godot::Callable& callable); + private: void resize_(int width, int height); @@ -769,6 +787,14 @@ class GDBrowserView: public godot::Node CefRefPtr download_item, CefRefPtr callback); + // ------------------------------------------------------------------------- + //! \brief Called when a message is received from a different process. + // ------------------------------------------------------------------------- + bool onProcessMessageReceived(CefRefPtr browser, + CefRefPtr frame, + CefProcessId source_process, + CefRefPtr message); + private: //! \brief CEF interface implementation @@ -818,6 +844,9 @@ class GDBrowserView: public godot::Node //! \brief Download folder (configured from Browser config) fs::path m_download_folder; + + //! \brief + std::unordered_map m_js_bindings; }; #if !defined(_WIN32) diff --git a/addons/gdcef/gdcef/src/gdcef.cpp b/addons/gdcef/gdcef/src/gdcef.cpp index 788693b..572cad3 100644 --- a/addons/gdcef/gdcef/src/gdcef.cpp +++ b/addons/gdcef/gdcef/src/gdcef.cpp @@ -26,6 +26,7 @@ //------------------------------------------------------------------------------ #include "gdcef.hpp" #include "gdbrowser.hpp" +#include "godot_js_binder.hpp" #include "helper_config.hpp" #include "helper_files.hpp" @@ -122,7 +123,7 @@ void GDCef::_bind_methods() GDCEF_DEBUG(""); using namespace godot; - ClassDB::bind_method(D_METHOD("initialize"), &GDCef::initialize); + ClassDB::bind_method(D_METHOD("initialize", "config"), &GDCef::initialize); ClassDB::bind_method(D_METHOD("get_full_version"), &GDCef::version); ClassDB::bind_method(D_METHOD("get_version_part"), &GDCef::versionPart); ClassDB::bind_method(D_METHOD("create_browser"), &GDCef::createBrowser); diff --git a/addons/gdcef/gdcef/src/gdcef.hpp b/addons/gdcef/gdcef/src/gdcef.hpp index 30337d6..a6ec768 100644 --- a/addons/gdcef/gdcef/src/gdcef.hpp +++ b/addons/gdcef/gdcef/src/gdcef.hpp @@ -273,14 +273,14 @@ class GDCef: public godot::Node //! \param[in] texture_rect the texture container in where to paint the CEF //! output. \param[in] config dictionary of Browser config with default //! values: - //! - {"frame_rate", 30} - //! - {"javascript", STATE_ENABLED} - //! - {"javascript_close_windows", STATE_DISABLED} - //! - {"javascript_access_clipboard", STATE_DISABLED} - //! - {"javascript_dom_paste", STATE_DISABLED} - //! - {"image_loading", STATE_ENABLED} - //! - {"databases", STATE_ENABLED} - //! - {"webgl", STATE_ENABLED} + //! - {"frame_rate": 30} + //! - {"javascript": STATE_ENABLED} + //! - {"javascript_close_windows": STATE_DISABLED} + //! - {"javascript_access_clipboard": STATE_DISABLED} + //! - {"javascript_dom_paste": STATE_DISABLED} + //! - {"image_loading": STATE_ENABLED} + //! - {"databases": STATE_ENABLED} + //! - {"webgl": STATE_ENABLED} //! Wherer STATE_DISABLED / STATE_ENABLED == false / true //! \return the address of the newly created browser (or nullptr in case of //! error). diff --git a/addons/gdcef/gdcef/src/godot_js_binder.cpp b/addons/gdcef/gdcef/src/godot_js_binder.cpp new file mode 100644 index 0000000..ed8f9ce --- /dev/null +++ b/addons/gdcef/gdcef/src/godot_js_binder.cpp @@ -0,0 +1,171 @@ +//***************************************************************************** +// MIT License +// +// Copyright (c) 2022 Alain Duron +// Copyright (c) 2022 Quentin Quadrat +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//***************************************************************************** + +#include "godot_js_binder.hpp" + +//------------------------------------------------------------------------------ +bool GodotMethodInvoker::Execute(const CefString& name, + CefRefPtr object, + const CefV8ValueList& arguments, + CefRefPtr& retval, + CefString& exception) +{ + // Convert arguments JavaScript to Godot arguments + godot::Array godot_args; + for (const auto& arg : arguments) + { + godot_args.append(V8ToGodot(arg)); + } + + // Call the Godot method + godot::Variant result = m_godot_object->call(m_method_name, godot_args); + + // Convert Godot result to V8 value + retval = GodotToV8(result); + return true; +} + +//------------------------------------------------------------------------------ +CefRefPtr GodotToV8(const godot::Variant& godot_value) +{ + switch (godot_value.get_type()) + { + case godot::Variant::BOOL: + return CefV8Value::CreateBool(godot_value.operator bool()); + + case godot::Variant::INT: + return CefV8Value::CreateInt(godot_value.operator int64_t()); + + case godot::Variant::FLOAT: + return CefV8Value::CreateDouble(godot_value.operator double()); + + case godot::Variant::STRING: + return CefV8Value::CreateString(CefString( + godot_value.operator godot::String().utf8().get_data())); + + case godot::Variant::ARRAY: { + godot::Array godot_array = godot_value.operator godot::Array(); + CefRefPtr js_array = + CefV8Value::CreateArray(godot_array.size()); + + for (int i = 0; i < godot_array.size(); ++i) + { + js_array->SetValue(i, GodotToV8(godot_array[i])); + } + return js_array; + } + + case godot::Variant::DICTIONARY: { + godot::Dictionary godot_dict = + godot_value.operator godot::Dictionary(); + CefRefPtr js_object = + CefV8Value::CreateObject(nullptr, nullptr); + + for (int i = 0; i < godot_dict.size(); ++i) + { + godot::Variant key = godot_dict.keys()[i]; + godot::Variant value = godot_dict.values()[i]; + + js_object->SetValue( + key.operator godot::String().utf8().get_data(), + GodotToV8(value), + V8_PROPERTY_ATTRIBUTE_NONE); + } + return js_object; + } + + default: + return CefV8Value::CreateNull(); + } +} + +//------------------------------------------------------------------------------ +godot::Variant V8ToGodot(CefRefPtr v8_value) +{ + if (!v8_value.get()) + { + return godot::Variant(); + } + + if (v8_value->IsNull() || v8_value->IsUndefined()) + { + return godot::Variant(); + } + + if (v8_value->IsBool()) + { + return godot::Variant(v8_value->GetBoolValue()); + } + + if (v8_value->IsInt()) + { + return godot::Variant(v8_value->GetIntValue()); + } + + if (v8_value->IsDouble()) + { + return godot::Variant(v8_value->GetDoubleValue()); + } + + if (v8_value->IsString()) + { + return godot::Variant( + godot::String(v8_value->GetStringValue().ToString().c_str())); + } + + if (v8_value->IsArray()) + { + godot::Array godot_array; + int length = v8_value->GetArrayLength(); + + for (int i = 0; i < length; ++i) + { + godot_array.append(V8ToGodot(v8_value->GetValue(i))); + } + + return godot_array; + } + + if (v8_value->IsObject()) + { + godot::Dictionary godot_dict; + std::vector keys; + v8_value->GetKeys(keys); + + for (const auto& key : keys) + { + CefRefPtr property = v8_value->GetValue(key); + if (property.get()) + { + godot_dict[godot::String(key.ToString().c_str())] = + V8ToGodot(property); + } + } + + return godot_dict; + } + + return godot::Variant(); +} \ No newline at end of file diff --git a/addons/gdcef/gdcef/src/godot_js_binder.hpp b/addons/gdcef/gdcef/src/godot_js_binder.hpp new file mode 100644 index 0000000..419753b --- /dev/null +++ b/addons/gdcef/gdcef/src/godot_js_binder.hpp @@ -0,0 +1,74 @@ +//***************************************************************************** +// MIT License +// +// Copyright (c) 2022 Alain Duron +// Copyright (c) 2022 Quentin Quadrat +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//***************************************************************************** + +#ifndef GDCEF_GODOT_JS_BINDER_HPP +#define GDCEF_GODOT_JS_BINDER_HPP + +// Godot 4 +#include +#include +#include + +// Chromium Embedded Framework +#include "cef_v8.h" + +// ----------------------------------------------------------------------------- +//! \brief Convert a Godot variant to a V8 value +// ----------------------------------------------------------------------------- +CefRefPtr GodotToV8(const godot::Variant& godot_value); + +// ----------------------------------------------------------------------------- +//! \brief Convert a V8 value to a Godot variant +// ----------------------------------------------------------------------------- +godot::Variant V8ToGodot(CefRefPtr v8_value); + +// **************************************************************************** +//! \class GodotMethodInvoker +//! \brief Class to handle binding between JavaScript and GDScript methods +// **************************************************************************** +class GodotMethodInvoker: public CefV8Handler +{ +public: + + GodotMethodInvoker(godot::Object* obj, const godot::StringName& method) + : m_godot_object(obj), m_method_name(method) + { + } + + bool Execute(const CefString& name, + CefRefPtr object, + const CefV8ValueList& arguments, + CefRefPtr& retval, + CefString& exception) override; + + IMPLEMENT_REFCOUNTING(GodotMethodInvoker); + +private: + + godot::Object* m_godot_object; + godot::StringName m_method_name; +}; + +#endif // GDCEF_GODOT_JS_BINDER_HPP \ No newline at end of file diff --git a/addons/gdcef/gdcef/src/register_types.cpp b/addons/gdcef/gdcef/src/register_types.cpp index 0cebe34..00f4d91 100644 --- a/addons/gdcef/gdcef/src/register_types.cpp +++ b/addons/gdcef/gdcef/src/register_types.cpp @@ -27,6 +27,7 @@ #include "gdbrowser.hpp" #include "gdcef.hpp" +#include "godot_js_binder.hpp" #include "helper_log.hpp" #include #include diff --git a/addons/gdcef/render_process/src/render_process.cpp b/addons/gdcef/render_process/src/render_process.cpp index 4792d31..ff3f0bf 100644 --- a/addons/gdcef/render_process/src/render_process.cpp +++ b/addons/gdcef/render_process/src/render_process.cpp @@ -25,6 +25,7 @@ #include "render_process.hpp" +//------------------------------------------------------------------------------ #define DEBUG_RENDER_PROCESS(txt) \ { \ std::stringstream ss; \ @@ -33,6 +34,7 @@ std::cout << ss.str() << std::endl; \ } +//------------------------------------------------------------------------------ #define DEBUG_BROWSER_PROCESS(txt) \ { \ std::stringstream ss; \ @@ -41,90 +43,127 @@ std::cout << ss.str() << std::endl; \ } -//------------------------------------------------------------------------------ -RenderProcess::~RenderProcess() -{ - DEBUG_RENDER_PROCESS(""); -} - -#if 0 -//------------------------------------------------------------------------------ -void RenderProcess::OnContextInitialized() -{ - CEF_REQUIRE_UI_THREAD(); - DEBUG_RENDER_PROCESS(""); - - // Information used when creating the native window. - CefWindowInfo window_info; - -# if defined(OS_WIN) - // On Windows we need to specify certain flags that will be passed to - // CreateWindowEx(). - window_info.SetAsPopup(NULL, "CEF"); -# endif - - // GDCefBrowser implements browser-level callbacks. - DEBUG_RENDER_PROCESS("Create client handler"); - CefRefPtr handler(new GDCefBrowser()); - - // Specify CEF browser settings here. - CefBrowserSettings browser_settings; - - // Create the first browser window. - DEBUG_RENDER_PROCESS("Create the browser"); - CefBrowserHost::CreateBrowser( - window_info, handler.get(), "", browser_settings, nullptr, nullptr); -} +#ifndef CALL_GODOT_METHOD +# define CALL_GODOT_METHOD "callGodotMethod" #endif //------------------------------------------------------------------------------ -void RenderProcess::OnContextCreated(CefRefPtr browser, - CefRefPtr frame, - CefRefPtr context) +bool GodotMethodHandler::Execute(const CefString& name, + CefRefPtr object, + const CefV8ValueList& arguments, + CefRefPtr& retval, + CefString& exception) { - DEBUG_RENDER_PROCESS(""); -} + DEBUG_RENDER_PROCESS(name.ToString()); -#if 0 -//------------------------------------------------------------------------------ -GDCefBrowser::~GDCefBrowser() -{ - DEBUG_BROWSER_PROCESS(""); -} + // Function does not exist. + if (name != CALL_GODOT_METHOD) + { + exception = "Function does not exist"; + DEBUG_RENDER_PROCESS(exception.ToString()); + return false; + } -//------------------------------------------------------------------------------ -void GDCefBrowser::OnAfterCreated(CefRefPtr browser) -{ - CEF_REQUIRE_UI_THREAD(); - DEBUG_BROWSER_PROCESS(""); + // No browser created, we cannot call the method. + if (m_browser == nullptr) + { + exception = "Browser pointer at NULL"; + DEBUG_RENDER_PROCESS(exception.ToString()); + return true; + } - // Add to the list of existing browsers. - m_browser_list.push_back(browser); -} + // Check that there is at least the method name as argument. + if (arguments.size() < 1 || !arguments[0]->IsString()) + { + exception = "First argument must be the method name"; + DEBUG_RENDER_PROCESS(exception.ToString()); + return false; + } -//------------------------------------------------------------------------------ -void GDCefBrowser::OnBeforeClose(CefRefPtr browser) -{ - CEF_REQUIRE_UI_THREAD(); - DEBUG_BROWSER_PROCESS(""); + // Create and configure the IPC message to the main process. + CefRefPtr msg = + CefProcessMessage::Create(CALL_GODOT_METHOD); + CefRefPtr args = msg->GetArgumentList(); - // Remove from the list of existing browsers. - BrowserList::iterator bit = m_browser_list.begin(); - for (; bit != m_browser_list.end(); ++bit) + // Add the method name as first argument. + args->SetString(0, arguments[0]->GetStringValue()); + + // Add the arguments directly from V8 types. + for (size_t i = 1; i < arguments.size(); ++i) { - if ((*bit)->IsSame(browser)) + auto arg = arguments[i]; + if (arg->IsBool()) + { + args->SetBool(i, arg->GetBoolValue()); + } + else if (arg->IsInt()) { - m_browser_list.erase(bit); - break; + args->SetInt(i, arg->GetIntValue()); + } + else if (arg->IsDouble()) + { + args->SetDouble(i, arg->GetDoubleValue()); + } + else if (arg->IsString()) + { + args->SetString(i, arg->GetStringValue()); + } + else + { + // For other types, convert them to string + args->SetString(i, arg->GetStringValue()); } } - if (m_browser_list.empty()) - { - DEBUG_BROWSER_PROCESS("CefQuitMessageLoop"); - // All browser windows have closed. - // Quit the application message loop. - CefQuitMessageLoop(); - } + // Send the message to the main process + m_browser->GetMainFrame()->SendProcessMessage(PID_BROWSER, msg); + retval = CefV8Value::CreateBool(true); + + return true; } -#endif \ No newline at end of file + +//------------------------------------------------------------------------------ +// TODO Faire OnContextReleased ? +void RenderProcess::OnContextCreated(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr context) +{ + DEBUG_RENDER_PROCESS(browser->GetIdentifier()); + + // No handler yet, we need to create it first + m_handler = new GodotMethodHandler(browser); + + // Create global JavaScript objects and bind methods + CefRefPtr global = context->GetGlobal(); + + // Create a global Godot bridge object + CefRefPtr godotBridge = + CefV8Value::CreateObject(nullptr, nullptr); + + // Bind only the base callGodotMethod + godotBridge->SetValue( + CALL_GODOT_METHOD, + CefV8Value::CreateFunction(CALL_GODOT_METHOD, m_handler), + V8_PROPERTY_ATTRIBUTE_NONE); + + // Define the godot object + global->SetValue("godot", godotBridge, V8_PROPERTY_ATTRIBUTE_NONE); + + // Create the JavaScript Proxy that intercepts method calls + const char* proxySetup = R"( + const rawGodot = godot; + window.godot = new Proxy({}, { + get: function(target, prop) { + if (prop === 'callGodotMethod') { + return rawGodot.callGodotMethod; + } + return function(...args) { + return rawGodot.callGodotMethod(prop, ...args); + }; + } + }); + )"; + + // Install the Proxy + frame->ExecuteJavaScript(proxySetup, frame->GetURL(), 0); +} \ No newline at end of file diff --git a/addons/gdcef/render_process/src/render_process.hpp b/addons/gdcef/render_process/src/render_process.hpp index 423c72b..46c9653 100644 --- a/addons/gdcef/render_process/src/render_process.hpp +++ b/addons/gdcef/render_process/src/render_process.hpp @@ -63,94 +63,56 @@ #endif // ***************************************************************************** -//! \brief Entry point for the render process +//! \brief JavaScript Method Handler // ***************************************************************************** -class RenderProcess: public CefApp, - // public CefBrowserProcessHandler, - public CefRenderProcessHandler +class GodotMethodHandler: public CefV8Handler { public: - ~RenderProcess(); + GodotMethodHandler(CefRefPtr browser) : m_browser(browser) {} -private: // CefApp methods + bool Execute(const CefString& name, + CefRefPtr object, + const CefV8ValueList& arguments, + CefRefPtr& retval, + CefString& exception) override; - // ------------------------------------------------------------------------- - // virtual CefRefPtr - // GetBrowserProcessHandler() override - // { - // return this; - //} - - virtual CefRefPtr - GetRenderProcessHandler() override - { - return this; - } - -private: // CefBrowserProcessHandler methods - - // ------------------------------------------------------------------------- - // virtual void OnContextInitialized() override; - -private: // CefRenderProcessHandler methods - - // ------------------------------------------------------------------------- - virtual void OnContextCreated(CefRefPtr browser, - CefRefPtr frame, - CefRefPtr context) override; + IMPLEMENT_REFCOUNTING(GodotMethodHandler); private: - IMPLEMENT_REFCOUNTING(RenderProcess); + CefRefPtr m_browser; }; -#if 0 // ***************************************************************************** -//! \brief Browser process handler +//! \brief Entry point for the render process // ***************************************************************************** -class GDCefBrowser: public CefClient, - public CefLifeSpanHandler, - public CefDisplayHandler +class RenderProcess: public CefApp, public CefRenderProcessHandler { public: - ~GDCefBrowser(); - -private: // CefDisplayHandler methods - - // ------------------------------------------------------------------------- - virtual CefRefPtr GetDisplayHandler() override - { - return this; - } + IMPLEMENT_REFCOUNTING(RenderProcess); -private: // CefLifeSpanHandler methods +private: // CefApp methods // ------------------------------------------------------------------------- - virtual CefRefPtr GetLifeSpanHandler() override + virtual CefRefPtr + GetRenderProcessHandler() override { return this; } - // ------------------------------------------------------------------------- - virtual void OnAfterCreated(CefRefPtr browser) override; - - // ------------------------------------------------------------------------- - virtual void OnBeforeClose(CefRefPtr browser) override; - -private: +private: // CefRenderProcessHandler methods // ------------------------------------------------------------------------- - // Include the default reference counting implementation. - IMPLEMENT_REFCOUNTING(GDCefBrowser); + virtual void OnContextCreated(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr context) override; private: - using BrowserList = std::list>; - BrowserList m_browser_list; + CefRefPtr m_handler; }; -#endif #if !defined(_WIN32) # if defined(__clang__)