From aaf459621790e0e1f3de936258f3916f41610da4 Mon Sep 17 00:00:00 2001 From: Onek8 Date: Fri, 20 Feb 2026 23:40:15 -0800 Subject: [PATCH] Here comes he RunT! --- .../System/Linux/Sources/kinc/backend/funcs.h | 1 + .../Linux/Sources/kinc/backend/linuxunit.c | 3 + .../Linux/Sources/kinc/backend/window.c.h | 4 + .../Linux/Sources/kinc/backend/x11/window.c.h | 10 + .../Windows/Sources/kinc/backend/window.c.h | 5 + Kinc/Sources/kinc/window.h | 5 + README.md | 17 +- Sources/async_engine.cpp | 9 + Sources/async_engine.h | 199 ++ Sources/broadcast_queue.h | 75 + Sources/connection_pool.cpp | 181 ++ Sources/connection_pool.h | 48 + Sources/global_thread_pool.cpp | 4 + Sources/global_thread_pool.h | 25 + Sources/httprequest.cpp | 2387 ++++++++++++++ Sources/httprequest.h | 235 ++ Sources/lockfree_queue.h | 75 + Sources/main.cpp | 1350 +++++--- Sources/ring_buffer.h | 259 ++ Sources/socket_bridge.cpp | 947 ++++++ Sources/socket_bridge.h | 25 + Sources/socket_optimization.h | 86 + Sources/socket_v8_bindings.cpp | 311 ++ Sources/socket_v8_bindings.h | 20 + Sources/thread_pool.h | 79 + Sources/viewport_server.cpp | 580 ++++ Sources/viewport_server.h | 122 + Sources/websocket.cpp | 2865 +++++++++++++++++ Sources/websocket.h | 199 ++ Sources/websocket_bridge.cpp | 1249 +++++++ Sources/websocket_bridge.h | 160 + Sources/websocket_config.h | 43 + Sources/websocket_v8_bindings.cpp | 230 ++ Sources/websocket_v8_bindings.h | 12 + Sources/worker.cpp | 17 +- kfile.js | 87 +- 36 files changed, 11508 insertions(+), 416 deletions(-) create mode 100644 Sources/async_engine.cpp create mode 100644 Sources/async_engine.h create mode 100644 Sources/broadcast_queue.h create mode 100644 Sources/connection_pool.cpp create mode 100644 Sources/connection_pool.h create mode 100644 Sources/global_thread_pool.cpp create mode 100644 Sources/global_thread_pool.h create mode 100644 Sources/httprequest.cpp create mode 100644 Sources/httprequest.h create mode 100644 Sources/lockfree_queue.h create mode 100644 Sources/ring_buffer.h create mode 100644 Sources/socket_bridge.cpp create mode 100644 Sources/socket_bridge.h create mode 100644 Sources/socket_optimization.h create mode 100644 Sources/socket_v8_bindings.cpp create mode 100644 Sources/socket_v8_bindings.h create mode 100644 Sources/thread_pool.h create mode 100644 Sources/viewport_server.cpp create mode 100644 Sources/viewport_server.h create mode 100644 Sources/websocket.cpp create mode 100644 Sources/websocket.h create mode 100644 Sources/websocket_bridge.cpp create mode 100644 Sources/websocket_bridge.h create mode 100644 Sources/websocket_config.h create mode 100644 Sources/websocket_v8_bindings.cpp create mode 100644 Sources/websocket_v8_bindings.h diff --git a/Kinc/Backends/System/Linux/Sources/kinc/backend/funcs.h b/Kinc/Backends/System/Linux/Sources/kinc/backend/funcs.h index 96182f8..b9efce7 100644 --- a/Kinc/Backends/System/Linux/Sources/kinc/backend/funcs.h +++ b/Kinc/Backends/System/Linux/Sources/kinc/backend/funcs.h @@ -29,6 +29,7 @@ struct linux_procs { int (*window_display)(int window_index); void (*window_show)(int window_index); void (*window_hide)(int window_index); + void (*window_set_foreground)(int window_index); void (*window_set_title)(int window_index, const char *title); void (*window_change_mode)(int window_index, kinc_window_mode_t mode); kinc_window_mode_t (*window_get_mode)(int window_index); diff --git a/Kinc/Backends/System/Linux/Sources/kinc/backend/linuxunit.c b/Kinc/Backends/System/Linux/Sources/kinc/backend/linuxunit.c index e72969b..832dddc 100644 --- a/Kinc/Backends/System/Linux/Sources/kinc/backend/linuxunit.c +++ b/Kinc/Backends/System/Linux/Sources/kinc/backend/linuxunit.c @@ -59,6 +59,7 @@ void kinc_linux_init_procs() { procs.window_resize = kinc_wayland_window_resize; procs.window_show = kinc_wayland_window_show; procs.window_hide = kinc_wayland_window_hide; + procs.window_set_foreground = kinc_wayland_window_set_foreground; procs.count_windows = kinc_wayland_count_windows; procs.mouse_can_lock = kinc_wl_mouse_can_lock; @@ -111,6 +112,8 @@ void kinc_linux_init_procs() { procs.window_resize = kinc_x11_window_resize; procs.window_show = kinc_x11_window_show; procs.window_hide = kinc_x11_window_hide; + procs.window_set_foreground = kinc_x11_window_set_foreground; + procs.count_windows = kinc_x11_count_windows; procs.display_init = kinc_x11_display_init; diff --git a/Kinc/Backends/System/Linux/Sources/kinc/backend/window.c.h b/Kinc/Backends/System/Linux/Sources/kinc/backend/window.c.h index ff54ba7..4f2599c 100644 --- a/Kinc/Backends/System/Linux/Sources/kinc/backend/window.c.h +++ b/Kinc/Backends/System/Linux/Sources/kinc/backend/window.c.h @@ -80,6 +80,10 @@ void kinc_window_hide(int window_index) { procs.window_hide(window_index); } +void kinc_window_set_foreground(int window_index) { + procs.window_set_foreground(window_index); +} + void kinc_window_set_title(int window_index, const char *title) { procs.window_set_title(window_index, title); } diff --git a/Kinc/Backends/System/Linux/Sources/kinc/backend/x11/window.c.h b/Kinc/Backends/System/Linux/Sources/kinc/backend/x11/window.c.h index fb8ada0..c2b76b6 100644 --- a/Kinc/Backends/System/Linux/Sources/kinc/backend/x11/window.c.h +++ b/Kinc/Backends/System/Linux/Sources/kinc/backend/x11/window.c.h @@ -145,6 +145,16 @@ void kinc_x11_window_hide(int window_index) { xlib.XUnmapWindow(x11_ctx.display, window->window); } +void kinc_x11_window_set_foreground(int window_index) { + struct kinc_x11_window *window = &x11_ctx.windows[window_index]; + if (window->window == None) { + return; + } + xlib.XRaiseWindow(x11_ctx.display, window->window); + xlib.XSetInputFocus(x11_ctx.display, window->window, RevertToParent, CurrentTime); + xlib.XFlush(x11_ctx.display); +} + kinc_window_mode_t kinc_x11_window_get_mode(int window_index) { return x11_ctx.windows[window_index].mode; } diff --git a/Kinc/Backends/System/Windows/Sources/kinc/backend/window.c.h b/Kinc/Backends/System/Windows/Sources/kinc/backend/window.c.h index e50392e..c46cccc 100644 --- a/Kinc/Backends/System/Windows/Sources/kinc/backend/window.c.h +++ b/Kinc/Backends/System/Windows/Sources/kinc/backend/window.c.h @@ -392,6 +392,11 @@ void kinc_window_hide(int window_index) { UpdateWindow(windows[window_index].handle); } +void kinc_window_set_foreground(int window_index) { + SetForegroundWindow(windows[window_index].handle); + SetFocus(windows[window_index].handle); +} + void kinc_window_set_title(int window_index, const char *title) { wchar_t buffer[1024]; MultiByteToWideChar(CP_UTF8, 0, title, -1, buffer, 1024); diff --git a/Kinc/Sources/kinc/window.h b/Kinc/Sources/kinc/window.h index 3610f7b..ba863ee 100644 --- a/Kinc/Sources/kinc/window.h +++ b/Kinc/Sources/kinc/window.h @@ -139,6 +139,11 @@ KINC_FUNC void kinc_window_show(int window); /// KINC_FUNC void kinc_window_hide(int window); +/// +/// Set a window to the foreground. +/// +KINC_FUNC void kinc_window_set_foreground(int window); + /// /// Sets the title of a window. /// diff --git a/README.md b/README.md index cbcad28..47e0f58 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,20 @@ -# LNXCORE +# LNXRNT -LeenkxCore for Leenkx Full Stack SDK. +Leenkx RunT - Runtime for Leenkx Full Stack SDK. Based on [Krom](https://github.com/Kode/Krom). Powered by [Kinc](https://github.com/Kode/Kinc). ```bash -git clone --recursive https://dev.leenkx.com/LeenkxTeam/LNXCORE -cd LNXCORE +git clone --recursive https://dev.leenkx.com/LeenkxTeam/LNXRNT +cd LNXRNT ``` **Windows** ```bash # Unpack `v8\libraries\win32\release\v8_monolith.7z` using 7-Zip - Extract Here (exceeds 100MB) -Kinc/make -g direct3d11 -# Open generated Visual Studio project at `build\Krom.sln` +Kinc\make -g direct3d11 +# Open generated Visual Studio project at `build\RunT.sln` +# or use command line for solution file like C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe" "build\Leenkx-RunT.sln" /p:Configuration=Release /p:Platform=x64 /m # Build for x64 & release ``` @@ -21,12 +22,12 @@ Kinc/make -g direct3d11 ```bash Kinc/make -g opengl --compiler clang --compile cd Deployment -strip Krom +strip RunT ``` **macOS** ```bash Kinc/make -g metal -# Open generated Xcode project at `build/Krom.xcodeproj` +# Open generated Xcode project at `build/RunT.xcodeproj` # Build ``` diff --git a/Sources/async_engine.cpp b/Sources/async_engine.cpp new file mode 100644 index 0000000..c7011c2 --- /dev/null +++ b/Sources/async_engine.cpp @@ -0,0 +1,9 @@ +#include "async_engine.h" +#include + +namespace EngineManager { + +std::unique_ptr AsyncEngine::instance_; +std::once_flag AsyncEngine::initialized_; + +} \ No newline at end of file diff --git a/Sources/async_engine.h b/Sources/async_engine.h new file mode 100644 index 0000000..2ab7584 --- /dev/null +++ b/Sources/async_engine.h @@ -0,0 +1,199 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace EngineManager { + +// thread communication +template +class LockFreeQueue { +private: + struct Node { + std::atomic data{nullptr}; + std::atomic next{nullptr}; + }; + + std::atomic head_{nullptr}; + std::atomic tail_{nullptr}; + +public: + LockFreeQueue() { + Node* dummy = new Node; + head_.store(dummy); + tail_.store(dummy); + } + + ~LockFreeQueue() { + while (Node* const old_head = head_.load()) { + head_.store(old_head->next); + delete old_head->data.load(); + delete old_head; + } + } + + void push(T item) { + Node* new_node = new Node; + T* data = new T(std::move(item)); + new_node->data.store(data); + + Node* prev_tail = tail_.exchange(new_node); + prev_tail->next.store(new_node); + } + + bool try_pop(T& result) { + Node* head = head_.load(); + Node* next = head->next.load(); + + if (next == nullptr) return false; + + T* data = next->data.load(); + if (data == nullptr) return false; + + result = *data; + delete data; + head_.store(next); + delete head; + return true; + } + + bool empty() const { + Node* head = head_.load(); + return (head->next.load() == nullptr); + } +}; + +// thread pool stealing +class ThreadPool { +private: + std::vector workers_; + std::queue> tasks_; + std::mutex queue_mutex_; + std::condition_variable condition_; + std::atomic stop_{false}; + +public: + explicit ThreadPool(size_t num_threads = std::thread::hardware_concurrency()) { + for (size_t i = 0; i < num_threads; ++i) { + workers_.emplace_back([this] { + for (;;) { + std::function task; + + { + std::unique_lock lock(queue_mutex_); + condition_.wait(lock, [this] { return stop_ || !tasks_.empty(); }); + + if (stop_ && tasks_.empty()) return; + + task = std::move(tasks_.front()); + tasks_.pop(); + } + + task(); + } + }); + } + } + + template + auto enqueue(F&& f, Args&&... args) -> std::future::type> { + using return_type = typename std::invoke_result::type; + + auto task = std::make_shared>( + std::bind(std::forward(f), std::forward(args)...) + ); + + std::future res = task->get_future(); + + { + std::unique_lock lock(queue_mutex_); + if (stop_) { + throw std::runtime_error("ThreadPool: enqueue stopped"); + } + + tasks_.emplace([task](){ (*task)(); }); + } + + condition_.notify_one(); + return res; + } + + size_t pending_tasks() const { + std::lock_guard lock(const_cast(queue_mutex_)); + return tasks_.size(); + } + + ~ThreadPool() { + stop_ = true; + condition_.notify_all(); + + for (std::thread& worker : workers_) { + worker.join(); + } + } +}; + +class AsyncEngine { +private: + static std::unique_ptr instance_; + static std::once_flag initialized_; + + std::unique_ptr main_pool_; + std::unique_ptr io_pool_; + LockFreeQueue> event_queue_; + + AsyncEngine() { + unsigned int hw_threads = std::thread::hardware_concurrency(); + size_t main_threads = hw_threads > 4 ? hw_threads / 2 : 2; + size_t io_threads = hw_threads > 8 ? 4 : 2; + + main_pool_ = std::make_unique(main_threads); + io_pool_ = std::make_unique(io_threads); + } + +public: + static AsyncEngine& instance() { + std::call_once(initialized_, []() { + instance_ = std::unique_ptr(new AsyncEngine()); + }); + return *instance_; + } + + // main + template + auto execute(F&& f, Args&&... args) -> std::future::type> { + return main_pool_->enqueue(std::forward(f), std::forward(args)...); + } + + template + auto execute_io(F&& f, Args&&... args) -> std::future::type> { + return io_pool_->enqueue(std::forward(f), std::forward(args)...); + } + + void push_event(std::function event) { + event_queue_.push(std::move(event)); + } + + void process_events() { + std::function event; + while (event_queue_.try_pop(event)) { + event(); + } + } + + size_t pending_main_tasks() const { return main_pool_->pending_tasks(); } + size_t pending_io_tasks() const { return io_pool_->pending_tasks(); } + + static void shutdown() { + instance_.reset(); + } +}; + +} diff --git a/Sources/broadcast_queue.h b/Sources/broadcast_queue.h new file mode 100644 index 0000000..3023832 --- /dev/null +++ b/Sources/broadcast_queue.h @@ -0,0 +1,75 @@ +#pragma once +#include +#include +#include + +// message queue +struct BroadcastMessage { + std::vector frameData; + int excludeClientId; + + BroadcastMessage() = default; + BroadcastMessage(std::vector data, int excludeId) + : frameData(std::move(data)), excludeClientId(excludeId) {} +}; + +class LockFreeBroadcastQueue { +private: + static const size_t QUEUE_SIZE = 65536; // Must be power of 2 + static const size_t QUEUE_MASK = QUEUE_SIZE - 1; + + struct alignas(64) QueueSlot { + std::atomic ready{false}; + BroadcastMessage message; + }; + + alignas(64) std::atomic head{0}; + alignas(64) std::atomic tail{0}; + QueueSlot queue[QUEUE_SIZE]; + +public: + bool push(BroadcastMessage&& message) { + const size_t currentTail = tail.load(std::memory_order_relaxed); + const size_t nextTail = (currentTail + 1) & QUEUE_MASK; + + // queue full + if (nextTail == head.load(std::memory_order_acquire)) { + return false; + } + + QueueSlot& slot = queue[currentTail]; + slot.message = std::move(message); + slot.ready.store(true, std::memory_order_release); + tail.store(nextTail, std::memory_order_release); + + return true; + } + + bool pop(BroadcastMessage& message) { + const size_t currentHead = head.load(std::memory_order_relaxed); + + // empty + if (currentHead == tail.load(std::memory_order_acquire)) { + return false; + } + + QueueSlot& slot = queue[currentHead]; + if (!slot.ready.load(std::memory_order_acquire)) { + return false; + } + + message = std::move(slot.message); + slot.ready.store(false, std::memory_order_release); + head.store((currentHead + 1) & QUEUE_MASK, std::memory_order_release); + + return true; + } + + size_t size() const { + return (tail.load(std::memory_order_relaxed) - head.load(std::memory_order_relaxed)) & QUEUE_MASK; + } + + bool empty() const { + return head.load(std::memory_order_relaxed) == tail.load(std::memory_order_relaxed); + } +}; diff --git a/Sources/connection_pool.cpp b/Sources/connection_pool.cpp new file mode 100644 index 0000000..8a6a3c1 --- /dev/null +++ b/Sources/connection_pool.cpp @@ -0,0 +1,181 @@ +#include "connection_pool.h" +#include "socket_optimization.h" +#include + +#ifdef _WIN32 +#include +#pragma comment(lib, "ws2_32.lib") +#else +#include +#include +#include +#include +#include +#endif + +// global +ConnectionPool g_connection_pool; + +ConnectionPool::~ConnectionPool() { + std::lock_guard lock(pool_mutex_); + + for (auto& host_pair : pool_) { + while (!host_pair.second.empty()) { + auto conn = host_pair.second.front(); + host_pair.second.pop(); +#ifdef _WIN32 + closesocket(conn.socket); +#else + close(conn.socket); +#endif + } + } +} + +socket_t ConnectionPool::acquire_connection(const std::string& host, int port, bool is_ssl) { + std::string key = make_key(host, port, is_ssl); + + { + std::lock_guard lock(pool_mutex_); + auto it = pool_.find(key); + if (it != pool_.end() && !it->second.empty()) { + auto conn = it->second.front(); + it->second.pop(); + + if (is_socket_alive(conn.socket)) { + kinc_log(KINC_LOG_LEVEL_INFO, "Reusing pooled connection for %s:%d", host.c_str(), port); + return conn.socket; + } else { +#ifdef _WIN32 + closesocket(conn.socket); +#else + close(conn.socket); +#endif + } + } + } + + socket_t sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == INVALID_SOCKET) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to create socket for %s:%d", host.c_str(), port); + return INVALID_SOCKET; + } + + SocketOptimization::optimizeHttp(static_cast(sock)); + + struct sockaddr_in server_addr; + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(port); + + struct hostent* host_entry = gethostbyname(host.c_str()); + if (!host_entry) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to resolve hostname %s", host.c_str()); +#ifdef _WIN32 + closesocket(sock); +#else + close(sock); +#endif + return INVALID_SOCKET; + } + + memcpy(&server_addr.sin_addr, host_entry->h_addr, host_entry->h_length); + + if (::connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to connect to %s:%d", host.c_str(), port); +#ifdef _WIN32 + closesocket(sock); +#else + close(sock); +#endif + return INVALID_SOCKET; + } + + kinc_log(KINC_LOG_LEVEL_INFO, "Created new connection for %s:%d", host.c_str(), port); + return sock; +} + +void ConnectionPool::release_connection(const std::string& host, int port, socket_t socket, bool is_ssl) { + if (socket == INVALID_SOCKET) return; + + std::string key = make_key(host, port, is_ssl); + + std::lock_guard lock(pool_mutex_); + auto& queue = pool_[key]; + + if (queue.size() >= MAX_CONNECTIONS_PER_HOST) { +#ifdef _WIN32 + closesocket(socket); +#else + close(socket); +#endif + return; + } + + PooledConnection conn; + conn.socket = socket; + conn.last_used = std::chrono::steady_clock::now(); + conn.is_ssl = is_ssl; + + queue.push(conn); + kinc_log(KINC_LOG_LEVEL_INFO, "Pooled connection for %s:%d", host.c_str(), port); +} + +void ConnectionPool::cleanup_expired() { + std::lock_guard lock(pool_mutex_); + auto now = std::chrono::steady_clock::now(); + + for (auto& host_pair : pool_) { + auto& queue = host_pair.second; + std::queue new_queue; + + while (!queue.empty()) { + auto conn = queue.front(); + queue.pop(); + + auto age = std::chrono::duration_cast(now - conn.last_used); + if (age.count() < CONNECTION_TIMEOUT_SECONDS && is_socket_alive(conn.socket)) { + new_queue.push(conn); + } else { +#ifdef _WIN32 + closesocket(conn.socket); +#else + close(conn.socket); +#endif + } + } + + queue = std::move(new_queue); + } +} + +size_t ConnectionPool::get_pool_size() const { + std::lock_guard lock(pool_mutex_); + size_t total = 0; + for (const auto& host_pair : pool_) { + total += host_pair.second.size(); + } + return total; +} + +std::string ConnectionPool::make_key(const std::string& host, int port, bool is_ssl) const { + return host + ":" + std::to_string(port) + (is_ssl ? ":ssl" : ""); +} + +bool ConnectionPool::is_socket_alive(socket_t socket) const { + char test_byte; + int result = ::recv(socket, &test_byte, 1, MSG_PEEK); + +#ifdef _WIN32 + if (result == SOCKET_ERROR) { + int error = WSAGetLastError(); + return (error == WSAEWOULDBLOCK); + } +#else + if (result < 0) { + return (errno == EWOULDBLOCK || errno == EAGAIN); + } +#endif + + return true; +} diff --git a/Sources/connection_pool.h b/Sources/connection_pool.h new file mode 100644 index 0000000..8054f6a --- /dev/null +++ b/Sources/connection_pool.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +typedef SOCKET socket_t; +#else +#include +typedef int socket_t; +#endif + +class ConnectionPool { +private: + struct PooledConnection { + socket_t socket; + std::chrono::steady_clock::time_point last_used; + bool is_ssl; + }; + + std::unordered_map> pool_; + mutable std::mutex pool_mutex_; + static constexpr int MAX_CONNECTIONS_PER_HOST = 4; + static constexpr int CONNECTION_TIMEOUT_SECONDS = 30; + +public: + ConnectionPool() = default; + ~ConnectionPool(); + + socket_t acquire_connection(const std::string& host, int port, bool is_ssl = false); + + // returns connection to pool for reuse + void release_connection(const std::string& host, int port, socket_t socket, bool is_ssl = false); + + void cleanup_expired(); + + size_t get_pool_size() const; + +private: + std::string make_key(const std::string& host, int port, bool is_ssl) const; + bool is_socket_alive(socket_t socket) const; +}; + +extern ConnectionPool g_connection_pool; diff --git a/Sources/global_thread_pool.cpp b/Sources/global_thread_pool.cpp new file mode 100644 index 0000000..41ad9a0 --- /dev/null +++ b/Sources/global_thread_pool.cpp @@ -0,0 +1,4 @@ +#include "global_thread_pool.h" + +std::unique_ptr GlobalThreadPool::instance_; +std::once_flag GlobalThreadPool::initialized_; diff --git a/Sources/global_thread_pool.h b/Sources/global_thread_pool.h new file mode 100644 index 0000000..99cdee3 --- /dev/null +++ b/Sources/global_thread_pool.h @@ -0,0 +1,25 @@ +#pragma once + +#include "thread_pool.h" +#include +#include + +class GlobalThreadPool { +private: + static std::unique_ptr instance_; + static std::once_flag initialized_; + +public: + static ThreadPool& getInstance() { + std::call_once(initialized_, []() { + unsigned int hw_threads = std::thread::hardware_concurrency(); + size_t num_threads = hw_threads > 4 ? hw_threads / 2 : 2; + instance_ = std::make_unique(num_threads); + }); + return *instance_; + } + + static void shutdown() { + instance_.reset(); + } +}; diff --git a/Sources/httprequest.cpp b/Sources/httprequest.cpp new file mode 100644 index 0000000..250b4bc --- /dev/null +++ b/Sources/httprequest.cpp @@ -0,0 +1,2387 @@ +#ifdef WITH_NETWORKING + +#include "httprequest.h" +#include "connection_pool.h" +#include "global_thread_pool.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#define NOMINMAX +#include +#include +#include +#pragma comment(lib, "ws2_32.lib") +#else +#include +#include +#include +#include +#include +#include +#include +#include +#define SOCKET int +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#define closesocket close +#endif + +#ifdef WITH_SSL +#ifdef _WIN32 +#define SECURITY_WIN32 +#include +#include +#include +#include +#pragma comment(lib, "crypt32.lib") +#pragma comment(lib, "secur32.lib") +#else +#include +#include +#include +#include +#include +#include +#endif +#endif + +using namespace v8; + +namespace HttpRequestWrapper { + // timeout helper functions for SSL/socket operations + static int receiveWithTimeout(SOCKET socket, char* buffer, int buffer_size, int timeout_ms) { + // non-blocking +#ifdef _WIN32 + u_long mode = 1; + if (ioctlsocket(socket, FIONBIO, &mode) != 0) { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to set socket to non-blocking mode: %d", WSAGetLastError()); + #endif + return -1; + } +#else + int flags = fcntl(socket, F_GETFL, 0); + if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) < 0) { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to set socket to non-blocking mode: %s", strerror(errno)); + #endif + return -1; + } +#endif + + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(socket, &read_fds); + + struct timeval timeout; + timeout.tv_sec = timeout_ms / 1000; + timeout.tv_usec = (timeout_ms % 1000) * 1000; + +#ifdef _WIN32 + int select_result = select(0, &read_fds, NULL, NULL, &timeout); +#else + int select_result = select(socket + 1, &read_fds, NULL, NULL, &timeout); +#endif + + if (select_result > 0 && FD_ISSET(socket, &read_fds)) { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Socket has data available, attempting recv..."); + #endif + int received = ::recv(socket, buffer, buffer_size, 0); + + // restore blocking mode +#ifdef _WIN32 + mode = 0; + ioctlsocket(socket, FIONBIO, &mode); +#else + // restore original flags + fcntl(socket, F_SETFL, flags); +#endif + + if (received > 0) { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Received %d bytes with timeout handling", received); + #endif + return received; + } else if (received == 0) { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Connection closed gracefully by server"); + #endif + return 0; + } else { +#ifdef _WIN32 + int error = WSAGetLastError(); + if (error == WSAEWOULDBLOCK) { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "No data available (WSAEWOULDBLOCK)"); + #endif + return 0; + } else { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_ERROR, "Receive error: %d", error); + #endif + return -1; + } +#else + if (errno == EAGAIN || errno == EWOULDBLOCK) { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "No data available (EAGAIN/EWOULDBLOCK)"); + #endif + return 0; + } else { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_ERROR, "Receive error: %s", strerror(errno)); + #endif + return -1; + } +#endif + } + } else if (select_result == 0) { + // timeouts are expected + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Socket receive timeout after %d ms - no data available", timeout_ms); + #endif + + // restore +#ifdef _WIN32 + mode = 0; + ioctlsocket(socket, FIONBIO, &mode); +#else + fcntl(socket, F_SETFL, flags); +#endif + return 0; + } else { +#ifdef _WIN32 + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_ERROR, "Select error: %d", WSAGetLastError()); + #endif +#else + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_ERROR, "Select error: %s", strerror(errno)); + #endif +#endif + +#ifdef _WIN32 + mode = 0; + ioctlsocket(socket, FIONBIO, &mode); +#else + fcntl(socket, F_SETFL, flags); +#endif + return -1; + } + } + + static std::unordered_map> active_requests; + static std::atomic next_request_id{1}; + static std::atomic initialized{false}; + + void initialize() { + if (!initialized.exchange(true)) { +#ifdef _WIN32 + WSADATA wsaData; + int result = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (result != 0) { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_ERROR, "WSAStartup failed: %d", result); + #endif + } else { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Initializing native HTTP request support"); + #endif + } +#else + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Initializing native HTTP request support"); + #endif +#endif + } + } + + void cleanup() { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Starting HTTP request cleanup, waiting for active operations..."); + #endif + + bool has_active_operations = true; + int wait_count = 0; + const int max_wait_ms = 5000; + + while (has_active_operations && wait_count < max_wait_ms) { + has_active_operations = false; + + for (const auto& pair : active_requests) { + if (pair.second && pair.second->active_operation_.load()) { + has_active_operations = true; + break; + } + } + + if (has_active_operations) { + #ifdef _WIN32 + Sleep(10); + #else + usleep(10000); + #endif + wait_count += 10; + } + } + + if (wait_count >= max_wait_ms) { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_WARNING, "HTTP request cleanup timed out after %d ms, forcing cleanup", max_wait_ms); + #endif + } else { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "All active operations completed after %d ms", wait_count); + #endif + } + + active_requests.clear(); + if (initialized.exchange(false)) { +#ifdef _WIN32 + WSACleanup(); +#endif + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "HTTP request cleanup complete"); + #endif + } + } + + void processEvents() { + // called from main thread + for (auto& pair : active_requests) { + if (pair.second) { + pair.second->processEvents(); + } + } + } + + int createHttpRequest(v8::Isolate* isolate) { + int id = next_request_id++; + auto client = std::make_unique(isolate, getGlobalContext()); + active_requests[id] = std::move(client); + return id; + } + + HttpRequestClient::HttpRequestClient(Isolate* isolate, Global* global_context) + : isolate_(isolate), global_context_(global_context), ready_state_(UNSENT), + response_type_(DEFAULT), async_(true), timeout_(0), with_credentials_(false), + done_event_fired_(false), active_operation_(false) + #ifdef WITH_SSL + #ifdef _WIN32 + , ssl_cred_handle_(nullptr), ssl_context_handle_(nullptr), ssl_context_initialized_(false), ssl_socket_(-1) + #else + , ssl_ctx_(nullptr), ssl_(nullptr), ssl_initialized_(false) + #endif + #endif + { + #ifdef WITH_SSL + #ifdef _WIN32 + // SSL handles to zero + memset(&ssl_cred_handle_, 0, sizeof(ssl_cred_handle_)); + memset(&ssl_context_handle_, 0, sizeof(ssl_context_handle_)); + #endif + #endif + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Creating HTTP request client"); + #endif + } + + HttpRequestClient::~HttpRequestClient() { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Destroying HTTP request client"); + #endif + #ifdef WITH_SSL + cleanupSSL(); + #endif + } + + void HttpRequestClient::open(const std::string& method, const std::string& url, bool async) { + std::lock_guard lock(state_mutex_); + + method_ = method; + url_ = url; + async_ = async; + + response_ = HttpResponse(); + + setReadyState(OPENED); + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "HTTP request opened: %s %s", method.c_str(), url.c_str()); + #endif + } + + void HttpRequestClient::setRequestHeader(const std::string& header, const std::string& value) { + std::lock_guard lock(state_mutex_); + + if (ready_state_ != OPENED) { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_ERROR, "Cannot set request header: request not opened"); + #endif + return; + } + + request_headers_[header] = value; + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Set request header: %s = %s", header.c_str(), value.c_str()); + #endif + } + + void HttpRequestClient::send(const std::string& data) { + std::lock_guard lock(state_mutex_); + + if (ready_state_ != OPENED) { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_ERROR, "Cannot send: request not opened"); + #endif + return; + } + + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Sending HTTP request with data length: %zu", data.length()); + #endif + + request_data_ = data; + + if (async_) { + // use thread pool + GlobalThreadPool::getInstance().enqueue([this]() { + this->performRequest(); + }); + } else { + // synchronously + performRequest(); + } + } + + void HttpRequestClient::abort() { + std::lock_guard lock(state_mutex_); + + if (ready_state_ == DONE) { + return; + } + + setReadyState(DONE); + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "HTTP request aborted"); + #endif + + if (!on_abort_.IsEmpty()) { + callCallback(on_abort_, 0, nullptr); + } + } + + std::string HttpRequestClient::getAllResponseHeaders() { + std::lock_guard lock(state_mutex_); + + std::ostringstream headers; + for (const auto& header : response_.headers) { + headers << header.first << ": " << header.second << "\r\n"; + } + return headers.str(); + } + + std::string HttpRequestClient::getResponseHeader(const std::string& header) { + std::lock_guard lock(state_mutex_); + + std::string lower_header = header; + std::transform(lower_header.begin(), lower_header.end(), lower_header.begin(), ::tolower); + + for (const auto& h : response_.headers) { + std::string lower_h = h.first; + std::transform(lower_h.begin(), lower_h.end(), lower_h.begin(), ::tolower); + if (lower_h == lower_header) { + return h.second; + } + } + return ""; + } + + void HttpRequestClient::setOnReadyStateChange(Local callback) { + on_ready_state_change_.Reset(isolate_, callback); + } + + void HttpRequestClient::setOnLoad(Local callback) { + on_load_.Reset(isolate_, callback); + } + + void HttpRequestClient::setOnError(Local callback) { + on_error_.Reset(isolate_, callback); + } + + void HttpRequestClient::setOnTimeout(Local callback) { + on_timeout_.Reset(isolate_, callback); + } + + void HttpRequestClient::setOnAbort(Local callback) { + on_abort_.Reset(isolate_, callback); + } + + void HttpRequestClient::setOnProgress(Local callback) { + on_progress_.Reset(isolate_, callback); + } + + void HttpRequestClient::setOnLoadStart(Local callback) { + on_load_start_.Reset(isolate_, callback); + } + + void HttpRequestClient::setOnLoadEnd(Local callback) { + on_load_end_.Reset(isolate_, callback); + } + + void HttpRequestClient::performRequest() { + active_operation_ = true; + + handleLoadStart(); + + try { + std::string host, path; + int port; + bool is_https; + + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "HTTP DEBUG: Starting request to URL: %s", url_.c_str()); + #endif + + if (!parseUrl(url_, host, port, path, is_https)) { + response_.error = "Invalid URL"; + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_ERROR, "HTTP DEBUG: Failed to parse URL: %s", url_.c_str()); + #endif + setReadyState(DONE); + handleError(response_.error); + active_operation_ = false; + return; + } + + // check if its a local file request + if (port == 0) { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Loading local file: %s", path.c_str()); + #endif + loadLocalFile(path); + active_operation_ = false; + return; + } + + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Parsed URL - host: %s, port: %d, path: %s, https: %s", + host.c_str(), port, path.c_str(), is_https ? "true" : "false"); + #endif + + #ifdef WITH_SSL + if (is_https) { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Using HTTPS/SSL for request"); + #endif + } + #else + if (is_https) { + response_.error = "HTTPS not supported - build with WITH_SSL=1"; + setReadyState(DONE); + handleError(response_.error); + active_operation_ = false; + return; + } + #endif + + // use connection pool + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Acquiring connection for HTTP request"); + #endif + SOCKET sock = g_connection_pool.acquire_connection(host, port, is_https); + if (sock == INVALID_SOCKET) { + response_.error = "Failed to acquire connection"; + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_ERROR, "Connection acquisition failed"); + #endif + setReadyState(DONE); + handleError(response_.error); + active_operation_ = false; + return; + } + + // set timeout when specified + if (timeout_ > 0) { +#ifdef _WIN32 + DWORD timeout_ms = timeout_; + ::setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout_ms, sizeof(timeout_ms)); + ::setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout_ms, sizeof(timeout_ms)); +#else + struct timeval timeout; + timeout.tv_sec = timeout_ / 1000; + timeout.tv_usec = (timeout_ % 1000) * 1000; + ::setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + ::setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); +#endif + } + kinc_log(KINC_LOG_LEVEL_INFO, "Connected successfully to %s:%d", host.c_str(), port); + + #ifdef WITH_SSL + if (is_https) { + kinc_log(KINC_LOG_LEVEL_INFO, "Performing SSL handshake"); + ssl_socket_ = static_cast(sock); + if (!performSSLHandshake(static_cast(sock), host)) { + response_.error = "SSL handshake failed"; + closesocket(sock); + setReadyState(DONE); + handleError(response_.error); + active_operation_ = false; + return; + } + kinc_log(KINC_LOG_LEVEL_INFO, "SSL handshake completed successfully"); + } + #endif + + std::string request_data = formatHttpRequest(method_, path, host, request_data_); + kinc_log(KINC_LOG_LEVEL_INFO, "Sending HTTP request (%zu bytes):", request_data.length()); + kinc_log(KINC_LOG_LEVEL_INFO, "Request preview: %.200s", request_data.c_str()); + + int bytes_sent = 0; + #ifdef WITH_SSL + if (is_https) { + bytes_sent = sslWrite(request_data.c_str(), static_cast(request_data.length())); + } else { + bytes_sent = ::send(sock, request_data.c_str(), static_cast(request_data.length()), 0); + } + #else + bytes_sent = ::send(sock, request_data.c_str(), static_cast(request_data.length()), 0); + #endif + + if (bytes_sent == SOCKET_ERROR || bytes_sent == 0) { + response_.error = "Failed to send request"; + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to send HTTP request"); + setReadyState(DONE); + handleError(response_.error); + active_operation_ = false; + return; + } + + kinc_log(KINC_LOG_LEVEL_INFO, "HTTP request sent successfully"); + + char buffer[4096]; + int bytes_received; + std::string response_data; + int total_bytes = 0; + + kinc_log(KINC_LOG_LEVEL_INFO, "Starting to receive HTTP response"); + while (true) { + #ifdef WITH_SSL + if (is_https) { + kinc_log(KINC_LOG_LEVEL_INFO, "Calling sslRead for %zu bytes", sizeof(buffer) - 1); + bytes_received = sslRead(buffer, sizeof(buffer) - 1); + kinc_log(KINC_LOG_LEVEL_INFO, "sslRead returned %d bytes", bytes_received); + } else { + bytes_received = ::recv(sock, buffer, sizeof(buffer) - 1, 0); + } + #else + bytes_received = ::recv(sock, buffer, sizeof(buffer) - 1, 0); + #endif + + if (bytes_received <= 0) { + kinc_log(KINC_LOG_LEVEL_INFO, "Breaking from response reading loop, bytes_received: %d", bytes_received); + break; + } + + buffer[bytes_received] = '\0'; + response_data += buffer; + total_bytes += bytes_received; + kinc_log(KINC_LOG_LEVEL_INFO, "Received %d bytes (total: %d)", bytes_received, total_bytes); + } + kinc_log(KINC_LOG_LEVEL_INFO, "Finished receiving data, total bytes: %d", total_bytes); + + #ifdef WITH_SSL + if (is_https) { + cleanupSSL(); + } + #endif + + // return connection to pool for reuse instead of closing + g_connection_pool.release_connection(host, port, sock, is_https); + + if (bytes_received == SOCKET_ERROR) { + response_.error = "Failed to receive response"; + setReadyState(DONE); + handleError(response_.error); + active_operation_ = false; + return; + } + + kinc_log(KINC_LOG_LEVEL_INFO, "Raw HTTP response data (%zu bytes):", response_data.length()); + kinc_log(KINC_LOG_LEVEL_INFO, "Response preview: %.200s", response_data.c_str()); + + parseHttpResponse(response_data); + + kinc_log(KINC_LOG_LEVEL_INFO, "Parsed response - status: %d, body length: %zu", response_.status, response_.responseText.length()); + kinc_log(KINC_LOG_LEVEL_INFO, "Response body preview: %.200s", response_.responseText.c_str()); + + setReadyState(HEADERS_RECEIVED); + + setReadyState(DONE); + + handleLoad(); + + handleLoadEnd(); + + active_operation_ = false; + + } catch (const std::exception& e) { + response_.error = e.what(); + setReadyState(DONE); + handleError(response_.error); + handleLoadEnd(); + active_operation_ = false; + } + } + + void HttpRequestClient::setReadyState(ReadyState state) { + if (ready_state_ != state) { + ready_state_ = state; + handleReadyStateChange(); + } + } + + // event handle is called from the background thread + void HttpRequestClient::handleReadyStateChange() { + std::lock_guard lock(event_queue_mutex_); + event_queue_.push(Event(HTTP_READY_STATE_CHANGE)); + } + + void HttpRequestClient::handleLoad() { + std::lock_guard lock(event_queue_mutex_); + event_queue_.push(Event(HTTP_LOAD)); + } + + void HttpRequestClient::handleError(const std::string& error) { + std::lock_guard lock(event_queue_mutex_); + event_queue_.push(Event(HTTP_ERROR, error)); + } + + void HttpRequestClient::handleTimeout() { + std::lock_guard lock(event_queue_mutex_); + event_queue_.push(Event(HTTP_TIMEOUT)); + } + + void HttpRequestClient::handleAbort() { + std::lock_guard lock(event_queue_mutex_); + event_queue_.push(Event(HTTP_ABORT)); + } + + void HttpRequestClient::handleProgress() { + std::lock_guard lock(event_queue_mutex_); + event_queue_.push(Event(HTTP_PROGRESS)); + } + + void HttpRequestClient::handleLoadStart() { + std::lock_guard lock(event_queue_mutex_); + event_queue_.push(Event(HTTP_LOAD_START)); + } + + void HttpRequestClient::handleLoadEnd() { + std::lock_guard lock(event_queue_mutex_); + event_queue_.push(Event(HTTP_LOAD_END)); + } + + // event called from main thread + void HttpRequestClient::processReadyStateChangeEvent() { + if (ready_state_ == DONE && done_event_fired_) { + kinc_log(KINC_LOG_LEVEL_INFO, "Skipping duplicate DONE ready state change event"); + return; + } + if (ready_state_ == DONE) { + done_event_fired_ = true; + } + + kinc_log(KINC_LOG_LEVEL_INFO, "Processing HTTP ready state change event, ready state: %d", ready_state_); + if (!on_ready_state_change_.IsEmpty()) { + callCallback(on_ready_state_change_, 0, nullptr); + } else { + kinc_log(KINC_LOG_LEVEL_INFO, "No ready state change callback set for HTTP request"); + } + } + + void HttpRequestClient::processLoadEvent() { + kinc_log(KINC_LOG_LEVEL_INFO, "Processing HTTP load event"); + if (!on_load_.IsEmpty()) { + callCallback(on_load_, 0, nullptr); + } else { + kinc_log(KINC_LOG_LEVEL_INFO, "No load callback set for HTTP request"); + } + } + + void HttpRequestClient::processErrorEvent(const std::string& error) { + kinc_log(KINC_LOG_LEVEL_INFO, "Processing HTTP error event: %s", error.c_str()); + if (!on_error_.IsEmpty()) { + Locker locker{isolate_}; + + Isolate::Scope isolate_scope(isolate_); + HandleScope handle_scope(isolate_); + Local context = global_context_->Get(isolate_); + Context::Scope context_scope(context); + + Local event = Object::New(isolate_); + event->Set(context, String::NewFromUtf8(isolate_, "message").ToLocalChecked(), + String::NewFromUtf8(isolate_, error.c_str()).ToLocalChecked()); + + Local argv[1] = { event }; + callCallback(on_error_, 1, argv); + } else { + kinc_log(KINC_LOG_LEVEL_INFO, "No error callback set for HTTP request"); + } + } + + void HttpRequestClient::processTimeoutEvent() { + if (!on_timeout_.IsEmpty()) { + callCallback(on_timeout_, 0, nullptr); + } + } + + void HttpRequestClient::processAbortEvent() { + if (!on_abort_.IsEmpty()) { + callCallback(on_abort_, 0, nullptr); + } + } + + void HttpRequestClient::processProgressEvent() { + if (!on_progress_.IsEmpty()) { + callCallback(on_progress_, 0, nullptr); + } + } + + void HttpRequestClient::processLoadStartEvent() { + if (!on_load_start_.IsEmpty()) { + callCallback(on_load_start_, 0, nullptr); + } + } + + void HttpRequestClient::processLoadEndEvent() { + if (!on_load_end_.IsEmpty()) { + callCallback(on_load_end_, 0, nullptr); + } + } + + void HttpRequestClient::processEvents() { + std::lock_guard lock(event_queue_mutex_); + while (!event_queue_.empty()) { + Event event = event_queue_.front(); + event_queue_.pop(); + + switch (event.type) { + case HTTP_READY_STATE_CHANGE: + processReadyStateChangeEvent(); + break; + case HTTP_LOAD: + processLoadEvent(); + break; + case HTTP_ERROR: + processErrorEvent(event.data); + break; + case HTTP_TIMEOUT: + processTimeoutEvent(); + break; + case HTTP_ABORT: + processAbortEvent(); + break; + case HTTP_PROGRESS: + processProgressEvent(); + break; + case HTTP_LOAD_START: + processLoadStartEvent(); + break; + case HTTP_LOAD_END: + processLoadEndEvent(); + break; + } + } + } + + void HttpRequestClient::callCallback(Global& callback, int argc, Local argv[]) { + if (callback.IsEmpty()) return; + + Locker locker{isolate_}; + + Isolate::Scope isolate_scope(isolate_); + HandleScope handle_scope(isolate_); + + Local context = global_context_->Get(isolate_); + Context::Scope context_scope(context); + + TryCatch try_catch(isolate_); + + Local func = callback.Get(isolate_); + Local result; + if (func->Call(context, context->Global(), argc, argv).ToLocal(&result)) { + // Success + } else { + if (try_catch.HasCaught()) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Error in HTTP request callback"); + Local exception = try_catch.Exception(); + String::Utf8Value exception_str(isolate_, exception); + kinc_log(KINC_LOG_LEVEL_ERROR, "Exception details: %s", *exception_str); + } + } + } + + bool HttpRequestClient::parseUrl(const std::string& url, std::string& host, int& port, std::string& path, bool& is_https) { + if (url.find("://") == std::string::npos) { + kinc_log(KINC_LOG_LEVEL_INFO, "Detected local file path: %s", url.c_str()); + host = "localhost"; + port = 0; + path = url; + is_https = false; + return true; + } + + if (url.substr(0, 7) == "file://") { + kinc_log(KINC_LOG_LEVEL_INFO, "Detected file:// URL: %s", url.c_str()); + host = "localhost"; + port = 0; // Special port to indicate local file + path = url.substr(7); // Remove "file://" prefix + is_https = false; + return true; + } + + std::regex url_regex(R"(^(https?):\/\/([^\/\s:]+)(?::(\d+))?(\/[^\s]*)?(?:\?[^\s]*)?(?:#[^\s]*)?$)"); + std::smatch matches; + + kinc_log(KINC_LOG_LEVEL_INFO, "Parsing HTTP URL: %s", url.c_str()); + + if (!std::regex_match(url, matches, url_regex)) { + // fallback parsing for simpler URLs + std::regex simple_regex(R"(^(https?):\/\/([^\/\s:]+)(?::(\d+))?(?:\/.*)?$)"); + if (!std::regex_match(url, matches, simple_regex)) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to parse URL with both regex patterns: %s", url.c_str()); + return false; + } + } + + std::string scheme = matches[1].str(); + host = matches[2].str(); + std::string port_str = matches[3].str(); + path = matches[4].str(); + + if (path.empty()) { + path = "/"; + } + + // remove query string and fragment for HTTP request path + size_t query_pos = path.find('?'); + if (query_pos != std::string::npos) { + path = path.substr(0, query_pos); + } + + size_t fragment_pos = path.find('#'); + if (fragment_pos != std::string::npos) { + path = path.substr(0, fragment_pos); + } + + if (path.empty()) { + path = "/"; + } + + is_https = (scheme == "https"); + + if (!port_str.empty()) { + try { + port = std::stoi(port_str); + } catch (const std::exception&) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Invalid port number: %s", port_str.c_str()); + port = is_https ? 443 : 80; + } + } else { + port = is_https ? 443 : 80; + } + + kinc_log(KINC_LOG_LEVEL_INFO, "URL parsed successfully - scheme: %s, host: %s, port: %d, path: %s", + scheme.c_str(), host.c_str(), port, path.c_str()); + + return true; + } + + std::string HttpRequestClient::formatHttpRequest(const std::string& method, const std::string& path, const std::string& host, const std::string& data) { + std::ostringstream request; + + request << method << " " << path << " HTTP/1.1\r\n"; + request << "Host: " << host << "\r\n"; + request << "Connection: close\r\n"; + request << "User-Agent: LNXCORE/1.0\r\n"; + request << "Accept: */*\r\n"; + request << "Accept-Encoding: identity\r\n"; + + for (const auto& header : request_headers_) { + request << header.first << ": " << header.second << "\r\n"; + } + + if (!data.empty()) { + request << "Content-Length: " << data.length() << "\r\n"; + + if (request_headers_.find("Content-Type") == request_headers_.end()) { + request << "Content-Type: application/x-www-form-urlencoded\r\n"; + } + } + + request << "\r\n"; + + if (!data.empty()) { + request << data; + } + + std::string request_str = request.str(); + kinc_log(KINC_LOG_LEVEL_INFO, "Formatted HTTP request (%zu bytes):", request_str.length()); + kinc_log(KINC_LOG_LEVEL_INFO, "Request headers: %s", request_str.substr(0, request_str.find("\r\n\r\n")).c_str()); + + return request_str; + } + + void HttpRequestClient::parseHttpResponse(const std::string& response_data) { + std::istringstream stream(response_data); + std::string line; + + // initialized with defaults + response_.status = 0; + response_.statusText = "Unknown"; + response_.responseText = ""; + response_.headers.clear(); + + if (response_data.empty()) { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_ERROR, "HTTP DEBUG: Response data is empty - potential network/SSL failure"); + #endif + response_.status = 0; + response_.statusText = "Empty Response"; + return; + } + + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "HTTP DEBUG: Parsing response (%zu bytes), first 200 chars: %.200s", response_data.length(), response_data.c_str()); + #endif + + if (std::getline(stream, line)) { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Status line: %s", line.c_str()); + #endif + std::istringstream status_stream(line); + std::string http_version; + std::string status_text_rest; + + if (status_stream >> http_version >> response_.status) { + // Read the rest of the line as status text + std::getline(status_stream, status_text_rest); + if (!status_text_rest.empty()) { + // Remove leading space + response_.statusText = status_text_rest.substr(1); + } + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "Parsed status: %d, text: %s", response_.status, response_.statusText.c_str()); + #endif + } else { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to parse status line: %s", line.c_str()); + #endif + response_.status = 0; + response_.statusText = "Invalid Status Line"; + } + } else { + #ifdef DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_ERROR, "No status line found in HTTP response"); + #endif + response_.status = 0; + response_.statusText = "No Status Line"; + } + + // Parse headers + int header_count = 0; + while (std::getline(stream, line) && !line.empty() && line != "\r") { + size_t colon_pos = line.find(':'); + if (colon_pos != std::string::npos) { + std::string header_name = line.substr(0, colon_pos); + std::string header_value = line.substr(colon_pos + 1); + + // Trim whitespace + header_value.erase(0, header_value.find_first_not_of(" \t")); + header_value.erase(header_value.find_last_not_of(" \t\r\n") + 1); + + response_.headers[header_name] = header_value; + header_count++; + kinc_log(KINC_LOG_LEVEL_INFO, "Header: %s = %s", header_name.c_str(), header_value.c_str()); + } + } + + kinc_log(KINC_LOG_LEVEL_INFO, "Parsed %d headers", header_count); + + std::ostringstream body_stream; + std::string body_line; + size_t body_lines = 0; + + while (std::getline(stream, body_line)) { + if (body_lines > 0) { + body_stream << "\n"; + } + body_stream << body_line; + body_lines++; + } + + response_.responseText = body_stream.str(); + kinc_log(KINC_LOG_LEVEL_INFO, "Parsed body: %zu lines, %zu bytes", body_lines, response_.responseText.length()); + + if (!response_.responseText.empty()) { + std::string preview = response_.responseText.substr(0, 100); + kinc_log(KINC_LOG_LEVEL_INFO, "Response body preview: %s", preview.c_str()); + } + } + + void HttpRequestClient::loadLocalFile(const std::string& path) { + std::ifstream file(path, std::ios::in | std::ios::binary); + if (!file.is_open()) { + response_.error = "Failed to open local file: " + path; + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to open local file: %s", path.c_str()); + setReadyState(DONE); + handleError(response_.error); + return; + } + + file.seekg(0, std::ios::end); + size_t file_size = file.tellg(); + file.seekg(0, std::ios::beg); + + std::string file_content(file_size, '\0'); + file.read(&file_content[0], file_size); + file.close(); + + if (file.fail() && !file.eof()) { + response_.error = "Failed to read local file: " + path; + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to read local file: %s", path.c_str()); + setReadyState(DONE); + handleError(response_.error); + return; + } + + response_.responseText = file_content; + response_.status = 200; + response_.statusText = "OK"; + + std::string content_type = "application/octet-stream"; + size_t dot_pos = path.find_last_of('.'); + if (dot_pos != std::string::npos) { + std::string extension = path.substr(dot_pos); + if (extension == ".js") { + content_type = "application/javascript"; + } else if (extension == ".html") { + content_type = "text/html"; + } else if (extension == ".css") { + content_type = "text/css"; + } else if (extension == ".json") { + content_type = "application/json"; + } else if (extension == ".txt") { + content_type = "text/plain"; + } + } + + response_.headers["Content-Type"] = content_type; + response_.headers["Content-Length"] = std::to_string(file_size); + + kinc_log(KINC_LOG_LEVEL_INFO, "Local file loaded: %s, size: %zu, content-type: %s", + path.c_str(), file_size, content_type.c_str()); + + setReadyState(DONE); + handleLoad(); + } + + #ifdef WITH_SSL + bool HttpRequestClient::initSSL() { + #ifdef _WIN32 + // windows SChannel implementation + if (ssl_context_initialized_) { + return true; + } + + ssl_cred_handle_ = new CredHandle(); + memset(ssl_cred_handle_, 0, sizeof(CredHandle)); + + SCHANNEL_CRED cred = {0}; + cred.dwVersion = SCHANNEL_CRED_VERSION; + cred.grbitEnabledProtocols = SP_PROT_TLS1_2 | SP_PROT_TLS1_1; + cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; + cred.dwMinimumCipherStrength = 0; + cred.dwMaximumCipherStrength = 0; + + SECURITY_STATUS status = AcquireCredentialsHandleA( + NULL, const_cast(UNISP_NAME_A), SECPKG_CRED_OUTBOUND, NULL, &cred, NULL, NULL, + reinterpret_cast(ssl_cred_handle_), NULL); + + if (status != SEC_E_OK) { + const char* cred_error_desc = "Unknown credential error"; + switch (status) { + case SEC_E_SECPKG_NOT_FOUND: cred_error_desc = "SEC_E_SECPKG_NOT_FOUND - Security package not found"; break; + case SEC_E_NOT_OWNER: cred_error_desc = "SEC_E_NOT_OWNER - Not owner"; break; + case SEC_E_CANNOT_INSTALL: cred_error_desc = "SEC_E_CANNOT_INSTALL - Cannot install"; break; + case SEC_E_INVALID_TOKEN: cred_error_desc = "SEC_E_INVALID_TOKEN - Invalid token"; break; + case SEC_E_LOGON_DENIED: cred_error_desc = "SEC_E_LOGON_DENIED - Logon denied"; break; + } + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to acquire SSL credentials: 0x%x (%s)", status, cred_error_desc); + if (ssl_cred_handle_) { + delete reinterpret_cast(ssl_cred_handle_); + ssl_cred_handle_ = nullptr; + } + return false; + } + + kinc_log(KINC_LOG_LEVEL_INFO, "Successfully acquired SSL credentials"); + + ssl_context_initialized_ = true; + return true; + #else + // OpenSSL implementation + if (ssl_initialized_) { + return true; + } + + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + + ssl_ctx_ = SSL_CTX_new(TLS_client_method()); + if (!ssl_ctx_) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to create SSL context"); + return false; + } + + SSL_CTX_set_options(ssl_ctx_, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + SSL_CTX_set_verify(ssl_ctx_, SSL_VERIFY_PEER, NULL); + SSL_CTX_set_default_verify_paths(ssl_ctx_); + + // SSL OPTIMIZATIONS - Session resumption + SSL_CTX_set_session_cache_mode(ssl_ctx_, SSL_SESS_CACHE_CLIENT); + SSL_CTX_set_timeout(ssl_ctx_, 300); + + // optimized cipher list + SSL_CTX_set_cipher_list(ssl_ctx_, "ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM"); + + ssl_initialized_ = true; + return true; + #endif + } + + void HttpRequestClient::cleanupSSL() { + #ifdef _WIN32 + if (ssl_context_initialized_) { + if (ssl_context_handle_) { + DeleteSecurityContext(reinterpret_cast(ssl_context_handle_)); + delete reinterpret_cast(ssl_context_handle_); + ssl_context_handle_ = nullptr; + } + if (ssl_cred_handle_) { + FreeCredentialsHandle(reinterpret_cast(ssl_cred_handle_)); + delete reinterpret_cast(ssl_cred_handle_); + ssl_cred_handle_ = nullptr; + } + ssl_context_initialized_ = false; + } + ssl_buffer_.clear(); + ssl_socket_ = -1; + #else + if (ssl_) { + SSL_free(ssl_); + ssl_ = nullptr; + } + if (ssl_ctx_) { + SSL_CTX_free(ssl_ctx_); + ssl_ctx_ = nullptr; + } + ssl_initialized_ = false; + #endif + } + + bool HttpRequestClient::performSSLHandshake(int socket, const std::string& host) { + if (!initSSL()) { + return false; + } + + #ifdef _WIN32 + // windows SChannel handshake + #if DEBUG_NETWORK + kinc_log(KINC_LOG_LEVEL_INFO, "HTTP DEBUG: Performing SChannel SSL handshake for host: %s", host.c_str()); + #endif + kinc_log(KINC_LOG_LEVEL_INFO, "Performing SChannel SSL handshake..."); + + const int MAX_SSL_RETRIES = 3; + int retry_count = 0; + + SecBufferDesc outbuffer_desc, inbuffer_desc; + SecBuffer outbuffers[1], inbuffers[2]; + DWORD context_attributes; + TimeStamp expiry; + + outbuffers[0].pvBuffer = nullptr; + outbuffers[0].BufferType = SECBUFFER_TOKEN; + outbuffers[0].cbBuffer = 0; + outbuffer_desc.cBuffers = 1; + outbuffer_desc.pBuffers = outbuffers; + outbuffer_desc.ulVersion = SECBUFFER_VERSION; + + inbuffers[0].pvBuffer = nullptr; + inbuffers[0].BufferType = SECBUFFER_TOKEN; + inbuffers[0].cbBuffer = 0; + inbuffers[1].pvBuffer = nullptr; + inbuffers[1].BufferType = SECBUFFER_EMPTY; + inbuffers[1].cbBuffer = 0; + inbuffer_desc.cBuffers = 2; + inbuffer_desc.pBuffers = inbuffers; + inbuffer_desc.ulVersion = SECBUFFER_VERSION; + + SECURITY_STATUS status = SEC_I_CONTINUE_NEEDED; + bool first_call = true; + + std::vector handshake_buffer; + + while (status == SEC_I_CONTINUE_NEEDED || status == SEC_E_INCOMPLETE_MESSAGE) { + if (first_call) { + // first call should use NULL for phContext + ssl_context_handle_ = new CtxtHandle(); + memset(ssl_context_handle_, 0, sizeof(CtxtHandle)); + + kinc_log(KINC_LOG_LEVEL_INFO, "Calling InitializeSecurityContextA for first handshake..."); + kinc_log(KINC_LOG_LEVEL_INFO, "Buffer before call: cbBuffer=%d, pvBuffer=%p", outbuffers[0].cbBuffer, outbuffers[0].pvBuffer); + + status = InitializeSecurityContextA( + reinterpret_cast(ssl_cred_handle_), + nullptr, // phContext = NULL for first call + const_cast(host.c_str()), + ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | + ISC_REQ_CONFIDENTIALITY | ISC_REQ_EXTENDED_ERROR | + ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM, + 0, SECURITY_NATIVE_DREP, nullptr, 0, + reinterpret_cast(ssl_context_handle_), // phNewContext receives the new context + &outbuffer_desc, &context_attributes, &expiry); + + kinc_log(KINC_LOG_LEVEL_INFO, "InitializeSecurityContextA result: status=0x%x", status); + kinc_log(KINC_LOG_LEVEL_INFO, "Buffer after call: cbBuffer=%d, pvBuffer=%p", outbuffers[0].cbBuffer, outbuffers[0].pvBuffer); + + if (status != SEC_I_CONTINUE_NEEDED && status != SEC_E_OK) { + const char* init_error_desc = "Unknown initialization error"; + switch (status) { + case SEC_E_INVALID_HANDLE: init_error_desc = "SEC_E_INVALID_HANDLE - Invalid handle"; break; + case SEC_E_INVALID_TOKEN: init_error_desc = "SEC_E_INVALID_TOKEN - Invalid token"; break; + case SEC_E_LOGON_DENIED: init_error_desc = "SEC_E_LOGON_DENIED - Logon denied"; break; + case SEC_E_TARGET_UNKNOWN: init_error_desc = "SEC_E_TARGET_UNKNOWN - Target unknown"; break; + case SEC_E_INTERNAL_ERROR: init_error_desc = "SEC_E_INTERNAL_ERROR - Internal error"; break; + case SEC_E_SECPKG_NOT_FOUND: init_error_desc = "SEC_E_SECPKG_NOT_FOUND - Security package not found"; break; + } + kinc_log(KINC_LOG_LEVEL_ERROR, "InitializeSecurityContextA failed: 0x%x (%s)", status, init_error_desc); + if (ssl_context_handle_) { + delete reinterpret_cast(ssl_context_handle_); + ssl_context_handle_ = nullptr; + } + return false; + } + + first_call = false; + } else { + outbuffers[0].pvBuffer = nullptr; + outbuffers[0].BufferType = SECBUFFER_TOKEN; + outbuffers[0].cbBuffer = 0; + + if (!handshake_buffer.empty()) { + inbuffers[0].pvBuffer = handshake_buffer.data(); + inbuffers[0].cbBuffer = static_cast(handshake_buffer.size()); + inbuffers[0].BufferType = SECBUFFER_TOKEN; + + inbuffers[1].pvBuffer = nullptr; + inbuffers[1].BufferType = SECBUFFER_EMPTY; + inbuffers[1].cbBuffer = 0; + } + + kinc_log(KINC_LOG_LEVEL_INFO, "Calling InitializeSecurityContextA for continuation handshake..."); + kinc_log(KINC_LOG_LEVEL_INFO, "Input buffer: cbBuffer=%d, pvBuffer=%p", inbuffers[0].cbBuffer, inbuffers[0].pvBuffer); + kinc_log(KINC_LOG_LEVEL_INFO, "Context handle: %p", ssl_context_handle_); + + status = InitializeSecurityContextA( + reinterpret_cast(ssl_cred_handle_), + reinterpret_cast(ssl_context_handle_), + const_cast(host.c_str()), + ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | + ISC_REQ_CONFIDENTIALITY | ISC_REQ_EXTENDED_ERROR | + ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM, + 0, SECURITY_NATIVE_DREP, &inbuffer_desc, 0, + reinterpret_cast(ssl_context_handle_), + &outbuffer_desc, &context_attributes, &expiry); + + kinc_log(KINC_LOG_LEVEL_INFO, "Continuation InitializeSecurityContextA result: status=0x%x", status); + kinc_log(KINC_LOG_LEVEL_INFO, "Output buffer after continuation: cbBuffer=%d, pvBuffer=%p", outbuffers[0].cbBuffer, outbuffers[0].pvBuffer); + + // handle where some data was consumed and check if extra data was left in the buffer + if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED) { + if (inbuffers[1].BufferType == SECBUFFER_EXTRA && inbuffers[1].cbBuffer > 0) { + // move extra data to the beginning of the buffer + size_t extra_size = static_cast(inbuffers[1].cbBuffer); + size_t consumed = handshake_buffer.size() - extra_size; + std::memmove(handshake_buffer.data(), handshake_buffer.data() + consumed, extra_size); + handshake_buffer.resize(extra_size); + } else { + // all consumed + handshake_buffer.clear(); + } + } + } + + if (status == SEC_I_CONTINUE_NEEDED && outbuffers[0].cbBuffer > 0 && outbuffers[0].pvBuffer != nullptr) { + if (outbuffers[0].cbBuffer < 0 || outbuffers[0].cbBuffer > 65536) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Invalid SSL handshake buffer size: %d bytes (corrupted data)", outbuffers[0].cbBuffer); + if (outbuffers[0].pvBuffer) FreeContextBuffer(outbuffers[0].pvBuffer); + return false; + } + + kinc_log(KINC_LOG_LEVEL_INFO, "Sending SSL handshake data: %d bytes", outbuffers[0].cbBuffer); + int sent = ::send(socket, static_cast(outbuffers[0].pvBuffer), + outbuffers[0].cbBuffer, 0); + if (sent <= 0) { + int error = WSAGetLastError(); + const char* error_desc = "Unknown socket error"; + switch (error) { + case WSAENOTCONN: error_desc = "WSAENOTCONN - Socket is not connected"; break; + case WSAECONNRESET: error_desc = "WSAECONNRESET - Connection reset by peer"; break; + case WSAEWOULDBLOCK: error_desc = "WSAEWOULDBLOCK - Resource temporarily unavailable"; break; + case WSAENETDOWN: error_desc = "WSAENETDOWN - Network is down"; break; + case WSAEFAULT: error_desc = "WSAEFAULT - Bad address"; break; + case WSAEINVAL: error_desc = "WSAEINVAL - Invalid argument"; break; + case WSAENOTSOCK: error_desc = "WSAENOTSOCK - Socket operation on non-socket"; break; + } + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to send SSL handshake data: sent=%d, error=%d (%s)", sent, error, error_desc); + if (outbuffers[0].pvBuffer) FreeContextBuffer(outbuffers[0].pvBuffer); + return false; + } + kinc_log(KINC_LOG_LEVEL_INFO, "Successfully sent SSL handshake data: %d bytes", sent); + FreeContextBuffer(outbuffers[0].pvBuffer); + outbuffers[0].pvBuffer = nullptr; + outbuffers[0].cbBuffer = 0; + } + + if (status == SEC_I_CONTINUE_NEEDED || status == SEC_E_INCOMPLETE_MESSAGE) { + char recv_buffer[8192]; + kinc_log(KINC_LOG_LEVEL_INFO, "Waiting to receive SSL handshake response..."); + int received = ::recv(socket, recv_buffer, sizeof(recv_buffer), 0); + if (received <= 0) { + int error = WSAGetLastError(); + const char* error_desc = "Unknown socket error"; + switch (error) { + case WSAENOTCONN: error_desc = "WSAENOTCONN - Socket is not connected"; break; + case WSAECONNRESET: error_desc = "WSAECONNRESET - Connection reset by peer"; break; + case WSAEWOULDBLOCK: error_desc = "WSAEWOULDBLOCK - Resource temporarily unavailable"; break; + case WSAENETDOWN: error_desc = "WSAENETDOWN - Network is down"; break; + case WSAEFAULT: error_desc = "WSAEFAULT - Bad address"; break; + case WSAEINVAL: error_desc = "WSAEINVAL - Invalid argument"; break; + case WSAENOTSOCK: error_desc = "WSAENOTSOCK - Socket operation on non-socket"; break; + case WSAECONNABORTED: error_desc = "WSAECONNABORTED - Connection aborted"; break; + } + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to receive SSL handshake data: received=%d, error=%d (%s)", received, error, error_desc); + return false; + } + kinc_log(KINC_LOG_LEVEL_INFO, "Received SSL handshake response: %d bytes", received); + + handshake_buffer.insert(handshake_buffer.end(), recv_buffer, recv_buffer + received); + } + } + + if (status == SEC_E_OK) { + ssl_context_initialized_ = true; + kinc_log(KINC_LOG_LEVEL_INFO, "SChannel SSL handshake completed successfully"); + + kinc_log(KINC_LOG_LEVEL_INFO, "SSL buffer size after handshake: %zu bytes", ssl_buffer_.size()); + + return true; + } else { + const char* error_desc = "Unknown error"; + switch (status) { + case SEC_E_LOGON_DENIED: error_desc = "SEC_E_LOGON_DENIED - The logon attempt failed"; break; + case SEC_E_INVALID_TOKEN: error_desc = "SEC_E_INVALID_TOKEN - Invalid security token"; break; + case SEC_E_INVALID_HANDLE: error_desc = "SEC_E_INVALID_HANDLE - Invalid handle"; break; + case SEC_E_INTERNAL_ERROR: error_desc = "SEC_E_INTERNAL_ERROR - Internal error"; break; + case SEC_E_NO_CREDENTIALS: error_desc = "SEC_E_NO_CREDENTIALS - No credentials available"; break; + case SEC_E_WRONG_PRINCIPAL: error_desc = "SEC_E_WRONG_PRINCIPAL - Wrong principal"; break; + case SEC_E_CERT_EXPIRED: error_desc = "SEC_E_CERT_EXPIRED - Certificate expired"; break; + case SEC_E_CERT_UNKNOWN: error_desc = "SEC_E_CERT_UNKNOWN - Certificate unknown"; break; + case SEC_E_UNTRUSTED_ROOT: error_desc = "SEC_E_UNTRUSTED_ROOT - Untrusted root certificate"; break; + } + kinc_log(KINC_LOG_LEVEL_ERROR, "SChannel SSL handshake failed with status: 0x%x (%s)", status, error_desc); + return false; + } + + kinc_log(KINC_LOG_LEVEL_ERROR, "SSL handshake failed after %d retries", MAX_SSL_RETRIES); + return false; + + #else + ssl_ = SSL_new(ssl_ctx_); + if (!ssl_) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to create SSL structure"); + return false; + } + + if (SSL_set_fd(ssl_, socket) != 1) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to set SSL socket"); + SSL_free(ssl_); + ssl_ = nullptr; + return false; + } + + SSL_set_tlsext_host_name(ssl_, host.c_str()); + + int result = SSL_connect(ssl_); + if (result != 1) { + int error = SSL_get_error(ssl_, result); + kinc_log(KINC_LOG_LEVEL_ERROR, "SSL handshake failed: %d", error); + SSL_free(ssl_); + ssl_ = nullptr; + return false; + } + + ssl_initialized_ = true; + kinc_log(KINC_LOG_LEVEL_INFO, "OpenSSL handshake completed successfully"); + return true; + #endif + } + + int HttpRequestClient::sslRead(char* buffer, int length) { + #ifdef _WIN32 + // windows SChannel read implementation with SEC_E_INCOMPLETE_MESSAGE handling + if (!ssl_context_initialized_) { + return -1; + } + + kinc_log(KINC_LOG_LEVEL_INFO, "Enhanced sslRead called, SSL buffer size: %zu bytes", ssl_buffer_.size()); + + const int MAX_SSL_RETRIES = 3; + const int SSL_TIMEOUT_MS = 30000; + int retry_count = 0; + + SECURITY_STATUS status = SEC_E_OK; + + while (retry_count < MAX_SSL_RETRIES) { + if (ssl_buffer_.empty()) { + kinc_log(KINC_LOG_LEVEL_INFO, "SSL buffer empty, reading encrypted data from socket..."); + char temp_buffer[8192]; + + // use shorter timeout and non-blocking approach + int received = receiveWithTimeout(ssl_socket_, temp_buffer, sizeof(temp_buffer), 5000); // 5 sec timeout + kinc_log(KINC_LOG_LEVEL_INFO, "receiveWithTimeout returned: %d bytes", received); + + if (received > 0) { + ssl_buffer_.insert(ssl_buffer_.end(), temp_buffer, temp_buffer + received); + kinc_log(KINC_LOG_LEVEL_INFO, "Read %d encrypted bytes from socket, SSL buffer now: %zu bytes", + received, ssl_buffer_.size()); + } else if (received == 0) { + kinc_log(KINC_LOG_LEVEL_INFO, "SSL connection closed by server (no data available)"); + return 0; + } else { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to read SSL data from socket (timeout or error)"); + return -1; + } + } + + if (!ssl_buffer_.empty()) { + kinc_log(KINC_LOG_LEVEL_INFO, "Processing SSL buffer: %zu bytes, attempt %d", + ssl_buffer_.size(), retry_count + 1); + + SecBufferDesc message_desc; + SecBuffer message_buffers[4]; + + message_desc.ulVersion = SECBUFFER_VERSION; + message_desc.cBuffers = 4; + message_desc.pBuffers = message_buffers; + message_buffers[0].BufferType = SECBUFFER_DATA; + message_buffers[0].pvBuffer = ssl_buffer_.data(); + message_buffers[0].cbBuffer = static_cast(ssl_buffer_.size()); + message_buffers[1].BufferType = SECBUFFER_EMPTY; + message_buffers[1].pvBuffer = nullptr; + message_buffers[1].cbBuffer = 0; + message_buffers[2].BufferType = SECBUFFER_EMPTY; + message_buffers[2].pvBuffer = nullptr; + message_buffers[2].cbBuffer = 0; + message_buffers[3].BufferType = SECBUFFER_EMPTY; + message_buffers[3].pvBuffer = nullptr; + message_buffers[3].cbBuffer = 0; + + status = DecryptMessage( + reinterpret_cast(ssl_context_handle_), + &message_desc, 0, nullptr); + + if (status == SEC_E_OK) { + SecBuffer* data_buffer = nullptr; + SecBuffer* extra_buffer = nullptr; + + for (int i = 0; i < 4; i++) { + if (message_buffers[i].BufferType == SECBUFFER_DATA) { + data_buffer = &message_buffers[i]; + } else if (message_buffers[i].BufferType == SECBUFFER_EXTRA) { + extra_buffer = &message_buffers[i]; + } + } + + if (data_buffer && data_buffer->cbBuffer > 0) { + int bytes_to_copy = (length < (int)data_buffer->cbBuffer) ? length : (int)data_buffer->cbBuffer; + memcpy(buffer, data_buffer->pvBuffer, static_cast(bytes_to_copy)); + + if (extra_buffer && extra_buffer->cbBuffer > 0) { + std::memmove(ssl_buffer_.data(), extra_buffer->pvBuffer, static_cast(extra_buffer->cbBuffer)); + ssl_buffer_.resize(static_cast(extra_buffer->cbBuffer)); + } else { + ssl_buffer_.clear(); + } + + return bytes_to_copy; + } + + ssl_buffer_.clear(); + + } else if (status == SEC_E_INCOMPLETE_MESSAGE) { + // expected behavior + kinc_log(KINC_LOG_LEVEL_INFO, "SSL decrypt needs more data (SEC_E_INCOMPLETE_MESSAGE) - reading more..."); + + char recv_buffer[8192]; + int received = receiveWithTimeout(ssl_socket_, recv_buffer, sizeof(recv_buffer), SSL_TIMEOUT_MS); + + if (received > 0) { + ssl_buffer_.insert(ssl_buffer_.end(), recv_buffer, recv_buffer + received); + kinc_log(KINC_LOG_LEVEL_INFO, "Received %d more SSL bytes, total buffer: %zu", + received, ssl_buffer_.size()); + continue; + } else if (received == 0) { + kinc_log(KINC_LOG_LEVEL_INFO, "SSL connection closed gracefully by server"); + return 0; + } else { + // leftover data treated as a connection close + if (ssl_buffer_.size() < 50) { + kinc_log(KINC_LOG_LEVEL_INFO, "Incomplete message with small buffer (%zu bytes) and no more data - likely connection close", + ssl_buffer_.size()); + ssl_buffer_.clear(); + return 0; + } + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to receive additional SSL data (timeout or error)"); + return -1; + } + + } else if (status == SEC_I_CONTEXT_EXPIRED || status == 0x00090317) { + kinc_log(KINC_LOG_LEVEL_INFO, "SSL context expired in inner loop"); + ssl_buffer_.clear(); + return 0; + } else { + // small buffer is likely connection close with partial data + if (ssl_buffer_.size() < 50) { + kinc_log(KINC_LOG_LEVEL_INFO, "SChannel status 0x%x with small buffer (%zu bytes) - treating as connection close", + status, ssl_buffer_.size()); + ssl_buffer_.clear(); + return 0; + } + kinc_log(KINC_LOG_LEVEL_ERROR, "SChannel decrypt failed: 0x%x", status); + return -1; + } + + ssl_buffer_.clear(); + + } else if (status == SEC_E_INCOMPLETE_MESSAGE) { + // expected behavior + kinc_log(KINC_LOG_LEVEL_INFO, "SSL decrypt needs more data (SEC_E_INCOMPLETE_MESSAGE) - reading more..."); + + char recv_buffer[8192]; + kinc_log(KINC_LOG_LEVEL_INFO, "About to call receiveWithTimeout on socket, timeout=%d ms", SSL_TIMEOUT_MS); + int received = receiveWithTimeout(ssl_socket_, recv_buffer, sizeof(recv_buffer), SSL_TIMEOUT_MS); + kinc_log(KINC_LOG_LEVEL_INFO, "receiveWithTimeout returned: %d bytes", received); + + if (received > 0) { + ssl_buffer_.insert(ssl_buffer_.end(), recv_buffer, recv_buffer + received); + kinc_log(KINC_LOG_LEVEL_INFO, "Received %d more SSL bytes, total buffer: %zu", + received, ssl_buffer_.size()); + continue; + } else if (received == 0) { + kinc_log(KINC_LOG_LEVEL_INFO, "SSL connection closed gracefully by server"); + return 0; + } else { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to receive additional SSL data (timeout or error)"); + return -1; + } + + } else if (status == SEC_I_CONTEXT_EXPIRED || status == 0x00090317) { + // closed by server - SEC_I_CONTEXT_EXPIRED = 0x00090317 is normal for HTTP with "Connection: close" + kinc_log(KINC_LOG_LEVEL_INFO, "SSL context expired (connection closed by server)"); + ssl_buffer_.clear(); + return 0; + } else if (status == SEC_I_RENEGOTIATE) { + kinc_log(KINC_LOG_LEVEL_INFO, "SSL renegotiation requested, treating as connection close"); + ssl_buffer_.clear(); + return 0; + } else { + // possible when connection closes with partial SSL record + if (ssl_buffer_.size() < 50) { + kinc_log(KINC_LOG_LEVEL_INFO, "SChannel decrypt status 0x%x with small buffer (%zu bytes) - likely connection close", + status, ssl_buffer_.size()); + ssl_buffer_.clear(); + return 0; + } + kinc_log(KINC_LOG_LEVEL_ERROR, "SChannel decrypt failed: 0x%x", status); + return -1; + } + + retry_count++; + } + + kinc_log(KINC_LOG_LEVEL_ERROR, "SSL read failed after %d retries", MAX_SSL_RETRIES); + return -1; + + #else + if (!ssl_) { + return -1; + } + + int bytes_read = SSL_read(ssl_, buffer, length); + if (bytes_read <= 0) { + int error = SSL_get_error(ssl_, bytes_read); + if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) { + return 0; + } + return -1; + } + return bytes_read; + #endif + } + + int HttpRequestClient::sslWrite(const char* buffer, int length) { + #ifdef _WIN32 + // windows SChannel write implementation + if (!ssl_context_initialized_) { + return -1; + } + + SecBufferDesc message_desc; + SecBuffer message_buffers[4]; + + SecPkgContext_StreamSizes stream_sizes; + SECURITY_STATUS status = QueryContextAttributesA( + reinterpret_cast(ssl_context_handle_), + SECPKG_ATTR_STREAM_SIZES, &stream_sizes); + + if (status != SEC_E_OK) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to get stream sizes: 0x%x", status); + return -1; + } + + int total_size = stream_sizes.cbHeader + length + stream_sizes.cbTrailer; + char* encrypt_buffer = new char[total_size]; + + message_desc.ulVersion = SECBUFFER_VERSION; + message_desc.cBuffers = 4; + message_desc.pBuffers = message_buffers; + + message_buffers[0].BufferType = SECBUFFER_STREAM_HEADER; + message_buffers[0].pvBuffer = encrypt_buffer; + message_buffers[0].cbBuffer = stream_sizes.cbHeader; + + message_buffers[1].BufferType = SECBUFFER_DATA; + message_buffers[1].pvBuffer = encrypt_buffer + stream_sizes.cbHeader; + message_buffers[1].cbBuffer = length; + memcpy(message_buffers[1].pvBuffer, buffer, length); + + message_buffers[2].BufferType = SECBUFFER_STREAM_TRAILER; + message_buffers[2].pvBuffer = encrypt_buffer + stream_sizes.cbHeader + length; + message_buffers[2].cbBuffer = stream_sizes.cbTrailer; + + message_buffers[3].BufferType = SECBUFFER_EMPTY; + message_buffers[3].pvBuffer = nullptr; + message_buffers[3].cbBuffer = 0; + + status = EncryptMessage( + reinterpret_cast(ssl_context_handle_), + 0, &message_desc, 0); + + if (status != SEC_E_OK) { + kinc_log(KINC_LOG_LEVEL_ERROR, "SChannel encrypt failed: 0x%x", status); + delete[] encrypt_buffer; + return -1; + } + + int total_bytes_to_send = stream_sizes.cbHeader + length + stream_sizes.cbTrailer; + int bytes_sent = ::send(ssl_socket_, encrypt_buffer, total_bytes_to_send, 0); + + kinc_log(KINC_LOG_LEVEL_INFO, "SSL encrypted and sent %d bytes (original: %d)", bytes_sent, length); + + delete[] encrypt_buffer; + + if (bytes_sent == total_bytes_to_send) { + // return original length on success + return length; + } else { + kinc_log(KINC_LOG_LEVEL_ERROR, "SSL send failed: sent %d of %d bytes", bytes_sent, total_bytes_to_send); + return -1; + } + #else + if (!ssl_) { + return -1; + } + + int bytes_written = SSL_write(ssl_, buffer, length); + if (bytes_written <= 0) { + int error = SSL_get_error(ssl_, bytes_written); + if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) { + return 0; + } + return -1; + } + return bytes_written; + #endif + } + #endif + +} + +// V8 binding functions +void runt_httprequest_create(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + int id = HttpRequestWrapper::createHttpRequest(isolate); + args.GetReturnValue().Set(Number::New(isolate, id)); +} + +void runt_httprequest_open(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 3 || !args[0]->IsNumber() || !args[1]->IsString() || !args[2]->IsString()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + String::Utf8Value method(isolate, args[1]); + String::Utf8Value url(isolate, args[2]); + bool async = args.Length() > 3 ? args[3]->BooleanValue(isolate) : true; + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->open(*method, *url, async); + } +} + +void runt_httprequest_send(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 1 || !args[0]->IsNumber()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + std::string data = ""; + + if (args.Length() > 1 && args[1]->IsString()) { + String::Utf8Value data_str(isolate, args[1]); + data = *data_str; + } + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->send(data); + } +} + +void runt_httprequest_abort(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 1 || !args[0]->IsNumber()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->abort(); + } +} + +void runt_httprequest_set_request_header(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 3 || !args[0]->IsNumber() || !args[1]->IsString() || !args[2]->IsString()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + String::Utf8Value header(isolate, args[1]); + String::Utf8Value value(isolate, args[2]); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->setRequestHeader(*header, *value); + } +} + +void runt_httprequest_get_all_response_headers(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 1 || !args[0]->IsNumber()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + std::string headers = it->second->getAllResponseHeaders(); + args.GetReturnValue().Set(String::NewFromUtf8(isolate, headers.c_str()).ToLocalChecked()); + } else { + args.GetReturnValue().Set(String::NewFromUtf8(isolate, "").ToLocalChecked()); + } +} + +void runt_httprequest_get_response_header(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsString()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + String::Utf8Value header(isolate, args[1]); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + std::string value = it->second->getResponseHeader(*header); + args.GetReturnValue().Set(String::NewFromUtf8(isolate, value.c_str()).ToLocalChecked()); + } else { + args.GetReturnValue().Set(String::NewFromUtf8(isolate, "").ToLocalChecked()); + } +} + +void runt_httprequest_get_ready_state(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 1 || !args[0]->IsNumber()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + args.GetReturnValue().Set(Number::New(isolate, it->second->getReadyState())); + } else { + args.GetReturnValue().Set(Number::New(isolate, 0)); + } +} + +void runt_httprequest_get_status(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 1 || !args[0]->IsNumber()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + args.GetReturnValue().Set(Number::New(isolate, it->second->getStatus())); + } else { + args.GetReturnValue().Set(Number::New(isolate, 0)); + } +} + +void runt_httprequest_get_status_text(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 1 || !args[0]->IsNumber()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + std::string statusText = it->second->getStatusText(); + args.GetReturnValue().Set(String::NewFromUtf8(isolate, statusText.c_str()).ToLocalChecked()); + } else { + args.GetReturnValue().Set(String::NewFromUtf8(isolate, "").ToLocalChecked()); + } +} + +void runt_httprequest_get_response_text(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 1 || !args[0]->IsNumber()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + std::string responseText = it->second->getResponseText(); + + if (responseText.empty()) { + kinc_log(KINC_LOG_LEVEL_WARNING, "HTTP response text is empty for request ID: %d", id); + } + + // ArrayBuffer response type, return an ArrayBuffer instead of string + if (it->second->getResponseType() == HttpRequestWrapper::ARRAY_BUFFER) { + if (responseText.empty()) { + Local buffer = ArrayBuffer::New(isolate, 0); + args.GetReturnValue().Set(buffer); + } else { + size_t length = responseText.length(); + std::unique_ptr backing = v8::ArrayBuffer::NewBackingStore(isolate, length); + memcpy(backing->Data(), responseText.c_str(), length); + Local buffer = ArrayBuffer::New(isolate, std::move(backing)); + args.GetReturnValue().Set(buffer); + } + } else { + if (responseText.empty()) { + args.GetReturnValue().Set(String::NewFromUtf8(isolate, "").ToLocalChecked()); + } else { + args.GetReturnValue().Set(String::NewFromUtf8(isolate, responseText.c_str()).ToLocalChecked()); + } + } + } else { + kinc_log(KINC_LOG_LEVEL_ERROR, "HTTP request not found for ID: %d", id); + // return empty ArrayBuffer instead of empty string to prevent hxBytes errors + Local buffer = ArrayBuffer::New(isolate, 0); + args.GetReturnValue().Set(buffer); + } +} + +void runt_httprequest_get_response_url(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 1 || !args[0]->IsNumber()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + std::string responseURL = it->second->getResponseURL(); + args.GetReturnValue().Set(String::NewFromUtf8(isolate, responseURL.c_str()).ToLocalChecked()); + } else { + args.GetReturnValue().Set(String::NewFromUtf8(isolate, "").ToLocalChecked()); + } +} + +void runt_httprequest_set_timeout(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsNumber()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + int timeout = args[1]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->setTimeout(timeout); + } +} + +void runt_httprequest_set_response_type(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsNumber()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + int type = args[1]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->setResponseType(static_cast(type)); + } +} + +void runt_httprequest_set_with_credentials(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsBoolean()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + bool withCredentials = args[1]->BooleanValue(isolate); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->setWithCredentials(withCredentials); + } +} + +void runt_httprequest_set_onreadystatechange(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsFunction()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + Local callback = Local::Cast(args[1]); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->setOnReadyStateChange(callback); + } +} + +void runt_httprequest_set_onload(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsFunction()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + Local callback = Local::Cast(args[1]); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->setOnLoad(callback); + } +} + +void runt_httprequest_set_onerror(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsFunction()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + Local callback = Local::Cast(args[1]); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->setOnError(callback); + } +} + +void runt_httprequest_set_ontimeout(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsFunction()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + Local callback = Local::Cast(args[1]); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->setOnTimeout(callback); + } +} + +void runt_httprequest_set_onabort(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsFunction()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + Local callback = Local::Cast(args[1]); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->setOnAbort(callback); + } +} + +void runt_httprequest_set_onprogress(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsFunction()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + Local callback = Local::Cast(args[1]); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->setOnProgress(callback); + } +} + +void runt_httprequest_set_onloadstart(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsFunction()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + Local callback = Local::Cast(args[1]); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->setOnLoadStart(callback); + } +} + +void runt_httprequest_set_onloadend(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsFunction()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Invalid arguments").ToLocalChecked())); + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).ToChecked(); + Local callback = Local::Cast(args[1]); + + auto it = HttpRequestWrapper::active_requests.find(id); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->setOnLoadEnd(callback); + } +} + +// Native XMLHttpRequest class implementation for V8 +static void XMLHttpRequestConstructor(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + Local context = isolate->GetCurrentContext(); + + if (!args.IsConstructCall()) { + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8(isolate, "XMLHttpRequest constructor requires 'new'").ToLocalChecked())); + return; + } + + int reqId = HttpRequestWrapper::createHttpRequest(isolate); + + Local instance = args.This(); + instance->Set(context, String::NewFromUtf8(isolate, "_id").ToLocalChecked(), Integer::New(isolate, reqId)); + instance->Set(context, String::NewFromUtf8(isolate, "readyState").ToLocalChecked(), Integer::New(isolate, 0)); + instance->Set(context, String::NewFromUtf8(isolate, "response").ToLocalChecked(), Null(isolate)); + instance->Set(context, String::NewFromUtf8(isolate, "responseText").ToLocalChecked(), String::NewFromUtf8(isolate, "").ToLocalChecked()); + instance->Set(context, String::NewFromUtf8(isolate, "responseType").ToLocalChecked(), String::NewFromUtf8(isolate, "").ToLocalChecked()); + instance->Set(context, String::NewFromUtf8(isolate, "responseURL").ToLocalChecked(), String::NewFromUtf8(isolate, "").ToLocalChecked()); + instance->Set(context, String::NewFromUtf8(isolate, "status").ToLocalChecked(), Integer::New(isolate, 0)); + instance->Set(context, String::NewFromUtf8(isolate, "statusText").ToLocalChecked(), String::NewFromUtf8(isolate, "").ToLocalChecked()); + instance->Set(context, String::NewFromUtf8(isolate, "timeout").ToLocalChecked(), Integer::New(isolate, 0)); + instance->Set(context, String::NewFromUtf8(isolate, "withCredentials").ToLocalChecked(), Boolean::New(isolate, false)); + instance->Set(context, String::NewFromUtf8(isolate, "onreadystatechange").ToLocalChecked(), Null(isolate)); + instance->Set(context, String::NewFromUtf8(isolate, "onload").ToLocalChecked(), Null(isolate)); + instance->Set(context, String::NewFromUtf8(isolate, "onerror").ToLocalChecked(), Null(isolate)); + instance->Set(context, String::NewFromUtf8(isolate, "onabort").ToLocalChecked(), Null(isolate)); + instance->Set(context, String::NewFromUtf8(isolate, "ontimeout").ToLocalChecked(), Null(isolate)); + instance->Set(context, String::NewFromUtf8(isolate, "onprogress").ToLocalChecked(), Null(isolate)); + instance->Set(context, String::NewFromUtf8(isolate, "onloadstart").ToLocalChecked(), Null(isolate)); + instance->Set(context, String::NewFromUtf8(isolate, "onloadend").ToLocalChecked(), Null(isolate)); + + args.GetReturnValue().Set(instance); +} + +static void XMLHttpRequestOpen(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + Local context = isolate->GetCurrentContext(); + Local self = args.Holder(); + + if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString()) { + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8(isolate, "Method and URL required").ToLocalChecked())); + return; + } + + Local idVal = self->Get(context, String::NewFromUtf8(isolate, "_id").ToLocalChecked()).ToLocalChecked(); + int reqId = idVal->Int32Value(context).FromJust(); + + String::Utf8Value method(isolate, args[0]); + String::Utf8Value url(isolate, args[1]); + bool async = args.Length() > 2 ? args[2]->BooleanValue(isolate) : true; + + auto it = HttpRequestWrapper::active_requests.find(reqId); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->open(*method, *url, async); + self->Set(context, String::NewFromUtf8(isolate, "readyState").ToLocalChecked(), + Integer::New(isolate, it->second->getReadyState())); + } +} + +static void XMLHttpRequestSend(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + Local context = isolate->GetCurrentContext(); + Local self = args.Holder(); + + Local idVal = self->Get(context, String::NewFromUtf8(isolate, "_id").ToLocalChecked()).ToLocalChecked(); + int reqId = idVal->Int32Value(context).FromJust(); + + std::string data = ""; + if (args.Length() > 0 && args[0]->IsString()) { + String::Utf8Value str(isolate, args[0]); + data = *str; + } + + auto it = HttpRequestWrapper::active_requests.find(reqId); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->send(data); + } +} + +static void XMLHttpRequestAbort(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + Local context = isolate->GetCurrentContext(); + Local self = args.Holder(); + + Local idVal = self->Get(context, String::NewFromUtf8(isolate, "_id").ToLocalChecked()).ToLocalChecked(); + int reqId = idVal->Int32Value(context).FromJust(); + + auto it = HttpRequestWrapper::active_requests.find(reqId); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->abort(); + } +} + +static void XMLHttpRequestSetRequestHeader(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + Local context = isolate->GetCurrentContext(); + Local self = args.Holder(); + + if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString()) { + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8(isolate, "Header name and value required").ToLocalChecked())); + return; + } + + Local idVal = self->Get(context, String::NewFromUtf8(isolate, "_id").ToLocalChecked()).ToLocalChecked(); + int reqId = idVal->Int32Value(context).FromJust(); + + String::Utf8Value header(isolate, args[0]); + String::Utf8Value value(isolate, args[1]); + + auto it = HttpRequestWrapper::active_requests.find(reqId); + if (it != HttpRequestWrapper::active_requests.end()) { + it->second->setRequestHeader(*header, *value); + } +} + +static void XMLHttpRequestGetAllResponseHeaders(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + Local context = isolate->GetCurrentContext(); + Local self = args.Holder(); + + Local idVal = self->Get(context, String::NewFromUtf8(isolate, "_id").ToLocalChecked()).ToLocalChecked(); + int reqId = idVal->Int32Value(context).FromJust(); + + auto it = HttpRequestWrapper::active_requests.find(reqId); + if (it != HttpRequestWrapper::active_requests.end()) { + std::string headers = it->second->getAllResponseHeaders(); + args.GetReturnValue().Set(String::NewFromUtf8(isolate, headers.c_str()).ToLocalChecked()); + } else { + args.GetReturnValue().Set(String::NewFromUtf8(isolate, "").ToLocalChecked()); + } +} + +static void XMLHttpRequestGetResponseHeader(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + Local context = isolate->GetCurrentContext(); + Local self = args.Holder(); + + if (args.Length() < 1 || !args[0]->IsString()) { + args.GetReturnValue().SetNull(); + return; + } + + Local idVal = self->Get(context, String::NewFromUtf8(isolate, "_id").ToLocalChecked()).ToLocalChecked(); + int reqId = idVal->Int32Value(context).FromJust(); + + String::Utf8Value header(isolate, args[0]); + + auto it = HttpRequestWrapper::active_requests.find(reqId); + if (it != HttpRequestWrapper::active_requests.end()) { + std::string value = it->second->getResponseHeader(*header); + if (value.empty()) { + args.GetReturnValue().SetNull(); + } else { + args.GetReturnValue().Set(String::NewFromUtf8(isolate, value.c_str()).ToLocalChecked()); + } + } else { + args.GetReturnValue().SetNull(); + } +} + +void createXMLHttpRequestClass(Isolate* isolate, Local& global) { + Local xhrTpl = FunctionTemplate::New(isolate, XMLHttpRequestConstructor); + xhrTpl->SetClassName(String::NewFromUtf8(isolate, "XMLHttpRequest").ToLocalChecked()); + xhrTpl->InstanceTemplate()->SetInternalFieldCount(1); + + Local proto = xhrTpl->PrototypeTemplate(); + proto->Set(isolate, "open", FunctionTemplate::New(isolate, XMLHttpRequestOpen)); + proto->Set(isolate, "send", FunctionTemplate::New(isolate, XMLHttpRequestSend)); + proto->Set(isolate, "abort", FunctionTemplate::New(isolate, XMLHttpRequestAbort)); + proto->Set(isolate, "setRequestHeader", FunctionTemplate::New(isolate, XMLHttpRequestSetRequestHeader)); + proto->Set(isolate, "getAllResponseHeaders", FunctionTemplate::New(isolate, XMLHttpRequestGetAllResponseHeaders)); + proto->Set(isolate, "getResponseHeader", FunctionTemplate::New(isolate, XMLHttpRequestGetResponseHeader)); + + Local xhrFunc = xhrTpl->GetFunction(isolate->GetCurrentContext()).ToLocalChecked(); + xhrFunc->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "UNSENT").ToLocalChecked(), Integer::New(isolate, 0)); + xhrFunc->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "OPENED").ToLocalChecked(), Integer::New(isolate, 1)); + xhrFunc->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "HEADERS_RECEIVED").ToLocalChecked(), Integer::New(isolate, 2)); + xhrFunc->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "LOADING").ToLocalChecked(), Integer::New(isolate, 3)); + xhrFunc->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "DONE").ToLocalChecked(), Integer::New(isolate, 4)); + + global->Set(isolate, "XMLHttpRequest", xhrTpl); +} + +#endif \ No newline at end of file diff --git a/Sources/httprequest.h b/Sources/httprequest.h new file mode 100644 index 0000000..4604c48 --- /dev/null +++ b/Sources/httprequest.h @@ -0,0 +1,235 @@ +#pragma once + +#ifdef WITH_NETWORKING + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WITH_SSL +#ifndef _WIN32 +#include +#include +#include +#endif +#endif + +namespace HttpRequestWrapper { + + enum ReadyState { + UNSENT = 0, + OPENED = 1, + HEADERS_RECEIVED = 2, + LOADING = 3, + DONE = 4 + }; + + enum ResponseType { + DEFAULT = 0, + ARRAY_BUFFER = 1, + BLOB = 2, + DOCUMENT = 3, + JSON = 4, + TEXT = 5 + }; + + struct HttpResponse { + int status; + std::string statusText; + std::map headers; + std::string responseText; + std::string error; + + HttpResponse() : status(0), statusText("") {} + }; + + class HttpRequestClient { + public: + HttpRequestClient(v8::Isolate* isolate, v8::Global* global_context); + ~HttpRequestClient(); + + void open(const std::string& method, const std::string& url, bool async = true); + void setRequestHeader(const std::string& header, const std::string& value); + void send(const std::string& data = ""); + void abort(); + std::string getAllResponseHeaders(); + std::string getResponseHeader(const std::string& header); + + ReadyState getReadyState() const { return ready_state_; } + int getStatus() const { return response_.status; } + std::string getStatusText() const { return response_.statusText; } + std::string getResponseText() const { return response_.responseText; } + std::string getResponseURL() const { return url_; } + ResponseType getResponseType() const { return response_type_; } + int getTimeout() const { return timeout_; } + bool getWithCredentials() const { return with_credentials_; } + + void setTimeout(int timeout) { timeout_ = timeout; } + void setResponseType(ResponseType type) { response_type_ = type; } + void setWithCredentials(bool withCredentials) { with_credentials_ = withCredentials; } + + void setOnReadyStateChange(v8::Local callback); + void setOnLoad(v8::Local callback); + void setOnError(v8::Local callback); + void setOnTimeout(v8::Local callback); + void setOnAbort(v8::Local callback); + void setOnProgress(v8::Local callback); + void setOnLoadStart(v8::Local callback); + void setOnLoadEnd(v8::Local callback); + + // from main thread + void processEvents(); + + private: + v8::Isolate* isolate_; + v8::Global* global_context_; + std::string method_; + std::string url_; + std::map request_headers_; + std::string request_data_; + HttpResponse response_; + ReadyState ready_state_; + ResponseType response_type_; + int timeout_; + bool with_credentials_; + bool async_; + std::thread request_thread_; + std::atomic should_abort_; + + v8::Global on_ready_state_change_; + v8::Global on_load_; + v8::Global on_error_; + v8::Global on_timeout_; + v8::Global on_abort_; + v8::Global on_progress_; + v8::Global on_load_start_; + v8::Global on_load_end_; + + std::mutex state_mutex_; + + public: + std::atomic active_operation_{false}; + + private: + // TODO: find real solution for duplicate done + bool done_event_fired_{false}; + + #ifdef WITH_SSL + #ifdef _WIN32 + void* ssl_cred_handle_; + void* ssl_context_handle_; + bool ssl_context_initialized_; + int ssl_socket_; + std::vector ssl_buffer_; + #else + SSL_CTX* ssl_ctx_; + SSL* ssl_; + bool ssl_initialized_; + #endif + bool initSSL(); + void cleanupSSL(); + bool performSSLHandshake(int socket, const std::string& host); + int sslRead(char* buffer, int length); + int sslWrite(const char* buffer, int length); + #endif + + enum EventType { + HTTP_READY_STATE_CHANGE = 100, + HTTP_LOAD = 101, + HTTP_ERROR = 102, + HTTP_TIMEOUT = 103, + HTTP_ABORT = 104, + HTTP_PROGRESS = 105, + HTTP_LOAD_START = 106, + HTTP_LOAD_END = 107 + }; + + struct Event { + EventType type; + std::string data; + int code; + + Event(EventType t, const std::string& d = "", int c = 0) + : type(t), data(d), code(c) {} + }; + + std::queue event_queue_; + std::mutex event_queue_mutex_; + + // handle* called from background thread + void handleReadyStateChange(); + void handleLoad(); + void handleError(const std::string& error); + void handleTimeout(); + void handleAbort(); + void handleProgress(); + void handleLoadStart(); + void handleLoadEnd(); + + // process* called from main thread + void processReadyStateChangeEvent(); + void processLoadEvent(); + void processErrorEvent(const std::string& error); + void processTimeoutEvent(); + void processAbortEvent(); + void processProgressEvent(); + void processLoadStartEvent(); + void processLoadEndEvent(); + + void performRequest(); + void loadLocalFile(const std::string& path); + void setReadyState(ReadyState state); + void callReadyStateChangeCallback(); + void callCallback(v8::Global& callback, int argc, v8::Local argv[]); + + bool parseUrl(const std::string& url, std::string& host, int& port, std::string& path, bool& is_https); + std::string formatHttpRequest(const std::string& method, const std::string& path, const std::string& host, const std::string& data); + void parseHttpResponse(const std::string& response); + }; + + void initialize(); + void cleanup(); + + // process events from main thread + void processEvents(); + + int createHttpRequest(v8::Isolate* isolate); + + v8::Global* getGlobalContext(); +} + +// V8 binding functions called from main.cpp +void runt_httprequest_create(const v8::FunctionCallbackInfo& args); +void runt_httprequest_open(const v8::FunctionCallbackInfo& args); +void runt_httprequest_send(const v8::FunctionCallbackInfo& args); +void runt_httprequest_abort(const v8::FunctionCallbackInfo& args); +void runt_httprequest_set_request_header(const v8::FunctionCallbackInfo& args); +void runt_httprequest_get_all_response_headers(const v8::FunctionCallbackInfo& args); +void runt_httprequest_get_response_header(const v8::FunctionCallbackInfo& args); +void runt_httprequest_get_ready_state(const v8::FunctionCallbackInfo& args); +void runt_httprequest_get_status(const v8::FunctionCallbackInfo& args); +void runt_httprequest_get_status_text(const v8::FunctionCallbackInfo& args); +void runt_httprequest_get_response_text(const v8::FunctionCallbackInfo& args); +void runt_httprequest_get_response_url(const v8::FunctionCallbackInfo& args); +void runt_httprequest_set_timeout(const v8::FunctionCallbackInfo& args); +void runt_httprequest_set_response_type(const v8::FunctionCallbackInfo& args); +void runt_httprequest_set_with_credentials(const v8::FunctionCallbackInfo& args); +void runt_httprequest_set_onreadystatechange(const v8::FunctionCallbackInfo& args); +void runt_httprequest_set_onload(const v8::FunctionCallbackInfo& args); +void runt_httprequest_set_onerror(const v8::FunctionCallbackInfo& args); +void runt_httprequest_set_ontimeout(const v8::FunctionCallbackInfo& args); +void runt_httprequest_set_onabort(const v8::FunctionCallbackInfo& args); +void runt_httprequest_set_onprogress(const v8::FunctionCallbackInfo& args); +void runt_httprequest_set_onloadstart(const v8::FunctionCallbackInfo& args); +void runt_httprequest_set_onloadend(const v8::FunctionCallbackInfo& args); + +void createXMLHttpRequestClass(v8::Isolate* isolate, v8::Local& global); + +#endif \ No newline at end of file diff --git a/Sources/lockfree_queue.h b/Sources/lockfree_queue.h new file mode 100644 index 0000000..66303f4 --- /dev/null +++ b/Sources/lockfree_queue.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +template +class LockFreeQueue { +private: + struct Node { + std::atomic data{nullptr}; + std::atomic next{nullptr}; + }; + + std::atomic head_{nullptr}; + std::atomic tail_{nullptr}; + +public: + LockFreeQueue() { + Node* dummy = new Node; + head_.store(dummy); + tail_.store(dummy); + } + + ~LockFreeQueue() { + Node* current = head_.load(); + while (current != nullptr) { + Node* next = current->next.load(); + T* data = current->data.load(); + if (data != nullptr) { + delete data; + } + delete current; + current = next; + } + head_.store(nullptr); + tail_.store(nullptr); + } + + void push(T item) { + Node* new_node = new Node; + T* data = new T(std::move(item)); + new_node->data.store(data); + + Node* prev_tail = tail_.exchange(new_node); + prev_tail->next.store(new_node); + } + + bool try_pop(T& result) { + Node* head = head_.load(); + Node* next = head->next.load(); + + if (next == nullptr) { + return false; + } + + T* data = next->data.load(); + if (data == nullptr) { + return false; + } + + result = *data; + delete data; + + head_.store(next); + delete head; + + return true; + } + + bool empty() const { + Node* head = head_.load(); + Node* next = head->next.load(); + return (next == nullptr); + } +}; diff --git a/Sources/main.cpp b/Sources/main.cpp index 0c4d742..e98646a 100644 --- a/Sources/main.cpp +++ b/Sources/main.cpp @@ -21,7 +21,32 @@ #include #include #include +#ifdef WITH_NETWORKING #include +#include "httprequest.h" +#include "websocket_config.h" + +#include "socket_bridge.h" +#include "socket_v8_bindings.h" + +#ifdef WITH_UWS +#include "uws_websocket_bridge.h" +#include "uws_websocket_v8_bindings.h" +#else +#include "websocket_bridge.h" +#include "websocket_v8_bindings.h" +#include "websocket.h" +#endif + +#ifdef WITH_BENCHMARK +#include "BENCHMARK/websocket_benchmark.h" +extern "C" void websocket_run_benchmark(); +extern "C" void websocket_run_groupchat_test(int rate, int duration); +#endif + +#endif + +//#include "socket_connection_test.cpp" #include #include #include @@ -65,6 +90,19 @@ bool show_window = false; #include "worker.h" #endif +#ifdef WITH_VIEWPORT +#include "viewport_server.h" +#endif + +// TODO +//#ifdef KINC_KONG +//#include +//#endif + +#include +#include +#include + using namespace v8; #ifdef KORE_MACOS @@ -83,6 +121,13 @@ namespace { bool in_background = false; int paused_frames = 0; + #ifdef WITH_VIEWPORT + bool viewport_server_mode = false; + char viewport_shmem_name[256] = VIEWPORT_SHMEM_NAME; + int viewport_width = 1920; + int viewport_height = 1080; + #endif + Isolate *isolate; std::unique_ptr plat; Global global_context; @@ -158,7 +203,7 @@ namespace { wchar_t temp_wstring1[1024]; #endif - class KromCallbackdata { + class RunTCallbackdata { public: int32_t size; Global func; @@ -194,8 +239,15 @@ namespace { write_stack_trace(*stack_trace); } } + + void runt_log(const FunctionCallbackInfo &args); + void runt_set_timeout(const FunctionCallbackInfo &args); + void runt_set_interval(const FunctionCallbackInfo &args); + void runt_clear_timeout(const FunctionCallbackInfo &args); + void runt_clear_interval(const FunctionCallbackInfo &args); + void execute_timers(); - void krom_init(const FunctionCallbackInfo &args) { + void runt_init(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; String::Utf8Value title(isolate, arg); @@ -290,17 +342,21 @@ namespace { kinc_gamepad_set_button_callback(gamepad_button); } - void krom_set_application_name(const FunctionCallbackInfo &args) { + void runt_set_application_name(const FunctionCallbackInfo &args) { // Name used by kinc_internal_save_path() HandleScope scope(args.GetIsolate()); String::Utf8Value name(isolate, args[0]); kinc_set_application_name(*name); } - void krom_log(const FunctionCallbackInfo &args) { - HandleScope scope(args.GetIsolate()); + void runt_log(const FunctionCallbackInfo &args) { + Isolate* current_isolate = args.GetIsolate(); + HandleScope scope(current_isolate); + if (args.Length() == 0) { + return; + } Local arg = args[0]; - String::Utf8Value value(isolate, arg); + String::Utf8Value value(current_isolate, arg); size_t len = strlen(*value); if (len < 2048) { kinc_log(KINC_LOG_LEVEL_INFO, *value); @@ -316,34 +372,34 @@ namespace { } } - void krom_clear_fast(Local receiver, int flags, int color, float depth, int stencil) { + void runt_clear_fast(Local receiver, int flags, int color, float depth, int stencil) { kinc_g4_clear(flags, color, depth, stencil); } - void krom_clear(const FunctionCallbackInfo &args) { + void runt_clear(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int flags = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); int color = args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); float depth = (float)args[2]->ToNumber(isolate->GetCurrentContext()).ToLocalChecked()->Value(); int stencil = args[3]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); - krom_clear_fast(args.This(), flags, color, depth, stencil); + runt_clear_fast(args.This(), flags, color, depth, stencil); } - void krom_set_callback(const FunctionCallbackInfo &args) { + void runt_set_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); update_func.Reset(isolate, func); } - void krom_set_drop_files_callback(const FunctionCallbackInfo &args) { + void runt_set_drop_files_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); drop_files_func.Reset(isolate, func); } - void krom_set_cut_copy_paste_callback(const FunctionCallbackInfo &args) { + void runt_set_cut_copy_paste_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local cutArg = args[0]; Local cutFunc = Local::Cast(cutArg); @@ -356,7 +412,7 @@ namespace { paste_func.Reset(isolate, pasteFunc); } - void krom_set_application_state_callback(const FunctionCallbackInfo &args) { + void runt_set_application_state_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local foregroundArg = args[0]; Local foregroundFunc = Local::Cast(foregroundArg); @@ -375,180 +431,180 @@ namespace { shutdown_func.Reset(isolate, shutdownFunc); } - void krom_set_keyboard_down_callback(const FunctionCallbackInfo &args) { + void runt_set_keyboard_down_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); keyboard_down_func.Reset(isolate, func); } - void krom_set_keyboard_up_callback(const FunctionCallbackInfo &args) { + void runt_set_keyboard_up_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); keyboard_up_func.Reset(isolate, func); } - void krom_set_keyboard_press_callback(const FunctionCallbackInfo &args) { + void runt_set_keyboard_press_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); keyboard_press_func.Reset(isolate, func); } - void krom_set_mouse_down_callback(const FunctionCallbackInfo &args) { + void runt_set_mouse_down_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); mouse_down_func.Reset(isolate, func); } - void krom_set_mouse_up_callback(const FunctionCallbackInfo &args) { + void runt_set_mouse_up_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); mouse_up_func.Reset(isolate, func); } - void krom_set_mouse_move_callback(const FunctionCallbackInfo &args) { + void runt_set_mouse_move_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); mouse_move_func.Reset(isolate, func); } - void krom_set_touch_down_callback(const FunctionCallbackInfo &args) { + void runt_set_touch_down_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); touch_down_func.Reset(isolate, func); } - void krom_set_touch_up_callback(const FunctionCallbackInfo &args) { + void runt_set_touch_up_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); touch_up_func.Reset(isolate, func); } - void krom_set_touch_move_callback(const FunctionCallbackInfo &args) { + void runt_set_touch_move_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); touch_move_func.Reset(isolate, func); } - void krom_set_mouse_wheel_callback(const FunctionCallbackInfo &args) { + void runt_set_mouse_wheel_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); mouse_wheel_func.Reset(isolate, func); } - void krom_set_pen_down_callback(const FunctionCallbackInfo &args) { + void runt_set_pen_down_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); pen_down_func.Reset(isolate, func); } - void krom_set_pen_up_callback(const FunctionCallbackInfo &args) { + void runt_set_pen_up_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); pen_up_func.Reset(isolate, func); } - void krom_set_pen_move_callback(const FunctionCallbackInfo &args) { + void runt_set_pen_move_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); pen_move_func.Reset(isolate, func); } - void krom_set_gamepad_axis_callback(const FunctionCallbackInfo &args) { + void runt_set_gamepad_axis_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); gamepad_axis_func.Reset(isolate, func); } - void krom_set_gamepad_button_callback(const FunctionCallbackInfo &args) { + void runt_set_gamepad_button_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); gamepad_button_func.Reset(isolate, func); } - void krom_lock_mouse_fast(Local receiver) { + void runt_lock_mouse_fast(Local receiver) { kinc_mouse_lock(0); } - void krom_lock_mouse(const FunctionCallbackInfo &args) { + void runt_lock_mouse(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); - krom_lock_mouse_fast(args.This()); + runt_lock_mouse_fast(args.This()); } - void krom_unlock_mouse_fast(Local receiver) { + void runt_unlock_mouse_fast(Local receiver) { kinc_mouse_unlock(); } - void krom_unlock_mouse(const FunctionCallbackInfo &args) { + void runt_unlock_mouse(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); - krom_unlock_mouse_fast(args.This()); + runt_unlock_mouse_fast(args.This()); } - int krom_can_lock_mouse_fast(Local receiver) { + int runt_can_lock_mouse_fast(Local receiver) { return kinc_mouse_can_lock(); } - void krom_can_lock_mouse(const FunctionCallbackInfo &args) { + void runt_can_lock_mouse(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); - args.GetReturnValue().Set(Int32::New(isolate, krom_can_lock_mouse_fast(args.This()))); + args.GetReturnValue().Set(Int32::New(isolate, runt_can_lock_mouse_fast(args.This()))); } - int krom_is_mouse_locked_fast(Local receiver) { + int runt_is_mouse_locked_fast(Local receiver) { return kinc_mouse_is_locked(); } - void krom_is_mouse_locked(const FunctionCallbackInfo &args) { + void runt_is_mouse_locked(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); - args.GetReturnValue().Set(Int32::New(isolate, krom_is_mouse_locked_fast(args.This()))); + args.GetReturnValue().Set(Int32::New(isolate, runt_is_mouse_locked_fast(args.This()))); } - void krom_set_mouse_position_fast(Local receiver, int windowId, int x, int y) { + void runt_set_mouse_position_fast(Local receiver, int windowId, int x, int y) { kinc_mouse_set_position(windowId, x, y); } - void krom_set_mouse_position(const FunctionCallbackInfo &args) { + void runt_set_mouse_position(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int windowId = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); int x = args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); int y = args[2]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); - krom_set_mouse_position_fast(args.This(), windowId, x, y); + runt_set_mouse_position_fast(args.This(), windowId, x, y); } - void krom_show_mouse_fast(Local receiver, int show) { + void runt_show_mouse_fast(Local receiver, int show) { show ? kinc_mouse_show() : kinc_mouse_hide(); } - void krom_show_mouse(const FunctionCallbackInfo &args) { + void runt_show_mouse(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int show = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); - krom_show_mouse_fast(args.This(), show); + runt_show_mouse_fast(args.This(), show); } - void krom_show_keyboard_fast(Local receiver, int show) { + void runt_show_keyboard_fast(Local receiver, int show) { show ? kinc_keyboard_show() : kinc_keyboard_hide(); } - void krom_show_keyboard(const FunctionCallbackInfo &args) { + void runt_show_keyboard(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int show = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); - krom_show_keyboard_fast(args.This(), show); + runt_show_keyboard_fast(args.This(), show); } - void krom_create_indexbuffer(const FunctionCallbackInfo &args) { + void runt_create_indexbuffer(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local templ = ObjectTemplate::New(isolate); templ->SetInternalFieldCount(1); @@ -559,7 +615,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_delete_indexbuffer(const FunctionCallbackInfo &args) { + void runt_delete_indexbuffer(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local field = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_index_buffer_t *buffer = (kinc_g4_index_buffer_t *)field->Value(); @@ -567,7 +623,7 @@ namespace { free(buffer); } - void krom_lock_indexbuffer(const FunctionCallbackInfo &args) { + void runt_lock_indexbuffer(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local field = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_index_buffer_t *buffer = (kinc_g4_index_buffer_t *)field->Value(); @@ -577,14 +633,14 @@ namespace { args.GetReturnValue().Set(Uint32Array::New(abuffer, 0, kinc_g4_index_buffer_count(buffer))); } - void krom_unlock_indexbuffer(const FunctionCallbackInfo &args) { + void runt_unlock_indexbuffer(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local field = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_index_buffer_t *buffer = (kinc_g4_index_buffer_t *)field->Value(); kinc_g4_index_buffer_unlock_all(buffer); } - void krom_set_indexbuffer(const FunctionCallbackInfo &args) { + void runt_set_indexbuffer(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local field = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_index_buffer_t *buffer = (kinc_g4_index_buffer_t *)field->Value(); @@ -671,7 +727,7 @@ namespace { return KINC_G4_VERTEX_DATA_NONE; } - void krom_create_vertexbuffer(const FunctionCallbackInfo &args) { + void runt_create_vertexbuffer(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local templ = ObjectTemplate::New(isolate); templ->SetInternalFieldCount(1); @@ -694,7 +750,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_delete_vertexbuffer(const FunctionCallbackInfo &args) { + void runt_delete_vertexbuffer(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local field = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_vertex_buffer_t *buffer = (kinc_g4_vertex_buffer_t *)field->Value(); @@ -702,7 +758,7 @@ namespace { free(buffer); } - void krom_lock_vertex_buffer(const FunctionCallbackInfo &args) { + void runt_lock_vertex_buffer(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local field = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_vertex_buffer_t *buffer = (kinc_g4_vertex_buffer_t *)field->Value(); @@ -714,7 +770,7 @@ namespace { args.GetReturnValue().Set(abuffer); } - void krom_unlock_vertex_buffer(const FunctionCallbackInfo &args) { + void runt_unlock_vertex_buffer(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local field = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_vertex_buffer_t *buffer = (kinc_g4_vertex_buffer_t *)field->Value(); @@ -722,14 +778,14 @@ namespace { kinc_g4_vertex_buffer_unlock(buffer, count); } - void krom_set_vertexbuffer(const FunctionCallbackInfo &args) { + void runt_set_vertexbuffer(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local field = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_vertex_buffer_t *buffer = (kinc_g4_vertex_buffer_t *)field->Value(); kinc_g4_set_vertex_buffer(buffer); } - void krom_set_vertexbuffers(const FunctionCallbackInfo &args) { + void runt_set_vertexbuffers(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); kinc_g4_vertex_buffer_t *vertex_buffers[8] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; Local jsarray = args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked(); @@ -743,32 +799,32 @@ namespace { kinc_g4_set_vertex_buffers(vertex_buffers, length); } - void krom_draw_indexed_vertices_fast(Local receiver, int start, int count) { + void runt_draw_indexed_vertices_fast(Local receiver, int start, int count) { if (count < 0) kinc_g4_draw_indexed_vertices(); else kinc_g4_draw_indexed_vertices_from_to(start, count); } - void krom_draw_indexed_vertices(const FunctionCallbackInfo &args) { + void runt_draw_indexed_vertices(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int start = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); int count = args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); - krom_draw_indexed_vertices_fast(args.This(), start, count); + runt_draw_indexed_vertices_fast(args.This(), start, count); } - void krom_draw_indexed_vertices_instanced_fast(Local receiver, int instance_count, int start, int count) { + void runt_draw_indexed_vertices_instanced_fast(Local receiver, int instance_count, int start, int count) { if (count < 0) kinc_g4_draw_indexed_vertices_instanced(instance_count); else kinc_g4_draw_indexed_vertices_instanced_from_to(instance_count, start, count); } - void krom_draw_indexed_vertices_instanced(const FunctionCallbackInfo &args) { + void runt_draw_indexed_vertices_instanced(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int instance_count = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); int start = args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); int count = args[2]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); - krom_draw_indexed_vertices_instanced_fast(args.This(), instance_count, start, count); + runt_draw_indexed_vertices_instanced_fast(args.This(), instance_count, start, count); } - void krom_create_vertex_shader(const FunctionCallbackInfo &args) { + void runt_create_vertex_shader(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local buffer = Local::Cast(args[0]); std::shared_ptr content = buffer->GetBackingStore(); @@ -784,7 +840,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_create_vertex_shader_from_source(const FunctionCallbackInfo &args) { + void runt_create_vertex_shader_from_source(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); String::Utf8Value utf8_value(isolate, args[0]); @@ -803,7 +859,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_create_fragment_shader(const FunctionCallbackInfo &args) { + void runt_create_fragment_shader(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local buffer = Local::Cast(args[0]); std::shared_ptr content = buffer->GetBackingStore(); @@ -819,7 +875,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_create_fragment_shader_from_source(const FunctionCallbackInfo &args) { + void runt_create_fragment_shader_from_source(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); String::Utf8Value utf8_value(isolate, args[0]); @@ -838,7 +894,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_create_geometry_shader(const FunctionCallbackInfo &args) { + void runt_create_geometry_shader(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local buffer = Local::Cast(args[0]); std::shared_ptr content = buffer->GetBackingStore(); @@ -854,7 +910,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_create_tessellation_control_shader(const FunctionCallbackInfo &args) { + void runt_create_tessellation_control_shader(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local buffer = Local::Cast(args[0]); std::shared_ptr content = buffer->GetBackingStore(); @@ -870,7 +926,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_create_tessellation_evaluation_shader(const FunctionCallbackInfo &args) { + void runt_create_tessellation_evaluation_shader(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local buffer = Local::Cast(args[0]); std::shared_ptr content = buffer->GetBackingStore(); @@ -886,7 +942,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_delete_shader(const FunctionCallbackInfo &args) { + void runt_delete_shader(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local field = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_shader_t *shader = (kinc_g4_shader_t *)field->Value(); @@ -894,7 +950,7 @@ namespace { free(shader); } - void krom_create_pipeline(const FunctionCallbackInfo &args) { + void runt_create_pipeline(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); kinc_g4_pipeline_t *pipeline = (kinc_g4_pipeline_t *)malloc(sizeof(kinc_g4_pipeline_t)); kinc_g4_pipeline_init(pipeline); @@ -907,7 +963,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_delete_pipeline(const FunctionCallbackInfo &args) { + void runt_delete_pipeline(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local pipeobj = args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked(); Local pipefield = Local::Cast(pipeobj->GetInternalField(0)); @@ -916,7 +972,7 @@ namespace { free(pipeline); } - void krom_compile_pipeline(const FunctionCallbackInfo &args) { + void runt_compile_pipeline(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local pipeobj = args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked(); @@ -1016,7 +1072,7 @@ namespace { kinc_g4_pipeline_compile(pipeline); } - void krom_set_pipeline(const FunctionCallbackInfo &args) { + void runt_set_pipeline(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local pipeobj = args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked(); Local pipefield = Local::Cast(pipeobj->GetInternalField(0)); @@ -1085,7 +1141,7 @@ namespace { return success; } - void krom_load_image(const FunctionCallbackInfo &args) { + void runt_load_image(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); String::Utf8Value utf8_value(isolate, args[0]); bool readable = args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); @@ -1147,7 +1203,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_unload_image(const FunctionCallbackInfo &args) { + void runt_unload_image(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); if (args[0]->IsNullOrUndefined()) return; Local image = args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked(); @@ -1169,21 +1225,21 @@ namespace { } #ifdef WITH_AUDIO - void krom_set_audio_callback(const FunctionCallbackInfo &args) { + void runt_set_audio_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); audio_func.Reset(isolate, func); } - void krom_audio_thread(const FunctionCallbackInfo &args) { + void runt_audio_thread(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); bool lock = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); if (lock) kinc_mutex_lock(&mutex); //Locker::Locker(isolate); else kinc_mutex_unlock(&mutex); //Unlocker(args.GetIsolate()); } - void krom_load_sound(const FunctionCallbackInfo &args) { + void runt_load_sound(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); String::Utf8Value utf8_value(isolate, args[0]); kinc_a1_sound_t *sound = kinc_a1_sound_create(*utf8_value); @@ -1206,7 +1262,7 @@ namespace { kinc_a1_sound_destroy(sound); } - void krom_write_audio_buffer(const FunctionCallbackInfo &args) { + void runt_write_audio_buffer(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local buffer = Local::Cast(args[0]); std::shared_ptr content = buffer->GetBackingStore(); @@ -1223,24 +1279,50 @@ namespace { } } - int krom_get_samples_per_second_fast(Local receiver) { + int runt_get_samples_per_second_fast(Local receiver) { kinc_log(KINC_LOG_LEVEL_INFO, "Samples per second: %d Hz.", kinc_a2_samples_per_second); return kinc_a2_samples_per_second; } - void krom_get_samples_per_second(const FunctionCallbackInfo &args) { + void runt_get_samples_per_second(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); - args.GetReturnValue().Set(Int32::New(isolate, krom_get_samples_per_second_fast(args.This()))); + args.GetReturnValue().Set(Int32::New(isolate, runt_get_samples_per_second_fast(args.This()))); } void update_audio(kinc_a2_buffer_t *buffer, int samples) { - // kinc_mutex_lock(&mutex); + if (!isolate || isolate->IsExecutionTerminating()) { + // TODO: Re-investigate fill buffer with silence during shutdown + for (int i = 0; i < samples; ++i) { + *(float *)&buffer->data[buffer->write_location] = 0.0f; + buffer->write_location += 4; + if (buffer->write_location >= buffer->data_size) { + buffer->write_location = 0; + } + } + return; + } + + kinc_mutex_lock(&mutex); Locker locker{isolate}; Isolate::Scope isolate_scope(isolate); MicrotasksScope microtasks_scope(isolate, MicrotasksScope::kRunMicrotasks); HandleScope handle_scope(isolate); + + if (global_context.IsEmpty()) { + kinc_mutex_unlock(&mutex); + // TODO: re-investigate + for (int i = 0; i < samples; ++i) { + *(float *)&buffer->data[buffer->write_location] = 0.0f; + buffer->write_location += 4; + if (buffer->write_location >= buffer->data_size) { + buffer->write_location = 0; + } + } + return; + } + Local context = Local::New(isolate, global_context); Context::Scope context_scope(context); @@ -1267,29 +1349,29 @@ namespace { } } - // kinc_mutex_unlock(&mutex); + kinc_mutex_unlock(&mutex); } #else - void krom_set_audio_callback(const FunctionCallbackInfo &args) { + void runt_set_audio_callback(const FunctionCallbackInfo &args) { } - void krom_audio_thread(const FunctionCallbackInfo &args) { + void runt_audio_thread(const FunctionCallbackInfo &args) { } - void krom_load_sound(const FunctionCallbackInfo &args) { + void runt_load_sound(const FunctionCallbackInfo &args) { } - void krom_write_audio_buffer(const FunctionCallbackInfo &args) { + void runt_write_audio_buffer(const FunctionCallbackInfo &args) { } - void krom_get_samples_per_second(const FunctionCallbackInfo &args) { + void runt_get_samples_per_second(const FunctionCallbackInfo &args) { } #endif - void krom_load_blob(const FunctionCallbackInfo &args) { + void runt_load_blob(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); String::Utf8Value utf8_value(isolate, args[0]); @@ -1305,19 +1387,19 @@ namespace { args.GetReturnValue().Set(buffer); } - void krom_load_url(const FunctionCallbackInfo &args) { + void runt_load_url(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); String::Utf8Value utf8_value(isolate, args[0]); kinc_load_url(*utf8_value); } - void krom_copy_to_clipboard(const FunctionCallbackInfo& args) { + void runt_copy_to_clipboard(const FunctionCallbackInfo& args) { HandleScope scope(args.GetIsolate()); String::Utf8Value utf8_value(isolate, args[0]); kinc_copy_to_clipboard(*utf8_value); } - void krom_get_constant_location(const FunctionCallbackInfo &args) { + void runt_get_constant_location(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local pipefield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_pipeline_t *pipeline = (kinc_g4_pipeline_t *)pipefield->Value(); @@ -1335,7 +1417,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_get_texture_unit(const FunctionCallbackInfo &args) { + void runt_get_texture_unit(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local pipefield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_pipeline_t *pipeline = (kinc_g4_pipeline_t *)pipefield->Value(); @@ -1353,7 +1435,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_set_texture(const FunctionCallbackInfo &args) { + void runt_set_texture(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local unitfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_texture_unit_t *unit = (kinc_g4_texture_unit_t *)unitfield->Value(); @@ -1362,7 +1444,7 @@ namespace { kinc_g4_set_texture(*unit, texture); } - void krom_set_render_target(const FunctionCallbackInfo &args) { + void runt_set_render_target(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local unitfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_texture_unit_t *unit = (kinc_g4_texture_unit_t *)unitfield->Value(); @@ -1371,7 +1453,7 @@ namespace { kinc_g4_render_target_use_color_as_texture(render_target, *unit); } - void krom_set_texture_depth(const FunctionCallbackInfo &args) { + void runt_set_texture_depth(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local unitfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_texture_unit_t *unit = (kinc_g4_texture_unit_t *)unitfield->Value(); @@ -1380,7 +1462,7 @@ namespace { kinc_g4_render_target_use_depth_as_texture(render_target, *unit); } - void krom_set_image_texture(const FunctionCallbackInfo &args) { + void runt_set_image_texture(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local unitfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_texture_unit_t *unit = (kinc_g4_texture_unit_t *)unitfield->Value(); @@ -1389,7 +1471,7 @@ namespace { kinc_g4_set_image_texture(*unit, texture); } - void krom_set_texture_parameters(const FunctionCallbackInfo &args) { + void runt_set_texture_parameters(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local unitfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_texture_unit_t *unit = (kinc_g4_texture_unit_t *)unitfield->Value(); @@ -1400,7 +1482,7 @@ namespace { kinc_g4_set_texture_mipmap_filter(*unit, (kinc_g4_mipmap_filter_t)args[5]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Int32Value(isolate->GetCurrentContext()).FromJust()); } - void krom_set_texture3d_parameters(const FunctionCallbackInfo &args) { + void runt_set_texture3d_parameters(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local unitfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_texture_unit_t *unit = (kinc_g4_texture_unit_t *)unitfield->Value(); @@ -1412,21 +1494,21 @@ namespace { kinc_g4_set_texture3d_mipmap_filter(*unit, (kinc_g4_mipmap_filter_t)args[6]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Int32Value(isolate->GetCurrentContext()).FromJust()); } - void krom_set_texture_compare_mode(const FunctionCallbackInfo &args) { + void runt_set_texture_compare_mode(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local unitfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_texture_unit_t *unit = (kinc_g4_texture_unit_t *)unitfield->Value(); kinc_g4_set_texture_compare_mode(*unit, args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value()); } - void krom_set_cube_map_compare_mode(const FunctionCallbackInfo &args) { + void runt_set_cube_map_compare_mode(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local unitfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_texture_unit_t *unit = (kinc_g4_texture_unit_t *)unitfield->Value(); kinc_g4_set_cubemap_compare_mode(*unit, args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value()); } - void krom_set_bool(const FunctionCallbackInfo &args) { + void runt_set_bool(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_constant_location_t *location = (kinc_g4_constant_location_t *)locationfield->Value(); @@ -1434,7 +1516,7 @@ namespace { kinc_g4_set_bool(*location, value != 0); } - void krom_set_int(const FunctionCallbackInfo &args) { + void runt_set_int(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_constant_location_t *location = (kinc_g4_constant_location_t *)locationfield->Value(); @@ -1442,7 +1524,7 @@ namespace { kinc_g4_set_int(*location, value); } - void krom_set_float(const FunctionCallbackInfo &args) { + void runt_set_float(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_constant_location_t *location = (kinc_g4_constant_location_t *)locationfield->Value(); @@ -1450,7 +1532,7 @@ namespace { kinc_g4_set_float(*location, value); } - void krom_set_float2(const FunctionCallbackInfo &args) { + void runt_set_float2(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_constant_location_t *location = (kinc_g4_constant_location_t *)locationfield->Value(); @@ -1459,7 +1541,7 @@ namespace { kinc_g4_set_float2(*location, value1, value2); } - void krom_set_float3(const FunctionCallbackInfo &args) { + void runt_set_float3(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_constant_location_t *location = (kinc_g4_constant_location_t *)locationfield->Value(); @@ -1469,7 +1551,7 @@ namespace { kinc_g4_set_float3(*location, value1, value2, value3); } - void krom_set_float4(const FunctionCallbackInfo &args) { + void runt_set_float4(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_constant_location_t *location = (kinc_g4_constant_location_t *)locationfield->Value(); @@ -1480,7 +1562,7 @@ namespace { kinc_g4_set_float4(*location, value1, value2, value3, value4); } - void krom_set_floats(const FunctionCallbackInfo &args) { + void runt_set_floats(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_constant_location_t *location = (kinc_g4_constant_location_t *)locationfield->Value(); @@ -1492,7 +1574,7 @@ namespace { kinc_g4_set_floats(*location, from, int(content->ByteLength() / 4)); } - void krom_set_matrix(const FunctionCallbackInfo &args) { + void runt_set_matrix(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_constant_location_t *location = (kinc_g4_constant_location_t *)locationfield->Value(); @@ -1503,7 +1585,7 @@ namespace { kinc_g4_set_matrix4(*location, (kinc_matrix4x4_t *)from); } - void krom_set_matrix3(const FunctionCallbackInfo &args) { + void runt_set_matrix3(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_constant_location_t *location = (kinc_g4_constant_location_t *)locationfield->Value(); @@ -1514,56 +1596,56 @@ namespace { kinc_g4_set_matrix3(*location, (kinc_matrix3x3_t *)from); } - double krom_get_time_fast(Local receiver) { + double runt_get_time_fast(Local receiver) { return kinc_time(); } - void krom_get_time(const FunctionCallbackInfo &args) { + void runt_get_time(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); - args.GetReturnValue().Set(Number::New(isolate, krom_get_time_fast(args.This()))); + args.GetReturnValue().Set(Number::New(isolate, runt_get_time_fast(args.This()))); } - int krom_window_width_fast(Local receiver, int windowId) { + int runt_window_width_fast(Local receiver, int windowId) { return kinc_window_width(windowId); } - void krom_window_width(const FunctionCallbackInfo &args) { + void runt_window_width(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int windowId = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); - args.GetReturnValue().Set(Int32::New(isolate, krom_window_width_fast(args.This(), windowId))); + args.GetReturnValue().Set(Int32::New(isolate, runt_window_width_fast(args.This(), windowId))); } - int krom_window_height_fast(Local receiver, int windowId) { + int runt_window_height_fast(Local receiver, int windowId) { return kinc_window_height(windowId); } - void krom_window_height(const FunctionCallbackInfo &args) { + void runt_window_height(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int windowId = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); - args.GetReturnValue().Set(Int32::New(isolate, krom_window_height_fast(args.This(), windowId))); + args.GetReturnValue().Set(Int32::New(isolate, runt_window_height_fast(args.This(), windowId))); } - void krom_set_window_title(const FunctionCallbackInfo &args) { + void runt_set_window_title(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int windowId = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); String::Utf8Value title(isolate, args[1]); kinc_window_set_title(windowId, *title); } - void krom_get_window_mode(const FunctionCallbackInfo &args) { + void runt_get_window_mode(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int windowId = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); args.GetReturnValue().Set(Int32::New(isolate, kinc_window_get_mode(windowId))); } - void krom_set_window_mode(const FunctionCallbackInfo &args) { + void runt_set_window_mode(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int windowId = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); kinc_window_mode_t windowMode = (kinc_window_mode_t)args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); kinc_window_change_mode(windowId, windowMode); } - void krom_resize_window(const FunctionCallbackInfo &args) { + void runt_resize_window(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int windowId = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); int width = args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); @@ -1571,7 +1653,7 @@ namespace { kinc_window_resize(windowId, width, height); } - void krom_move_window(const FunctionCallbackInfo &args) { + void runt_move_window(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int windowId = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); int x = args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); @@ -1579,58 +1661,58 @@ namespace { kinc_window_move(windowId, x, y); } - void krom_screen_dpi(const FunctionCallbackInfo &args) { + void runt_screen_dpi(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int ppi = kinc_display_current_mode(kinc_primary_display()).pixels_per_inch; args.GetReturnValue().Set(Int32::New(isolate, ppi)); } - void krom_system_id(const FunctionCallbackInfo &args) { + void runt_system_id(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); args.GetReturnValue().Set(String::NewFromUtf8(isolate, kinc_system_id()).ToLocalChecked()); } - void krom_request_shutdown(const FunctionCallbackInfo &args) { + void runt_request_shutdown(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); kinc_stop(); } - void krom_display_count(const FunctionCallbackInfo &args) { + void runt_display_count(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); args.GetReturnValue().Set(Int32::New(isolate, kinc_count_displays())); } - void krom_display_width(const FunctionCallbackInfo &args) { + void runt_display_width(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int index = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); args.GetReturnValue().Set(Int32::New(isolate, kinc_display_current_mode(index).width)); } - void krom_display_height(const FunctionCallbackInfo &args) { + void runt_display_height(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int index = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); args.GetReturnValue().Set(Int32::New(isolate, kinc_display_current_mode(index).height)); } - void krom_display_x(const FunctionCallbackInfo &args) { + void runt_display_x(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int index = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); args.GetReturnValue().Set(Int32::New(isolate, kinc_display_current_mode(index).x)); } - void krom_display_y(const FunctionCallbackInfo &args) { + void runt_display_y(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int index = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); args.GetReturnValue().Set(Int32::New(isolate, kinc_display_current_mode(index).y)); } - void krom_display_frequency(const FunctionCallbackInfo &args) { + void runt_display_frequency(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int index = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); args.GetReturnValue().Set(Int32::New(isolate, kinc_display_current_mode(index).frequency)); } - void krom_display_is_primary(const FunctionCallbackInfo &args) { + void runt_display_is_primary(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int index = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); #ifdef KORE_LINUX // TODO: Primary display detection broken in Kinc @@ -1640,7 +1722,7 @@ namespace { #endif } - void krom_write_storage(const FunctionCallbackInfo &args) { + void runt_write_storage(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); String::Utf8Value utf8_name(isolate, args[0]); @@ -1653,7 +1735,7 @@ namespace { kinc_file_writer_close(&writer); } - void krom_read_storage(const FunctionCallbackInfo &args) { + void runt_read_storage(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); String::Utf8Value utf8_name(isolate, args[0]); @@ -1669,7 +1751,7 @@ namespace { args.GetReturnValue().Set(buffer); } - void krom_create_render_target(const FunctionCallbackInfo &args) { + void runt_create_render_target(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); kinc_g4_render_target_t *render_target = (kinc_g4_render_target_t *)malloc(sizeof(kinc_g4_render_target_t)); kinc_g4_render_target_init(render_target, args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(), args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(), (kinc_g4_render_target_format_t)args[2]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(), args[3]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(), args[4]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value()); @@ -1684,7 +1766,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_create_render_target_cube_map(const FunctionCallbackInfo &args) { + void runt_create_render_target_cube_map(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); kinc_g4_render_target_t *render_target = (kinc_g4_render_target_t *)malloc(sizeof(kinc_g4_render_target_t)); kinc_g4_render_target_init_cube(render_target, args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(), (kinc_g4_render_target_format_t)args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(), args[2]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(), args[3]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value()); @@ -1699,7 +1781,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_create_texture(const FunctionCallbackInfo &args) { + void runt_create_texture(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); kinc_g4_texture_t *texture = (kinc_g4_texture_t *)malloc(sizeof(kinc_g4_texture_t)); kinc_g4_texture_init(texture, args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(), args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(), (kinc_image_format_t)args[2]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value()); @@ -1716,7 +1798,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_create_texture3d(const FunctionCallbackInfo &args) { + void runt_create_texture3d(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); kinc_g4_texture_t *texture = (kinc_g4_texture_t *)malloc(sizeof(kinc_g4_texture_t)); kinc_g4_texture_init3d(texture, args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(), args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(), args[2]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(), (kinc_image_format_t)args[3]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value()); @@ -1732,7 +1814,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_create_texture_from_bytes(const FunctionCallbackInfo &args) { + void runt_create_texture_from_bytes(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local buffer = Local::Cast(args[0]); std::shared_ptr content = buffer->GetBackingStore(); @@ -1769,7 +1851,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_create_texture_from_bytes3d(const FunctionCallbackInfo &args) { + void runt_create_texture_from_bytes3d(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local buffer = Local::Cast(args[0]); std::shared_ptr content = buffer->GetBackingStore(); @@ -1805,7 +1887,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_create_texture_from_encoded_bytes(const FunctionCallbackInfo &args) { + void runt_create_texture_from_encoded_bytes(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local buffer = Local::Cast(args[0]); std::shared_ptr content = buffer->GetBackingStore(); @@ -1895,7 +1977,7 @@ namespace { } } - void krom_get_texture_pixels(const FunctionCallbackInfo &args) { + void runt_get_texture_pixels(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local field = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(1)); @@ -1908,7 +1990,7 @@ namespace { args.GetReturnValue().Set(buffer); } - void krom_get_render_target_pixels(const FunctionCallbackInfo &args) { + void runt_get_render_target_pixels(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local field = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); @@ -1921,7 +2003,7 @@ namespace { kinc_g4_render_target_get_pixels(rt, b); } - void krom_lock_texture(const FunctionCallbackInfo &args) { + void runt_lock_texture(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local textureobj = args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked(); Local field = Local::Cast(textureobj->GetInternalField(0)); @@ -1937,14 +2019,14 @@ namespace { args.GetReturnValue().Set(abuffer); } - void krom_unlock_texture(const FunctionCallbackInfo &args) { + void runt_unlock_texture(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local field = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_texture_t *texture = (kinc_g4_texture_t *)field->Value(); kinc_g4_texture_unlock(texture); } - void krom_clear_texture(const FunctionCallbackInfo &args) { + void runt_clear_texture(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local field = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_texture_t *texture = (kinc_g4_texture_t *)field->Value(); @@ -1958,21 +2040,21 @@ namespace { kinc_g4_texture_clear(texture, x, y, z, width, height, depth, color); } - void krom_generate_texture_mipmaps(const FunctionCallbackInfo &args) { + void runt_generate_texture_mipmaps(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local field = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_texture_t *texture = (kinc_g4_texture_t *)field->Value(); kinc_g4_texture_generate_mipmaps(texture, args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value()); } - void krom_generate_render_target_mipmaps(const FunctionCallbackInfo &args) { + void runt_generate_render_target_mipmaps(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local field = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_render_target_t *rt = (kinc_g4_render_target_t *)field->Value(); kinc_g4_render_target_generate_mipmaps(rt, args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value()); } - void krom_set_mipmaps(const FunctionCallbackInfo &args) { + void runt_set_mipmaps(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local field = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_texture_t *texture = (kinc_g4_texture_t *)field->Value(); @@ -1987,7 +2069,7 @@ namespace { } } - void krom_set_depth_stencil_from(const FunctionCallbackInfo &args) { + void runt_set_depth_stencil_from(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local targetfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_g4_render_target_t *render_target = (kinc_g4_render_target_t *)targetfield->Value(); @@ -1996,50 +2078,50 @@ namespace { kinc_g4_render_target_set_depth_stencil_from(render_target, source_target); } - void krom_viewport_fast(Local receiver, int x, int y, int w, int h) { + void runt_viewport_fast(Local receiver, int x, int y, int w, int h) { kinc_g4_viewport(x, y, w, h); } - void krom_viewport(const FunctionCallbackInfo &args) { + void runt_viewport(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int x = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Int32Value(isolate->GetCurrentContext()).FromJust(); int y = args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Int32Value(isolate->GetCurrentContext()).FromJust(); int w = args[2]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Int32Value(isolate->GetCurrentContext()).FromJust(); int h = args[3]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Int32Value(isolate->GetCurrentContext()).FromJust(); - krom_viewport_fast(args.This(), x, y, w, h); + runt_viewport_fast(args.This(), x, y, w, h); } - void krom_scissor_fast(Local receiver, int x, int y, int w, int h) { + void runt_scissor_fast(Local receiver, int x, int y, int w, int h) { kinc_g4_scissor(x, y, w, h); } - void krom_scissor(const FunctionCallbackInfo &args) { + void runt_scissor(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int x = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Int32Value(isolate->GetCurrentContext()).FromJust(); int y = args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Int32Value(isolate->GetCurrentContext()).FromJust(); int w = args[2]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Int32Value(isolate->GetCurrentContext()).FromJust(); int h = args[3]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Int32Value(isolate->GetCurrentContext()).FromJust(); - krom_scissor_fast(args.This(), x, y, w, h); + runt_scissor_fast(args.This(), x, y, w, h); } - void krom_disable_scissor_fast(Local receiver) { + void runt_disable_scissor_fast(Local receiver) { kinc_g4_disable_scissor(); } - void krom_disable_scissor(const FunctionCallbackInfo &args) { - krom_disable_scissor_fast(args.This()); + void runt_disable_scissor(const FunctionCallbackInfo &args) { + runt_disable_scissor_fast(args.This()); } - int krom_render_targets_inverted_y_fast(Local receiver) { + int runt_render_targets_inverted_y_fast(Local receiver) { return kinc_g4_render_targets_inverted_y(); } - void krom_render_targets_inverted_y(const FunctionCallbackInfo &args) { + void runt_render_targets_inverted_y(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); - args.GetReturnValue().Set(Int32::New(isolate, krom_render_targets_inverted_y_fast(args.This()))); + args.GetReturnValue().Set(Int32::New(isolate, runt_render_targets_inverted_y_fast(args.This()))); } - void krom_begin(const FunctionCallbackInfo &args) { + void runt_begin(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); if (args[0]->IsNullOrUndefined()) { kinc_g4_restore_render_target(); @@ -2066,7 +2148,7 @@ namespace { } } - void krom_begin_face(const FunctionCallbackInfo &args) { + void runt_begin_face(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local obj = args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->Get(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "renderTarget_").ToLocalChecked()).ToLocalChecked()->ToObject(isolate->GetCurrentContext()).ToLocalChecked(); Local rtfield = Local::Cast(obj->GetInternalField(0)); @@ -2075,11 +2157,11 @@ namespace { kinc_g4_set_render_target_face(render_target, face); } - void krom_end(const FunctionCallbackInfo &args) { + void runt_end(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); } - void krom_file_save_bytes(const FunctionCallbackInfo &args) { + void runt_file_save_bytes(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); String::Utf8Value utf8_path(isolate, args[0]); @@ -2114,30 +2196,30 @@ namespace { return result; } - void krom_sys_command(const FunctionCallbackInfo &args) { + void runt_sys_command(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); String::Utf8Value utf8_cmd(isolate, args[0]); int result = sys_command(*utf8_cmd); args.GetReturnValue().Set(Int32::New(isolate, result)); } - void krom_save_path(const FunctionCallbackInfo &args) { + void runt_save_path(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); args.GetReturnValue().Set(String::NewFromUtf8(isolate, kinc_internal_save_path()).ToLocalChecked()); } - void krom_get_arg_count(const FunctionCallbackInfo &args) { + void runt_get_arg_count(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); args.GetReturnValue().Set(Int32::New(isolate, _argc)); } - void krom_get_arg(const FunctionCallbackInfo &args) { + void runt_get_arg(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int index = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); args.GetReturnValue().Set(String::NewFromUtf8(isolate, _argv[index]).ToLocalChecked()); } - void krom_get_files_location(const FunctionCallbackInfo &args) { + void runt_get_files_location(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); #ifdef KORE_MACOS char path[1024]; @@ -2151,7 +2233,7 @@ namespace { #endif } - void krom_http_callback(int error, int response, const char *body, void *callbackdata) { + void runt_http_callback(int error, int response, const char *body, void *callbackdata) { #if defined(KORE_MACOS) Locker locker{isolate}; #endif @@ -2164,7 +2246,7 @@ namespace { TryCatch try_catch(isolate); Local result; Local argv[1]; - KromCallbackdata *cbd = (KromCallbackdata *)callbackdata; + RunTCallbackdata *cbd = (RunTCallbackdata *)callbackdata; if (body != NULL) { std::unique_ptr backing = v8::ArrayBuffer::NewBackingStore((void *)body, cbd->size > 0 ? cbd->size : strlen(body), [](void *, size_t, void *) {}, nullptr); argv[0] = ArrayBuffer::New(isolate, std::move(backing)); @@ -2176,11 +2258,11 @@ namespace { delete cbd; } - void krom_http_request(const FunctionCallbackInfo &args) { + void runt_http_request(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); String::Utf8Value url(isolate, args[0]); - KromCallbackdata *cbd = new KromCallbackdata(); + RunTCallbackdata *cbd = new RunTCallbackdata(); cbd->size = args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); Local func = Local::Cast(args[2]); cbd->func.Reset(isolate, func); @@ -2201,11 +2283,11 @@ namespace { url_path[j] = curl[i + 8 + j]; } url_path[j] = 0; - kinc_http_request(url_base, url_path, NULL, 443, true, 0, NULL, &krom_http_callback, cbd); + kinc_http_request(url_base, url_path, NULL, 443, true, 0, NULL, &runt_http_callback, cbd); } #ifdef WITH_COMPUTE - void krom_set_bool_compute(const FunctionCallbackInfo &args) { + void runt_set_bool_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_constant_location_t *location = (kinc_compute_constant_location_t *)locationfield->Value(); @@ -2213,7 +2295,7 @@ namespace { kinc_compute_set_bool(*location, value != 0); } - void krom_set_int_compute(const FunctionCallbackInfo &args) { + void runt_set_int_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_constant_location_t *location = (kinc_compute_constant_location_t *)locationfield->Value(); @@ -2221,7 +2303,7 @@ namespace { kinc_compute_set_int(*location, value); } - void krom_set_float_compute(const FunctionCallbackInfo &args) { + void runt_set_float_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_constant_location_t *location = (kinc_compute_constant_location_t *)locationfield->Value(); @@ -2229,7 +2311,7 @@ namespace { kinc_compute_set_float(*location, value); } - void krom_set_float2_compute(const FunctionCallbackInfo &args) { + void runt_set_float2_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_constant_location_t *location = (kinc_compute_constant_location_t *)locationfield->Value(); @@ -2238,7 +2320,7 @@ namespace { kinc_compute_set_float2(*location, value1, value2); } - void krom_set_float3_compute(const FunctionCallbackInfo &args) { + void runt_set_float3_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_constant_location_t *location = (kinc_compute_constant_location_t *)locationfield->Value(); @@ -2248,7 +2330,7 @@ namespace { kinc_compute_set_float3(*location, value1, value2, value3); } - void krom_set_float4_compute(const FunctionCallbackInfo &args) { + void runt_set_float4_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_constant_location_t *location = (kinc_compute_constant_location_t *)locationfield->Value(); @@ -2259,7 +2341,7 @@ namespace { kinc_compute_set_float4(*location, value1, value2, value3, value4); } - void krom_set_floats_compute(const FunctionCallbackInfo &args) { + void runt_set_floats_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_constant_location_t *location = (kinc_compute_constant_location_t *)locationfield->Value(); @@ -2270,7 +2352,7 @@ namespace { kinc_compute_set_floats(*location, from, int(content->ByteLength() / 4)); } - void krom_set_matrix_compute(const FunctionCallbackInfo &args) { + void runt_set_matrix_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_constant_location_t *location = (kinc_compute_constant_location_t *)locationfield->Value(); @@ -2280,7 +2362,7 @@ namespace { kinc_compute_set_matrix4(*location, (kinc_matrix4x4_t *)from); } - void krom_set_matrix3_compute(const FunctionCallbackInfo &args) { + void runt_set_matrix3_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local locationfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_constant_location_t *location = (kinc_compute_constant_location_t *)locationfield->Value(); @@ -2290,7 +2372,7 @@ namespace { kinc_compute_set_matrix3(*location, (kinc_matrix3x3_t *)from); } - void krom_set_texture_compute(const FunctionCallbackInfo &args) { + void runt_set_texture_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local unitfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_texture_unit_t *unit = (kinc_compute_texture_unit_t *)unitfield->Value(); @@ -2300,7 +2382,7 @@ namespace { kinc_compute_set_texture(*unit, texture, (kinc_compute_access_t)access); } - void krom_set_render_target_compute(const FunctionCallbackInfo &args) { + void runt_set_render_target_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local unitfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_texture_unit_t *unit = (kinc_compute_texture_unit_t *)unitfield->Value(); @@ -2310,7 +2392,7 @@ namespace { kinc_compute_set_render_target(*unit, render_target, (kinc_compute_access_t)access); } - void krom_set_sampled_texture_compute(const FunctionCallbackInfo &args) { + void runt_set_sampled_texture_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local unitfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_texture_unit_t *unit = (kinc_compute_texture_unit_t *)unitfield->Value(); @@ -2319,7 +2401,7 @@ namespace { kinc_compute_set_sampled_texture(*unit, texture); } - void krom_set_sampled_render_target_compute(const FunctionCallbackInfo &args) { + void runt_set_sampled_render_target_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local unitfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_texture_unit_t *unit = (kinc_compute_texture_unit_t *)unitfield->Value(); @@ -2328,7 +2410,7 @@ namespace { kinc_compute_set_sampled_render_target(*unit, render_target); } - void krom_set_sampled_depth_texture_compute(const FunctionCallbackInfo &args) { + void runt_set_sampled_depth_texture_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local unitfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_texture_unit_t *unit = (kinc_compute_texture_unit_t *)unitfield->Value(); @@ -2337,7 +2419,7 @@ namespace { kinc_compute_set_sampled_depth_from_render_target(*unit, render_target); } - void krom_set_texture_parameters_compute(const FunctionCallbackInfo &args) { + void runt_set_texture_parameters_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local unitfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_texture_unit_t *unit = (kinc_compute_texture_unit_t *)unitfield->Value(); @@ -2348,7 +2430,7 @@ namespace { kinc_compute_set_texture_mipmap_filter(*unit, (kinc_g4_mipmap_filter_t)args[5]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Int32Value(isolate->GetCurrentContext()).FromJust()); } - void krom_set_texture3d_parameters_compute(const FunctionCallbackInfo &args) { + void runt_set_texture3d_parameters_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local unitfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_texture_unit_t *unit = (kinc_compute_texture_unit_t *)unitfield->Value(); @@ -2360,14 +2442,14 @@ namespace { kinc_compute_set_texture3d_mipmap_filter(*unit, (kinc_g4_mipmap_filter_t)args[6]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Int32Value(isolate->GetCurrentContext()).FromJust()); } - void krom_set_shader_compute(const FunctionCallbackInfo &args) { + void runt_set_shader_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local shaderfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_shader *shader = (kinc_compute_shader *)shaderfield->Value(); kinc_compute_set_shader(shader); } - void krom_create_shader_compute(const FunctionCallbackInfo &args) { + void runt_create_shader_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local buffer = Local::Cast(args[0]); std::shared_ptr content = buffer->GetBackingStore(); @@ -2382,7 +2464,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_delete_shader_compute(const FunctionCallbackInfo &args) { + void runt_delete_shader_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local shaderobj = args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked(); Local shaderfield = Local::Cast(shaderobj->GetInternalField(0)); @@ -2391,7 +2473,7 @@ namespace { free(shader); } - void krom_get_constant_location_compute(const FunctionCallbackInfo &args) { + void runt_get_constant_location_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local shaderfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_shader *shader = (kinc_compute_shader *)shaderfield->Value(); @@ -2409,7 +2491,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_get_texture_unit_compute(const FunctionCallbackInfo &args) { + void runt_get_texture_unit_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local shaderfield = Local::Cast(args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked()->GetInternalField(0)); kinc_compute_shader *shader = (kinc_compute_shader *)shaderfield->Value(); @@ -2427,7 +2509,7 @@ namespace { args.GetReturnValue().Set(obj); } - void krom_compute(const FunctionCallbackInfo &args) { + void runt_compute(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int x = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); int y = args[1]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); @@ -2440,7 +2522,7 @@ namespace { return true; } - void krom_set_save_and_quit_callback(const FunctionCallbackInfo &args) { + void runt_set_save_and_quit_callback(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); Local arg = args[0]; Local func = Local::Cast(arg); @@ -2449,7 +2531,7 @@ namespace { kinc_window_set_close_callback(0, window_close_callback, NULL); } - void krom_set_mouse_cursor(const FunctionCallbackInfo &args) { + void runt_set_mouse_cursor(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int id = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); kinc_mouse_set_cursor(id); @@ -2459,15 +2541,15 @@ namespace { #endif } - void krom_delay_idle_sleep_fast(Local receiver) { + void runt_delay_idle_sleep_fast(Local receiver) { paused_frames = 0; } - void krom_delay_idle_sleep(const FunctionCallbackInfo &args) { - krom_delay_idle_sleep_fast(args.This()); + void runt_delay_idle_sleep(const FunctionCallbackInfo &args) { + runt_delay_idle_sleep_fast(args.This()); } - void krom_file_exists(const FunctionCallbackInfo &args) { + void runt_file_exists(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); bool exists = false; String::Utf8Value utf8_value(isolate, args[0]); @@ -2481,7 +2563,7 @@ namespace { args.GetReturnValue().Set(Int32::New(isolate, exists)); } - void krom_delete_file(const FunctionCallbackInfo &args) { + void runt_delete_file(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); String::Utf8Value utf8_value(isolate, args[0]); #if defined(KORE_WINDOWS) @@ -2499,23 +2581,41 @@ namespace { #endif } - void krom_window_x(const FunctionCallbackInfo &args) { + void runt_window_x(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int windowId = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); args.GetReturnValue().Set(Int32::New(isolate, kinc_window_x(windowId))); } - void krom_window_y(const FunctionCallbackInfo &args) { + void runt_window_y(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); int windowId = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); args.GetReturnValue().Set(Int32::New(isolate, kinc_window_y(windowId))); } - void krom_language(const FunctionCallbackInfo &args) { + void runt_language(const FunctionCallbackInfo &args) { HandleScope scope(args.GetIsolate()); args.GetReturnValue().Set(String::NewFromUtf8(isolate, kinc_language()).ToLocalChecked()); } + void runt_viewport_set_camera(const FunctionCallbackInfo &args) { + HandleScope scope(args.GetIsolate()); + float pos_x = (float)args[0]->NumberValue(isolate->GetCurrentContext()).FromMaybe(0.0); + float pos_y = (float)args[1]->NumberValue(isolate->GetCurrentContext()).FromMaybe(0.0); + float pos_z = (float)args[2]->NumberValue(isolate->GetCurrentContext()).FromMaybe(0.0); + float rot_x = (float)args[3]->NumberValue(isolate->GetCurrentContext()).FromMaybe(0.0); + float rot_y = (float)args[4]->NumberValue(isolate->GetCurrentContext()).FromMaybe(0.0); + float rot_z = (float)args[5]->NumberValue(isolate->GetCurrentContext()).FromMaybe(0.0); + float rot_w = (float)args[6]->NumberValue(isolate->GetCurrentContext()).FromMaybe(1.0); + viewport_server_set_camera(pos_x, pos_y, pos_z, rot_x, rot_y, rot_z, rot_w); + } + + void runt_window_set_foreground(const FunctionCallbackInfo &args) { + HandleScope scope(args.GetIsolate()); + int windowId = args[0]->ToInt32(isolate->GetCurrentContext()).ToLocalChecked()->Value(); + kinc_window_set_foreground(windowId); + } + #define SET_FUNCTION_FAST(object, name, fn)\ CFunction fn ## _ = CFunction::Make(fn ## _fast);\ object->Set(String::NewFromUtf8(isolate, name).ToLocalChecked(),\ @@ -2527,11 +2627,14 @@ namespace { FunctionTemplate::New(isolate, fn, Local(), Local(), 0,\ v8::ConstructorBehavior::kThrow, v8::SideEffectType::kHasNoSideEffect, nullptr)) - void start_v8(char *krom_bin, int krom_bin_size) { + void start_v8(char *runt_bin, int runt_bin_size) { plat = platform::NewDefaultPlatform(); V8::InitializePlatform(plat.get()); - std::string flags = ""; + // std::string flags = ""; + std::string flags = "--max-old-space-size=1024 --initial-old-space-size=128 --max-semi-space-size=64 " + "--gc-interval=100 --optimize-for-size --turbo-fast-api-calls --concurrent-marking " + "--parallel-scavenge --use-idle-notification --incremental-marking"; V8::SetFlagsFromString(flags.c_str(), (int)flags.size()); V8::Initialize(); @@ -2539,9 +2642,9 @@ namespace { Isolate::CreateParams create_params; create_params.array_buffer_allocator = ArrayBuffer::Allocator::NewDefaultAllocator(); StartupData blob; - if (krom_bin_size > 0) { - blob.data = krom_bin; - blob.raw_size = krom_bin_size; + if (runt_bin_size > 0) { + blob.data = runt_bin; + blob.raw_size = runt_bin_size; create_params.snapshot_blob = &blob; } isolate = Isolate::New(create_params); @@ -2549,184 +2652,328 @@ namespace { Isolate::Scope isolate_scope(isolate); HandleScope handle_scope(isolate); - Local krom = ObjectTemplate::New(isolate); - SET_FUNCTION(krom, "init", krom_init); - SET_FUNCTION(krom, "setApplicationName", krom_set_application_name); - SET_FUNCTION(krom, "log", krom_log); - SET_FUNCTION_FAST(krom, "clear", krom_clear); - SET_FUNCTION(krom, "setCallback", krom_set_callback); - SET_FUNCTION(krom, "setDropFilesCallback", krom_set_drop_files_callback); - SET_FUNCTION(krom, "setCutCopyPasteCallback", krom_set_cut_copy_paste_callback); //// - SET_FUNCTION(krom, "setApplicationStateCallback", krom_set_application_state_callback); - SET_FUNCTION(krom, "setKeyboardDownCallback", krom_set_keyboard_down_callback); - SET_FUNCTION(krom, "setKeyboardUpCallback", krom_set_keyboard_up_callback); - SET_FUNCTION(krom, "setKeyboardPressCallback", krom_set_keyboard_press_callback); - SET_FUNCTION(krom, "setMouseDownCallback", krom_set_mouse_down_callback); - SET_FUNCTION(krom, "setMouseUpCallback", krom_set_mouse_up_callback); - SET_FUNCTION(krom, "setMouseMoveCallback", krom_set_mouse_move_callback); - SET_FUNCTION(krom, "setTouchDownCallback", krom_set_touch_down_callback); - SET_FUNCTION(krom, "setTouchUpCallback", krom_set_touch_up_callback); - SET_FUNCTION(krom, "setTouchMoveCallback", krom_set_touch_move_callback); - SET_FUNCTION(krom, "setMouseWheelCallback", krom_set_mouse_wheel_callback); - SET_FUNCTION(krom, "setPenDownCallback", krom_set_pen_down_callback); - SET_FUNCTION(krom, "setPenUpCallback", krom_set_pen_up_callback); - SET_FUNCTION(krom, "setPenMoveCallback", krom_set_pen_move_callback); - SET_FUNCTION(krom, "setGamepadAxisCallback", krom_set_gamepad_axis_callback); - SET_FUNCTION(krom, "setGamepadButtonCallback", krom_set_gamepad_button_callback); - SET_FUNCTION_FAST(krom, "lockMouse", krom_lock_mouse); - SET_FUNCTION_FAST(krom, "unlockMouse", krom_unlock_mouse); - SET_FUNCTION_FAST(krom, "canLockMouse", krom_can_lock_mouse); - SET_FUNCTION_FAST(krom, "isMouseLocked", krom_is_mouse_locked); - SET_FUNCTION_FAST(krom, "setMousePosition", krom_set_mouse_position); - SET_FUNCTION_FAST(krom, "showMouse", krom_show_mouse); - SET_FUNCTION_FAST(krom, "showKeyboard", krom_show_keyboard); - SET_FUNCTION(krom, "createIndexBuffer", krom_create_indexbuffer); - SET_FUNCTION(krom, "deleteIndexBuffer", krom_delete_indexbuffer); - SET_FUNCTION(krom, "lockIndexBuffer", krom_lock_indexbuffer); - SET_FUNCTION(krom, "unlockIndexBuffer", krom_unlock_indexbuffer); - SET_FUNCTION(krom, "setIndexBuffer", krom_set_indexbuffer); - SET_FUNCTION(krom, "createVertexBuffer", krom_create_vertexbuffer); - SET_FUNCTION(krom, "deleteVertexBuffer", krom_delete_vertexbuffer); - SET_FUNCTION(krom, "lockVertexBuffer", krom_lock_vertex_buffer); - SET_FUNCTION(krom, "unlockVertexBuffer", krom_unlock_vertex_buffer); - SET_FUNCTION(krom, "setVertexBuffer", krom_set_vertexbuffer); - SET_FUNCTION(krom, "setVertexBuffers", krom_set_vertexbuffers); - SET_FUNCTION_FAST(krom, "drawIndexedVertices", krom_draw_indexed_vertices); - SET_FUNCTION_FAST(krom, "drawIndexedVerticesInstanced", krom_draw_indexed_vertices_instanced); - SET_FUNCTION(krom, "createVertexShader", krom_create_vertex_shader); - SET_FUNCTION(krom, "createVertexShaderFromSource", krom_create_vertex_shader_from_source); - SET_FUNCTION(krom, "createFragmentShader", krom_create_fragment_shader); - SET_FUNCTION(krom, "createFragmentShaderFromSource", krom_create_fragment_shader_from_source); - SET_FUNCTION(krom, "createGeometryShader", krom_create_geometry_shader); - SET_FUNCTION(krom, "createTessellationControlShader", krom_create_tessellation_control_shader); - SET_FUNCTION(krom, "createTessellationEvaluationShader", krom_create_tessellation_evaluation_shader); - SET_FUNCTION(krom, "deleteShader", krom_delete_shader); - SET_FUNCTION(krom, "createPipeline", krom_create_pipeline); - SET_FUNCTION(krom, "deletePipeline", krom_delete_pipeline); - SET_FUNCTION(krom, "compilePipeline", krom_compile_pipeline); - SET_FUNCTION(krom, "setPipeline", krom_set_pipeline); - SET_FUNCTION(krom, "loadImage", krom_load_image); - SET_FUNCTION(krom, "unloadImage", krom_unload_image); - // #ifdef WITH_AUDIO - SET_FUNCTION(krom, "loadSound", krom_load_sound); - SET_FUNCTION(krom, "setAudioCallback", krom_set_audio_callback); - SET_FUNCTION(krom, "audioThread", krom_audio_thread); - SET_FUNCTION(krom, "writeAudioBuffer", krom_write_audio_buffer); - SET_FUNCTION(krom, "getSamplesPerSecond", krom_get_samples_per_second); - // #endif - SET_FUNCTION(krom, "loadBlob", krom_load_blob); - SET_FUNCTION(krom, "loadUrl", krom_load_url); - SET_FUNCTION(krom, "copyToClipboard", krom_copy_to_clipboard); - SET_FUNCTION(krom, "getConstantLocation", krom_get_constant_location); - SET_FUNCTION(krom, "getTextureUnit", krom_get_texture_unit); - SET_FUNCTION(krom, "setTexture", krom_set_texture); - SET_FUNCTION(krom, "setRenderTarget", krom_set_render_target); - SET_FUNCTION(krom, "setTextureDepth", krom_set_texture_depth); - SET_FUNCTION(krom, "setImageTexture", krom_set_image_texture); - SET_FUNCTION(krom, "setTextureParameters", krom_set_texture_parameters); - SET_FUNCTION(krom, "setTexture3DParameters", krom_set_texture3d_parameters); - SET_FUNCTION(krom, "setTextureCompareMode", krom_set_texture_compare_mode); - SET_FUNCTION(krom, "setCubeMapCompareMode", krom_set_cube_map_compare_mode); - SET_FUNCTION(krom, "setBool", krom_set_bool); - SET_FUNCTION(krom, "setInt", krom_set_int); - SET_FUNCTION(krom, "setFloat", krom_set_float); - SET_FUNCTION(krom, "setFloat2", krom_set_float2); - SET_FUNCTION(krom, "setFloat3", krom_set_float3); - SET_FUNCTION(krom, "setFloat4", krom_set_float4); - SET_FUNCTION(krom, "setFloats", krom_set_floats); - SET_FUNCTION(krom, "setMatrix", krom_set_matrix); - SET_FUNCTION(krom, "setMatrix3", krom_set_matrix3); - SET_FUNCTION_FAST(krom, "getTime", krom_get_time); - SET_FUNCTION_FAST(krom, "windowWidth", krom_window_width); - SET_FUNCTION_FAST(krom, "windowHeight", krom_window_height); - SET_FUNCTION(krom, "setWindowTitle", krom_set_window_title); - SET_FUNCTION(krom, "getWindowMode", krom_get_window_mode); - SET_FUNCTION(krom, "setWindowMode", krom_set_window_mode); - SET_FUNCTION(krom, "resizeWindow", krom_resize_window); - SET_FUNCTION(krom, "moveWindow", krom_move_window); - SET_FUNCTION(krom, "screenDpi", krom_screen_dpi); - SET_FUNCTION(krom, "systemId", krom_system_id); - SET_FUNCTION(krom, "requestShutdown", krom_request_shutdown); - SET_FUNCTION(krom, "displayCount", krom_display_count); - SET_FUNCTION(krom, "displayWidth", krom_display_width); - SET_FUNCTION(krom, "displayHeight", krom_display_height); - SET_FUNCTION(krom, "displayX", krom_display_x); - SET_FUNCTION(krom, "displayY", krom_display_y); - SET_FUNCTION(krom, "displayFrequency", krom_display_frequency); - SET_FUNCTION(krom, "displayIsPrimary", krom_display_is_primary); - SET_FUNCTION(krom, "writeStorage", krom_write_storage); - SET_FUNCTION(krom, "readStorage", krom_read_storage); - SET_FUNCTION(krom, "createRenderTarget", krom_create_render_target); - SET_FUNCTION(krom, "createRenderTargetCubeMap", krom_create_render_target_cube_map); - SET_FUNCTION(krom, "createTexture", krom_create_texture); - SET_FUNCTION(krom, "createTexture3D", krom_create_texture3d); - SET_FUNCTION(krom, "createTextureFromBytes", krom_create_texture_from_bytes); - SET_FUNCTION(krom, "createTextureFromBytes3D", krom_create_texture_from_bytes3d); - SET_FUNCTION(krom, "createTextureFromEncodedBytes", krom_create_texture_from_encoded_bytes); - SET_FUNCTION(krom, "getTexturePixels", krom_get_texture_pixels); - SET_FUNCTION(krom, "getRenderTargetPixels", krom_get_render_target_pixels); - SET_FUNCTION(krom, "lockTexture", krom_lock_texture); - SET_FUNCTION(krom, "unlockTexture", krom_unlock_texture); - SET_FUNCTION(krom, "clearTexture", krom_clear_texture); - SET_FUNCTION(krom, "generateTextureMipmaps", krom_generate_texture_mipmaps); - SET_FUNCTION(krom, "generateRenderTargetMipmaps", krom_generate_render_target_mipmaps); - SET_FUNCTION(krom, "setMipmaps", krom_set_mipmaps); - SET_FUNCTION(krom, "setDepthStencilFrom", krom_set_depth_stencil_from); - SET_FUNCTION_FAST(krom, "viewport", krom_viewport); - SET_FUNCTION_FAST(krom, "scissor", krom_scissor); - SET_FUNCTION_FAST(krom, "disableScissor", krom_disable_scissor); - SET_FUNCTION_FAST(krom, "renderTargetsInvertedY", krom_render_targets_inverted_y); - SET_FUNCTION(krom, "begin", krom_begin); - SET_FUNCTION(krom, "beginFace", krom_begin_face); - SET_FUNCTION(krom, "end", krom_end); - SET_FUNCTION(krom, "fileSaveBytes", krom_file_save_bytes); - SET_FUNCTION(krom, "sysCommand", krom_sys_command); - SET_FUNCTION(krom, "savePath", krom_save_path); - SET_FUNCTION(krom, "getArgCount", krom_get_arg_count); - SET_FUNCTION(krom, "getArg", krom_get_arg); - SET_FUNCTION(krom, "getFilesLocation", krom_get_files_location); - SET_FUNCTION(krom, "httpRequest", krom_http_request); - #ifdef WITH_COMPUTE - SET_FUNCTION(krom, "setBoolCompute", krom_set_bool_compute); - SET_FUNCTION(krom, "setIntCompute", krom_set_int_compute); - SET_FUNCTION(krom, "setFloatCompute", krom_set_float_compute); - SET_FUNCTION(krom, "setFloat2Compute", krom_set_float2_compute); - SET_FUNCTION(krom, "setFloat3Compute", krom_set_float3_compute); - SET_FUNCTION(krom, "setFloat4Compute", krom_set_float4_compute); - SET_FUNCTION(krom, "setFloatsCompute", krom_set_floats_compute); - SET_FUNCTION(krom, "setMatrixCompute", krom_set_matrix_compute); - SET_FUNCTION(krom, "setMatrix3Compute", krom_set_matrix3_compute); - SET_FUNCTION(krom, "setTextureCompute", krom_set_texture_compute); - SET_FUNCTION(krom, "setRenderTargetCompute", krom_set_render_target_compute); - SET_FUNCTION(krom, "setSampledTextureCompute", krom_set_sampled_texture_compute); - SET_FUNCTION(krom, "setSampledRenderTargetCompute", krom_set_sampled_render_target_compute); - SET_FUNCTION(krom, "setSampledDepthTextureCompute", krom_set_sampled_depth_texture_compute); - SET_FUNCTION(krom, "setTextureParametersCompute", krom_set_texture_parameters_compute); - SET_FUNCTION(krom, "setTexture3DParametersCompute", krom_set_texture3d_parameters_compute); - SET_FUNCTION(krom, "setShaderCompute", krom_set_shader_compute); - SET_FUNCTION(krom, "deleteShaderCompute", krom_delete_shader_compute); - SET_FUNCTION(krom, "createShaderCompute", krom_create_shader_compute); - SET_FUNCTION(krom, "getConstantLocationCompute", krom_get_constant_location_compute); - SET_FUNCTION(krom, "getTextureUnitCompute", krom_get_texture_unit_compute); - SET_FUNCTION(krom, "compute", krom_compute); + Local runt = ObjectTemplate::New(isolate); + SET_FUNCTION(runt, "init", runt_init); + SET_FUNCTION(runt, "setApplicationName", runt_set_application_name); + SET_FUNCTION(runt, "log", runt_log); + SET_FUNCTION_FAST(runt, "clear", runt_clear); + SET_FUNCTION(runt, "setCallback", runt_set_callback); + SET_FUNCTION(runt, "setDropFilesCallback", runt_set_drop_files_callback); + SET_FUNCTION(runt, "setCutCopyPasteCallback", runt_set_cut_copy_paste_callback); //// + SET_FUNCTION(runt, "setApplicationStateCallback", runt_set_application_state_callback); + SET_FUNCTION(runt, "setKeyboardDownCallback", runt_set_keyboard_down_callback); + SET_FUNCTION(runt, "setKeyboardUpCallback", runt_set_keyboard_up_callback); + SET_FUNCTION(runt, "setKeyboardPressCallback", runt_set_keyboard_press_callback); + SET_FUNCTION(runt, "setMouseDownCallback", runt_set_mouse_down_callback); + SET_FUNCTION(runt, "setMouseUpCallback", runt_set_mouse_up_callback); + SET_FUNCTION(runt, "setMouseMoveCallback", runt_set_mouse_move_callback); + SET_FUNCTION(runt, "setTouchDownCallback", runt_set_touch_down_callback); + SET_FUNCTION(runt, "setTouchUpCallback", runt_set_touch_up_callback); + SET_FUNCTION(runt, "setTouchMoveCallback", runt_set_touch_move_callback); + SET_FUNCTION(runt, "setMouseWheelCallback", runt_set_mouse_wheel_callback); + SET_FUNCTION(runt, "setPenDownCallback", runt_set_pen_down_callback); + SET_FUNCTION(runt, "setPenUpCallback", runt_set_pen_up_callback); + SET_FUNCTION(runt, "setPenMoveCallback", runt_set_pen_move_callback); + SET_FUNCTION(runt, "setGamepadAxisCallback", runt_set_gamepad_axis_callback); + SET_FUNCTION(runt, "setGamepadButtonCallback", runt_set_gamepad_button_callback); + SET_FUNCTION_FAST(runt, "lockMouse", runt_lock_mouse); + SET_FUNCTION_FAST(runt, "unlockMouse", runt_unlock_mouse); + SET_FUNCTION_FAST(runt, "canLockMouse", runt_can_lock_mouse); + SET_FUNCTION_FAST(runt, "isMouseLocked", runt_is_mouse_locked); + SET_FUNCTION_FAST(runt, "setMousePosition", runt_set_mouse_position); + SET_FUNCTION_FAST(runt, "showMouse", runt_show_mouse); + SET_FUNCTION_FAST(runt, "showKeyboard", runt_show_keyboard); + SET_FUNCTION(runt, "createIndexBuffer", runt_create_indexbuffer); + SET_FUNCTION(runt, "deleteIndexBuffer", runt_delete_indexbuffer); + SET_FUNCTION(runt, "lockIndexBuffer", runt_lock_indexbuffer); + SET_FUNCTION(runt, "unlockIndexBuffer", runt_unlock_indexbuffer); + SET_FUNCTION(runt, "setIndexBuffer", runt_set_indexbuffer); + SET_FUNCTION(runt, "createVertexBuffer", runt_create_vertexbuffer); + SET_FUNCTION(runt, "deleteVertexBuffer", runt_delete_vertexbuffer); + SET_FUNCTION(runt, "lockVertexBuffer", runt_lock_vertex_buffer); + SET_FUNCTION(runt, "unlockVertexBuffer", runt_unlock_vertex_buffer); + SET_FUNCTION(runt, "setVertexBuffer", runt_set_vertexbuffer); + SET_FUNCTION(runt, "setVertexBuffers", runt_set_vertexbuffers); + SET_FUNCTION_FAST(runt, "drawIndexedVertices", runt_draw_indexed_vertices); + SET_FUNCTION_FAST(runt, "drawIndexedVerticesInstanced", runt_draw_indexed_vertices_instanced); + SET_FUNCTION(runt, "createVertexShader", runt_create_vertex_shader); + SET_FUNCTION(runt, "createVertexShaderFromSource", runt_create_vertex_shader_from_source); + SET_FUNCTION(runt, "createFragmentShader", runt_create_fragment_shader); + SET_FUNCTION(runt, "createFragmentShaderFromSource", runt_create_fragment_shader_from_source); + SET_FUNCTION(runt, "createGeometryShader", runt_create_geometry_shader); + SET_FUNCTION(runt, "createTessellationControlShader", runt_create_tessellation_control_shader); + SET_FUNCTION(runt, "createTessellationEvaluationShader", runt_create_tessellation_evaluation_shader); + SET_FUNCTION(runt, "deleteShader", runt_delete_shader); + SET_FUNCTION(runt, "createPipeline", runt_create_pipeline); + SET_FUNCTION(runt, "deletePipeline", runt_delete_pipeline); + SET_FUNCTION(runt, "compilePipeline", runt_compile_pipeline); + SET_FUNCTION(runt, "setPipeline", runt_set_pipeline); + SET_FUNCTION(runt, "loadImage", runt_load_image); + SET_FUNCTION(runt, "unloadImage", runt_unload_image); + #ifdef WITH_AUDIO + SET_FUNCTION(runt, "loadSound", runt_load_sound); + SET_FUNCTION(runt, "setAudioCallback", runt_set_audio_callback); + SET_FUNCTION(runt, "audioThread", runt_audio_thread); + SET_FUNCTION(runt, "writeAudioBuffer", runt_write_audio_buffer); + SET_FUNCTION(runt, "getSamplesPerSecond", runt_get_samples_per_second); #endif - SET_FUNCTION(krom, "setSaveAndQuitCallback", krom_set_save_and_quit_callback); - SET_FUNCTION(krom, "setMouseCursor", krom_set_mouse_cursor); - SET_FUNCTION_FAST(krom, "delayIdleSleep", krom_delay_idle_sleep); - SET_FUNCTION(krom, "fileExists", krom_file_exists); - SET_FUNCTION(krom, "deleteFile", krom_delete_file); - SET_FUNCTION(krom, "windowX", krom_window_x); - SET_FUNCTION(krom, "windowY", krom_window_y); - SET_FUNCTION(krom, "language", krom_language); + SET_FUNCTION(runt, "loadBlob", runt_load_blob); + SET_FUNCTION(runt, "loadUrl", runt_load_url); + SET_FUNCTION(runt, "copyToClipboard", runt_copy_to_clipboard); + SET_FUNCTION(runt, "getConstantLocation", runt_get_constant_location); + SET_FUNCTION(runt, "getTextureUnit", runt_get_texture_unit); + SET_FUNCTION(runt, "setTexture", runt_set_texture); + SET_FUNCTION(runt, "setRenderTarget", runt_set_render_target); + SET_FUNCTION(runt, "setTextureDepth", runt_set_texture_depth); + SET_FUNCTION(runt, "setImageTexture", runt_set_image_texture); + SET_FUNCTION(runt, "setTextureParameters", runt_set_texture_parameters); + SET_FUNCTION(runt, "setTexture3DParameters", runt_set_texture3d_parameters); + SET_FUNCTION(runt, "setTextureCompareMode", runt_set_texture_compare_mode); + SET_FUNCTION(runt, "setCubeMapCompareMode", runt_set_cube_map_compare_mode); + SET_FUNCTION(runt, "setBool", runt_set_bool); + SET_FUNCTION(runt, "setInt", runt_set_int); + SET_FUNCTION(runt, "setFloat", runt_set_float); + SET_FUNCTION(runt, "setFloat2", runt_set_float2); + SET_FUNCTION(runt, "setFloat3", runt_set_float3); + SET_FUNCTION(runt, "setFloat4", runt_set_float4); + SET_FUNCTION(runt, "setFloats", runt_set_floats); + SET_FUNCTION(runt, "setMatrix", runt_set_matrix); + SET_FUNCTION(runt, "setMatrix3", runt_set_matrix3); + SET_FUNCTION_FAST(runt, "getTime", runt_get_time); + SET_FUNCTION_FAST(runt, "windowWidth", runt_window_width); + SET_FUNCTION_FAST(runt, "windowHeight", runt_window_height); + SET_FUNCTION(runt, "setWindowTitle", runt_set_window_title); + SET_FUNCTION(runt, "getWindowMode", runt_get_window_mode); + SET_FUNCTION(runt, "setWindowMode", runt_set_window_mode); + SET_FUNCTION(runt, "resizeWindow", runt_resize_window); + SET_FUNCTION(runt, "moveWindow", runt_move_window); + SET_FUNCTION(runt, "screenDpi", runt_screen_dpi); + SET_FUNCTION(runt, "systemId", runt_system_id); + SET_FUNCTION(runt, "requestShutdown", runt_request_shutdown); + SET_FUNCTION(runt, "displayCount", runt_display_count); + SET_FUNCTION(runt, "displayWidth", runt_display_width); + SET_FUNCTION(runt, "displayHeight", runt_display_height); + SET_FUNCTION(runt, "displayX", runt_display_x); + SET_FUNCTION(runt, "displayY", runt_display_y); + SET_FUNCTION(runt, "displayFrequency", runt_display_frequency); + SET_FUNCTION(runt, "displayIsPrimary", runt_display_is_primary); + SET_FUNCTION(runt, "writeStorage", runt_write_storage); + SET_FUNCTION(runt, "readStorage", runt_read_storage); + SET_FUNCTION(runt, "createRenderTarget", runt_create_render_target); + SET_FUNCTION(runt, "createRenderTargetCubeMap", runt_create_render_target_cube_map); + SET_FUNCTION(runt, "createTexture", runt_create_texture); + SET_FUNCTION(runt, "createTexture3D", runt_create_texture3d); + SET_FUNCTION(runt, "createTextureFromBytes", runt_create_texture_from_bytes); + SET_FUNCTION(runt, "createTextureFromBytes3D", runt_create_texture_from_bytes3d); + SET_FUNCTION(runt, "createTextureFromEncodedBytes", runt_create_texture_from_encoded_bytes); + SET_FUNCTION(runt, "getTexturePixels", runt_get_texture_pixels); + SET_FUNCTION(runt, "getRenderTargetPixels", runt_get_render_target_pixels); + SET_FUNCTION(runt, "lockTexture", runt_lock_texture); + SET_FUNCTION(runt, "unlockTexture", runt_unlock_texture); + SET_FUNCTION(runt, "clearTexture", runt_clear_texture); + SET_FUNCTION(runt, "generateTextureMipmaps", runt_generate_texture_mipmaps); + SET_FUNCTION(runt, "generateRenderTargetMipmaps", runt_generate_render_target_mipmaps); + SET_FUNCTION(runt, "setMipmaps", runt_set_mipmaps); + SET_FUNCTION(runt, "setDepthStencilFrom", runt_set_depth_stencil_from); + SET_FUNCTION_FAST(runt, "viewport", runt_viewport); + SET_FUNCTION_FAST(runt, "scissor", runt_scissor); + SET_FUNCTION_FAST(runt, "disableScissor", runt_disable_scissor); + SET_FUNCTION_FAST(runt, "renderTargetsInvertedY", runt_render_targets_inverted_y); + SET_FUNCTION(runt, "begin", runt_begin); + SET_FUNCTION(runt, "beginFace", runt_begin_face); + SET_FUNCTION(runt, "end", runt_end); + SET_FUNCTION(runt, "fileSaveBytes", runt_file_save_bytes); + SET_FUNCTION(runt, "sysCommand", runt_sys_command); + SET_FUNCTION(runt, "savePath", runt_save_path); + SET_FUNCTION(runt, "getArgCount", runt_get_arg_count); + SET_FUNCTION(runt, "getArg", runt_get_arg); + SET_FUNCTION(runt, "getFilesLocation", runt_get_files_location); + SET_FUNCTION(runt, "httpRequest", runt_http_request); + SET_FUNCTION(runt, "viewportSetCamera", runt_viewport_set_camera); + SET_FUNCTION(runt, "windowSetForeground", runt_window_set_foreground); + + #ifdef WITH_NETWORKING + SET_FUNCTION(runt, "socketCreate", v8_runt_socket_create); + SET_FUNCTION(runt, "socketBind", v8_runt_socket_bind); + SET_FUNCTION(runt, "socketListen", v8_runt_socket_listen); + SET_FUNCTION(runt, "socketAccept", v8_runt_socket_accept); + SET_FUNCTION(runt, "socketConnect", v8_runt_socket_connect); + SET_FUNCTION(runt, "socketSend", v8_runt_socket_send); + SET_FUNCTION(runt, "socketRecv", v8_runt_socket_recv); + SET_FUNCTION(runt, "socketClose", v8_runt_socket_close); + SET_FUNCTION(runt, "socketSetBlocking", v8_runt_socket_set_blocking); + SET_FUNCTION(runt, "socketSelect", v8_runt_socket_select); + SET_FUNCTION(runt, "socketIsConnected", v8_runt_socket_is_connected); + #ifdef WITH_SSL + SET_FUNCTION(runt, "socketEnableSsl", v8_runt_socket_enable_ssl); + #endif + + SET_FUNCTION(runt, "websocketCreate", runt_websocket_create); + SET_FUNCTION(runt, "websocketSend", runt_websocket_send); + SET_FUNCTION(runt, "websocketSendBinary", runt_websocket_send_binary); + SET_FUNCTION(runt, "websocketClose", runt_websocket_close); + SET_FUNCTION(runt, "websocketGetReadyState", runt_websocket_get_ready_state); + SET_FUNCTION(runt, "websocketGetUrl", runt_websocket_get_url); + SET_FUNCTION(runt, "websocketGetBufferedAmount", runt_websocket_get_buffered_amount); + SET_FUNCTION(runt, "websocketSetOnOpen", runt_websocket_set_onopen); + SET_FUNCTION(runt, "websocketSetOnMessage", runt_websocket_set_onmessage); + SET_FUNCTION(runt, "websocketSetOnError", runt_websocket_set_onerror); + SET_FUNCTION(runt, "websocketSetOnClose", runt_websocket_set_onclose); + SET_FUNCTION(runt, "websocketServerCreate", v8_runt_websocket_server_create); + SET_FUNCTION(runt, "websocketServerStart", v8_runt_websocket_server_start); + SET_FUNCTION(runt, "websocketServerStop", v8_runt_websocket_server_stop); + SET_FUNCTION(runt, "websocketServerTick", v8_runt_websocket_server_tick); + SET_FUNCTION(runt, "websocketServerSendAll", v8_runt_websocket_server_send_all); + SET_FUNCTION(runt, "websocketServerSendClient", v8_runt_websocket_server_send_client); + + SET_FUNCTION(runt, "httpRequestCreate", runt_httprequest_create); + SET_FUNCTION(runt, "httpRequestOpen", runt_httprequest_open); + SET_FUNCTION(runt, "httpRequestSend", runt_httprequest_send); + SET_FUNCTION(runt, "httpRequestAbort", runt_httprequest_abort); + SET_FUNCTION(runt, "httpRequestSetRequestHeader", runt_httprequest_set_request_header); + SET_FUNCTION(runt, "httpRequestGetAllResponseHeaders", runt_httprequest_get_all_response_headers); + SET_FUNCTION(runt, "httpRequestGetResponseHeader", runt_httprequest_get_response_header); + SET_FUNCTION(runt, "httpRequestGetReadyState", runt_httprequest_get_ready_state); + SET_FUNCTION(runt, "httpRequestGetStatus", runt_httprequest_get_status); + SET_FUNCTION(runt, "httpRequestGetStatusText", runt_httprequest_get_status_text); + SET_FUNCTION(runt, "httpRequestGetResponseText", runt_httprequest_get_response_text); + SET_FUNCTION(runt, "httpRequestGetResponseURL", runt_httprequest_get_response_url); + SET_FUNCTION(runt, "httpRequestSetTimeout", runt_httprequest_set_timeout); + SET_FUNCTION(runt, "httpRequestSetResponseType", runt_httprequest_set_response_type); + SET_FUNCTION(runt, "httpRequestSetWithCredentials", runt_httprequest_set_with_credentials); + SET_FUNCTION(runt, "httpRequestSetOnReadyStateChange", runt_httprequest_set_onreadystatechange); + SET_FUNCTION(runt, "httpRequestSetOnLoad", runt_httprequest_set_onload); + SET_FUNCTION(runt, "httpRequestSetOnError", runt_httprequest_set_onerror); + SET_FUNCTION(runt, "httpRequestSetOnTimeout", runt_httprequest_set_ontimeout); + SET_FUNCTION(runt, "httpRequestSetOnAbort", runt_httprequest_set_onabort); + SET_FUNCTION(runt, "httpRequestSetOnProgress", runt_httprequest_set_onprogress); + SET_FUNCTION(runt, "httpRequestSetOnLoadStart", runt_httprequest_set_onloadstart); + SET_FUNCTION(runt, "httpRequestSetOnLoadEnd", runt_httprequest_set_onloadend); + #endif + + #ifdef WITH_COMPUTE + SET_FUNCTION(runt, "setBoolCompute", runt_set_bool_compute); + SET_FUNCTION(runt, "setIntCompute", runt_set_int_compute); + SET_FUNCTION(runt, "setFloatCompute", runt_set_float_compute); + SET_FUNCTION(runt, "setFloat2Compute", runt_set_float2_compute); + SET_FUNCTION(runt, "setFloat3Compute", runt_set_float3_compute); + SET_FUNCTION(runt, "setFloat4Compute", runt_set_float4_compute); + SET_FUNCTION(runt, "setFloatsCompute", runt_set_floats_compute); + SET_FUNCTION(runt, "setMatrixCompute", runt_set_matrix_compute); + SET_FUNCTION(runt, "setMatrix3Compute", runt_set_matrix3_compute); + SET_FUNCTION(runt, "setTextureCompute", runt_set_texture_compute); + SET_FUNCTION(runt, "setRenderTargetCompute", runt_set_render_target_compute); + SET_FUNCTION(runt, "setSampledTextureCompute", runt_set_sampled_texture_compute); + SET_FUNCTION(runt, "setSampledRenderTargetCompute", runt_set_sampled_render_target_compute); + SET_FUNCTION(runt, "setSampledDepthTextureCompute", runt_set_sampled_depth_texture_compute); + SET_FUNCTION(runt, "setTextureParametersCompute", runt_set_texture_parameters_compute); + SET_FUNCTION(runt, "setTexture3DParametersCompute", runt_set_texture3d_parameters_compute); + SET_FUNCTION(runt, "setShaderCompute", runt_set_shader_compute); + SET_FUNCTION(runt, "deleteShaderCompute", runt_delete_shader_compute); + SET_FUNCTION(runt, "createShaderCompute", runt_create_shader_compute); + SET_FUNCTION(runt, "getConstantLocationCompute", runt_get_constant_location_compute); + SET_FUNCTION(runt, "getTextureUnitCompute", runt_get_texture_unit_compute); + SET_FUNCTION(runt, "compute", runt_compute); + #endif + SET_FUNCTION(runt, "setSaveAndQuitCallback", runt_set_save_and_quit_callback); + SET_FUNCTION(runt, "setMouseCursor", runt_set_mouse_cursor); + SET_FUNCTION_FAST(runt, "delayIdleSleep", runt_delay_idle_sleep); + SET_FUNCTION(runt, "fileExists", runt_file_exists); + SET_FUNCTION(runt, "deleteFile", runt_delete_file); + SET_FUNCTION(runt, "windowX", runt_window_x); + SET_FUNCTION(runt, "windowY", runt_window_y); + SET_FUNCTION(runt, "language", runt_language); Local global = ObjectTemplate::New(isolate); - global->Set(String::NewFromUtf8(isolate, "Krom").ToLocalChecked(), krom); + global->Set(String::NewFromUtf8(isolate, "RunT").ToLocalChecked(), runt); + global->Set(String::NewFromUtf8(isolate, "runt").ToLocalChecked(), runt); // lowercase alias for JavaScript compatibility + + // additional common APIs + Local window_template = ObjectTemplate::New(isolate); + window_template->Set(String::NewFromUtf8(isolate, "innerWidth").ToLocalChecked(), Integer::New(isolate, kinc_window_width(0))); + window_template->Set(String::NewFromUtf8(isolate, "innerHeight").ToLocalChecked(), Integer::New(isolate, kinc_window_height(0))); + window_template->Set(String::NewFromUtf8(isolate, "devicePixelRatio").ToLocalChecked(), Number::New(isolate, 1.0)); + global->Set(String::NewFromUtf8(isolate, "window").ToLocalChecked(), window_template); + global->Set(String::NewFromUtf8(isolate, "global").ToLocalChecked(), window_template); + global->Set(String::NewFromUtf8(isolate, "globalThis").ToLocalChecked(), window_template); + Local console_template = ObjectTemplate::New(isolate); + console_template->Set(String::NewFromUtf8(isolate, "log").ToLocalChecked(), FunctionTemplate::New(isolate, runt_log)); + console_template->Set(String::NewFromUtf8(isolate, "warn").ToLocalChecked(), FunctionTemplate::New(isolate, runt_log)); + console_template->Set(String::NewFromUtf8(isolate, "error").ToLocalChecked(), FunctionTemplate::New(isolate, runt_log)); + console_template->Set(String::NewFromUtf8(isolate, "info").ToLocalChecked(), FunctionTemplate::New(isolate, runt_log)); + console_template->Set(String::NewFromUtf8(isolate, "debug").ToLocalChecked(), FunctionTemplate::New(isolate, runt_log)); + + global->Set(String::NewFromUtf8(isolate, "console").ToLocalChecked(), console_template); + window_template->Set(String::NewFromUtf8(isolate, "console").ToLocalChecked(), console_template); + + #ifdef WITH_NETWORKING + setupNativeWebSocketClass(isolate, global); + setupNativeEventClasses(isolate, global); + setupNativeXMLHttpRequestClass(isolate, global); + #endif Local context = Context::New(isolate, NULL, global); global_context.Reset(isolate, context); + + // needs self scope to add more browser APIs + { + Context::Scope context_scope(context); + Local global_obj = context->Global(); + Local window_obj = global_obj->Get(context, String::NewFromUtf8(isolate, "window").ToLocalChecked()).ToLocalChecked(); + + if (window_obj->IsObject()) { + Local window = window_obj->ToObject(context).ToLocalChecked(); + + window->Set(context, String::NewFromUtf8(isolate, "window").ToLocalChecked(), window); + window->Set(context, String::NewFromUtf8(isolate, "self").ToLocalChecked(), window); + window->Set(context, String::NewFromUtf8(isolate, "top").ToLocalChecked(), window); + window->Set(context, String::NewFromUtf8(isolate, "parent").ToLocalChecked(), window); + + Local location = Object::New(isolate); + location->Set(context, String::NewFromUtf8(isolate, "href").ToLocalChecked(), String::NewFromUtf8(isolate, "runt://localhost/").ToLocalChecked()); + location->Set(context, String::NewFromUtf8(isolate, "origin").ToLocalChecked(), String::NewFromUtf8(isolate, "runt://localhost").ToLocalChecked()); + location->Set(context, String::NewFromUtf8(isolate, "protocol").ToLocalChecked(), String::NewFromUtf8(isolate, "runt:").ToLocalChecked()); + location->Set(context, String::NewFromUtf8(isolate, "host").ToLocalChecked(), String::NewFromUtf8(isolate, "localhost").ToLocalChecked()); + location->Set(context, String::NewFromUtf8(isolate, "hostname").ToLocalChecked(), String::NewFromUtf8(isolate, "localhost").ToLocalChecked()); + window->Set(context, String::NewFromUtf8(isolate, "location").ToLocalChecked(), location); + + Local navigator = Object::New(isolate); + navigator->Set(context, String::NewFromUtf8(isolate, "userAgent").ToLocalChecked(), String::NewFromUtf8(isolate, "RunT/1.0").ToLocalChecked()); + navigator->Set(context, String::NewFromUtf8(isolate, "platform").ToLocalChecked(), String::NewFromUtf8(isolate, "RunT").ToLocalChecked()); + window->Set(context, String::NewFromUtf8(isolate, "navigator").ToLocalChecked(), navigator); + + Local document = Object::New(isolate); + document->Set(context, String::NewFromUtf8(isolate, "title").ToLocalChecked(), String::NewFromUtf8(isolate, "Leenkx Application").ToLocalChecked()); + window->Set(context, String::NewFromUtf8(isolate, "document").ToLocalChecked(), document); + + window->Set(context, String::NewFromUtf8(isolate, "localStorage").ToLocalChecked(), Null(isolate)); + window->Set(context, String::NewFromUtf8(isolate, "sessionStorage").ToLocalChecked(), Null(isolate)); + window->Set(context, String::NewFromUtf8(isolate, "indexedDB").ToLocalChecked(), Null(isolate)); + window->Set(context, String::NewFromUtf8(isolate, "setTimeout").ToLocalChecked(), Function::New(context, runt_set_timeout).ToLocalChecked()).Check(); + window->Set(context, String::NewFromUtf8(isolate, "clearTimeout").ToLocalChecked(), Function::New(context, runt_clear_timeout).ToLocalChecked()).Check(); + window->Set(context, String::NewFromUtf8(isolate, "setInterval").ToLocalChecked(), Function::New(context, runt_set_interval).ToLocalChecked()).Check(); + window->Set(context, String::NewFromUtf8(isolate, "clearInterval").ToLocalChecked(), Function::New(context, runt_clear_interval).ToLocalChecked()).Check(); + + global_obj->Set(context, String::NewFromUtf8(isolate, "location").ToLocalChecked(), location); + global_obj->Set(context, String::NewFromUtf8(isolate, "navigator").ToLocalChecked(), navigator); + global_obj->Set(context, String::NewFromUtf8(isolate, "document").ToLocalChecked(), document); + global_obj->Set(context, String::NewFromUtf8(isolate, "setTimeout").ToLocalChecked(), Function::New(context, runt_set_timeout).ToLocalChecked()).Check(); + global_obj->Set(context, String::NewFromUtf8(isolate, "clearTimeout").ToLocalChecked(), Function::New(context, runt_clear_timeout).ToLocalChecked()).Check(); + global_obj->Set(context, String::NewFromUtf8(isolate, "setInterval").ToLocalChecked(), Function::New(context, runt_set_interval).ToLocalChecked()).Check(); + global_obj->Set(context, String::NewFromUtf8(isolate, "clearInterval").ToLocalChecked(), Function::New(context, runt_clear_interval).ToLocalChecked()).Check(); + } + } + + #ifdef WITH_NETWORKING + WebSocketWrapper::initialize(); + //kinc_log(KINC_LOG_LEVEL_INFO, "WebSocket client support initialized successfully"); + HttpRequestWrapper::initialize(); + //kinc_log(KINC_LOG_LEVEL_INFO, "Initializing native HTTP request support"); + #endif + } - void start_krom(char *scriptfile) { + void start_runt(char *scriptfile) { Locker locker{isolate}; Isolate::Scope isolate_scope(isolate); @@ -2779,7 +3026,14 @@ namespace { #ifdef KORE_WINDOWS if (show_window && enable_window) { show_window = false; - kinc_window_show(0); + + #ifdef WITH_VIEWPORT + if (!viewport_server_mode) { + kinc_window_show(0); + } + #else + kinc_window_show(0); + #endif } #endif @@ -2793,9 +3047,66 @@ namespace { handle_worker_messages(isolate, global_context); #endif + #ifdef WITH_NETWORKING + WebSocketWrapper::processEvents(); + HttpRequestWrapper::processEvents(); + #endif + + execute_timers(); + + #ifdef WITH_VIEWPORT + if (viewport_server_mode && viewport_server_is_enabled()) { + if (viewport_server_check_shutdown()) { + kinc_stop(); + return; + } + + int new_width, new_height; + if (viewport_server_check_resize(&new_width, &new_height)) { + viewport_server_resize(new_width, new_height); + } + + InputEvent input_event; + while (viewport_server_read_input(&input_event)) { + switch (input_event.type) { + case INPUT_EVENT_MOUSE_MOVE: + kinc_internal_mouse_trigger_move(0, input_event.x, input_event.y); + break; + case INPUT_EVENT_MOUSE_DOWN: + kinc_internal_mouse_trigger_press(0, input_event.button, input_event.x, input_event.y); + break; + case INPUT_EVENT_MOUSE_UP: + kinc_internal_mouse_trigger_release(0, input_event.button, input_event.x, input_event.y); + break; + case INPUT_EVENT_MOUSE_WHEEL: + kinc_internal_mouse_trigger_scroll(0, input_event.delta); + break; + case INPUT_EVENT_KEY_DOWN: + kinc_internal_keyboard_trigger_key_down(input_event.button); + break; + case INPUT_EVENT_KEY_UP: + kinc_internal_keyboard_trigger_key_up(input_event.button); + break; + } + } + } + #endif kinc_g4_begin(0); + #ifdef WITH_VIEWPORT + if (viewport_server_mode && viewport_server_is_enabled()) { + viewport_server_begin_frame(); + } + #endif run_v8(); + kinc_g4_end(0); + + #ifdef WITH_VIEWPORT + // before the swap + if (viewport_server_mode && viewport_server_is_enabled()) { + viewport_server_end_frame(); + } + #endif kinc_g4_swap_buffers(); } @@ -2979,6 +3290,16 @@ namespace { if (!func->Call(context, context->Global(), 0, NULL).ToLocal(&result)) { handle_exception(&try_catch); } + + #ifdef WITH_NETWORKING + WebSocketWrapper::cleanup(); + HttpRequestWrapper::cleanup(); + #endif + #ifdef WITH_VIEWPORT + if (viewport_server_mode) { + viewport_server_shutdown(); + } + #endif } void key_down(int code, void *data) { @@ -3250,8 +3571,133 @@ namespace { handle_exception(&try_catch); } } + + // timer system for setTimeout and setInterval + struct Timer { + Global callback; + std::chrono::steady_clock::time_point executionTime; + int id; + bool isInterval; + int intervalMs; + }; + + std::map timers; + int nextTimerId = 1; + + void runt_set_timeout(const FunctionCallbackInfo &args) { + Isolate* isolate = args.GetIsolate(); + if (args.Length() < 2 || !args[0]->IsFunction()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "setTimeout requires a function and delay").ToLocalChecked())); + return; + } + + Local callback = Local::Cast(args[0]); + int delay = args[1]->Int32Value(isolate->GetCurrentContext()).FromJust(); + + Timer timer; + timer.callback.Reset(isolate, callback); + timer.executionTime = std::chrono::steady_clock::now() + std::chrono::milliseconds(delay); + timer.id = nextTimerId++; + timer.isInterval = false; + timer.intervalMs = 0; + + timers[timer.id] = std::move(timer); + + args.GetReturnValue().Set(Integer::New(isolate, timer.id)); + } + + void runt_set_interval(const FunctionCallbackInfo &args) { + Isolate* isolate = args.GetIsolate(); + if (args.Length() < 2 || !args[0]->IsFunction()) { + isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "setInterval requires a function and delay").ToLocalChecked())); + return; + } + + Local callback = Local::Cast(args[0]); + int delay = args[1]->Int32Value(isolate->GetCurrentContext()).FromJust(); + + Timer timer; + timer.callback.Reset(isolate, callback); + timer.executionTime = std::chrono::steady_clock::now() + std::chrono::milliseconds(delay); + timer.id = nextTimerId++; + timer.isInterval = true; + timer.intervalMs = delay; + + timers[timer.id] = std::move(timer); + + args.GetReturnValue().Set(Integer::New(isolate, timer.id)); + } + + void runt_clear_timeout(const FunctionCallbackInfo &args) { + Isolate* isolate = args.GetIsolate(); + if (args.Length() < 1) { + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust(); + timers.erase(id); + } + + void runt_clear_interval(const FunctionCallbackInfo &args) { + Isolate* isolate = args.GetIsolate(); + if (args.Length() < 1) { + return; + } + + int id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust(); + timers.erase(id); + } + + void execute_timers() { + if (timers.empty()) return; + + auto now = std::chrono::steady_clock::now(); + std::vector toRemove; + + for (auto& [id, timer] : timers) { + if (now >= timer.executionTime) { + Locker locker{isolate}; + + Isolate::Scope isolate_scope(isolate); + HandleScope handle_scope(isolate); + Local context = Local::New(isolate, global_context); + Context::Scope context_scope(context); + + TryCatch try_catch(isolate); + + Local callback = Local::New(isolate, timer.callback); + Local args[1] = {}; + callback->Call(context, context->Global(), 0, args); + + if (try_catch.HasCaught()) { + handle_exception(&try_catch); + } + + // reschedule interval + if (timer.isInterval) { + timer.executionTime = std::chrono::steady_clock::now() + std::chrono::milliseconds(timer.intervalMs); + } else { + toRemove.push_back(id); + } + } + } + + for (int id : toRemove) { + timers.erase(id); + } + } } +#ifdef WITH_NETWORKING +v8::Global* WebSocketWrapper::getGlobalContext() { + return &global_context; +} + +v8::Global* HttpRequestWrapper::getGlobalContext() { + return &global_context; +} +#endif + int kickstart(int argc, char **argv) { _argc = argc; _argv = argv; @@ -3269,6 +3715,26 @@ int kickstart(int argc, char **argv) { #else bindir = bindir.substr(0, bindir.find_last_of("/")); #endif + +//#if defined(WITH_NETWORKING) && defined(WITH_BENCHMARK) +// for (int i = 1; i < argc; ++i) { +// if (strcmp(argv[i], "--websocket-benchmark") == 0) { +// WSADATA wsaData; +// WSAStartup(MAKEWORD(2, 2), &wsaData); +// websocket_run_benchmark(); +// WSACleanup(); +// return 0; +// } +// else if (strcmp(argv[i], "--websocket-test") == 0) { +// WSADATA wsaData; +// WSAStartup(MAKEWORD(2, 2), &wsaData); +// websocket_run_groupchat_test(60, 5); +// WSACleanup(); +// return 0; +// } +// } +//#endif + assetsdir = argc > 1 ? argv[1] : bindir; // Opening a file @@ -3280,6 +3746,10 @@ int kickstart(int argc, char **argv) { } bool read_console_pid = false; + bool read_shmem_name = false; + bool read_viewport_width = false; + bool read_viewport_height = false; + for (int i = 2; i < argc; ++i) { if (strcmp(argv[i], "--nowindow") == 0) { enable_window = false; @@ -3301,6 +3771,43 @@ int kickstart(int argc, char **argv) { else if (strcmp(argv[i], "--consolepid") == 0) { read_console_pid = true; } + else if (strcmp(argv[i], "--viewport-server") == 0) { + viewport_server_mode = true; + } + else if (strcmp(argv[i], "--shmem") == 0) { + read_shmem_name = true; + } + else if (read_shmem_name) { + strncpy(viewport_shmem_name, argv[i], sizeof(viewport_shmem_name) - 1); + read_shmem_name = false; + } + else if (strcmp(argv[i], "--viewport-width") == 0) { + read_viewport_width = true; + } + else if (read_viewport_width) { + viewport_width = atoi(argv[i]); + read_viewport_width = false; + } + else if (strcmp(argv[i], "--viewport-height") == 0) { + read_viewport_height = true; + } + else if (read_viewport_height) { + viewport_height = atoi(argv[i]); + read_viewport_height = false; + } +//#if defined(WITH_NETWORKING) && defined(WITH_BENCHMARK) +// else if (strcmp(argv[i], "--websocket-benchmark") == 0) { +// kinc_log(KINC_LOG_LEVEL_INFO, "Running WebSocket benchmark..."); +// websocket_run_benchmark(); +// kinc_log(KINC_LOG_LEVEL_INFO, "Benchmark complete."); +// return 0; +// } +// else if (strcmp(argv[i], "--websocket-test") == 0) { +// websocket_run_groupchat_test(1008, 30); +// kinc_log(KINC_LOG_LEVEL_INFO, "Test complete."); +// return 0; +// } +//#endif } #if !defined(KORE_MACOS) @@ -3317,9 +3824,9 @@ int kickstart(int argc, char **argv) { bool snapshot_found = true; kinc_file_reader_t reader; - if (snapshot || !kinc_file_reader_open(&reader, "krom.bin", KINC_FILE_TYPE_ASSET)) { - if (!kinc_file_reader_open(&reader, "krom.js", KINC_FILE_TYPE_ASSET)) { - kinc_log(KINC_LOG_LEVEL_ERROR, "Could not load krom.js, aborting."); + if (snapshot || !kinc_file_reader_open(&reader, "runt.bin", KINC_FILE_TYPE_ASSET)) { + if (!kinc_file_reader_open(&reader, "runt.js", KINC_FILE_TYPE_ASSET)) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Could not load runt.js, aborting."); exit(1); } snapshot_found = false; @@ -3349,7 +3856,7 @@ int kickstart(int argc, char **argv) { Local context = Context::New(isolate_cache); Context::Scope context_scope(context); - ScriptOrigin origin(isolate_cache, String::NewFromUtf8(isolate_cache, "krom_cache").ToLocalChecked()); + ScriptOrigin origin(isolate_cache, String::NewFromUtf8(isolate_cache, "runt_cache").ToLocalChecked()); ScriptCompiler::Source source(String::NewFromUtf8(isolate_cache, code).ToLocalChecked(), origin); Local