123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173 |
- /*
- * MIT License
- *
- * Copyright (c) 2017 Serge Zaitsev
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
- #ifndef WEBVIEW_H
- #define WEBVIEW_H
- #ifndef WEBVIEW_API
- #define WEBVIEW_API extern
- #endif
- #include <stdint.h>
- #ifdef __cplusplus
- extern "C"
- {
- #endif
- typedef void *webview_t;
- typedef void (*webview_external_invoke_cb_t)(webview_t w, const char *arg);
- // Create a new webview instance
- WEBVIEW_API webview_t webview_create(webview_external_invoke_cb_t invoke_cb, int width, int height, int resizable, int debug);
- // Destroy a webview
- WEBVIEW_API void webview_destroy(webview_t w);
- // Run the main loop
- WEBVIEW_API void webview_run(webview_t w);
- // Stop the main loop
- WEBVIEW_API void webview_terminate(webview_t w);
- // Post a function to be executed on the main thread
- WEBVIEW_API void webview_dispatch(
- webview_t w, void (*fn)(webview_t w, void *arg), void *arg);
- WEBVIEW_API void *webview_get_window(webview_t w);
- WEBVIEW_API void webview_set_title(webview_t w, const char *title);
- WEBVIEW_API void webview_navigate(webview_t w, const char *url);
- WEBVIEW_API void webview_init(webview_t w, const char *js);
- WEBVIEW_API int webview_eval(webview_t w, const char *js);
- WEBVIEW_API int webview_loop(webview_t w, int blocking);
- WEBVIEW_API void *webview_get_userdata(webview_t w);
- WEBVIEW_API void webview_set_userdata(webview_t w, void *user_data);
- // Enable or disable window fullscreen
- WEBVIEW_API void webview_set_fullscreen(webview_t w, int fullscreen);
- // Set rgba color of the window's title bar
- WEBVIEW_API void webview_set_color(webview_t w, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
- // Inject css into webview's page
- WEBVIEW_API int webview_inject_css(webview_t w, const char *css);
- WEBVIEW_API void webview_dialog(webview_t w,
- enum webview_dialog_type dlgtype, int flags,
- const char *title, const char *arg,
- char *result, size_t resultsz);
- #ifdef __cplusplus
- }
- #endif
- enum webview_dialog_type
- {
- WEBVIEW_DIALOG_TYPE_OPEN = 0,
- WEBVIEW_DIALOG_TYPE_SAVE = 1,
- WEBVIEW_DIALOG_TYPE_ALERT = 2
- };
- #define WEBVIEW_DIALOG_FLAG_FILE (0 << 0)
- #define WEBVIEW_DIALOG_FLAG_DIRECTORY (1 << 0)
- #define WEBVIEW_DIALOG_FLAG_INFO (1 << 1)
- #define WEBVIEW_DIALOG_FLAG_WARNING (2 << 1)
- #define WEBVIEW_DIALOG_FLAG_ERROR (3 << 1)
- #define WEBVIEW_DIALOG_FLAG_ALERT_MASK (3 << 1)
- #ifndef WEBVIEW_HEADER
- #include <atomic>
- #include <cstring>
- #include <functional>
- #include <future>
- #include <map>
- #include <string>
- #include <vector>
- //
- // ====================================================================
- //
- // This implementation uses Win32 API to create a native window. It can
- // use either MSHTML or EdgeHTML backend as a browser engine.
- //
- // ====================================================================
- //
- #define WIN32_LEAN_AND_MEAN
- #include <objbase.h>
- #include <windows.h>
- #include <wingdi.h>
- #include <winrt/Windows.Foundation.h>
- #include <winrt/Windows.Web.UI.Interop.h>
- #pragma comment(lib, "windowsapp")
- #pragma comment(lib, "user32.lib")
- #pragma comment(lib, "gdi32")
- namespace webview
- {
- using dispatch_fn_t = std::function<void()>;
- using msg_cb_t = std::function<void(const char *msg)>;
- inline std::string url_encode(std::string s)
- {
- std::string encoded;
- for (unsigned int i = 0; i < s.length(); i++)
- {
- auto c = s[i];
- if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
- {
- encoded = encoded + c;
- }
- else
- {
- char hex[4];
- snprintf(hex, sizeof(hex), "%%%02x", c);
- encoded = encoded + hex;
- }
- }
- return encoded;
- }
- inline std::string url_decode(std::string s)
- {
- std::string decoded;
- for (unsigned int i = 0; i < s.length(); i++)
- {
- if (s[i] == '%')
- {
- int n;
- sscanf(s.substr(i + 1, 2).c_str(), "%x", &n);
- decoded = decoded + static_cast<char>(n);
- i = i + 2;
- }
- else if (s[i] == '+')
- {
- decoded = decoded + ' ';
- }
- else
- {
- decoded = decoded + s[i];
- }
- }
- return decoded;
- }
- inline std::string html_from_uri(std::string s)
- {
- if (s.substr(0, 15) == "data:text/html,")
- {
- return url_decode(s.substr(15));
- }
- return "";
- }
- inline int json_parse_c(const char *s, size_t sz, const char *key, size_t keysz,
- const char **value, size_t *valuesz)
- {
- enum
- {
- JSON_STATE_VALUE,
- JSON_STATE_LITERAL,
- JSON_STATE_STRING,
- JSON_STATE_ESCAPE,
- JSON_STATE_UTF8
- } state = JSON_STATE_VALUE;
- const char *k = NULL;
- int index = 1;
- int depth = 0;
- int utf8_bytes = 0;
- if (key == NULL)
- {
- index = keysz;
- keysz = 0;
- }
- *value = NULL;
- *valuesz = 0;
- for (; sz > 0; s++, sz--)
- {
- enum
- {
- JSON_ACTION_NONE,
- JSON_ACTION_START,
- JSON_ACTION_END,
- JSON_ACTION_START_STRUCT,
- JSON_ACTION_END_STRUCT
- } action = JSON_ACTION_NONE;
- unsigned char c = *s;
- switch (state)
- {
- case JSON_STATE_VALUE:
- if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' || c == ':')
- {
- continue;
- }
- else if (c == '"')
- {
- action = JSON_ACTION_START;
- state = JSON_STATE_STRING;
- }
- else if (c == '{' || c == '[')
- {
- action = JSON_ACTION_START_STRUCT;
- }
- else if (c == '}' || c == ']')
- {
- action = JSON_ACTION_END_STRUCT;
- }
- else if (c == 't' || c == 'f' || c == 'n' || c == '-' || (c >= '0' && c <= '9'))
- {
- action = JSON_ACTION_START;
- state = JSON_STATE_LITERAL;
- }
- else
- {
- return -1;
- }
- break;
- case JSON_STATE_LITERAL:
- if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' || c == ']' || c == '}' || c == ':')
- {
- state = JSON_STATE_VALUE;
- s--;
- sz++;
- action = JSON_ACTION_END;
- }
- else if (c < 32 || c > 126)
- {
- return -1;
- } // fallthrough
- case JSON_STATE_STRING:
- if (c < 32 || (c > 126 && c < 192))
- {
- return -1;
- }
- else if (c == '"')
- {
- action = JSON_ACTION_END;
- state = JSON_STATE_VALUE;
- }
- else if (c == '\\')
- {
- state = JSON_STATE_ESCAPE;
- }
- else if (c >= 192 && c < 224)
- {
- utf8_bytes = 1;
- state = JSON_STATE_UTF8;
- }
- else if (c >= 224 && c < 240)
- {
- utf8_bytes = 2;
- state = JSON_STATE_UTF8;
- }
- else if (c >= 240 && c < 247)
- {
- utf8_bytes = 3;
- state = JSON_STATE_UTF8;
- }
- else if (c >= 128 && c < 192)
- {
- return -1;
- }
- break;
- case JSON_STATE_ESCAPE:
- if (c == '"' || c == '\\' || c == '/' || c == 'b' || c == 'f' || c == 'n' || c == 'r' || c == 't' || c == 'u')
- {
- state = JSON_STATE_STRING;
- }
- else
- {
- return -1;
- }
- break;
- case JSON_STATE_UTF8:
- if (c < 128 || c > 191)
- {
- return -1;
- }
- utf8_bytes--;
- if (utf8_bytes == 0)
- {
- state = JSON_STATE_STRING;
- }
- break;
- default:
- return -1;
- }
- if (action == JSON_ACTION_END_STRUCT)
- {
- depth--;
- }
- if (depth == 1)
- {
- if (action == JSON_ACTION_START || action == JSON_ACTION_START_STRUCT)
- {
- if (index == 0)
- {
- *value = s;
- }
- else if (keysz > 0 && index == 1)
- {
- k = s;
- }
- else
- {
- index--;
- }
- }
- else if (action == JSON_ACTION_END || action == JSON_ACTION_END_STRUCT)
- {
- if (*value != NULL && index == 0)
- {
- *valuesz = (size_t)(s + 1 - *value);
- return 0;
- }
- else if (keysz > 0 && k != NULL)
- {
- if (keysz == (size_t)(s - k - 1) && memcmp(key, k + 1, keysz) == 0)
- {
- index = 0;
- }
- else
- {
- index = 2;
- }
- k = NULL;
- }
- }
- }
- if (action == JSON_ACTION_START_STRUCT)
- {
- depth++;
- }
- }
- return -1;
- }
- inline std::string json_escape(std::string s)
- {
- std::string r = "\"";
- r.reserve(s.size() + 4);
- static const char *h = "0123456789abcdef";
- const unsigned char *d = reinterpret_cast<const unsigned char *>(s.data());
- for (size_t i = 0; i < s.size(); ++i)
- {
- switch (const auto c = d[i])
- {
- case '\b':
- r += "\\b";
- break;
- case '\f':
- r += "\\f";
- break;
- case '\n':
- r += "\\n";
- break;
- case '\r':
- r += "\\r";
- break;
- case '\t':
- r += "\\t";
- break;
- case '\\':
- r += "\\\\";
- break;
- case '\"':
- r += "\\\"";
- break;
- default:
- if ((c < 32) || (c == 127))
- {
- r += "\\u00";
- r += h[(c & 0xf0) >> 4];
- r += h[c & 0x0f];
- continue;
- }
- r += c; // Assume valid UTF-8.
- break;
- }
- }
- r += '"';
- return r;
- }
- inline int json_unescape(const char *s, size_t n, char *out)
- {
- int r = 0;
- if (*s++ != '"')
- {
- return -1;
- }
- while (n > 2)
- {
- char c = *s;
- if (c == '\\')
- {
- s++;
- n--;
- switch (*s)
- {
- case 'b':
- c = '\b';
- break;
- case 'f':
- c = '\f';
- break;
- case 'n':
- c = '\n';
- break;
- case 'r':
- c = '\r';
- break;
- case 't':
- c = '\t';
- break;
- case '\\':
- c = '\\';
- break;
- case '/':
- c = '/';
- break;
- case '\"':
- c = '\"';
- break;
- default: // TODO: support unicode decoding
- return -1;
- }
- }
- if (out != NULL)
- {
- *out++ = c;
- }
- s++;
- n--;
- r++;
- }
- if (*s != '"')
- {
- return -1;
- }
- if (out != NULL)
- {
- *out = '\0';
- }
- return r;
- }
- inline std::string json_parse(std::string s, std::string key, int index)
- {
- const char *value;
- size_t value_sz;
- if (key == "")
- {
- json_parse_c(s.c_str(), s.length(), nullptr, index, &value, &value_sz);
- }
- else
- {
- json_parse_c(s.c_str(), s.length(), key.c_str(), key.length(), &value, &value_sz);
- }
- if (value != nullptr)
- {
- if (value[0] != '"')
- {
- return std::string(value, value_sz);
- }
- int n = json_unescape(value, value_sz, nullptr);
- if (n > 0)
- {
- char *decoded = new char[n];
- json_unescape(value, value_sz, decoded);
- auto result = std::string(decoded, n);
- delete[] decoded;
- return result;
- }
- }
- return "";
- }
- LRESULT CALLBACK WebviewWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
- class browser_window
- {
- public:
- browser_window(msg_cb_t cb, const char *title, int width, int height, bool resizable)
- : m_cb(cb)
- {
- HINSTANCE hInstance = GetModuleHandle(nullptr);
- WNDCLASSEX wc;
- ZeroMemory(&wc, sizeof(WNDCLASSEX));
- wc.cbSize = sizeof(WNDCLASSEX);
- wc.hInstance = hInstance;
- wc.lpfnWndProc = WebviewWndProc;
- wc.lpszClassName = "webview";
- RegisterClassEx(&wc);
- DWORD style = WS_OVERLAPPEDWINDOW;
- if (!resizable)
- {
- style = WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU;
- }
- RECT clientRect;
- RECT rect;
- rect.left = 0;
- rect.top = 0;
- rect.right = width;
- rect.bottom = height;
- AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
- GetClientRect(GetDesktopWindow(), &clientRect);
- int left = (clientRect.right / 2) - ((rect.right - rect.left) / 2);
- int top = (clientRect.bottom / 2) - ((rect.bottom - rect.top) / 2);
- rect.right = rect.right - rect.left + left;
- rect.left = left;
- rect.bottom = rect.bottom - rect.top + top;
- rect.top = top;
- m_window = CreateWindowEx(0, "webview", title, style, rect.left, rect.top,
- rect.right - rect.left, rect.bottom - rect.top,
- HWND_DESKTOP, NULL, hInstance, (void *)this);
- SetWindowLongPtr(m_window, GWLP_USERDATA, (LONG_PTR)this);
- ShowWindow(m_window, SW_SHOW);
- UpdateWindow(m_window);
- SetFocus(m_window);
- }
- void run()
- {
- while (this->loop(true) == 0)
- {
- }
- }
- int loop(int blocking)
- {
- MSG msg;
- if (blocking)
- {
- if (GetMessage(&msg, nullptr, 0, 0) < 0)
- return 0;
- }
- else
- {
- if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) == 0)
- return 0;
- }
- if (msg.hwnd)
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- return 0;
- }
- if (msg.message == WM_APP)
- {
- auto f = (dispatch_fn_t *)(msg.lParam);
- (*f)();
- delete f;
- }
- else if (msg.message == WM_QUIT)
- {
- return -1;
- }
- return 0;
- }
- void terminate() { PostQuitMessage(0); }
- void dispatch(dispatch_fn_t f)
- {
- PostThreadMessage(m_main_thread, WM_APP, 0, (LPARAM) new dispatch_fn_t(f));
- }
- void set_title(const char *title) { SetWindowText(m_window, title); }
- void set_size(int width, int height)
- {
- RECT r;
- r.left = 50;
- r.top = 50;
- r.right = width;
- r.bottom = height;
- AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, 0);
- SetWindowPos(m_window, NULL, r.left, r.top, r.right - r.left, r.bottom - r.top,
- SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
- }
- void set_fullscreen(bool fullscreen)
- {
- if (this->is_fullscreen == fullscreen)
- {
- return;
- }
- if (!this->is_fullscreen)
- {
- this->saved_style = GetWindowLong(this->m_window, GWL_STYLE);
- this->saved_ex_style = GetWindowLong(this->m_window, GWL_EXSTYLE);
- GetWindowRect(this->m_window, &this->saved_rect);
- }
- this->is_fullscreen = !!fullscreen;
- if (fullscreen)
- {
- MONITORINFO monitor_info;
- SetWindowLong(this->m_window, GWL_STYLE,
- this->saved_style & ~(WS_CAPTION | WS_THICKFRAME));
- SetWindowLong(this->m_window, GWL_EXSTYLE,
- this->saved_ex_style &
- ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE |
- WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
- monitor_info.cbSize = sizeof(monitor_info);
- GetMonitorInfo(MonitorFromWindow(this->m_window, MONITOR_DEFAULTTONEAREST),
- &monitor_info);
- RECT r;
- r.left = monitor_info.rcMonitor.left;
- r.top = monitor_info.rcMonitor.top;
- r.right = monitor_info.rcMonitor.right;
- r.bottom = monitor_info.rcMonitor.bottom;
- SetWindowPos(this->m_window, NULL, r.left, r.top, r.right - r.left,
- r.bottom - r.top,
- SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
- }
- else
- {
- SetWindowLong(this->m_window, GWL_STYLE, this->saved_style);
- SetWindowLong(this->m_window, GWL_EXSTYLE, this->saved_ex_style);
- SetWindowPos(this->m_window, NULL, this->saved_rect.left,
- this->saved_rect.top,
- this->saved_rect.right - this->saved_rect.left,
- this->saved_rect.bottom - this->saved_rect.top,
- SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
- }
- }
- void set_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
- {
- HBRUSH brush = CreateSolidBrush(RGB(r, g, b));
- SetClassLongPtr(this->m_window, GCLP_HBRBACKGROUND, (LONG_PTR)brush);
- }
- // protected:
- virtual void resize() {}
- HWND m_window;
- DWORD m_main_thread = GetCurrentThreadId();
- msg_cb_t m_cb;
- bool is_fullscreen = false;
- DWORD saved_style = 0;
- DWORD saved_ex_style = 0;
- RECT saved_rect;
- };
- LRESULT CALLBACK WebviewWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
- {
- auto w = (browser_window *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
- switch (msg)
- {
- case WM_SIZE:
- w->resize();
- break;
- case WM_CLOSE:
- DestroyWindow(hwnd);
- break;
- case WM_DESTROY:
- w->terminate();
- break;
- default:
- return DefWindowProc(hwnd, msg, wp, lp);
- }
- return 0;
- }
- using namespace winrt;
- using namespace Windows::Foundation;
- using namespace Windows::Web::UI;
- using namespace Windows::Web::UI::Interop;
- class webview : public browser_window
- {
- public:
- webview(webview_external_invoke_cb_t invoke_cb, const char *title, int width, int height, bool resizable, bool debug)
- : browser_window(std::bind(&webview::on_message, this, std::placeholders::_1), title, width, height, resizable), invoke_cb(invoke_cb)
- {
- init_apartment(winrt::apartment_type::single_threaded);
- WebViewControlProcessOptions options;
- options.PrivateNetworkClientServerCapability(WebViewControlProcessCapabilityState::Enabled);
- m_process = WebViewControlProcess(options);
- auto op = m_process.CreateWebViewControlAsync(
- reinterpret_cast<int64_t>(m_window), Rect());
- if (op.Status() != AsyncStatus::Completed)
- {
- handle h(CreateEvent(nullptr, false, false, nullptr));
- op.Completed([h = h.get()](auto, auto) { SetEvent(h); });
- HANDLE hs[] = {h.get()};
- DWORD i;
- CoWaitForMultipleHandles(COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS | COWAIT_INPUTAVAILABLE,
- INFINITE, 1, hs, &i);
- }
- m_webview = op.GetResults();
- m_webview.Settings().IsScriptNotifyAllowed(true);
- m_webview.IsVisible(true);
- m_webview.ScriptNotify([=](auto const &sender, auto const &args) {
- std::string s = winrt::to_string(args.Value());
- m_cb(s.c_str());
- });
- m_webview.NavigationStarting([=](auto const &sender, auto const &args) {
- m_webview.AddInitializeScript(winrt::to_hstring(init_js));
- });
- init("window.external.invoke = s => window.external.notify(s)");
- resize();
- }
- void navigate(const char *url)
- {
- std::string html = html_from_uri(url);
- if (html != "")
- {
- m_webview.NavigateToString(winrt::to_hstring(html.c_str()));
- }
- else
- {
- Uri uri(winrt::to_hstring(url));
- m_webview.Navigate(uri);
- }
- }
- void init(const char *js) { init_js = init_js + "(function(){" + js + "})();"; }
- void eval(const char *js)
- {
- m_webview.InvokeScriptAsync(
- L"eval", single_threaded_vector<hstring>({winrt::to_hstring(js)}));
- }
- void *window() { return (void *)m_window; }
- void *get_user_data() { return this->user_data; }
- void set_user_data(void *user_data) { this->user_data = user_data; }
- private:
- void on_message(const char *msg)
- {
- this->invoke_cb(this, msg);
- }
- void resize()
- {
- RECT r;
- GetClientRect(m_window, &r);
- Rect bounds(r.left, r.top, r.right - r.left, r.bottom - r.top);
- m_webview.Bounds(bounds);
- }
- WebViewControlProcess m_process;
- WebViewControl m_webview = nullptr;
- std::string init_js = "";
- void *user_data = nullptr;
- webview_external_invoke_cb_t invoke_cb;
- };
- } // namespace webview
- static inline char *webview_from_utf16(WCHAR *ws)
- {
- int n = WideCharToMultiByte(CP_UTF8, 0, ws, -1, NULL, 0, NULL, NULL);
- char *s = (char *)GlobalAlloc(GMEM_FIXED, n);
- if (s == NULL)
- {
- return NULL;
- }
- WideCharToMultiByte(CP_UTF8, 0, ws, -1, s, n, NULL, NULL);
- return s;
- }
- WEBVIEW_API webview_t webview_create(webview_external_invoke_cb_t invoke_cb, const char *title, int width, int height, int resizable, int debug)
- {
- return new webview::webview(invoke_cb, title, width, height, resizable, debug);
- }
- WEBVIEW_API void webview_destroy(webview_t w)
- {
- delete static_cast<webview::webview *>(w);
- }
- WEBVIEW_API void webview_run(webview_t w) { static_cast<webview::webview *>(w)->run(); }
- WEBVIEW_API void webview_terminate(webview_t w)
- {
- static_cast<webview::webview *>(w)->terminate();
- }
- WEBVIEW_API void webview_dispatch(
- webview_t w, void (*fn)(webview_t w, void *arg), void *arg)
- {
- static_cast<webview::webview *>(w)->dispatch([=]() { fn(w, arg); });
- }
- WEBVIEW_API void *webview_get_window(webview_t w)
- {
- return static_cast<webview::webview *>(w)->window();
- }
- WEBVIEW_API void webview_set_title(webview_t w, const char *title)
- {
- static_cast<webview::webview *>(w)->set_title(title);
- }
- WEBVIEW_API void webview_navigate(webview_t w, const char *url)
- {
- static_cast<webview::webview *>(w)->navigate(url);
- }
- WEBVIEW_API void webview_init(webview_t w, const char *js)
- {
- static_cast<webview::webview *>(w)->init(js);
- }
- WEBVIEW_API int webview_eval(webview_t w, const char *js)
- {
- static_cast<webview::webview *>(w)->eval(js);
- return 0;
- }
- WEBVIEW_API int webview_loop(webview_t w, int blocking)
- {
- return static_cast<webview::webview *>(w)->loop(blocking);
- }
- WEBVIEW_API void *webview_get_userdata(webview_t w)
- {
- return static_cast<webview::webview *>(w)->get_user_data();
- }
- WEBVIEW_API void webview_set_userdata(webview_t w, void *user_data)
- {
- static_cast<webview::webview *>(w)->set_user_data(user_data);
- }
- WEBVIEW_API void webview_set_fullscreen(webview_t w, int fullscreen)
- {
- static_cast<webview::webview *>(w)->set_fullscreen(fullscreen);
- }
- WEBVIEW_API void webview_set_color(webview_t w, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
- {
- static_cast<webview::webview *>(w)->set_color(r, g, b, a);
- }
- #define CSS_INJECT_FUNCTION \
- "(function(e){var " \
- "t=document.createElement('style'),d=document.head||document." \
- "getElementsByTagName('head')[0];t.setAttribute('type','text/" \
- "css'),t.styleSheet?t.styleSheet.cssText=e:t.appendChild(document." \
- "createTextNode(e)),d.appendChild(t)})"
- static int webview_js_encode(const char *s, char *esc, size_t n)
- {
- int r = 1; /* At least one byte for trailing zero */
- for (; *s; s++)
- {
- const unsigned char c = *s;
- if (c >= 0x20 && c < 0x80 && strchr("<>\\'\"", c) == NULL)
- {
- if (n > 0)
- {
- *esc++ = c;
- n--;
- }
- r++;
- }
- else
- {
- if (n > 0)
- {
- snprintf(esc, n, "\\x%02x", (int)c);
- esc += 4;
- n -= 4;
- }
- r += 4;
- }
- }
- return r;
- }
- WEBVIEW_API int webview_inject_css(webview_t w, const char *css)
- {
- int n = webview_js_encode(css, NULL, 0);
- char *esc = (char *)calloc(1, sizeof(CSS_INJECT_FUNCTION) + n + 4);
- if (esc == NULL)
- {
- return -1;
- }
- char *js = (char *)calloc(1, n);
- webview_js_encode(css, js, n);
- snprintf(esc, sizeof(CSS_INJECT_FUNCTION) + n + 4, "%s(\"%s\")",
- CSS_INJECT_FUNCTION, js);
- int r = webview_eval(w, esc);
- free(js);
- free(esc);
- return r;
- }
- #include <shobjidl.h>
- #ifdef __cplusplus
- #define iid_ref(x) &(x)
- #define iid_unref(x) *(x)
- #else
- #define iid_ref(x) (x)
- #define iid_unref(x) (x)
- #endif
- /* These are missing parts from MinGW */
- #ifndef __IFileDialog_INTERFACE_DEFINED__
- #define __IFileDialog_INTERFACE_DEFINED__
- enum _FILEOPENDIALOGOPTIONS
- {
- FOS_OVERWRITEPROMPT = 0x2,
- FOS_STRICTFILETYPES = 0x4,
- FOS_NOCHANGEDIR = 0x8,
- FOS_PICKFOLDERS = 0x20,
- FOS_FORCEFILESYSTEM = 0x40,
- FOS_ALLNONSTORAGEITEMS = 0x80,
- FOS_NOVALIDATE = 0x100,
- FOS_ALLOWMULTISELECT = 0x200,
- FOS_PATHMUSTEXIST = 0x800,
- FOS_FILEMUSTEXIST = 0x1000,
- FOS_CREATEPROMPT = 0x2000,
- FOS_SHAREAWARE = 0x4000,
- FOS_NOREADONLYRETURN = 0x8000,
- FOS_NOTESTFILECREATE = 0x10000,
- FOS_HIDEMRUPLACES = 0x20000,
- FOS_HIDEPINNEDPLACES = 0x40000,
- FOS_NODEREFERENCELINKS = 0x100000,
- FOS_DONTADDTORECENT = 0x2000000,
- FOS_FORCESHOWHIDDEN = 0x10000000,
- FOS_DEFAULTNOMINIMODE = 0x20000000,
- FOS_FORCEPREVIEWPANEON = 0x40000000
- };
- typedef DWORD FILEOPENDIALOGOPTIONS;
- typedef enum FDAP
- {
- FDAP_BOTTOM = 0,
- FDAP_TOP = 1
- } FDAP;
- DEFINE_GUID(IID_IFileDialog, 0x42f85136, 0xdb7e, 0x439c, 0x85, 0xf1, 0xe4, 0x07,
- 0x5d, 0x13, 0x5f, 0xc8);
- typedef struct IFileDialogVtbl
- {
- BEGIN_INTERFACE
- HRESULT(STDMETHODCALLTYPE *QueryInterface)
- (IFileDialog *This, REFIID riid, void **ppvObject);
- ULONG(STDMETHODCALLTYPE *AddRef)
- (IFileDialog *This);
- ULONG(STDMETHODCALLTYPE *Release)
- (IFileDialog *This);
- HRESULT(STDMETHODCALLTYPE *Show)
- (IFileDialog *This, HWND hwndOwner);
- HRESULT(STDMETHODCALLTYPE *SetFileTypes)
- (IFileDialog *This, UINT cFileTypes, const COMDLG_FILTERSPEC *rgFilterSpec);
- HRESULT(STDMETHODCALLTYPE *SetFileTypeIndex)
- (IFileDialog *This, UINT iFileType);
- HRESULT(STDMETHODCALLTYPE *GetFileTypeIndex)
- (IFileDialog *This, UINT *piFileType);
- HRESULT(STDMETHODCALLTYPE *Advise)
- (IFileDialog *This, IFileDialogEvents *pfde, DWORD *pdwCookie);
- HRESULT(STDMETHODCALLTYPE *Unadvise)
- (IFileDialog *This, DWORD dwCookie);
- HRESULT(STDMETHODCALLTYPE *SetOptions)
- (IFileDialog *This, FILEOPENDIALOGOPTIONS fos);
- HRESULT(STDMETHODCALLTYPE *GetOptions)
- (IFileDialog *This, FILEOPENDIALOGOPTIONS *pfos);
- HRESULT(STDMETHODCALLTYPE *SetDefaultFolder)
- (IFileDialog *This, IShellItem *psi);
- HRESULT(STDMETHODCALLTYPE *SetFolder)
- (IFileDialog *This, IShellItem *psi);
- HRESULT(STDMETHODCALLTYPE *GetFolder)
- (IFileDialog *This, IShellItem **ppsi);
- HRESULT(STDMETHODCALLTYPE *GetCurrentSelection)
- (IFileDialog *This, IShellItem **ppsi);
- HRESULT(STDMETHODCALLTYPE *SetFileName)
- (IFileDialog *This, LPCWSTR pszName);
- HRESULT(STDMETHODCALLTYPE *GetFileName)
- (IFileDialog *This, LPWSTR *pszName);
- HRESULT(STDMETHODCALLTYPE *SetTitle)
- (IFileDialog *This, LPCWSTR pszTitle);
- HRESULT(STDMETHODCALLTYPE *SetOkButtonLabel)
- (IFileDialog *This, LPCWSTR pszText);
- HRESULT(STDMETHODCALLTYPE *SetFileNameLabel)
- (IFileDialog *This, LPCWSTR pszLabel);
- HRESULT(STDMETHODCALLTYPE *GetResult)
- (IFileDialog *This, IShellItem **ppsi);
- HRESULT(STDMETHODCALLTYPE *AddPlace)
- (IFileDialog *This, IShellItem *psi, FDAP fdap);
- HRESULT(STDMETHODCALLTYPE *SetDefaultExtension)
- (IFileDialog *This, LPCWSTR pszDefaultExtension);
- HRESULT(STDMETHODCALLTYPE *Close)
- (IFileDialog *This, HRESULT hr);
- HRESULT(STDMETHODCALLTYPE *SetClientGuid)
- (IFileDialog *This, REFGUID guid);
- HRESULT(STDMETHODCALLTYPE *ClearClientData)
- (IFileDialog *This);
- HRESULT(STDMETHODCALLTYPE *SetFilter)
- (IFileDialog *This, IShellItemFilter *pFilter);
- END_INTERFACE
- } IFileDialogVtbl;
- interface IFileDialog
- {
- CONST_VTBL IFileDialogVtbl *lpVtbl;
- };
- DEFINE_GUID(IID_IFileOpenDialog, 0xd57c7288, 0xd4ad, 0x4768, 0xbe, 0x02, 0x9d,
- 0x96, 0x95, 0x32, 0xd9, 0x60);
- DEFINE_GUID(IID_IFileSaveDialog, 0x84bccd23, 0x5fde, 0x4cdb, 0xae, 0xa4, 0xaf,
- 0x64, 0xb8, 0x3d, 0x78, 0xab);
- #endif
- WEBVIEW_API void webview_dialog(webview_t w,
- enum webview_dialog_type dlgtype, int flags,
- const char *title, const char *arg,
- char *result, size_t resultsz)
- {
- HWND hwnd = static_cast<webview::webview *>(w)->m_window;
- if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ||
- dlgtype == WEBVIEW_DIALOG_TYPE_SAVE)
- {
- IFileDialog *dlg = NULL;
- IShellItem *res = NULL;
- WCHAR *ws = NULL;
- char *s = NULL;
- FILEOPENDIALOGOPTIONS opts = 0, add_opts = 0;
- if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN)
- {
- if (CoCreateInstance(
- iid_unref(&CLSID_FileOpenDialog), NULL, CLSCTX_INPROC_SERVER,
- iid_unref(&IID_IFileOpenDialog), (void **)&dlg) != S_OK)
- {
- goto error_dlg;
- }
- if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY)
- {
- add_opts |= FOS_PICKFOLDERS;
- }
- add_opts |= FOS_NOCHANGEDIR | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE |
- FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_SHAREAWARE |
- FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS |
- FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE;
- }
- else
- {
- if (CoCreateInstance(
- iid_unref(&CLSID_FileSaveDialog), NULL, CLSCTX_INPROC_SERVER,
- iid_unref(&IID_IFileSaveDialog), (void **)&dlg) != S_OK)
- {
- goto error_dlg;
- }
- add_opts |= FOS_OVERWRITEPROMPT | FOS_NOCHANGEDIR |
- FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | FOS_SHAREAWARE |
- FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS |
- FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE;
- }
- if (dlg->GetOptions(&opts) != S_OK)
- {
- goto error_dlg;
- }
- opts &= ~FOS_NOREADONLYRETURN;
- opts |= add_opts;
- if (dlg->SetOptions(opts) != S_OK)
- {
- goto error_dlg;
- }
- if (dlg->Show(hwnd) != S_OK)
- {
- goto error_dlg;
- }
- if (dlg->GetResult(&res) != S_OK)
- {
- goto error_dlg;
- }
- if (res->GetDisplayName(SIGDN_FILESYSPATH, &ws) != S_OK)
- {
- goto error_result;
- }
- s = webview_from_utf16(ws);
- CoTaskMemFree(ws);
- if (!s)
- goto error_result;
- strncpy(result, s, resultsz);
- GlobalFree(s);
- result[resultsz - 1] = '\0';
- error_result:
- res->Release();
- error_dlg:
- dlg->Release();
- return;
- }
- else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT)
- {
- #if 0
- /* MinGW often doesn't contain TaskDialog, we'll use MessageBox for now */
- WCHAR *wtitle = webview_to_utf16(title);
- WCHAR *warg = webview_to_utf16(arg);
- TaskDialog(hwnd, NULL, NULL, wtitle, warg, 0, NULL, NULL);
- GlobalFree(warg);
- GlobalFree(wtitle);
- #else
- UINT type = MB_OK;
- switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK)
- {
- case WEBVIEW_DIALOG_FLAG_INFO:
- type |= MB_ICONINFORMATION;
- break;
- case WEBVIEW_DIALOG_FLAG_WARNING:
- type |= MB_ICONWARNING;
- break;
- case WEBVIEW_DIALOG_FLAG_ERROR:
- type |= MB_ICONERROR;
- break;
- }
- MessageBox(hwnd, arg, title, type);
- #endif
- }
- }
- #endif /* WEBVIEW_HEADER */
- #endif /* WEBVIEW_H */
|