tauri-gtk.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. #include "tauri-gtk-webview.h"
  2. #include "tauri.h"
  3. static void external_message_received_cb(WebKitUserContentManager *m,
  4. WebKitJavascriptResult *r,
  5. gpointer arg) {
  6. (void)m;
  7. struct webview *w = (struct webview *)arg;
  8. if (w->external_invoke_cb == NULL) {
  9. return;
  10. }
  11. JSGlobalContextRef context = webkit_javascript_result_get_global_context(r);
  12. JSValueRef value = webkit_javascript_result_get_value(r);
  13. JSStringRef js = JSValueToStringCopy(context, value, NULL);
  14. size_t n = JSStringGetMaximumUTF8CStringSize(js);
  15. char *s = g_new(char, n);
  16. JSStringGetUTF8CString(js, s, n);
  17. w->external_invoke_cb(w, s);
  18. JSStringRelease(js);
  19. g_free(s);
  20. }
  21. static void webview_load_changed_cb(WebKitWebView *webview,
  22. WebKitLoadEvent event, gpointer arg) {
  23. (void)webview;
  24. struct webview *w = (struct webview *)arg;
  25. if (event == WEBKIT_LOAD_FINISHED) {
  26. w->priv.ready = 1;
  27. }
  28. }
  29. static void webview_destroy_cb(GtkWidget *widget, gpointer arg) {
  30. (void)widget;
  31. struct webview *w = (struct webview *)arg;
  32. webview_terminate(w);
  33. }
  34. static gboolean webview_context_menu_cb(WebKitWebView *webview,
  35. GtkWidget *default_menu,
  36. WebKitHitTestResult *hit_test_result,
  37. gboolean triggered_with_keyboard,
  38. gpointer userdata) {
  39. (void)webview;
  40. (void)default_menu;
  41. (void)hit_test_result;
  42. (void)triggered_with_keyboard;
  43. (void)userdata;
  44. return TRUE;
  45. }
  46. WEBVIEW_API int webview_init(struct webview *w) {
  47. if (gtk_init_check(0, NULL) == FALSE) {
  48. return -1;
  49. }
  50. w->priv.ready = 0;
  51. w->priv.should_exit = 0;
  52. w->priv.queue = g_async_queue_new();
  53. w->priv.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  54. gtk_window_set_title(GTK_WINDOW(w->priv.window), w->title);
  55. if (w->resizable) {
  56. gtk_window_set_default_size(GTK_WINDOW(w->priv.window), w->width,
  57. w->height);
  58. } else {
  59. gtk_widget_set_size_request(w->priv.window, w->width, w->height);
  60. }
  61. gtk_window_set_resizable(GTK_WINDOW(w->priv.window), !!w->resizable);
  62. gtk_window_set_position(GTK_WINDOW(w->priv.window), GTK_WIN_POS_CENTER);
  63. w->priv.scroller = gtk_scrolled_window_new(NULL, NULL);
  64. gtk_container_add(GTK_CONTAINER(w->priv.window), w->priv.scroller);
  65. WebKitUserContentManager *m = webkit_user_content_manager_new();
  66. webkit_user_content_manager_register_script_message_handler(m, "external");
  67. g_signal_connect(m, "script-message-received::external",
  68. G_CALLBACK(external_message_received_cb), w);
  69. w->priv.webview = webkit_web_view_new_with_user_content_manager(m);
  70. webkit_web_view_load_uri(WEBKIT_WEB_VIEW(w->priv.webview),
  71. webview_check_url(w->url));
  72. g_signal_connect(G_OBJECT(w->priv.webview), "load-changed",
  73. G_CALLBACK(webview_load_changed_cb), w);
  74. gtk_container_add(GTK_CONTAINER(w->priv.scroller), w->priv.webview);
  75. if (w->debug) {
  76. WebKitSettings *settings =
  77. webkit_web_view_get_settings(WEBKIT_WEB_VIEW(w->priv.webview));
  78. webkit_settings_set_enable_write_console_messages_to_stdout(settings, true);
  79. webkit_settings_set_enable_developer_extras(settings, true);
  80. } else {
  81. g_signal_connect(G_OBJECT(w->priv.webview), "context-menu",
  82. G_CALLBACK(webview_context_menu_cb), w);
  83. }
  84. gtk_widget_show_all(w->priv.window);
  85. webkit_web_view_run_javascript(
  86. WEBKIT_WEB_VIEW(w->priv.webview),
  87. "window.external={invoke:function(x){"
  88. "window.webkit.messageHandlers.external.postMessage(x);}}",
  89. NULL, NULL, NULL);
  90. g_signal_connect(G_OBJECT(w->priv.window), "destroy",
  91. G_CALLBACK(webview_destroy_cb), w);
  92. return 0;
  93. }
  94. WEBVIEW_API int webview_loop(struct webview *w, int blocking) {
  95. gtk_main_iteration_do(blocking);
  96. return w->priv.should_exit;
  97. }
  98. WEBVIEW_API void webview_set_title(struct webview *w, const char *title) {
  99. gtk_window_set_title(GTK_WINDOW(w->priv.window), title);
  100. }
  101. WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) {
  102. if (fullscreen) {
  103. gtk_window_fullscreen(GTK_WINDOW(w->priv.window));
  104. } else {
  105. gtk_window_unfullscreen(GTK_WINDOW(w->priv.window));
  106. }
  107. }
  108. WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g,
  109. uint8_t b, uint8_t a) {
  110. GdkRGBA color = {r / 255.0, g / 255.0, b / 255.0, a / 255.0};
  111. webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(w->priv.webview),
  112. &color);
  113. }
  114. WEBVIEW_API void webview_dialog(struct webview *w,
  115. enum webview_dialog_type dlgtype, int flags,
  116. const char *title, const char *arg,
  117. char *result, size_t resultsz) {
  118. GtkWidget *dlg;
  119. if (result != NULL) {
  120. result[0] = '\0';
  121. }
  122. if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ||
  123. dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) {
  124. dlg = gtk_file_chooser_dialog_new(
  125. title, GTK_WINDOW(w->priv.window),
  126. (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN
  127. ? (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY
  128. ? GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
  129. : GTK_FILE_CHOOSER_ACTION_OPEN)
  130. : GTK_FILE_CHOOSER_ACTION_SAVE),
  131. "_Cancel", GTK_RESPONSE_CANCEL,
  132. (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ? "_Open" : "_Save"),
  133. GTK_RESPONSE_ACCEPT, NULL);
  134. gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), FALSE);
  135. gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dlg), FALSE);
  136. gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dlg), TRUE);
  137. gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dlg), TRUE);
  138. gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dlg), TRUE);
  139. gint response = gtk_dialog_run(GTK_DIALOG(dlg));
  140. if (response == GTK_RESPONSE_ACCEPT) {
  141. gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg));
  142. g_strlcpy(result, filename, resultsz);
  143. g_free(filename);
  144. }
  145. gtk_widget_destroy(dlg);
  146. } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) {
  147. GtkMessageType type = GTK_MESSAGE_OTHER;
  148. switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) {
  149. case WEBVIEW_DIALOG_FLAG_INFO:
  150. type = GTK_MESSAGE_INFO;
  151. break;
  152. case WEBVIEW_DIALOG_FLAG_WARNING:
  153. type = GTK_MESSAGE_WARNING;
  154. break;
  155. case WEBVIEW_DIALOG_FLAG_ERROR:
  156. type = GTK_MESSAGE_ERROR;
  157. break;
  158. }
  159. dlg = gtk_message_dialog_new(GTK_WINDOW(w->priv.window), GTK_DIALOG_MODAL,
  160. type, GTK_BUTTONS_OK, "%s", title);
  161. gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dlg), "%s",
  162. arg);
  163. gtk_dialog_run(GTK_DIALOG(dlg));
  164. gtk_widget_destroy(dlg);
  165. }
  166. }
  167. static void webview_eval_finished(GObject *object, GAsyncResult *result,
  168. gpointer userdata) {
  169. (void)object;
  170. (void)result;
  171. struct webview *w = (struct webview *)userdata;
  172. w->priv.js_busy = 0;
  173. }
  174. WEBVIEW_API int webview_eval(struct webview *w, const char *js) {
  175. while (w->priv.ready == 0) {
  176. g_main_context_iteration(NULL, TRUE);
  177. }
  178. w->priv.js_busy = 1;
  179. webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(w->priv.webview), js, NULL,
  180. webview_eval_finished, w);
  181. while (w->priv.js_busy) {
  182. g_main_context_iteration(NULL, TRUE);
  183. }
  184. return 0;
  185. }
  186. static gboolean webview_dispatch_wrapper(gpointer userdata) {
  187. struct webview *w = (struct webview *)userdata;
  188. for (;;) {
  189. struct webview_dispatch_arg *arg =
  190. (struct webview_dispatch_arg *)g_async_queue_try_pop(w->priv.queue);
  191. if (arg == NULL) {
  192. break;
  193. }
  194. (arg->fn)(w, arg->arg);
  195. g_free(arg);
  196. }
  197. return FALSE;
  198. }
  199. WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn,
  200. void *arg) {
  201. struct webview_dispatch_arg *context =
  202. (struct webview_dispatch_arg *)g_new(struct webview_dispatch_arg, 1);
  203. context->w = w;
  204. context->arg = arg;
  205. context->fn = fn;
  206. g_async_queue_lock(w->priv.queue);
  207. g_async_queue_push_unlocked(w->priv.queue, context);
  208. if (g_async_queue_length_unlocked(w->priv.queue) == 1) {
  209. gdk_threads_add_idle(webview_dispatch_wrapper, w);
  210. }
  211. g_async_queue_unlock(w->priv.queue);
  212. }
  213. WEBVIEW_API void webview_terminate(struct webview *w) {
  214. w->priv.should_exit = 1;
  215. }
  216. WEBVIEW_API void webview_exit(struct webview *w) { (void)w; }
  217. WEBVIEW_API void webview_print_log(const char *s) {
  218. fprintf(stderr, "%s\n", s);
  219. }