diff --git a/nui/include/nui/backend/rpc_hub.hpp b/nui/include/nui/backend/rpc_hub.hpp index 94427e9..85e07b3 100644 --- a/nui/include/nui/backend/rpc_hub.hpp +++ b/nui/include/nui/backend/rpc_hub.hpp @@ -99,10 +99,14 @@ namespace Nui template void registerFunction(std::string const& name, T&& func) const { - using namespace std::string_literals; // window is threadsafe window_->bind(name, Detail::FunctionWrapper::wrapFunction(std::forward(func))); } + void unregisterFunction(std::string const& name) const + { + // window is threadsafe + window_->unbind(name); + } /** * @brief Returns the attached window. @@ -216,13 +220,11 @@ namespace Nui private: void callRemoteImpl(std::string const& name, nlohmann::json const& json) const { - using namespace std::string_literals; // window is threadsafe. window_->eval(fmt::format(remoteCallScript, name, json.dump())); } void callRemoteImpl(std::string const& name) const { - using namespace std::string_literals; // window is threadsafe. window_->eval(fmt::format(remoteCallScript0Args, name)); } diff --git a/nui/include/nui/frontend/utility/val_conversion.hpp b/nui/include/nui/frontend/utility/val_conversion.hpp index 7dbe05b..24c1f6e 100644 --- a/nui/include/nui/frontend/utility/val_conversion.hpp +++ b/nui/include/nui/frontend/utility/val_conversion.hpp @@ -156,18 +156,10 @@ namespace Nui return result; } - template < - typename T, - class Bases = boost::describe::describe_bases, - class Members = boost::describe::describe_members, - class Enable = std::enable_if_t::value>> - void convertFromVal(Nui::val const& val, T& obj) + template > + requires(!std::is_union_v) + void convertFromValObjImpl(Nui::val const& val, T& obj) { - boost::mp11::mp_for_each([&](auto&& base) { - using type = typename std::decay_t::type; - convertFromVal(val, static_cast(obj)); - }); - boost::mp11::mp_for_each([&](auto&& memAccessor) { if (val.hasOwnProperty(memAccessor.name)) { @@ -185,6 +177,28 @@ namespace Nui } }); } + + template > + requires(!std::is_union_v && !boost::describe::has_describe_bases::value) + void convertFromVal(Nui::val const& val, T& obj) + { + convertFromValObjImpl(val, obj); + } + + template < + typename T, + class Bases = boost::describe::describe_bases, + class Members = boost::describe::describe_members> + requires(!std::is_union_v && boost::describe::has_describe_bases::value) + void convertFromVal(Nui::val const& val, T& obj) + { + boost::mp11::mp_for_each([&](auto&& base) { + using type = typename std::decay_t::type; + convertFromVal(val, static_cast(obj)); + }); + convertFromValObjImpl(val, obj); + } + template requires Fundamental void convertFromVal(Nui::val const& val, T& value) diff --git a/nui/include/nui/window.hpp b/nui/include/nui/window.hpp index 227e929..319ccb0 100644 --- a/nui/include/nui/window.hpp +++ b/nui/include/nui/window.hpp @@ -157,6 +157,13 @@ namespace Nui */ void bind(std::string const& name, std::function const& callback); + /** + * @brief Unbind a function from the web context. + * + * @param name The name of the function. + */ + void unbind(std::string const& name); + boost::asio::any_io_executor getExecutor() const; /** @@ -225,6 +232,9 @@ namespace Nui struct SchemeContext; #endif + private: + void runInJavascriptThread(std::function&& func); + private: struct Implementation; std::shared_ptr impl_; diff --git a/nui/src/nui/backend/window.cpp b/nui/src/nui/backend/window.cpp index 455f405..d2d7c46 100644 --- a/nui/src/nui/backend/window.cpp +++ b/nui/src/nui/backend/window.cpp @@ -24,6 +24,7 @@ #endif #include +#include #include #include #include @@ -81,7 +82,7 @@ namespace Nui { webview::webview view; std::vector cleanupFiles; - std::vector> callbacks; + std::unordered_map> callbacks; boost::asio::thread_pool pool; std::recursive_mutex viewGuard; int width; @@ -209,13 +210,7 @@ extern "C" { void uriSchemeDestroyNotify(void* data) { - auto* schemeContext = static_cast(data); - auto impl = schemeContext->impl.lock(); - if (!impl) - return; - - std::lock_guard lock{impl->schemeResponseRegistryGuard}; - impl->schemeResponseRegistry.erase(schemeContext->id); + // Happens when everything else is already dead. } } #endif @@ -246,7 +241,7 @@ namespace Nui impl_->view.install_message_hook([this](std::string const& msg) { std::scoped_lock lock{impl_->viewGuard}; const auto obj = nlohmann::json::parse(msg); - impl_->callbacks[obj["id"].get()](obj["args"]); + impl_->callbacks[obj["id"].get()](obj["args"]); return false; }); // TODO: SetCustomSchemeRegistrations @@ -421,14 +416,14 @@ namespace Nui //--------------------------------------------------------------------------------------------------------------------- void Window::bind(std::string const& name, std::function const& callback) { - auto bindImpl = [this, name, callback]() { + runInJavascriptThread([this, name, callback]() { std::scoped_lock lock{impl_->viewGuard}; - impl_->callbacks.push_back(callback); + impl_->callbacks[name] = callback; auto script = fmt::format( R"( (() => {{ const name = "{}"; - const id = {}; + const id = "{}"; globalThis.nui_rpc = (globalThis.nui_rpc || {{ frontend: {{}}, backend: {{}}, tempId: 0 }}); @@ -442,23 +437,45 @@ namespace Nui }})(); )", name, - impl_->callbacks.size() - 1); + name); impl_->view.init(script); impl_->view.eval(script); - }; + }); + } + //-------------------------------------------------------------------------------------------------------------------- + void Window::unbind(std::string const& name) + { + runInJavascriptThread([this, &name]() { + std::scoped_lock lock{impl_->viewGuard}; + auto script = fmt::format( + R"( + (() => {{ + const name = "{}"; + delete globalThis.nui_rpc.backend[name]; + }})(); + )", + name); + impl_->callbacks.erase(name); + impl_->view.init(script); + impl_->view.eval(script); + }); + } + //-------------------------------------------------------------------------------------------------------------------- + void Window::runInJavascriptThread(std::function&& func) + { std::scoped_lock lock{impl_->viewGuard}; #if defined(_WIN32) if (GetCurrentThreadId() == impl_->windowThreadId) - bindImpl(); + func(); else { - impl_->toProcessOnWindowThread.push_back(bindImpl); + impl_->toProcessOnWindowThread.push_back(std::move(func)); PostThreadMessage(impl_->windowThreadId, wakeUpMessage, 0, 0); } #else - bindImpl(); + func(); #endif } //--------------------------------------------------------------------------------------------------------------------