plusui-native-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Core/CMakeLists.txt +34 -0
- package/Core/README.md +29 -0
- package/Core/build/win32/x64/ALL_BUILD.vcxproj +185 -0
- package/Core/build/win32/x64/ALL_BUILD.vcxproj.filters +8 -0
- package/Core/build/win32/x64/CMakeCache.txt +335 -0
- package/Core/build/win32/x64/CMakeFiles/11f7f2f432927ec8d1861dc42d4bd679/INSTALL_force.rule +1 -0
- package/Core/build/win32/x64/CMakeFiles/11f7f2f432927ec8d1861dc42d4bd679/generate.stamp.rule +1 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CMakeCXXCompiler.cmake +104 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CMakeDetermineCompilerABI_CXX.bin +0 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CMakeRCCompiler.cmake +6 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CMakeSystem.cmake +15 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/CMakeCXXCompilerId.cpp +949 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/CompilerIdCXX.exe +0 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/CompilerIdCXX.vcxproj +72 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CMakeCXXCompilerId.obj +0 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.exe.recipe +11 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.command.1.tlog +2 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.read.1.tlog +4 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.write.1.tlog +2 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/Cl.items.tlog +1 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CompilerIdCXX.lastbuildstate +2 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.command.1.tlog +2 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.read.1.tlog +22 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.secondary.1.tlog +1 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.write.1.tlog +2 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/VCTargetsPath/x64/Debug/VCTargetsPath.recipe +11 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/VCTargetsPath/x64/Debug/VCTargetsPath.tlog/VCTargetsPath.lastbuildstate +2 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/VCTargetsPath.txt +1 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/VCTargetsPath.vcxproj +31 -0
- package/Core/build/win32/x64/CMakeFiles/CMakeConfigureLog.yaml +2698 -0
- package/Core/build/win32/x64/CMakeFiles/InstallScripts.json +7 -0
- package/Core/build/win32/x64/CMakeFiles/TargetDirectories.txt +4 -0
- package/Core/build/win32/x64/CMakeFiles/cmake.check_cache +1 -0
- package/Core/build/win32/x64/CMakeFiles/generate.stamp +1 -0
- package/Core/build/win32/x64/CMakeFiles/generate.stamp.depend +34 -0
- package/Core/build/win32/x64/CMakeFiles/generate.stamp.list +1 -0
- package/Core/build/win32/x64/Capsule.sln +67 -0
- package/Core/build/win32/x64/INSTALL.vcxproj +209 -0
- package/Core/build/win32/x64/INSTALL.vcxproj.filters +13 -0
- package/Core/build/win32/x64/ZERO_CHECK.vcxproj +179 -0
- package/Core/build/win32/x64/ZERO_CHECK.vcxproj.filters +13 -0
- package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/CL.command.1.tlog +2 -0
- package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/CustomBuild.command.1.tlog +10 -0
- package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/CustomBuild.read.1.tlog +33 -0
- package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/CustomBuild.write.1.tlog +2 -0
- package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/capsule.lastbuildstate +2 -0
- package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/unsuccessfulbuild +0 -0
- package/Core/build/win32/x64/capsule.vcxproj +332 -0
- package/Core/build/win32/x64/capsule.vcxproj.filters +25 -0
- package/Core/build/win32/x64/cmake_install.cmake +72 -0
- package/Core/build/win32/x64/x64/Release/ZERO_CHECK/ZERO_CHECK.recipe +11 -0
- package/Core/build/win32/x64/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.command.1.tlog +10 -0
- package/Core/build/win32/x64/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.read.1.tlog +34 -0
- package/Core/build/win32/x64/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.write.1.tlog +2 -0
- package/Core/build/win32/x64/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate +2 -0
- package/Core/include/app.hpp +121 -0
- package/Core/include/display.hpp +90 -0
- package/Core/include/keyboard.hpp +135 -0
- package/Core/include/menu.hpp +79 -0
- package/Core/include/tray.hpp +81 -0
- package/Core/include/window.hpp +106 -0
- package/Core/src/app.cpp +311 -0
- package/Core/src/display.cpp +424 -0
- package/Core/src/tray.cpp +275 -0
- package/Core/src/window.cpp +528 -0
- package/Core/vendor/webview.h +551 -0
- package/dist/index.d.ts +205 -0
- package/dist/index.js +198 -0
- package/package.json +19 -0
- package/src/index.ts +574 -0
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* MIT License
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2017 Serge Zaitsev
|
|
5
|
+
*/
|
|
6
|
+
#ifndef WEBVIEW_H
|
|
7
|
+
#define WEBVIEW_H
|
|
8
|
+
|
|
9
|
+
#ifndef WEBVIEW_API
|
|
10
|
+
#define WEBVIEW_API extern
|
|
11
|
+
#endif
|
|
12
|
+
|
|
13
|
+
#ifdef __cplusplus
|
|
14
|
+
extern "C" {
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
typedef void *webview_t;
|
|
18
|
+
|
|
19
|
+
WEBVIEW_API webview_t webview_create(int debug, void *window);
|
|
20
|
+
WEBVIEW_API void webview_destroy(webview_t w);
|
|
21
|
+
WEBVIEW_API void webview_run(webview_t w);
|
|
22
|
+
WEBVIEW_API void webview_terminate(webview_t w);
|
|
23
|
+
WEBVIEW_API void webview_dispatch(webview_t w, void (*fn)(webview_t w, void *arg), void *arg);
|
|
24
|
+
WEBVIEW_API void *webview_get_window(webview_t w);
|
|
25
|
+
WEBVIEW_API void webview_set_title(webview_t w, const char *title);
|
|
26
|
+
|
|
27
|
+
#define WEBVIEW_HINT_NONE 0
|
|
28
|
+
#define WEBVIEW_HINT_MIN 1
|
|
29
|
+
#define WEBVIEW_HINT_MAX 2
|
|
30
|
+
#define WEBVIEW_HINT_FIXED 3
|
|
31
|
+
|
|
32
|
+
WEBVIEW_API void webview_set_size(webview_t w, int width, int height, int hints);
|
|
33
|
+
WEBVIEW_API void webview_navigate(webview_t w, const char *url);
|
|
34
|
+
WEBVIEW_API void webview_init(webview_t w, const char *js);
|
|
35
|
+
WEBVIEW_API void webview_eval(webview_t w, const char *js);
|
|
36
|
+
WEBVIEW_API void webview_bind(webview_t w, const char *name, void (*fn)(const char *seq, const char *req, void *arg), void *arg);
|
|
37
|
+
WEBVIEW_API void webview_return(webview_t w, const char *seq, int status, const char *result);
|
|
38
|
+
|
|
39
|
+
#ifdef __cplusplus
|
|
40
|
+
}
|
|
41
|
+
#endif
|
|
42
|
+
|
|
43
|
+
#ifndef WEBVIEW_HEADER
|
|
44
|
+
|
|
45
|
+
#if !defined(WEBVIEW_GTK) && !defined(WEBVIEW_COCOA) && !defined(WEBVIEW_EDGE)
|
|
46
|
+
#if defined(__linux__)
|
|
47
|
+
#define WEBVIEW_GTK
|
|
48
|
+
#elif defined(__APPLE__)
|
|
49
|
+
#define WEBVIEW_COCOA
|
|
50
|
+
#elif defined(_WIN32)
|
|
51
|
+
#define WEBVIEW_EDGE
|
|
52
|
+
#else
|
|
53
|
+
#error "please, specify webview backend"
|
|
54
|
+
#endif
|
|
55
|
+
#endif
|
|
56
|
+
|
|
57
|
+
#include <atomic>
|
|
58
|
+
#include <functional>
|
|
59
|
+
#include <future>
|
|
60
|
+
#include <map>
|
|
61
|
+
#include <string>
|
|
62
|
+
#include <utility>
|
|
63
|
+
#include <vector>
|
|
64
|
+
#include <cstring>
|
|
65
|
+
|
|
66
|
+
namespace webview {
|
|
67
|
+
using dispatch_fn_t = std::function<void()>;
|
|
68
|
+
|
|
69
|
+
inline std::string url_encode(const std::string s) {
|
|
70
|
+
std::string encoded;
|
|
71
|
+
for (unsigned int i = 0; i < s.length(); i++) {
|
|
72
|
+
auto c = s[i];
|
|
73
|
+
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
|
|
74
|
+
encoded = encoded + c;
|
|
75
|
+
} else {
|
|
76
|
+
char hex[4];
|
|
77
|
+
snprintf(hex, sizeof(hex), "%%%02x", c);
|
|
78
|
+
encoded = encoded + hex;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return encoded;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
inline std::string url_decode(const std::string s) {
|
|
85
|
+
std::string decoded;
|
|
86
|
+
for (unsigned int i = 0; i < s.length(); i++) {
|
|
87
|
+
if (s[i] == '%') {
|
|
88
|
+
int n;
|
|
89
|
+
n = std::stoul(s.substr(i + 1, 2), nullptr, 16);
|
|
90
|
+
decoded = decoded + static_cast<char>(n);
|
|
91
|
+
i = i + 2;
|
|
92
|
+
} else if (s[i] == '+') {
|
|
93
|
+
decoded = decoded + ' ';
|
|
94
|
+
} else {
|
|
95
|
+
decoded = decoded + s[i];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return decoded;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
inline std::string html_from_uri(const std::string s) {
|
|
102
|
+
if (s.substr(0, 15) == "data:text/html,") {
|
|
103
|
+
return url_decode(s.substr(15));
|
|
104
|
+
}
|
|
105
|
+
return "";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
inline int json_parse_c(const char *s, size_t sz, const char *key, size_t keysz,
|
|
109
|
+
const char **value, size_t *valuesz) {
|
|
110
|
+
enum { JSON_STATE_VALUE, JSON_STATE_LITERAL, JSON_STATE_STRING, JSON_STATE_ESCAPE, JSON_STATE_UTF8 } state = JSON_STATE_VALUE;
|
|
111
|
+
const char *k = NULL;
|
|
112
|
+
int index = 1;
|
|
113
|
+
int depth = 0;
|
|
114
|
+
int utf8_bytes = 0;
|
|
115
|
+
|
|
116
|
+
if (key == NULL) { index = keysz; keysz = 0; }
|
|
117
|
+
*value = NULL;
|
|
118
|
+
*valuesz = 0;
|
|
119
|
+
|
|
120
|
+
for (; sz > 0; s++, sz--) {
|
|
121
|
+
enum { JSON_ACTION_NONE, JSON_ACTION_START, JSON_ACTION_END, JSON_ACTION_START_STRUCT, JSON_ACTION_END_STRUCT } action = JSON_ACTION_NONE;
|
|
122
|
+
unsigned char c = *s;
|
|
123
|
+
switch (state) {
|
|
124
|
+
case JSON_STATE_VALUE:
|
|
125
|
+
if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' || c == ':') { continue; }
|
|
126
|
+
else if (c == '"') { action = JSON_ACTION_START; state = JSON_STATE_STRING; }
|
|
127
|
+
else if (c == '{' || c == '[') { action = JSON_ACTION_START_STRUCT; }
|
|
128
|
+
else if (c == '}' || c == ']') { action = JSON_ACTION_END_STRUCT; }
|
|
129
|
+
else if (c == 't' || c == 'f' || c == 'n' || c == '-' || (c >= '0' && c <= '9')) { action = JSON_ACTION_START; state = JSON_STATE_LITERAL; }
|
|
130
|
+
else { return -1; }
|
|
131
|
+
break;
|
|
132
|
+
case JSON_STATE_LITERAL:
|
|
133
|
+
if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ']' || c == '}' || c == ':') { state = JSON_STATE_VALUE; s--; sz++; action = JSON_ACTION_END; }
|
|
134
|
+
else if (c < 32 || c > 126) { return -1; }
|
|
135
|
+
case JSON_STATE_STRING:
|
|
136
|
+
if (c < 32 || (c > 126 && c < 192)) { return -1; }
|
|
137
|
+
else if (c == '"') { action = JSON_ACTION_END; state = JSON_STATE_VALUE; }
|
|
138
|
+
else if (c == '\\') { state = JSON_STATE_ESCAPE; }
|
|
139
|
+
else if (c >= 192 && c < 224) { utf8_bytes = 1; state = JSON_STATE_UTF8; }
|
|
140
|
+
else if (c >= 224 && c < 240) { utf8_bytes = 2; state = JSON_STATE_UTF8; }
|
|
141
|
+
else if (c >= 240 && c < 247) { utf8_bytes = 3; state = JSON_STATE_UTF8; }
|
|
142
|
+
else if (c >= 128 && c < 192) { return -1; }
|
|
143
|
+
break;
|
|
144
|
+
case JSON_STATE_ESCAPE:
|
|
145
|
+
if (c == '"' || c == '\\' || c == '/' || c == 'b' || c == 'f' || c == 'n' || c == 'r' || c == 't' || c == 'u') { state = JSON_STATE_STRING; }
|
|
146
|
+
else { return -1; }
|
|
147
|
+
break;
|
|
148
|
+
case JSON_STATE_UTF8:
|
|
149
|
+
if (c < 128 || c > 191) { return -1; }
|
|
150
|
+
utf8_bytes--;
|
|
151
|
+
if (utf8_bytes == 0) { state = JSON_STATE_STRING; }
|
|
152
|
+
break;
|
|
153
|
+
default: return -1;
|
|
154
|
+
}
|
|
155
|
+
if (action == JSON_ACTION_END_STRUCT) { depth--; }
|
|
156
|
+
if (depth == 1) {
|
|
157
|
+
if (action == JSON_ACTION_START || action == JSON_ACTION_START_STRUCT) {
|
|
158
|
+
if (index == 0) { *value = s; }
|
|
159
|
+
else if (keysz > 0 && index == 1) { k = s; }
|
|
160
|
+
else { index--; }
|
|
161
|
+
} else if (action == JSON_ACTION_END || action == JSON_ACTION_END_STRUCT) {
|
|
162
|
+
if (*value != NULL && index == 0) { *valuesz = (size_t)(s + 1 - *value); return 0; }
|
|
163
|
+
else if (keysz > 0 && k != NULL) {
|
|
164
|
+
if (keysz == (size_t)(s - k - 1) && memcmp(key, k + 1, keysz) == 0) { index = 0; }
|
|
165
|
+
else { index = 2; }
|
|
166
|
+
k = NULL;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (action == JSON_ACTION_START_STRUCT) { depth++; }
|
|
171
|
+
}
|
|
172
|
+
return -1;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
inline std::string json_escape(std::string s) { return '"' + s + '"'; }
|
|
176
|
+
|
|
177
|
+
inline int json_unescape(const char *s, size_t n, char *out) {
|
|
178
|
+
int r = 0;
|
|
179
|
+
if (*s++ != '"') { return -1; }
|
|
180
|
+
while (n > 2) {
|
|
181
|
+
char c = *s;
|
|
182
|
+
if (c == '\\') { s++; n--; switch (*s) {
|
|
183
|
+
case 'b': c = '\b'; break;
|
|
184
|
+
case 'f': c = '\f'; break;
|
|
185
|
+
case 'n': c = '\n'; break;
|
|
186
|
+
case 'r': c = '\r'; break;
|
|
187
|
+
case 't': c = '\t'; break;
|
|
188
|
+
case '\\': c = '\\'; break;
|
|
189
|
+
case '/': c = '/'; break;
|
|
190
|
+
case '\"': c = '\"'; break;
|
|
191
|
+
default: return -1;
|
|
192
|
+
}}
|
|
193
|
+
if (out != NULL) { *out++ = c; }
|
|
194
|
+
s++; n--; r++;
|
|
195
|
+
}
|
|
196
|
+
if (*s != '"') { return -1; }
|
|
197
|
+
if (out != NULL) { *out = '\0'; }
|
|
198
|
+
return r;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
inline std::string json_parse(const std::string s, const std::string key, const int index) {
|
|
202
|
+
const char *value; size_t value_sz;
|
|
203
|
+
if (key == "") { json_parse_c(s.c_str(), s.length(), nullptr, index, &value, &value_sz); }
|
|
204
|
+
else { json_parse_c(s.c_str(), s.length(), key.c_str(), key.length(), &value, &value_sz); }
|
|
205
|
+
if (value != nullptr) {
|
|
206
|
+
if (value[0] != '"') { return std::string(value, value_sz); }
|
|
207
|
+
int n = json_unescape(value, value_sz, nullptr);
|
|
208
|
+
if (n > 0) { char *decoded = new char[n + 1]; json_unescape(value, value_sz, decoded); std::string result(decoded, n); delete[] decoded; return result; }
|
|
209
|
+
}
|
|
210
|
+
return "";
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
} // namespace webview
|
|
214
|
+
|
|
215
|
+
#if defined(WEBVIEW_GTK)
|
|
216
|
+
#include <JavaScriptCore/JavaScript.h>
|
|
217
|
+
#include <gtk/gtk.h>
|
|
218
|
+
#include <webkit2/webkit2.h>
|
|
219
|
+
namespace webview {
|
|
220
|
+
class gtk_webkit_engine {
|
|
221
|
+
public:
|
|
222
|
+
gtk_webkit_engine(bool debug, void *window) : m_window(static_cast<GtkWidget *>(window)) {
|
|
223
|
+
gtk_init_check(0, NULL);
|
|
224
|
+
m_window = static_cast<GtkWidget *>(window);
|
|
225
|
+
if (m_window == nullptr) { m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); }
|
|
226
|
+
g_signal_connect(G_OBJECT(m_window), "destroy", G_CALLBACK(+[](GtkWidget *, gpointer arg) { static_cast<gtk_webkit_engine *>(arg)->terminate(); }), this);
|
|
227
|
+
m_webview = webkit_web_view_new();
|
|
228
|
+
WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(m_webview));
|
|
229
|
+
g_signal_connect(manager, "script-message-received::external", G_CALLBACK(+[](WebKitUserContentManager *, WebKitJavascriptResult *r, gpointer arg) {
|
|
230
|
+
auto *w = static_cast<gtk_webkit_engine *>(arg);
|
|
231
|
+
#if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22
|
|
232
|
+
JSCValue *value = webkit_javascript_result_get_js_value(r);
|
|
233
|
+
char *s = jsc_value_to_string(value);
|
|
234
|
+
#else
|
|
235
|
+
JSGlobalContextRef ctx = webkit_javascript_result_get_global_context(r);
|
|
236
|
+
JSValueRef value = webkit_javascript_result_get_value(r);
|
|
237
|
+
JSStringRef js = JSValueToStringCopy(ctx, value, NULL);
|
|
238
|
+
size_t n = JSStringGetMaximumUTF8CStringSize(js);
|
|
239
|
+
char *s = g_new(char, n);
|
|
240
|
+
JSStringGetUTF8CString(js, s, n);
|
|
241
|
+
JSStringRelease(js);
|
|
242
|
+
#endif
|
|
243
|
+
w->on_message(s);
|
|
244
|
+
g_free(s);
|
|
245
|
+
}), this);
|
|
246
|
+
webkit_user_content_manager_register_script_message_handler(manager, "external");
|
|
247
|
+
init("window.external={invoke:function(s){window.webkit.messageHandlers.external.postMessage(s);}}");
|
|
248
|
+
gtk_container_add(GTK_CONTAINER(m_window), GTK_WIDGET(m_webview));
|
|
249
|
+
gtk_widget_grab_focus(GTK_WIDGET(m_webview));
|
|
250
|
+
if (debug) {
|
|
251
|
+
WebKitSettings *settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(m_webview));
|
|
252
|
+
webkit_settings_set_enable_write_console_messages_to_stdout(settings, true);
|
|
253
|
+
webkit_settings_set_enable_developer_extras(settings, true);
|
|
254
|
+
}
|
|
255
|
+
gtk_widget_show_all(m_window);
|
|
256
|
+
}
|
|
257
|
+
void *window() { return (void *)m_window; }
|
|
258
|
+
void run() { gtk_main(); }
|
|
259
|
+
void terminate() { gtk_main_quit(); }
|
|
260
|
+
void dispatch(std::function<void()> f) { g_idle_add_full(G_PRIORITY_HIGH_IDLE, (GSourceFunc)([](void *f) -> int { (*static_cast<dispatch_fn_t *>(f))(); return G_SOURCE_REMOVE; }), new std::function<void()>(f), [](void *f) { delete static_cast<dispatch_fn_t *>(f); }); }
|
|
261
|
+
void set_title(const std::string title) { gtk_window_set_title(GTK_WINDOW(m_window), title.c_str()); }
|
|
262
|
+
void set_size(int width, int height, int hints) {
|
|
263
|
+
gtk_window_set_resizable(GTK_WINDOW(m_window), hints != WEBVIEW_HINT_FIXED);
|
|
264
|
+
if (hints == WEBVIEW_HINT_NONE) { gtk_window_resize(GTK_WINDOW(m_window), width, height); }
|
|
265
|
+
else if (hints == WEBVIEW_HINT_FIXED) { gtk_widget_set_size_request(m_window, width, height); }
|
|
266
|
+
else { GdkGeometry g; g.min_width = g.max_width = width; g.min_height = g.max_height = height; GdkWindowHints h = (hints == WEBVIEW_HINT_MIN ? GDK_HINT_MIN_SIZE : GDK_HINT_MAX_SIZE); gtk_window_set_geometry_hints(GTK_WINDOW(m_window), nullptr, &g, h); }
|
|
267
|
+
}
|
|
268
|
+
void navigate(const std::string url) { webkit_web_view_load_uri(WEBKIT_WEB_VIEW(m_webview), url.c_str()); }
|
|
269
|
+
void init(const std::string js) {
|
|
270
|
+
WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(m_webview));
|
|
271
|
+
webkit_user_content_manager_add_script(manager, webkit_user_script_new(js.c_str(), WEBKIT_USER_CONTENT_INJECT_TOP_FRAME, WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START, NULL, NULL));
|
|
272
|
+
}
|
|
273
|
+
void eval(const std::string js) { webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(m_webview), js.c_str(), NULL, NULL, NULL); }
|
|
274
|
+
private:
|
|
275
|
+
virtual void on_message(const std::string msg) = 0;
|
|
276
|
+
GtkWidget *m_window;
|
|
277
|
+
GtkWidget *m_webview;
|
|
278
|
+
};
|
|
279
|
+
using browser_engine = gtk_webkit_engine;
|
|
280
|
+
} // namespace webview
|
|
281
|
+
|
|
282
|
+
#elif defined(WEBVIEW_COCOA)
|
|
283
|
+
#define OBJC_OLD_DISPATCH_PROTOTYPES 1
|
|
284
|
+
#include <CoreGraphics/CoreGraphics.h>
|
|
285
|
+
#include <objc/objc-runtime.h>
|
|
286
|
+
#define NSBackingStoreBuffered 2
|
|
287
|
+
#define NSWindowStyleMaskResizable 8
|
|
288
|
+
#define NSWindowStyleMaskMiniaturizable 4
|
|
289
|
+
#define NSWindowStyleMaskTitled 1
|
|
290
|
+
#define NSWindowStyleMaskClosable 2
|
|
291
|
+
#define NSApplicationActivationPolicyRegular 0
|
|
292
|
+
#define WKUserScriptInjectionTimeAtDocumentStart 0
|
|
293
|
+
namespace webview {
|
|
294
|
+
id operator"" _cls(const char *s, std::size_t) { return (id)objc_getClass(s); }
|
|
295
|
+
SEL operator"" _sel(const char *s, std::size_t) { return sel_registerName(s); }
|
|
296
|
+
id operator"" _str(const char *s, std::size_t) { return objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, s); }
|
|
297
|
+
class cocoa_wkwebview_engine {
|
|
298
|
+
public:
|
|
299
|
+
cocoa_wkwebview_engine(bool debug, void *window) {
|
|
300
|
+
id app = objc_msgSend("NSApplication"_cls, "sharedApplication"_sel);
|
|
301
|
+
objc_msgSend(app, "setActivationPolicy:"_sel, NSApplicationActivationPolicyRegular);
|
|
302
|
+
auto cls = objc_allocateClassPair((Class)"NSResponder"_cls, "AppDelegate", 0);
|
|
303
|
+
class_addProtocol(cls, objc_getProtocol("NSTouchBarProvider"));
|
|
304
|
+
class_addMethod(cls, "applicationShouldTerminateAfterLastWindowClosed:"_sel, (IMP)(+[](id, SEL, id) -> BOOL { return 1; }), "c@:@");
|
|
305
|
+
class_addMethod(cls, "userContentController:didReceiveScriptMessage:"_sel, (IMP)(+[](id self, SEL, id, id msg) {
|
|
306
|
+
auto w = (cocoa_wkwebview_engine *)objc_getAssociatedObject(self, "webview");
|
|
307
|
+
w->on_message((const char *)objc_msgSend(objc_msgSend(msg, "body"_sel), "UTF8String"_sel));
|
|
308
|
+
}), "v@:@@");
|
|
309
|
+
objc_registerClassPair(cls);
|
|
310
|
+
auto delegate = objc_msgSend((id)cls, "new"_sel);
|
|
311
|
+
objc_setAssociatedObject(delegate, "webview", (id)this, OBJC_ASSOCIATION_ASSIGN);
|
|
312
|
+
objc_msgSend(app, sel_registerName("setDelegate:"), delegate);
|
|
313
|
+
if (window == nullptr) {
|
|
314
|
+
m_window = objc_msgSend("NSWindow"_cls, "alloc"_sel);
|
|
315
|
+
m_window = objc_msgSend(m_window, "initWithContentRect:styleMask:backing:defer:"_sel, CGRectMake(0, 0, 0, 0), 0, NSBackingStoreBuffered, 0);
|
|
316
|
+
} else { m_window = (id)window; }
|
|
317
|
+
auto config = objc_msgSend("WKWebViewConfiguration"_cls, "new"_sel);
|
|
318
|
+
m_manager = objc_msgSend(config, "userContentController"_sel);
|
|
319
|
+
m_webview = objc_msgSend("WKWebView"_cls, "alloc"_sel);
|
|
320
|
+
if (debug) { objc_msgSend(objc_msgSend(config, "preferences"_sel), "setValue:forKey:"_sel, objc_msgSend("NSNumber"_cls, "numberWithBool:"_sel, 1), "developerExtrasEnabled"_str); }
|
|
321
|
+
objc_msgSend(m_webview, "initWithFrame:configuration:"_sel, CGRectMake(0, 0, 0, 0), config);
|
|
322
|
+
objc_msgSend(m_manager, "addScriptMessageHandler:name:"_sel, delegate, "external"_str);
|
|
323
|
+
init(R"script(window.external = { invoke: function(s) { window.webkit.messageHandlers.external.postMessage(s); }, };)script");
|
|
324
|
+
objc_msgSend(m_window, "setContentView:"_sel, m_webview);
|
|
325
|
+
objc_msgSend(m_window, "makeKeyAndOrderFront:"_sel, nullptr);
|
|
326
|
+
}
|
|
327
|
+
~cocoa_wkwebview_engine() { close(); }
|
|
328
|
+
void *window() { return (void *)m_window; }
|
|
329
|
+
void terminate() { close(); objc_msgSend("NSApp"_cls, "terminate:"_sel, nullptr); }
|
|
330
|
+
void run() { id app = objc_msgSend("NSApplication"_cls, "sharedApplication"_sel); dispatch([&]() { objc_msgSend(app, "activateIgnoringOtherApps:"_sel, 1); }); objc_msgSend(app, "run"_sel); }
|
|
331
|
+
void dispatch(std::function<void()> f) { dispatch_async_f(dispatch_get_main_queue(), new dispatch_fn_t(f), (dispatch_function_t)([](void *arg) { auto f = static_cast<dispatch_fn_t *>(arg); (*f)(); delete f; })); }
|
|
332
|
+
void set_title(const std::string title) { objc_msgSend(m_window, "setTitle:"_sel, objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, title.c_str())); }
|
|
333
|
+
void set_size(int width, int height, int hints) {
|
|
334
|
+
auto style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniurizable;
|
|
335
|
+
if (hints != WEBVIEW_HINT_FIXED) { style = style | NSWindowStyleMaskResizable; }
|
|
336
|
+
objc_msgSend(m_window, "setStyleMask:"_sel, style);
|
|
337
|
+
struct { CGFloat width; CGFloat height; } size;
|
|
338
|
+
if (hints == WEBVIEW_HINT_MIN) { size.width = width; size.height = height; objc_msgSend(m_window, "setContentMinSize:"_sel, size); }
|
|
339
|
+
else if (hints == WEBVIEW_HINT_MAX) { size.width = width; size.height = height; objc_msgSend(m_window, "setContentMaxSize:"_sel, size); }
|
|
340
|
+
else { objc_msgSend(m_window, "setFrame:display:animate:"_sel, CGRectMake(0, 0, width, height), 1, 0); }
|
|
341
|
+
}
|
|
342
|
+
void navigate(const std::string url) { auto nsurl = objc_msgSend("NSURL"_cls, "URLWithString:"_sel, objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, url.c_str())); objc_msgSend(m_webview, "loadRequest:"_sel, objc_msgSend("NSURLRequest"_cls, "requestWithURL:"_sel, nsurl)); }
|
|
343
|
+
void init(const std::string js) { objc_msgSend(m_manager, "addUserScript:"_sel, objc_msgSend(objc_msgSend("WKUserScript"_cls, "alloc"_sel), "initWithSource:injectionTime:forMainFrameOnly:"_sel, objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, js.c_str()), WKUserScriptInjectionTimeAtDocumentStart, 1)); }
|
|
344
|
+
void eval(const std::string js) { objc_msgSend(m_webview, "evaluateJavaScript:completionHandler:"_sel, objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, js.c_str()), nullptr); }
|
|
345
|
+
private:
|
|
346
|
+
virtual void on_message(const std::string msg) = 0;
|
|
347
|
+
void close() { objc_msgSend(m_window, "close"_sel); }
|
|
348
|
+
id m_window;
|
|
349
|
+
id m_webview;
|
|
350
|
+
id m_manager;
|
|
351
|
+
};
|
|
352
|
+
using browser_engine = cocoa_wkwebview_engine;
|
|
353
|
+
} // namespace webview
|
|
354
|
+
|
|
355
|
+
#elif defined(WEBVIEW_EDGE)
|
|
356
|
+
#define WIN32_LEAN_AND_MEAN
|
|
357
|
+
#include <windows.h>
|
|
358
|
+
#pragma comment(lib, "user32.lib")
|
|
359
|
+
#include <objbase.h>
|
|
360
|
+
#include <winrt/Windows.Foundation.h>
|
|
361
|
+
#include <winrt/Windows.Web.UI.Interop.h>
|
|
362
|
+
#pragma comment(lib, "windowsapp")
|
|
363
|
+
#include "webview2.h"
|
|
364
|
+
#pragma comment(lib, "ole32.lib")
|
|
365
|
+
#pragma comment(lib, "oleaut32.lib")
|
|
366
|
+
namespace webview {
|
|
367
|
+
using msg_cb_t = std::function<void(const std::string)>;
|
|
368
|
+
class browser {
|
|
369
|
+
public:
|
|
370
|
+
virtual ~browser() = default;
|
|
371
|
+
virtual bool embed(HWND, bool, msg_cb_t) = 0;
|
|
372
|
+
virtual void navigate(const std::string url) = 0;
|
|
373
|
+
virtual void eval(const std::string js) = 0;
|
|
374
|
+
virtual void init(const std::string js) = 0;
|
|
375
|
+
virtual void resize(HWND) = 0;
|
|
376
|
+
};
|
|
377
|
+
class edge_html : public browser {
|
|
378
|
+
public:
|
|
379
|
+
bool embed(HWND wnd, bool debug, msg_cb_t cb) override {
|
|
380
|
+
init_apartment(winrt::apartment_type::single_threaded);
|
|
381
|
+
auto process = WebViewControlProcess();
|
|
382
|
+
auto op = process.CreateWebViewControlAsync(reinterpret_cast<int64_t>(wnd), Rect());
|
|
383
|
+
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); }
|
|
384
|
+
m_webview = op.GetResults();
|
|
385
|
+
m_webview.Settings().IsScriptNotifyAllowed(true);
|
|
386
|
+
m_webview.IsVisible(true);
|
|
387
|
+
m_webview.ScriptNotify([=](auto const &sender, auto const &args) { std::string s = winrt::to_string(args.Value()); cb(s.c_str()); });
|
|
388
|
+
m_webview.NavigationStarting([=](auto const &sender, auto const &args) { m_webview.AddInitializeScript(winrt::to_hstring(init_js)); });
|
|
389
|
+
init("window.external.invoke = s => window.external.notify(s)");
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
void navigate(const std::string url) override { std::string html = html_from_uri(url); if (html != "") { m_webview.NavigateToString(winrt::to_hstring(html)); } else { Uri uri(winrt::to_hstring(url)); m_webview.Navigate(uri); } }
|
|
393
|
+
void init(const std::string js) override { init_js = init_js + "(function(){" + js + "})();"; }
|
|
394
|
+
void eval(const std::string js) override { m_webview.InvokeScriptAsync(L"eval", single_threaded_vector<hstring>({winrt::to_hstring(js)})); }
|
|
395
|
+
void resize(HWND wnd) override { if (m_webview == nullptr) { return; } RECT r; GetClientRect(wnd, &r); Rect bounds(r.left, r.top, r.right - r.left, r.bottom - r.top); m_webview.Bounds(bounds); }
|
|
396
|
+
private:
|
|
397
|
+
WebViewControl m_webview = nullptr;
|
|
398
|
+
std::string init_js = "";
|
|
399
|
+
};
|
|
400
|
+
class edge_chromium : public browser {
|
|
401
|
+
public:
|
|
402
|
+
bool embed(HWND wnd, bool debug, msg_cb_t cb) override {
|
|
403
|
+
CoInitializeEx(nullptr, 0);
|
|
404
|
+
std::atomic_flag flag = ATOMIC_FLAG_INIT;
|
|
405
|
+
flag.test_and_set();
|
|
406
|
+
HRESULT res = CreateWebView2EnvironmentWithDetails(nullptr, nullptr, nullptr, new webview2_com_handler(wnd, [&](IWebView2WebView *webview) { m_webview = webview; flag.clear(); }));
|
|
407
|
+
if (res != S_OK) { CoUninitialize(); return false; }
|
|
408
|
+
MSG msg = {};
|
|
409
|
+
while (flag.test_and_set() && GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
|
|
410
|
+
init("window.external={invoke:s=>window.chrome.webview.postMessage(s)}");
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
void resize(HWND wnd) override { if (m_webview == nullptr) { return; } RECT bounds; GetClientRect(wnd, &bounds); m_webview->put_Bounds(bounds); }
|
|
414
|
+
void navigate(const std::string url) override { auto wurl = to_lpwstr(url); m_webview->Navigate(wurl); delete[] wurl; }
|
|
415
|
+
void init(const std::string js) override { LPCWSTR wjs = to_lpwstr(js); m_webview->AddScriptToExecuteOnDocumentCreated(wjs, nullptr); delete[] wjs; }
|
|
416
|
+
void eval(const std::string js) override { LPCWSTR wjs = to_lpwstr(js); m_webview->ExecuteScript(wjs, nullptr); delete[] wjs; }
|
|
417
|
+
private:
|
|
418
|
+
LPWSTR to_lpwstr(const std::string s) { int n = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0); wchar_t *ws = new wchar_t[n]; MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, ws, n); return ws; }
|
|
419
|
+
IWebView2WebView *m_webview = nullptr;
|
|
420
|
+
class webview2_com_handler : public IWebView2CreateWebView2EnvironmentCompletedHandler, public IWebView2CreateWebViewCompletedHandler {
|
|
421
|
+
using webview2_com_handler_cb_t = std::function<void(IWebView2WebView *)>;
|
|
422
|
+
public:
|
|
423
|
+
webview2_com_handler(HWND hwnd, webview2_com_handler_cb_t cb) : m_window(hwnd), m_cb(cb) {}
|
|
424
|
+
ULONG STDMETHODCALLTYPE AddRef() { return 1; }
|
|
425
|
+
ULONG STDMETHODCALLTYPE Release() { return 1; }
|
|
426
|
+
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppv) { return S_OK; }
|
|
427
|
+
HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, IWebView2Environment *env) { env->CreateWebView(m_window, this); return S_OK; }
|
|
428
|
+
HRESULT STDMETHODCALLTYPE Invoke( HRESULT res, IWebView2WebView *webview) { webview->AddRef(); m_cb(webview); return S_OK; }
|
|
429
|
+
private:
|
|
430
|
+
HWND m_window;
|
|
431
|
+
webview2_com_handler_cb_t m_cb;
|
|
432
|
+
};
|
|
433
|
+
};
|
|
434
|
+
class win32_edge_engine {
|
|
435
|
+
public:
|
|
436
|
+
win32_edge_engine(bool debug, void *window) {
|
|
437
|
+
if (window == nullptr) {
|
|
438
|
+
WNDCLASSEX wc; ZeroMemory(&wc, sizeof(WNDCLASSEX));
|
|
439
|
+
wc.cbSize = sizeof(WNDCLASSEX); wc.hInstance = GetModuleHandle(nullptr); wc.lpszClassName = "webview";
|
|
440
|
+
wc.lpfnWndProc = (WNDPROC)(+[](HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) -> int {
|
|
441
|
+
auto w = (win32_edge_engine *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
442
|
+
switch (msg) {
|
|
443
|
+
case WM_SIZE: w->m_browser->resize(hwnd); break;
|
|
444
|
+
case WM_CLOSE: DestroyWindow(hwnd); break;
|
|
445
|
+
case WM_DESTROY: w->terminate(); break;
|
|
446
|
+
case WM_GETMINMAXINFO: { auto lpmmi = (LPMINMAXINFO)lp; if (w == nullptr) { return 0; }
|
|
447
|
+
if (w->m_maxsz.x > 0 && w->m_maxsz.y > 0) { lpmmi->ptMaxSize = w->m_maxsz; lpmmi->ptMaxTrackSize = w->m_maxsz; }
|
|
448
|
+
if (w->m_minsz.x > 0 && w->m_minsz.y > 0) { lpmmi->ptMinTrackSize = w->m_minsz; }
|
|
449
|
+
} break;
|
|
450
|
+
default: return DefWindowProc(hwnd, msg, wp, lp);
|
|
451
|
+
}
|
|
452
|
+
return 0;
|
|
453
|
+
});
|
|
454
|
+
RegisterClassEx(&wc);
|
|
455
|
+
m_window = CreateWindow("webview", "", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
|
|
456
|
+
SetWindowLongPtr(m_window, GWLP_USERDATA, (LONG_PTR)this);
|
|
457
|
+
} else { m_window = *(static_cast<HWND *>(window)); }
|
|
458
|
+
ShowWindow(m_window, SW_SHOW); UpdateWindow(m_window); SetFocus(m_window);
|
|
459
|
+
auto cb = std::bind(&win32_edge_engine::on_message, this, std::placeholders::_1);
|
|
460
|
+
if (!m_browser->embed(m_window, debug, cb)) { m_browser = std::make_unique<webview::edge_html>(); m_browser->embed(m_window, debug, cb); }
|
|
461
|
+
m_browser->resize(m_window);
|
|
462
|
+
}
|
|
463
|
+
void run() { MSG msg; BOOL res; while ((res = GetMessage(&msg, nullptr, 0, 0)) != -1) { if (msg.hwnd) { TranslateMessage(&msg); DispatchMessage(&msg); continue; }
|
|
464
|
+
if (msg.message == WM_APP) { auto f = (dispatch_fn_t *)(msg.lParam); (*f)(); delete f; }
|
|
465
|
+
else if (msg.message == WM_QUIT) { return; }
|
|
466
|
+
}}
|
|
467
|
+
void *window() { return (void *)m_window; }
|
|
468
|
+
void terminate() { PostQuitMessage(0); }
|
|
469
|
+
void dispatch(dispatch_fn_t f) { PostThreadMessage(m_main_thread, WM_APP, 0, (LPARAM) new dispatch_fn_t(f)); }
|
|
470
|
+
void set_title(const std::string title) { SetWindowText(m_window, title.c_str()); }
|
|
471
|
+
void set_size(int width, int height, int hints) {
|
|
472
|
+
auto style = GetWindowLong(m_window, GWL_STYLE);
|
|
473
|
+
if (hints == WEBVIEW_HINT_FIXED) { style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX); }
|
|
474
|
+
else { style |= (WS_THICKFRAME | WS_MAXIMIZEBOX); }
|
|
475
|
+
SetWindowLong(m_window, GWL_STYLE, style);
|
|
476
|
+
if (hints == WEBVIEW_HINT_MAX) { m_maxsz.x = width; m_maxsz.y = height; }
|
|
477
|
+
else if (hints == WEBVIEW_HINT_MIN) { m_minsz.x = width; m_minsz.y = height; }
|
|
478
|
+
else { RECT r; r.left = r.top = 0; 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_NOMOVE | SWP_FRAMECHANGED); m_browser->resize(m_window); }
|
|
479
|
+
}
|
|
480
|
+
void navigate(const std::string url) { m_browser->navigate(url); }
|
|
481
|
+
void eval(const std::string js) { m_browser->eval(js); }
|
|
482
|
+
void init(const std::string js) { m_browser->init(js); }
|
|
483
|
+
private:
|
|
484
|
+
virtual void on_message(const std::string msg) = 0;
|
|
485
|
+
HWND m_window;
|
|
486
|
+
POINT m_minsz;
|
|
487
|
+
POINT m_maxsz;
|
|
488
|
+
DWORD m_main_thread = GetCurrentThreadId();
|
|
489
|
+
std::unique_ptr<webview::browser> m_browser = std::make_unique<webview::edge_chromium>();
|
|
490
|
+
};
|
|
491
|
+
using browser_engine = win32_edge_engine;
|
|
492
|
+
} // namespace webview
|
|
493
|
+
#endif
|
|
494
|
+
|
|
495
|
+
namespace webview {
|
|
496
|
+
class webview : public browser_engine {
|
|
497
|
+
public:
|
|
498
|
+
webview(bool debug = false, void *wnd = nullptr) : browser_engine(debug, wnd) {}
|
|
499
|
+
void navigate(const std::string url) {
|
|
500
|
+
if (url == "") { browser_engine::navigate("data:text/html," + url_encode("<html><body>Hello</body></html>")); return; }
|
|
501
|
+
std::string html = html_from_uri(url);
|
|
502
|
+
if (html != "") { browser_engine::navigate("data:text/html," + url_encode(html)); }
|
|
503
|
+
else { browser_engine::navigate(url); }
|
|
504
|
+
}
|
|
505
|
+
using binding_t = std::function<void(std::string, std::string, void *)>;
|
|
506
|
+
using binding_ctx_t = std::pair<binding_t *, void *>;
|
|
507
|
+
using sync_binding_t = std::function<std::string(std::string)>;
|
|
508
|
+
using sync_binding_ctx_t = std::pair<webview *, sync_binding_t>;
|
|
509
|
+
void bind(const std::string name, sync_binding_t fn) {
|
|
510
|
+
bind(name, [](std::string seq, std::string req, void *arg) { auto pair = static_cast<sync_binding_ctx_t *>(arg); pair->first->resolve(seq, 0, pair->second(req)); }, new sync_binding_ctx_t(this, fn));
|
|
511
|
+
}
|
|
512
|
+
void bind(const std::string name, binding_t f, void *arg) {
|
|
513
|
+
auto js = "(function() { var name = '" + name + "'; var RPC = window._rpc = (window._rpc || {nextSeq: 1}); window[name] = function() { var seq = RPC.nextSeq++; var promise = new Promise(function(resolve, reject) { RPC[seq] = { resolve: resolve, reject: reject, }; }); window.external.invoke(JSON.stringify({ id: seq, method: name, params: Array.prototype.slice.call(arguments), })); return promise; } })())";
|
|
514
|
+
init(js);
|
|
515
|
+
bindings[name] = new binding_ctx_t(new binding_t(f), arg);
|
|
516
|
+
}
|
|
517
|
+
void resolve(const std::string seq, int status, const std::string result) {
|
|
518
|
+
dispatch([=]() {
|
|
519
|
+
if (status == 0) { eval("window._rpc[" + seq + "].resolve(" + result + "); window._rpc[" + seq + "] = undefined"); }
|
|
520
|
+
else { eval("window._rpc[" + seq + "].reject(" + result + "); window._rpc[" + seq + "] = undefined"); }
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
private:
|
|
524
|
+
void on_message(const std::string msg) {
|
|
525
|
+
auto seq = json_parse(msg, "id", 0);
|
|
526
|
+
auto name = json_parse(msg, "method", 0);
|
|
527
|
+
auto args = json_parse(msg, "params", 0);
|
|
528
|
+
if (bindings.find(name) == bindings.end()) { return; }
|
|
529
|
+
auto fn = bindings[name];
|
|
530
|
+
(*fn->first)(seq, args, fn->second);
|
|
531
|
+
}
|
|
532
|
+
std::map<std::string, binding_ctx_t *> bindings;
|
|
533
|
+
};
|
|
534
|
+
} // namespace webview
|
|
535
|
+
|
|
536
|
+
WEBVIEW_API webview_t webview_create(int debug, void *wnd) { return new webview::webview(debug, wnd); }
|
|
537
|
+
WEBVIEW_API void webview_destroy(webview_t w) { delete static_cast<webview::webview *>(w); }
|
|
538
|
+
WEBVIEW_API void webview_run(webview_t w) { static_cast<webview::webview *>(w)->run(); }
|
|
539
|
+
WEBVIEW_API void webview_terminate(webview_t w) { static_cast<webview::webview *>(w)->terminate(); }
|
|
540
|
+
WEBVIEW_API void webview_dispatch(webview_t w, void (*fn)(webview_t, void *), void *arg) { static_cast<webview::webview *>(w)->dispatch([=]() { fn(w, arg); }); }
|
|
541
|
+
WEBVIEW_API void *webview_get_window(webview_t w) { return static_cast<webview::webview *>(w)->window(); }
|
|
542
|
+
WEBVIEW_API void webview_set_title(webview_t w, const char *title) { static_cast<webview::webview *>(w)->set_title(title); }
|
|
543
|
+
WEBVIEW_API void webview_set_size(webview_t w, int width, int height, int hints) { static_cast<webview::webview *>(w)->set_size(width, height, hints); }
|
|
544
|
+
WEBVIEW_API void webview_navigate(webview_t w, const char *url) { static_cast<webview::webview *>(w)->navigate(url); }
|
|
545
|
+
WEBVIEW_API void webview_init(webview_t w, const char *js) { static_cast<webview::webview *>(w)->init(js); }
|
|
546
|
+
WEBVIEW_API void webview_eval(webview_t w, const char *js) { static_cast<webview::webview *>(w)->eval(js); }
|
|
547
|
+
WEBVIEW_API void webview_bind(webview_t w, const char *name, void (*fn)(const char *seq, const char *req, void *arg), void *arg) { static_cast<webview::webview *>(w)->bind(name, [=](std::string seq, std::string req, void *arg) { fn(seq.c_str(), req.c_str(), arg); }, arg); }
|
|
548
|
+
WEBVIEW_API void webview_return(webview_t w, const char *seq, int status, const char *result) { static_cast<webview::webview *>(w)->resolve(seq, status, result); }
|
|
549
|
+
|
|
550
|
+
#endif
|
|
551
|
+
#endif
|