diff --git a/v2/internal/bridge/windows.js b/v2/internal/bridge/windows.js new file mode 100644 index 000000000..1c183241e --- /dev/null +++ b/v2/internal/bridge/windows.js @@ -0,0 +1 @@ +var Wails=function(n){var e={};function t(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return n[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}return t.m=n,t.c=e,t.d=function(n,e,r){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:r})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(r,o,function(e){return n[e]}.bind(null,o));return r},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=1)}([function(n,e,t){"use strict";t.r(e),t.d(e,"AppType",(function(){return r}));var r="desktop"},function(n,e,t){"use strict";t.r(e);var r={};t.r(r),t.d(r,"Trace",(function(){return v})),t.d(r,"Print",(function(){return y})),t.d(r,"Debug",(function(){return g})),t.d(r,"Info",(function(){return O})),t.d(r,"Warning",(function(){return m})),t.d(r,"Error",(function(){return h})),t.d(r,"Fatal",(function(){return S})),t.d(r,"SetLogLevel",(function(){return j})),t.d(r,"Level",(function(){return P}));var o={};t.r(o),t.d(o,"Open",(function(){return M}));var i={};t.r(i),t.d(i,"Center",(function(){return N})),t.d(i,"SetTitle",(function(){return T})),t.d(i,"Fullscreen",(function(){return x})),t.d(i,"UnFullscreen",(function(){return I})),t.d(i,"SetSize",(function(){return J})),t.d(i,"SetPosition",(function(){return A})),t.d(i,"Hide",(function(){return L})),t.d(i,"Show",(function(){return R})),t.d(i,"Maximise",(function(){return _})),t.d(i,"Unmaximise",(function(){return F})),t.d(i,"Minimise",(function(){return U})),t.d(i,"Unminimise",(function(){return H})),t.d(i,"Close",(function(){return B}));var c={};t.r(c),t.d(c,"Open",(function(){return G})),t.d(c,"Save",(function(){return q})),t.d(c,"Message",(function(){return z}));var u={};t.r(u),t.d(u,"New",(function(){return on}));var a={};function s(n,e){var t=Object.keys(n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(n);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),t.push.apply(t,r)}return t}function f(n,e,t){return e in n?Object.defineProperty(n,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):n[e]=t,n}t.r(a),t.d(a,"SetIcon",(function(){return cn}));var l=function(n){for(var e=1;e0)for(var e=0;e0)var c=setTimeout((function(){o(Error("Call to "+n+" timed out. Request ID: "+i))}),t);k[i]={timeoutHandle:c,reject:o,resolve:r};try{var u={name:n,args:e,callbackID:i};p("C"+JSON.stringify(u))}catch(n){console.error(n)}}))}function C(n){var e;try{e=JSON.parse(n)}catch(e){var t="Invalid JSON passed to callback: ".concat(e.message,". Message: ").concat(n);throw g(t),new Error(t)}var r=e.callbackid,o=k[r];if(!o){var i="Callback '".concat(r,"' not registered!!!");throw console.error(i),new Error(i)}clearTimeout(o.timeoutHandle),delete k[r],e.error?o.reject(e.error):o.resolve(e.result)}function W(n){var e=[].slice.apply(arguments).slice(1);return D(".wails."+n,e)}function M(n){return p("RBO"+n)}function N(){p("Wc")}function T(n){p("WT"+n)}function x(){p("WF")}function I(){p("Wf")}function J(n,e){p("Ws:"+n+":"+e)}function A(n,e){p("Wp:"+n+":"+e)}function L(){p("WH")}function R(){p("WS")}function _(){p("WM")}function F(){p("WU")}function U(){p("Wm")}function H(){p("Wu")}function B(){p("WC")}function G(n){return W("Dialog.Open",n)}function q(n){return W("Dialog.Save",n)}function z(n){return W("Dialog.Message",n)}E=window.crypto?function(){var n=new Uint32Array(1);return window.crypto.getRandomValues(n)[0]}:function(){return 9007199254740991*Math.random()},window.backend={};var V=function n(e,t){!function(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}(this,n),t=t||-1,this.Callback=function(n){return e.apply(null,n),-1!==t&&0===(t-=1)}},K={};function Q(n,e,t){K[n]=K[n]||[];var r=new V(e,t);console.log("Pushing event listener: "+n),K[n].push(r)}function X(n,e){Q(n,e)}function Y(n,e){Q(n,e,1)}function Z(n){var e=n.name;if(K[e]){for(var t=K[e].slice(),r=0;r @@ -7,15 +9,35 @@ #include #include #include "windows/WebView2.h" +#include int debug = 0; DWORD mainThread; +// --- Assets +extern const unsigned char runtime; +extern const unsigned char *defaultDialogIcons[]; + // dispatch will execute the given `func` pointer void dispatch(dispatchFunction func) { PostThreadMessage(mainThread, WM_APP, 0, (LPARAM) new dispatchFunction(func)); } +LPWSTR cstrToLPWSTR(const char *cstr) { + int wchars_num = MultiByteToWideChar( CP_UTF8 , 0 , cstr , -1, NULL , 0 ); + wchar_t* wstr = new wchar_t[wchars_num+1]; + MultiByteToWideChar( CP_UTF8 , 0 , cstr , -1, wstr , wchars_num ); + return wstr; +} + +// Credit: https://stackoverflow.com/a/9842450 +char* LPWSTRToCstr(LPWSTR input) { + int length = WideCharToMultiByte(CP_UTF8, 0, input, -1, 0, 0, NULL, NULL); + char* output = new char[length]; + WideCharToMultiByte(CP_UTF8, 0, input, -1, output , length, NULL, NULL); + return output; +} + struct Application *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose) { // Create application @@ -60,6 +82,15 @@ void SetMaxWindowSize(struct Application* app, int maxWidth, int maxHeight) { app->maxHeight = (LONG)maxHeight; } +void SetBindings(struct Application *app, const char *bindings) { + std::string temp = std::string("window.wailsbindings = \"") + std::string(bindings) + std::string("\";"); + app->bindings = new char[temp.length()+1]; + memcpy(app->bindings, temp.c_str(), temp.length()+1); + printf("length = %d\n", temp.length()); + printf("app->bindings: %s\n", app->bindings); +} + + LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { struct Application *app = (struct Application *)GetWindowLongPtr(hwnd, GWLP_USERDATA); @@ -116,30 +147,77 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { } return 0; } + +void init(struct Application *app, const char* js) { + LPCWSTR wjs = cstrToLPWSTR(js); + app->webview->AddScriptToExecuteOnDocumentCreated(wjs, nullptr); + delete[] wjs; +} + +void loadAssets(struct Application* app) { + + // Load bindings + ExecJS(app, app->bindings); + delete[] app->bindings; + + // Load runtime + ExecJS(app, (const char*)&runtime); + + int index = 1; + while(1) { + // Get next asset pointer + const unsigned char *asset = assets[index]; + + // If we have no more assets, break + if (asset == 0x00) { + break; + } + + ExecJS(app, (const char*)asset); + index++; + }; + + // Disable context menu if not in debug mode + if( debug != 1 ) { + ExecJS(app, "wails._.DisableDefaultContextMenu();"); + } + + // Show app if we need to + if( app->startHidden == false ) { + Show(app); + } +} + + // -bool initWebView2(struct Application *app, int debug, messageCallback cb) { +bool initWebView2(struct Application *app, int debugEnabled, messageCallback cb) { + + debug = debugEnabled; + CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); std::atomic_flag flag = ATOMIC_FLAG_INIT; flag.test_and_set(); - char currentExePath[MAX_PATH]; - GetModuleFileNameA(NULL, currentExePath, MAX_PATH); - char *currentExeName = PathFindFileNameA(currentExePath); - - +// char currentExePath[MAX_PATH]; +// GetModuleFileNameA(NULL, currentExePath, MAX_PATH); +// char *currentExeName = PathFindFileNameA(currentExePath); // std::wstring_convert> wideCharConverter; -// std::wstring userDataFolder = wideCharConverter.from_bytes(std::getenv("APPDATA")); -// std::wstring currentExeNameW = wideCharConverter.from_bytes(currentExeName); - -// printf("userdata folder = %s\n", userDataFolder.c_str()); +// auto exeName = wideCharConverter.from_bytes(currentExeName); +// +// PWSTR path; +// HRESULT appDataResult = SHGetFolderPathAndSubDir(app->window, CSIDL_LOCAL_APPDATA, nullptr, SHGFP_TYPE_CURRENT, exeName.c_str(), path); +// if ( appDataResult == false ) { +// path = nullptr; +// } +// ICoreWebView2Controller *controller; ICoreWebView2* webview; HRESULT res = CreateCoreWebView2EnvironmentWithOptions( - nullptr, /*(userDataFolder + L"/" + currentExeNameW).c_str()*/ nullptr, nullptr, - new wv2ComHandler(app->window, cb, + nullptr, nullptr, nullptr, + new wv2ComHandler(app, app->window, cb, [&](ICoreWebView2Controller *webviewController) { controller = webviewController; controller->get_CoreWebView2(&webview); @@ -163,9 +241,13 @@ bool initWebView2(struct Application *app, int debug, messageCallback cb) { GetClientRect(app->window, &bounds); app->webviewController->put_Bounds(bounds); - // Schedule an async task to navigate to Bing - app->webview->Navigate(L"https://wails.app/"); -//// init("window.external={invoke:s=>window.chrome.webview.postMessage(s)}"); + // Callback hack + app->webview->AddScriptToExecuteOnDocumentCreated(L"window.chrome.webview.postMessage('I');", nullptr); + // Load the HTML + LPCWSTR html = (LPCWSTR) cstrToLPWSTR((char*)assets[0]); + app->webview->Navigate(html); + + messageFromWindowCallback("Ej{\"name\":\"wails:launched\",\"data\":[]}"); return true; } @@ -217,12 +299,6 @@ void Run(struct Application* app, int argc, char **argv) { // private center() as we are on main thread center(app); -// if( debug == 1 ) { -// BOOL supported = SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); -// if( !supported ) { -// SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); -// } -// } ShowWindow(app->window, startVisibility); UpdateWindow(app->window); SetFocus(app->window); @@ -256,10 +332,13 @@ void SetDebug(struct Application* app, int flag) { debug = flag; } -void SetBindings(struct Application* app, const char *bindings) { -} - void ExecJS(struct Application* app, const char *script) { + printf("Exec: %s\n", script); + ON_MAIN_THREAD( + LPWSTR s = cstrToLPWSTR(script); + app->webview->ExecuteScript(s, nullptr); + delete[] s; + ); } void hide(struct Application* app) { @@ -407,13 +486,12 @@ void SetPosition(struct Application* app, int x, int y) { void Quit(struct Application* app) { } + // Credit: https://stackoverflow.com/a/6693107 void setTitle(struct Application* app, const char *title) { - int wchars_num = MultiByteToWideChar( CP_UTF8 , 0 , title , -1, NULL , 0 ); - wchar_t* wstr = new wchar_t[wchars_num]; - MultiByteToWideChar( CP_UTF8 , 0 , title , -1, wstr , wchars_num ); - SetWindowText(app->window, wstr); - delete[] wstr; + LPCTSTR text = cstrToLPWSTR(title); + SetWindowText(app->window, text); + delete[] text; } void SetTitle(struct Application* app, const char *title) { diff --git a/v2/internal/ffenestri/ffenestri_windows.h b/v2/internal/ffenestri/ffenestri_windows.h index 5ca6e40f2..c42ff9dc3 100644 --- a/v2/internal/ffenestri/ffenestri_windows.h +++ b/v2/internal/ffenestri/ffenestri_windows.h @@ -9,8 +9,15 @@ #include #include #include +#include #include "windows/WebView2.h" +#include "assets.h" + +// TODO: +//#include "userdialogicons.h" + + struct Application{ // Window specific HWND window; @@ -34,6 +41,8 @@ struct Application{ LONG maxWidth; LONG maxHeight; int frame; + + char* bindings; }; #define ON_MAIN_THREAD(code) dispatch( [=]{ code; } ) @@ -44,5 +53,12 @@ typedef std::function comHandlerCallback; void center(struct Application*); void setTitle(struct Application* app, const char *title); +char* LPWSTRToCstr(LPWSTR input); +void loadAssets(struct Application* app); + +// Callback +extern "C" { + void messageFromWindowCallback(const char *); +} #endif \ No newline at end of file diff --git a/v2/internal/ffenestri/wv2ComHandler_windows.h b/v2/internal/ffenestri/wv2ComHandler_windows.h index 3ab9f0be1..368d92b86 100644 --- a/v2/internal/ffenestri/wv2ComHandler_windows.h +++ b/v2/internal/ffenestri/wv2ComHandler_windows.h @@ -12,14 +12,17 @@ class wv2ComHandler : public ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler, public ICoreWebView2CreateCoreWebView2ControllerCompletedHandler, public ICoreWebView2WebMessageReceivedEventHandler, - public ICoreWebView2PermissionRequestedEventHandler { + public ICoreWebView2PermissionRequestedEventHandler +{ + struct Application *app; HWND window; messageCallback mcb; comHandlerCallback cb; public: - wv2ComHandler(HWND window, messageCallback mcb, comHandlerCallback cb) { + wv2ComHandler(struct Application *app, HWND window, messageCallback mcb, comHandlerCallback cb) { + this->app = app; this->window = window; this->mcb = mcb; this->cb = cb; @@ -47,17 +50,32 @@ class wv2ComHandler cb(controller); return S_OK; } + + // This is called when JS posts a message back to webkit HRESULT STDMETHODCALLTYPE Invoke( ICoreWebView2 *sender, ICoreWebView2WebMessageReceivedEventArgs *args) { - printf("CCCCCCCCCCC\n"); LPWSTR message; args->TryGetWebMessageAsString(&message); + if ( message == nullptr ) { + return S_OK; + } + const char *m = LPWSTRToCstr(message); + switch(m[0]) { - std::wstring_convert> wideCharConverter; - mcb(wideCharConverter.to_bytes(message)); - sender->PostWebMessageAsString(message); + // Standard message for backend + case 'S': + printf("--> Message to backend: %s\n", &m[1]); + messageFromWindowCallback(&m[1]); + break; + // DOM Initialised + case 'I': + loadAssets(app); + break; - CoTaskMemFree(message); + default: + printf("----> Unknown message type: %c\n", m[0]); + } + delete[] m; return S_OK; } HRESULT STDMETHODCALLTYPE diff --git a/v2/internal/runtime/assets/desktop_darwin.js b/v2/internal/runtime/assets/desktop_darwin.js new file mode 100644 index 000000000..ef152bb58 --- /dev/null +++ b/v2/internal/runtime/assets/desktop_darwin.js @@ -0,0 +1 @@ +var Wails=function(n){var e={};function t(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return n[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}return t.m=n,t.c=e,t.d=function(n,e,r){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:r})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(r,o,function(e){return n[e]}.bind(null,o));return r},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=1)}([function(n,e,t){"use strict";t.r(e),t.d(e,"AppType",(function(){return r}));var r="desktop"},function(n,e,t){"use strict";t.r(e);var r={};t.r(r),t.d(r,"Trace",(function(){return b})),t.d(r,"Print",(function(){return y})),t.d(r,"Debug",(function(){return g})),t.d(r,"Info",(function(){return m})),t.d(r,"Warning",(function(){return O})),t.d(r,"Error",(function(){return h})),t.d(r,"Fatal",(function(){return S})),t.d(r,"SetLogLevel",(function(){return E})),t.d(r,"Level",(function(){return j}));var o={};t.r(o),t.d(o,"Open",(function(){return D}));var i={};t.r(i),t.d(i,"Center",(function(){return N})),t.d(i,"SetTitle",(function(){return x})),t.d(i,"Fullscreen",(function(){return T})),t.d(i,"UnFullscreen",(function(){return I})),t.d(i,"SetSize",(function(){return J})),t.d(i,"SetPosition",(function(){return A})),t.d(i,"Hide",(function(){return L})),t.d(i,"Show",(function(){return H})),t.d(i,"Maximise",(function(){return R})),t.d(i,"Unmaximise",(function(){return _})),t.d(i,"Minimise",(function(){return F})),t.d(i,"Unminimise",(function(){return U})),t.d(i,"Close",(function(){return B}));var a={};t.r(a),t.d(a,"Open",(function(){return G})),t.d(a,"Save",(function(){return q})),t.d(a,"Message",(function(){return z}));var u={};t.r(u),t.d(u,"New",(function(){return on}));var c={};function s(n,e){var t=Object.keys(n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(n);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),t.push.apply(t,r)}return t}function l(n,e,t){return e in n?Object.defineProperty(n,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):n[e]=t,n}t.r(c),t.d(c,"SetIcon",(function(){return an}));var f=function(n){for(var e=1;e0)for(var e=0;e0)var a=setTimeout((function(){o(Error("Call to "+n+" timed out. Request ID: "+i))}),t);P[i]={timeoutHandle:a,reject:o,resolve:r};try{var u={name:n,args:e,callbackID:i};p("C"+JSON.stringify(u))}catch(n){console.error(n)}}))}function M(n){var e;try{e=JSON.parse(n)}catch(e){var t="Invalid JSON passed to callback: ".concat(e.message,". Message: ").concat(n);throw g(t),new Error(t)}var r=e.callbackid,o=P[r];if(!o){var i="Callback '".concat(r,"' not registered!!!");throw console.error(i),new Error(i)}clearTimeout(o.timeoutHandle),delete P[r],e.error?o.reject(e.error):o.resolve(e.result)}function W(n){var e=[].slice.apply(arguments).slice(1);return C(".wails."+n,e)}function D(n){return p("RBO"+n)}function N(){p("Wc")}function x(n){p("WT"+n)}function T(){p("WF")}function I(){p("Wf")}function J(n,e){p("Ws:"+n+":"+e)}function A(n,e){p("Wp:"+n+":"+e)}function L(){p("WH")}function H(){p("WS")}function R(){p("WM")}function _(){p("WU")}function F(){p("Wm")}function U(){p("Wu")}function B(){p("WC")}function G(n){return W("Dialog.Open",n)}function q(n){return W("Dialog.Save",n)}function z(n){return W("Dialog.Message",n)}k=window.crypto?function(){var n=new Uint32Array(1);return window.crypto.getRandomValues(n)[0]}:function(){return 9007199254740991*Math.random()},window.backend={};var V=function n(e,t){!function(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}(this,n),t=t||-1,this.Callback=function(n){return e.apply(null,n),-1!==t&&0===(t-=1)}},K={};function Q(n,e,t){K[n]=K[n]||[];var r=new V(e,t);console.log("Pushing event listener: "+n),K[n].push(r)}function X(n,e){Q(n,e)}function Y(n,e){Q(n,e,1)}function Z(n){var e=n.name;if(K[e]){for(var t=K[e].slice(),r=0;r0)for(var e=0;e0)var c=setTimeout((function(){o(Error("Call to "+n+" timed out. Request ID: "+i))}),t);k[i]={timeoutHandle:c,reject:o,resolve:r};try{var u={name:n,args:e,callbackID:i};p("C"+JSON.stringify(u))}catch(n){console.error(n)}}))}function C(n){var e;try{e=JSON.parse(n)}catch(e){var t="Invalid JSON passed to callback: ".concat(e.message,". Message: ").concat(n);throw g(t),new Error(t)}var r=e.callbackid,o=k[r];if(!o){var i="Callback '".concat(r,"' not registered!!!");throw console.error(i),new Error(i)}clearTimeout(o.timeoutHandle),delete k[r],e.error?o.reject(e.error):o.resolve(e.result)}function W(n){var e=[].slice.apply(arguments).slice(1);return D(".wails."+n,e)}function M(n){return p("RBO"+n)}function N(){p("Wc")}function T(n){p("WT"+n)}function x(){p("WF")}function I(){p("Wf")}function J(n,e){p("Ws:"+n+":"+e)}function A(n,e){p("Wp:"+n+":"+e)}function L(){p("WH")}function R(){p("WS")}function _(){p("WM")}function F(){p("WU")}function U(){p("Wm")}function H(){p("Wu")}function B(){p("WC")}function G(n){return W("Dialog.Open",n)}function q(n){return W("Dialog.Save",n)}function z(n){return W("Dialog.Message",n)}E=window.crypto?function(){var n=new Uint32Array(1);return window.crypto.getRandomValues(n)[0]}:function(){return 9007199254740991*Math.random()},window.backend={};var V=function n(e,t){!function(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}(this,n),t=t||-1,this.Callback=function(n){return e.apply(null,n),-1!==t&&0===(t-=1)}},K={};function Q(n,e,t){K[n]=K[n]||[];var r=new V(e,t);console.log("Pushing event listener: "+n),K[n].push(r)}function X(n,e){Q(n,e)}function Y(n,e){Q(n,e,1)}function Z(n){var e=n.name;if(K[e]){for(var t=K[e].slice(),r=0;r