diff --git a/game/engines/default/data/html/test.html b/game/engines/default/data/html/test.html index 0babbb5e8f4ce06979d7a9d14ab586e69391a74e..590e11bb1fea274f814be161f70fc6efb4f69401 100644 --- a/game/engines/default/data/html/test.html +++ b/game/engines/default/data/html/test.html @@ -1,6 +1,15 @@ -<script type="text/javascript" src='asset://te4/data/html/jquery-1.10.2.min.js'></script> +<script type="text/javascript" src='asset://te4/html/jquery-1.10.2.min.js'></script> <p><strong>lol</strong> lolll</p> -<script type="text/javascript" src='asset://te4/data/html/test.js'></script> +<script type="text/javascript" src='asset://te4/html/test.js'></script> + +<script type="text/javascript"> + +te4.lua("print('plop')"); +console.log("=====ret: " + te4.lolzor(1, "plop")); + +$('strong').css("color", "red"); + +</script> diff --git a/game/engines/default/data/html/test.js b/game/engines/default/data/html/test.js index 51bf74c9c0f3a0674556d5124cef2670f0dd0858..2fd232a70325fee2aab578ef3e58ae553edb73cb 100644 --- a/game/engines/default/data/html/test.js +++ b/game/engines/default/data/html/test.js @@ -1,4 +1,3 @@ $('strong').click(function() { $(this).html("CLICKED"); - te4core.testclick("theclick", 7); }); \ No newline at end of file diff --git a/game/engines/default/engine/init.lua b/game/engines/default/engine/init.lua index 07807a24e41ae3b5356c90beaf974275f7eda457..a3b3afb8ec9e1cd455b63a390596c4b16df0f65d 100644 --- a/game/engines/default/engine/init.lua +++ b/game/engines/default/engine/init.lua @@ -139,6 +139,9 @@ core.display.setGamma(config.settings.gamma_correction / 100) if not config.settings.fbo_active then core.display.disableFBO() print("Disabling FBO") end if not config.settings.shaders_active then core.shader.disable() print("Disabling Shaders") end +-- Webcore local request resolver +dofile("/engine/webcore.lua") + -- Load profile configs core.profile.createThread() profile = engine.PlayerProfile.new() diff --git a/game/engines/default/engine/ui/WebView.lua b/game/engines/default/engine/ui/WebView.lua index 01c09341640911375958e71c567416cd2d5852a2..9b66004eefe5d77e53d83740bd228e5ddcfd6734 100644 --- a/game/engines/default/engine/ui/WebView.lua +++ b/game/engines/default/engine/ui/WebView.lua @@ -34,6 +34,7 @@ function _M:init(t) self.never_clean = t.never_clean self.allow_popup = t.allow_popup self.allow_login = t.allow_login + self.custom_calls = t.custom_calls or {} if self.allow_login == nil then self.allow_login = true end if self.allow_login and self.url:find("^http://te4%.org/") and profile.auth then @@ -60,18 +61,28 @@ function _M:generate() self.key:reset() local handlers = { - on_title = function(view, title) if self.on_title then self.on_title(title) end end, - on_popup = function(view, url, w, h) if self.allow_popup then + on_title = function(title) if self.on_title then self.on_title(title) end end, + on_popup = function(url, w, h) if self.allow_popup then local Dialog = require "engine.ui.Dialog" Dialog:webPopup(url, w, h) end end, - on_loading = function(view, url, status) - print("===loading", url, status) + on_loading = function(url, status) self.loading = status end, } if self.allow_downloads then self:onDownload(handlers) end - self.view = core.webview.new(self.w, self.h, self.url, handlers) + self.view = core.webview.new(self.w, self.h, handlers) + + self.custom_calls.lolzor = function(nb, str) + print("call from js got: ", nb, str) + return "PLAP" + end + + for name, fct in pairs(self.custom_calls) do + handlers[name] = fct + self.view:setMethod(name) + end + self.view:loadURL(self.url) self.loading = 0 self.loading_rotation = 0 self.scroll_inertia = 0 @@ -155,7 +166,7 @@ end function _M:onDownload(handlers) local Dialog = require "engine.ui.Dialog" - handlers.on_download_request = function(view, downid, url, file, mime) + handlers.on_download_request = function(downid, url, file, mime) if mime == "application/t-engine-addon" and self.allow_downloads.addons and url:find("^http://te4%.org/") then local path = fs.getRealPath("/addons/") if path then @@ -192,12 +203,12 @@ function _M:onDownload(handlers) self.view:downloadAction(downid, false) end - handlers.on_download_update = function(view, downid, cur_size, total_size, percent, speed) + handlers.on_download_update = function(downid, cur_size, total_size, percent, speed) if not self.download_dialog then return end self.download_dialog:updateFill(cur_size, total_size, ("%d%% - %d KB/s"):format(cur_size * 100 / total_size, speed / 1024)) end - handlers.on_download_finish = function(view, downid) + handlers.on_download_finish = function(downid) if not self.download_dialog then return end game:unregisterDialog(self.download_dialog) if self.download_dialog.install_kind == "Addon" then diff --git a/game/engines/default/engine/webcore.lua b/game/engines/default/engine/webcore.lua new file mode 100644 index 0000000000000000000000000000000000000000..6dcb8fc9a3e1c7025e83f9486d10777e7b43bc8c --- /dev/null +++ b/game/engines/default/engine/webcore.lua @@ -0,0 +1,62 @@ +-- TE4 - T-Engine 4 +-- Copyright (C) 2009 - 2014 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +if not core.webview then return end + +local class = require "class" + +core.webview.paths = {} + +function core.webview.responder(id, path) + path = "/"..path + print("[WEBCORE] path request: ", path) + + -- Let hook do their stuff + local reply = {} + if class:triggerHook{"Web:request", path=path, reply=reply} and reply.data then + core.webview.localReplyData(id, reply.mime, reply.data) + return + end + + -- No hooks, perhaps we have a registered path matching + for mpath, fct in pairs(core.webview.paths) do + local r = {path:find("^"..mpath)} + if r[1] then + table.remove(r, 1) table.remove(r, 1) + local mime, data = fct(path, unpack(r)) + if mime and data then + core.webview.localReplyData(id, mime, data) + return + end + end + end + + -- Default, check for a file in /data/ + local mime = "application/octet-stream" + if path:find("%.html$") then mime = "text/html" + elseif path:find("%.js$") then mime = "text/javascript" + elseif path:find("%.css$") then mime = "text/css" + elseif path:find("%.png$") then mime = "image/png" + end + core.webview.localReplyFile(id, mime, "/data"..path) +end + +core.webview.paths["/example/(.*)"] = function(path, sub) + return "text/html", "example sub url was: "..sub +end diff --git a/game/engines/default/modules/boot/dialogs/MainMenu.lua b/game/engines/default/modules/boot/dialogs/MainMenu.lua index deb2e7bc94279ca6504db298c7ca5cb9c4f7bf46..72420b7c8c3ac807171f063fb3f64d225179d15c 100644 --- a/game/engines/default/modules/boot/dialogs/MainMenu.lua +++ b/game/engines/default/modules/boot/dialogs/MainMenu.lua @@ -57,7 +57,7 @@ function _M:init() l[#l+1] = {name="Credits", fct=function() game:registerDialog(require("mod.dialogs.Credits").new()) end} l[#l+1] = {name="Exit", fct=function() game:onQuit() end} if config.settings.cheat then l[#l+1] = {name="Reboot", fct=function() util.showMainMenu() end} end - -- if config.settings.cheat then l[#l+1] = {name="webtest", fct=function() util.browserOpenUrl("http://te4.org/addons/tome?_te4") end} end + if config.settings.cheat then l[#l+1] = {name="webtest", fct=function() util.browserOpenUrl("asset://te4/html/test.html") end} end self.c_background = Button.new{text=game.stopped and "Enable background" or "Disable background", fct=function() self:switchBackground() end} self.c_version = Textzone.new{auto_width=true, auto_height=true, text=("#{bold}##B9E100#T-Engine4 version: %d.%d.%d"):format(engine.version[1], engine.version[2], engine.version[3])} diff --git a/src/web-awesomium/web.cpp b/src/web-awesomium/web.cpp index 4fc86094b8a673df1656b64827e8e37ce81446a1..e8875e593f5c3f56a369779cf32523c1a0f06bae 100644 --- a/src/web-awesomium/web.cpp +++ b/src/web-awesomium/web.cpp @@ -27,14 +27,15 @@ unsigned int (*web_make_texture)(int w, int h); void (*web_del_texture)(unsigned int tex); void (*web_texture_update)(unsigned int tex, int w, int h, const void* buffer); static void (*web_key_mods)(bool *shift, bool *ctrl, bool *alt, bool *meta); +static void (*web_instant_js)(int handlers, const char *fct, int nb_args, WebJsValue *args, WebJsValue *ret); using namespace Awesomium; -class PhysfsDataSource; +class TE4DataSource; static WebCore *web_core = NULL; static WebSession *web_session = NULL; -static PhysfsDataSource *web_data_source = NULL; +static TE4DataSource *web_data_source = NULL; class WebListener; @@ -58,6 +59,7 @@ class WebListener : private: int handlers; public: + JSObject te4_js; WebListener(int handlers) { this->handlers = handlers; } virtual void OnChangeTitle(Awesomium::WebView* caller, const Awesomium::WebString& title) { @@ -85,6 +87,11 @@ public: } virtual void OnAddConsoleMessage(Awesomium::WebView* caller, const Awesomium::WebString& message, int line_number, const Awesomium::WebString& source) { + char *msg = webstring_to_buf(message, NULL); + char *src = webstring_to_buf(source, NULL); + printf("[WEBCORE:console %s:%d] %s\n", src, line_number, msg); + free(msg); + free(src); } virtual void OnShowCreatedWebView(Awesomium::WebView* caller, Awesomium::WebView* new_view, const Awesomium::WebURL& opener_url, const Awesomium::WebURL& target_url, const Awesomium::Rect& initial_pos, bool is_popup) { @@ -101,7 +108,7 @@ public: event->data.popup.h = initial_pos.height; push_event(event); - printf("[WEB] stopped popup to %s (%dx%d), pushing event...\n", url, event->data.popup.w, event->data.popup.h); + printf("[WEBCORE] stopped popup to %s (%dx%d), pushing event...\n", url, event->data.popup.w, event->data.popup.h); } void OnRequestDownload(WebView* caller, int download_id, const WebURL& wurl, const WebString& suggested_filename, const WebString& mime_type) { @@ -109,7 +116,7 @@ public: const char *mime = webstring_to_buf(mime_type, NULL); const char *url = webstring_to_buf(rurl, NULL); const char *name = webstring_to_buf(suggested_filename, NULL); - printf("[WEB] Download request [name: %s] [mime: %s] [url: %s]\n", name, mime, url); + printf("[WEBCORE] Download request [name: %s] [mime: %s] [url: %s]\n", name, mime, url); WebEvent *event = new WebEvent(); event->kind = TE4_WEB_EVENT_DOWNLOAD_REQUEST; @@ -187,17 +194,79 @@ public: } virtual void OnMethodCall(WebView* caller, unsigned int remote_object_id, const WebString& method_name, const JSArray& args) { + if (remote_object_id == te4_js.remote_id() && method_name == WSLit("lua")) { + JSValue arg = args[0]; + WebString wcode = arg.ToString(); + const char *code = webstring_to_buf(wcode, NULL); + + WebEvent *event = new WebEvent(); + event->kind = TE4_WEB_EVENT_RUN_LUA; + event->handlers = handlers; + event->data.run_lua.code = code; + push_event(event); + } } - virtual JSValue OnMethodCallWithReturnValue(WebView* caller, unsigned int remote_object_id, const WebString& method_name, const JSArray& args) { - JSValue ret(false); - return ret; + virtual JSValue OnMethodCallWithReturnValue(WebView* caller, unsigned int remote_object_id, const WebString& method_name, const JSArray& jsargs) { + if (remote_object_id != te4_js.remote_id()) { + JSValue ret(false); + return ret; + } + + const char *fct = webstring_to_buf(method_name, NULL); + WebJsValue ret; + int nb_args = jsargs.size(); + WebJsValue *args = new WebJsValue[nb_args]; + for (int i = 0; i < nb_args; i++) { + WebJsValue *wv = &args[i]; + JSValue v = jsargs[i]; + if (v.IsNull()) { + wv->kind = TE4_WEB_JS_NULL; + } else if (v.IsBoolean()) { + wv->kind = TE4_WEB_JS_BOOLEAN; + wv->data.b = v.ToBoolean(); + } else if (v.IsNumber()) { + wv->kind = TE4_WEB_JS_NUMBER; + wv->data.n = v.ToDouble(); + } else if (v.IsString()) { + wv->kind = TE4_WEB_JS_STRING; + const char *s = webstring_to_buf(v.ToString(), NULL); + wv->data.s = s; + } + } + + web_instant_js(handlers, fct, nb_args, args, &ret); + + // Free the fucking strings. I love GC. I want a GC :/ + for (int i = 0; i < nb_args; i++) { + WebJsValue *wv = &args[i]; + JSValue v = jsargs[i]; + if (v.IsString()) free((void*)wv->data.s); + } + delete args; + free((void*)fct); + + if (ret.kind == TE4_WEB_JS_NULL) return JSValue::Null(); + else if (ret.kind == TE4_WEB_JS_BOOLEAN) return JSValue(ret.data.b); + else if (ret.kind == TE4_WEB_JS_NUMBER) return JSValue(ret.data.n); + else if (ret.kind == TE4_WEB_JS_STRING) { + WebString s = WebString::CreateFromUTF8(ret.data.s, strlen(ret.data.s)); + return JSValue(s); + } + return JSValue(); } }; -class PhysfsDataSource : public DataSource { +class TE4DataSource : public DataSource { public: - virtual void OnRequest(int request_id, const WebString& path) { + virtual void OnRequest(int request_id, const WebString& wpath) { + const char *path = webstring_to_buf(wpath, NULL); + + WebEvent *event = new WebEvent(); + event->kind = TE4_WEB_EVENT_LOCAL_REQUEST; + event->data.local_request.id = request_id; + event->data.local_request.path = path; + push_event(event); } }; @@ -206,12 +275,9 @@ class WebViewOpaque { public: WebView *view; WebListener *listener; - JSObject *te4core; }; -void te4_web_new(web_view_type *view, const char *url, int w, int h) { - size_t urllen = strlen(url); - +void te4_web_new(web_view_type *view, int w, int h) { WebViewOpaque *opaque = new WebViewOpaque(); view->opaque = (void*)opaque; @@ -220,15 +286,16 @@ void te4_web_new(web_view_type *view, const char *url, int w, int h) { opaque->view->set_view_listener(opaque->listener); opaque->view->set_download_listener(opaque->listener); opaque->view->set_load_listener(opaque->listener); - opaque->te4core = NULL; + opaque->view->set_js_method_handler(opaque->listener); view->w = w; view->h = h; view->closed = false; - WebURL lurl(WebString::CreateFromUTF8(url, urllen)); - opaque->view->LoadURL(lurl); + opaque->listener->te4_js = (opaque->view->CreateGlobalJavascriptObject(WSLit("te4"))).ToObject(); + opaque->listener->te4_js.SetCustomMethod(WSLit("lua"), false); + opaque->view->SetTransparent(true); - printf("Created webview: %s\n", url); + printf("Created webview: %dx%d\n", w, h); } bool te4_web_close(web_view_type *view) { @@ -237,13 +304,28 @@ bool te4_web_close(web_view_type *view) { opaque->view->Destroy(); delete opaque->listener; view->closed = true; - if (opaque->te4core) delete opaque->te4core; printf("Destroyed webview\n"); return true; } return false; } +void te4_web_load_url(web_view_type *view, const char *url) { + WebViewOpaque *opaque = (WebViewOpaque*)view->opaque; + if (view->closed) return; + + size_t urllen = strlen(url); + WebURL lurl(WebString::CreateFromUTF8(url, urllen)); + opaque->view->LoadURL(lurl); +} + +void te4_web_set_js_call(web_view_type *view, const char *name) { + WebViewOpaque *opaque = (WebViewOpaque*)view->opaque; + if (view->closed) return; + + opaque->listener->te4_js.SetCustomMethod(WebString::CreateFromUTF8(name, strlen(name)), true); +} + bool te4_web_toscreen(web_view_type *view, int *w, int *h, unsigned int *tex) { WebViewOpaque *opaque = (WebViewOpaque*)view->opaque; if (view->closed) return false; @@ -352,6 +434,12 @@ void te4_web_download_action(web_view_type *view, long id, const char *path) { } } +void te4_web_reply_local(int id, const char *mime, const char *result, size_t len) { + WebString wmime = WebString::CreateFromUTF8(mime, strlen(mime)); + web_data_source->SendResponse(id, len, (unsigned char *)result, wmime); +} + + void te4_web_do_update(void (*cb)(WebEvent*)) { if (!web_core) return; @@ -375,6 +463,12 @@ void te4_web_do_update(void (*cb)(WebEvent*)) { case TE4_WEB_EVENT_LOADING: free((void*)event->data.loading.url); break; + case TE4_WEB_EVENT_LOCAL_REQUEST: + free((void*)event->data.local_request.path); + break; + case TE4_WEB_EVENT_RUN_LUA: + free((void*)event->data.run_lua.code); + break; } delete event; @@ -385,7 +479,8 @@ void te4_web_setup( int argc, char **gargv, char *spawnc, void*(*mutex_create)(), void(*mutex_destroy)(void*), void(*mutex_lock)(void*), void(*mutex_unlock)(void*), unsigned int (*make_texture)(int, int), void (*del_texture)(unsigned int), void (*texture_update)(unsigned int, int, int, const void*), - void (*key_mods)(bool*, bool*, bool*, bool*) + void (*key_mods)(bool*, bool*, bool*, bool*), + void (*instant_js)(int handlers, const char *fct, int nb_args, WebJsValue *args, WebJsValue *ret) ) { web_mutex_create = mutex_create; @@ -396,11 +491,12 @@ void te4_web_setup( web_del_texture = del_texture; web_texture_update = texture_update; web_key_mods = key_mods; + web_instant_js = instant_js; if (!web_core) { web_core = WebCore::Initialize(WebConfig()); web_core->set_surface_factory(new GLTextureSurfaceFactory()); web_session = web_core->CreateWebSession(WSLit(""), WebPreferences()); - web_data_source = new PhysfsDataSource(); + web_data_source = new TE4DataSource(); web_session->AddDataSource(WSLit("te4"), web_data_source); } } diff --git a/src/web-awesomium/web.h b/src/web-awesomium/web.h index 9c48d336c5a485175b23c1c8a2c3a7bc759dc369..fc428718bb810f091e6f0dbf0536f5e4e60b7a8d 100644 --- a/src/web-awesomium/web.h +++ b/src/web-awesomium/web.h @@ -22,10 +22,16 @@ #define WEB_TE4_API #endif -WEB_TE4_API void te4_web_setup(int argc, char **argv, char *spawn, void*(*mutex_create)(), void(*mutex_destroy)(void*), void(*mutex_lock)(void*), void(*mutex_unlock)(void*), unsigned int (*make_texture)(int, int), void (*del_texture)(unsigned int), void (*texture_update)(unsigned int, int, int, const void*), void (*)(bool*, bool*, bool*, bool*)); +WEB_TE4_API void te4_web_setup( + int argc, char **argv, char *spawn, + void*(*mutex_create)(), void(*mutex_destroy)(void*), void(*mutex_lock)(void*), void(*mutex_unlock)(void*), + unsigned int (*make_texture)(int, int), void (*del_texture)(unsigned int), void (*texture_update)(unsigned int, int, int, const void*), + void (*key_mods)(bool*, bool*, bool*, bool*), + void (*web_instant_js)(int handlers, const char *fct, int nb_args, WebJsValue *args, WebJsValue *ret) +); WEB_TE4_API void te4_web_initialize(); WEB_TE4_API void te4_web_do_update(void (*cb)(WebEvent*)); -WEB_TE4_API void te4_web_new(web_view_type *view, const char *url, int w, int h); +WEB_TE4_API void te4_web_new(web_view_type *view, int w, int h); WEB_TE4_API bool te4_web_close(web_view_type *view); WEB_TE4_API bool te4_web_toscreen(web_view_type *view, int *w, int *h, unsigned int *tex); WEB_TE4_API bool te4_web_loading(web_view_type *view); @@ -35,5 +41,8 @@ WEB_TE4_API void te4_web_inject_mouse_wheel(web_view_type *view, int x, int y); WEB_TE4_API void te4_web_inject_mouse_button(web_view_type *view, int kind, bool up); WEB_TE4_API void te4_web_inject_key(web_view_type *view, int scancode, int asymb, const char *uni, int unilen, bool up); WEB_TE4_API void te4_web_download_action(web_view_type *view, long id, const char *path); +WEB_TE4_API void te4_web_reply_local(int id, const char *mime, const char *result, size_t len); +WEB_TE4_API void te4_web_load_url(web_view_type *view, const char *url); +WEB_TE4_API void te4_web_set_js_call(web_view_type *view, const char *name); #endif diff --git a/src/web-external.h b/src/web-external.h index fad9f0f41ea97298dd73cb8ade8be50d100d2712..47f53ebf613316331bce8013a8687fcaa64cba31 100644 --- a/src/web-external.h +++ b/src/web-external.h @@ -15,6 +15,8 @@ enum web_event_kind { TE4_WEB_EVENT_DOWNLOAD_UPDATE, TE4_WEB_EVENT_DOWNLOAD_FINISH, TE4_WEB_EVENT_LOADING, + TE4_WEB_EVENT_LOCAL_REQUEST, + TE4_WEB_EVENT_RUN_LUA, }; typedef struct { @@ -44,9 +46,32 @@ typedef struct { const char *url; signed char status; } loading; + struct { + int id; + const char *path; + } local_request; + struct { + const char *code; + } run_lua; } data; } WebEvent; +enum web_js_kind { + TE4_WEB_JS_NULL, + TE4_WEB_JS_BOOLEAN, + TE4_WEB_JS_NUMBER, + TE4_WEB_JS_STRING, +}; + +typedef struct { + enum web_js_kind kind; + union { + bool b; + double n; + const char *s; + } data; +} WebJsValue; + typedef struct { void *opaque; int w, h; diff --git a/src/web.c b/src/web.c index 51fc103c3130bf3f2b3678e6515943c3ee1c3a22..275563b6884dcdc7a474dc8782068e713cffb050 100644 --- a/src/web.c +++ b/src/web.c @@ -24,6 +24,7 @@ #include "lauxlib.h" #include "lualib.h" #include "auxiliar.h" +#include "physfs.h" #include "core_lua.h" #include "types.h" #include "main.h" @@ -35,10 +36,16 @@ * Grab web browser methods -- availabe only here */ static bool webcore = FALSE; -static void (*te4_web_setup)(int, char**, char*, void*(*)(), void(*)(void*), void(*)(void*), void(*)(void*), unsigned int (*)(int, int), void (*)(unsigned int), void (*)(unsigned int, int, int, const void*), void (*)(bool*, bool*, bool*, bool*)); +static void (*te4_web_setup)( + int, char**, char*, + void*(*)(), void(*)(void*), void(*)(void*), void(*)(void*), + unsigned int (*)(int, int), void (*)(unsigned int), void (*)(unsigned int, int, int, const void*), + void (*)(bool*, bool*, bool*, bool*), + void (*)(int handlers, const char *fct, int nb_args, WebJsValue *args, WebJsValue *ret) +); static void (*te4_web_initialize)(); static void (*te4_web_do_update)(void (*cb)(WebEvent*)); -static void (*te4_web_new)(web_view_type *view, const char *url, int w, int h); +static void (*te4_web_new)(web_view_type *view, int w, int h); static bool (*te4_web_close)(web_view_type *view); static bool (*te4_web_toscreen)(web_view_type *view, int *w, int *h, unsigned int *tex); static bool (*te4_web_loading)(web_view_type *view); @@ -48,19 +55,21 @@ static void (*te4_web_inject_mouse_wheel)(web_view_type *view, int x, int y); static void (*te4_web_inject_mouse_button)(web_view_type *view, int kind, bool up); static void (*te4_web_inject_key)(web_view_type *view, int scancode, int asymb, const char *uni, int unilen, bool up); static void (*te4_web_download_action)(web_view_type *view, long id, const char *path); +static void (*te4_web_reply_local)(int id, const char *mime, const char *result, size_t len); +static void (*te4_web_load_url)(web_view_type *view, const char *url); +static void (*te4_web_set_js_call)(web_view_type *view, const char *name); static int lua_web_new(lua_State *L) { int w = luaL_checknumber(L, 1); int h = luaL_checknumber(L, 2); - const char* url = luaL_checkstring(L, 3); web_view_type *view = (web_view_type*)lua_newuserdata(L, sizeof(web_view_type)); auxiliar_setclass(L, "web{view}", -1); - lua_pushvalue(L, 4); + lua_pushvalue(L, 3); view->handlers = luaL_ref(L, LUA_REGISTRYINDEX); - te4_web_new(view, url, w, h); + te4_web_new(view, w, h); return 1; } @@ -73,6 +82,13 @@ static int lua_web_close(lua_State *L) { return 0; } +static int lua_web_load_url(lua_State *L) { + web_view_type *view = (web_view_type*)auxiliar_checkclass(L, "web{view}", 1); + const char* url = luaL_checkstring(L, 2); + te4_web_load_url(view, url); + return 0; +} + static int lua_web_toscreen(lua_State *L) { web_view_type *view = (web_view_type*)auxiliar_checkclass(L, "web{view}", 1); int x = luaL_checknumber(L, 2); @@ -177,10 +193,53 @@ static int lua_web_download_action(lua_State *L) { return 0; } +static int lua_web_set_method(lua_State *L) { + web_view_type *view = (web_view_type*)auxiliar_checkclass(L, "web{view}", 1); + const char *name = luaL_checkstring(L, 2); + te4_web_set_js_call(view, name); + return 0; +} + +static int lua_web_local_reply_file(lua_State *L) { + int id = lua_tonumber(L, 1); + const char *mime = luaL_checkstring(L, 2); + const char *file = luaL_checkstring(L, 3); + + PHYSFS_file *f = PHYSFS_openRead(file); + if (!f) { + te4_web_reply_local(id, mime, NULL, 0); + return 0; + } + + size_t len = PHYSFS_fileLength(f); + char *data = malloc(len * sizeof(char)); + size_t read = 0; + while (read < len) { + size_t rl = PHYSFS_read(f, data + read, sizeof(char), len - read); + if (rl <= 0) break; + read += rl; + } + PHYSFS_close(f); + + te4_web_reply_local(id, mime, data, read); + return 0; +} + +static int lua_web_local_reply_data(lua_State *L) { + int id = lua_tonumber(L, 1); + const char *mime = luaL_checkstring(L, 2); + size_t len; + const char *data = luaL_checklstring(L, 3, &len); + + te4_web_reply_local(id, mime, data, len); + return 0; +} + static const struct luaL_Reg view_reg[] = { {"__gc", lua_web_close}, {"downloadAction", lua_web_download_action}, + {"loadURL", lua_web_load_url}, {"toScreen", lua_web_toscreen}, {"focus", lua_web_focus}, {"loading", lua_web_loading}, @@ -188,13 +247,15 @@ static const struct luaL_Reg view_reg[] = {"injectMouseWheel", lua_web_inject_mouse_wheel}, {"injectMouseButton", lua_web_inject_mouse_button}, {"injectKey", lua_web_inject_key}, -// {"setMethod", lua_web_set_method}, + {"setMethod", lua_web_set_method}, {NULL, NULL}, }; static const struct luaL_Reg weblib[] = { {"new", lua_web_new}, + {"localReplyData", lua_web_local_reply_data}, + {"localReplyFile", lua_web_local_reply_file}, {NULL, NULL}, }; @@ -207,9 +268,8 @@ static void handle_event(WebEvent *event) { lua_gettable(he_L, -2); lua_remove(he_L, -2); if (!lua_isnil(he_L, -1)) { - lua_rawgeti(he_L, LUA_REGISTRYINDEX, event->handlers); lua_pushstring(he_L, event->data.title); - docall(he_L, 2, 0); + docall(he_L, 1, 0); } else lua_pop(he_L, 1); break; @@ -219,11 +279,10 @@ static void handle_event(WebEvent *event) { lua_gettable(he_L, -2); lua_remove(he_L, -2); if (!lua_isnil(he_L, -1)) { - lua_rawgeti(he_L, LUA_REGISTRYINDEX, event->handlers); lua_pushstring(he_L, event->data.popup.url); lua_pushnumber(he_L, event->data.popup.w); lua_pushnumber(he_L, event->data.popup.h); - docall(he_L, 4, 0); + docall(he_L, 3, 0); } else lua_pop(he_L, 1); break; @@ -233,12 +292,11 @@ static void handle_event(WebEvent *event) { lua_gettable(he_L, -2); lua_remove(he_L, -2); if (!lua_isnil(he_L, -1)) { - lua_rawgeti(he_L, LUA_REGISTRYINDEX, event->handlers); lua_pushnumber(he_L, event->data.download_request.id); lua_pushstring(he_L, event->data.download_request.url); lua_pushstring(he_L, event->data.download_request.name); lua_pushstring(he_L, event->data.download_request.mime); - docall(he_L, 5, 0); + docall(he_L, 4, 0); } else lua_pop(he_L, 1); break; @@ -248,13 +306,12 @@ static void handle_event(WebEvent *event) { lua_gettable(he_L, -2); lua_remove(he_L, -2); if (!lua_isnil(he_L, -1)) { - lua_rawgeti(he_L, LUA_REGISTRYINDEX, event->handlers); lua_pushnumber(he_L, event->data.download_update.id); lua_pushnumber(he_L, event->data.download_update.got); lua_pushnumber(he_L, event->data.download_update.total); lua_pushnumber(he_L, event->data.download_update.percent); lua_pushnumber(he_L, event->data.download_update.speed); - docall(he_L, 6, 0); + docall(he_L, 5, 0); } else lua_pop(he_L, 1); break; @@ -264,9 +321,8 @@ static void handle_event(WebEvent *event) { lua_gettable(he_L, -2); lua_remove(he_L, -2); if (!lua_isnil(he_L, -1)) { - lua_rawgeti(he_L, LUA_REGISTRYINDEX, event->handlers); lua_pushnumber(he_L, event->data.download_update.id); - docall(he_L, 2, 0); + docall(he_L, 1, 0); } else lua_pop(he_L, 1); break; @@ -276,19 +332,40 @@ static void handle_event(WebEvent *event) { lua_gettable(he_L, -2); lua_remove(he_L, -2); if (!lua_isnil(he_L, -1)) { - lua_rawgeti(he_L, LUA_REGISTRYINDEX, event->handlers); lua_pushstring(he_L, event->data.loading.url); lua_pushnumber(he_L, event->data.loading.status); - docall(he_L, 3, 0); + docall(he_L, 2, 0); + } else lua_pop(he_L, 1); + break; + + case TE4_WEB_EVENT_LOCAL_REQUEST: + lua_getglobal(he_L, "core"); + lua_getfield(he_L, -1, "webview"); + lua_getfield(he_L, -1, "responder"); + lua_remove(he_L, -2); + lua_remove(he_L, -2); + if (!lua_isnil(he_L, -1)) { + lua_pushnumber(he_L, event->data.local_request.id); + lua_pushstring(he_L, event->data.local_request.path); + docall(he_L, 2, 0); } else lua_pop(he_L, 1); break; + + case TE4_WEB_EVENT_RUN_LUA: + if (!luaL_loadstring(he_L, event->data.run_lua.code)) { + docall(he_L, 0, 0); + } else { + printf("[WEBCORE] Failed to run lua code:\n%s\n ==>> Error: %s\n", event->data.run_lua.code, lua_tostring(he_L, -1)); + lua_pop(he_L, 1); + } + break; } } void te4_web_update(lua_State *L) { if (webcore) { he_L = L; - te4_web_do_update(handle_event); + te4_web_do_update(handle_event); } } @@ -353,6 +430,42 @@ static void web_key_mods(bool *shift, bool *ctrl, bool *alt, bool *meta) { if (smod & KMOD_GUI) *meta = TRUE; } +static void web_instant_js(int handlers, const char *fct, int nb_args, WebJsValue *args, WebJsValue *ret) { + lua_rawgeti(he_L, LUA_REGISTRYINDEX, handlers); + lua_pushstring(he_L, fct); + lua_gettable(he_L, -2); + lua_remove(he_L, -2); + if (!lua_isnil(he_L, -1)) { + int i; + for (i = 0; i < nb_args; i++) { + if (args[i].kind == TE4_WEB_JS_NULL) lua_pushnil(he_L); + else if (args[i].kind == TE4_WEB_JS_BOOLEAN) lua_pushboolean(he_L, args[i].data.b); + else if (args[i].kind == TE4_WEB_JS_NUMBER) lua_pushnumber(he_L, args[i].data.n); + else if (args[i].kind == TE4_WEB_JS_STRING) lua_pushstring(he_L, args[i].data.s); + } + if (!docall(he_L, nb_args, 1)) { + if (lua_isnumber(he_L, -1)) { + ret->kind = TE4_WEB_JS_NUMBER; + ret->data.n = lua_tonumber(he_L, -1); + } else if (lua_isstring(he_L, -1)) { + ret->kind = TE4_WEB_JS_STRING; + ret->data.s = lua_tostring(he_L, -1); + } else if (lua_isboolean(he_L, -1)) { + ret->kind = TE4_WEB_JS_BOOLEAN; + ret->data.b = lua_toboolean(he_L, -1); + } else { + ret->kind = TE4_WEB_JS_NULL; + } + lua_pop(he_L, 1); + } else { + ret->kind = TE4_WEB_JS_NULL; + } + } else { + ret->kind = TE4_WEB_JS_NULL; + lua_pop(he_L, 1); + } +} + void te4_web_load() { #if defined(SELFEXE_LINUX) void *web = SDL_LoadObject("libte4-web.so"); @@ -369,10 +482,16 @@ void te4_web_load() { if (web) { webcore = TRUE; - te4_web_setup = (void (*)(int, char**, char*, void*(*)(), void(*)(void*), void(*)(void*), void(*)(void*), unsigned int (*)(int, int), void (*)(unsigned int), void (*)(unsigned int, int, int, const void*), void (*)(bool*, bool*, bool*, bool*) )) SDL_LoadFunction(web, "te4_web_setup"); + te4_web_setup = (void (*)( + int, char**, char*, + void*(*)(), void(*)(void*), void(*)(void*), void(*)(void*), + unsigned int (*)(int, int), void (*)(unsigned int), void (*)(unsigned int, int, int, const void*), + void (*)(bool*, bool*, bool*, bool*), + void (*)(int handlers, const char *fct, int nb_args, WebJsValue *args, WebJsValue *ret) + )) SDL_LoadFunction(web, "te4_web_setup"); te4_web_initialize = (void (*)()) SDL_LoadFunction(web, "te4_web_initialize"); te4_web_do_update = (void (*)(void (*cb)(WebEvent*))) SDL_LoadFunction(web, "te4_web_do_update"); - te4_web_new = (void (*)(web_view_type *view, const char *url, int w, int h)) SDL_LoadFunction(web, "te4_web_new"); + te4_web_new = (void (*)(web_view_type *view, int w, int h)) SDL_LoadFunction(web, "te4_web_new"); te4_web_close = (bool (*)(web_view_type *view)) SDL_LoadFunction(web, "te4_web_close"); te4_web_toscreen = (bool (*)(web_view_type *view, int *w, int *h, unsigned int *tex)) SDL_LoadFunction(web, "te4_web_toscreen"); te4_web_loading = (bool (*)(web_view_type *view)) SDL_LoadFunction(web, "te4_web_loading"); @@ -382,12 +501,16 @@ void te4_web_load() { te4_web_inject_mouse_button = (void (*)(web_view_type *view, int kind, bool up)) SDL_LoadFunction(web, "te4_web_inject_mouse_button"); te4_web_inject_key = (void (*)(web_view_type *view, int scancode, int asymb, const char *uni, int unilen, bool up)) SDL_LoadFunction(web, "te4_web_inject_key"); te4_web_download_action = (void (*)(web_view_type *view, long id, const char *path)) SDL_LoadFunction(web, "te4_web_download_action"); + te4_web_reply_local = (void (*)(int id, const char *mime, const char *result, size_t len)) SDL_LoadFunction(web, "te4_web_reply_local"); + te4_web_load_url = (void (*)(web_view_type *view, const char *url)) SDL_LoadFunction(web, "te4_web_load_url"); + te4_web_set_js_call = (void (*)(web_view_type *view, const char *name)) SDL_LoadFunction(web, "te4_web_set_js_call"); te4_web_setup( g_argc, g_argv, NULL, web_mutex_create, web_mutex_destroy, web_mutex_lock, web_mutex_unlock, web_make_texture, web_del_texture, web_texture_update, - web_key_mods + web_key_mods, + web_instant_js ); } }