feat(ui) webview impl on separate files (#32)

This commit is contained in:
Lucas Fernandes Nogueira 2019-09-12 16:37:40 -03:00 committed by Tensor-Programming
parent 45ee86a47f
commit 9e6454155f
9 changed files with 2119 additions and 2111 deletions

View File

@ -2,12 +2,12 @@ use std::{env, path::PathBuf};
fn main() {
let tauri_path = PathBuf::from("../../../ui");
let mut tauri_impl_path = tauri_path.clone();
let mut build = cc::Build::new();
build
.include(&tauri_path)
.file("tauri.c")
.flag_if_supported("-std=c11")
.flag_if_supported("-w");
@ -20,11 +20,13 @@ fn main() {
let target = env::var("TARGET").unwrap();
if target.contains("windows") {
tauri_impl_path.push("tauri-windows.c");
build.define("WEBVIEW_WINAPI", None);
for &lib in &["ole32", "comctl32", "oleaut32", "uuid", "gdi32"] {
println!("cargo:rustc-link-lib={}", lib);
}
} else if target.contains("linux") || target.contains("bsd") {
tauri_impl_path.push("tauri-gtk.c");
let webkit = pkg_config::Config::new()
.atleast_version("2.8")
.probe("webkit2gtk-4.0")
@ -35,6 +37,7 @@ fn main() {
}
build.define("WEBVIEW_GTK", None);
} else if target.contains("apple") {
tauri_impl_path.push("tauri-cocoa.m");
build
.define("WEBVIEW_COCOA", None)
.flag("-x")
@ -45,5 +48,9 @@ fn main() {
panic!("unsupported target");
}
build
.file(tauri_impl_path.into_os_string().into_string().unwrap())
.file("tauri.c");
build.compile("tauri");
}

View File

@ -1,3 +1,14 @@
#if defined(WEBVIEW_GTK)
#include "tauri-gtk-webview.h"
#elif defined(WEBVIEW_WINAPI)
#define CINTERFACE
#include "tauri-windows-webview.h"
#elif defined(WEBVIEW_COCOA)
#include "tauri-cocoa-webview.h"
#else
#error "Define one of: WEBVIEW_GTK, WEBVIEW_COCOA or WEBVIEW_WINAPI"
#endif
#define WEBVIEW_IMPLEMENTATION
#include "tauri.h"

11
ui/tauri-cocoa-webview.h Normal file
View File

@ -0,0 +1,11 @@
#include <objc/objc-runtime.h>
#include <CoreGraphics/CoreGraphics.h>
#include <limits.h>
struct webview_priv {
id pool;
id window;
id webview;
id windowDelegate;
int should_exit;
};

636
ui/tauri-cocoa.m Normal file
View File

@ -0,0 +1,636 @@
#include "tauri-cocoa-webview.h"
#include "tauri.h"
#define NSAlertStyleWarning 0
#define NSAlertStyleCritical 2
#define NSWindowStyleMaskResizable 8
#define NSWindowStyleMaskMiniaturizable 4
#define NSWindowStyleMaskTitled 1
#define NSWindowStyleMaskClosable 2
#define NSWindowStyleMaskFullScreen (1 << 14)
#define NSViewWidthSizable 2
#define NSViewHeightSizable 16
#define NSBackingStoreBuffered 2
#define NSEventMaskAny ULONG_MAX
#define NSEventModifierFlagCommand (1 << 20)
#define NSEventModifierFlagOption (1 << 19)
#define NSAlertStyleInformational 1
#define NSAlertFirstButtonReturn 1000
#define WKNavigationActionPolicyDownload 2
#define NSModalResponseOK 1
#define WKNavigationActionPolicyDownload 2
#define WKNavigationResponsePolicyAllow 1
#define WKUserScriptInjectionTimeAtDocumentStart 0
#define NSApplicationActivationPolicyRegular 0
static id get_nsstring(const char *c_str) {
return objc_msgSend((id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"), c_str);
}
static id create_menu_item(id title, const char *action, const char *key) {
id item =
objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc"));
objc_msgSend(item, sel_registerName("initWithTitle:action:keyEquivalent:"),
title, sel_registerName(action), get_nsstring(key));
objc_msgSend(item, sel_registerName("autorelease"));
return item;
}
static void webview_window_will_close(id self, SEL cmd, id notification) {
struct webview *w =
(struct webview *)objc_getAssociatedObject(self, "webview");
webview_terminate(w);
}
static void webview_external_invoke(id self, SEL cmd, id contentController,
id message) {
struct webview *w =
(struct webview *)objc_getAssociatedObject(contentController, "webview");
if (w == NULL || w->external_invoke_cb == NULL) {
return;
}
w->external_invoke_cb(w, (const char *)objc_msgSend(
objc_msgSend(message, sel_registerName("body")),
sel_registerName("UTF8String")));
}
static void run_open_panel(id self, SEL cmd, id webView, id parameters,
id frame, void (^completionHandler)(id)) {
id openPanel = objc_msgSend((id)objc_getClass("NSOpenPanel"),
sel_registerName("openPanel"));
objc_msgSend(
openPanel, sel_registerName("setAllowsMultipleSelection:"),
objc_msgSend(parameters, sel_registerName("allowsMultipleSelection")));
objc_msgSend(openPanel, sel_registerName("setCanChooseFiles:"), 1);
objc_msgSend(
openPanel, sel_registerName("beginWithCompletionHandler:"), ^(id result) {
if (result == (id)NSModalResponseOK) {
completionHandler(objc_msgSend(openPanel, sel_registerName("URLs")));
} else {
completionHandler(nil);
}
});
}
static void run_save_panel(id self, SEL cmd, id download, id filename,
void (^completionHandler)(int allowOverwrite,
id destination)) {
id savePanel = objc_msgSend((id)objc_getClass("NSSavePanel"),
sel_registerName("savePanel"));
objc_msgSend(savePanel, sel_registerName("setCanCreateDirectories:"), 1);
objc_msgSend(savePanel, sel_registerName("setNameFieldStringValue:"),
filename);
objc_msgSend(savePanel, sel_registerName("beginWithCompletionHandler:"),
^(id result) {
if (result == (id)NSModalResponseOK) {
id url = objc_msgSend(savePanel, sel_registerName("URL"));
id path = objc_msgSend(url, sel_registerName("path"));
completionHandler(1, path);
} else {
completionHandler(NO, nil);
}
});
}
static void run_confirmation_panel(id self, SEL cmd, id webView, id message,
id frame, void (^completionHandler)(bool)) {
id alert =
objc_msgSend((id)objc_getClass("NSAlert"), sel_registerName("new"));
objc_msgSend(alert, sel_registerName("setIcon:"),
objc_msgSend((id)objc_getClass("NSImage"),
sel_registerName("imageNamed:"),
get_nsstring("NSCaution")));
objc_msgSend(alert, sel_registerName("setShowsHelp:"), 0);
objc_msgSend(alert, sel_registerName("setInformativeText:"), message);
objc_msgSend(alert, sel_registerName("addButtonWithTitle:"),
get_nsstring("OK"));
objc_msgSend(alert, sel_registerName("addButtonWithTitle:"),
get_nsstring("Cancel"));
if (objc_msgSend(alert, sel_registerName("runModal")) ==
(id)NSAlertFirstButtonReturn) {
completionHandler(true);
} else {
completionHandler(false);
}
objc_msgSend(alert, sel_registerName("release"));
}
static void run_alert_panel(id self, SEL cmd, id webView, id message, id frame,
void (^completionHandler)(void)) {
id alert =
objc_msgSend((id)objc_getClass("NSAlert"), sel_registerName("new"));
objc_msgSend(alert, sel_registerName("setIcon:"),
objc_msgSend((id)objc_getClass("NSImage"),
sel_registerName("imageNamed:"),
get_nsstring("NSCaution")));
objc_msgSend(alert, sel_registerName("setShowsHelp:"), 0);
objc_msgSend(alert, sel_registerName("setInformativeText:"), message);
objc_msgSend(alert, sel_registerName("addButtonWithTitle:"),
get_nsstring("OK"));
objc_msgSend(alert, sel_registerName("runModal"));
objc_msgSend(alert, sel_registerName("release"));
completionHandler();
}
static void download_failed(id self, SEL cmd, id download, id error) {
printf("%s",
(const char *)objc_msgSend(
objc_msgSend(error, sel_registerName("localizedDescription")),
sel_registerName("UTF8String")));
}
static void make_nav_policy_decision(id self, SEL cmd, id webView, id response,
void (^decisionHandler)(int)) {
if (objc_msgSend(response, sel_registerName("canShowMIMEType")) == 0) {
decisionHandler(WKNavigationActionPolicyDownload);
} else {
decisionHandler(WKNavigationResponsePolicyAllow);
}
}
WEBVIEW_API int webview_init(struct webview *w) {
w->priv.pool = objc_msgSend((id)objc_getClass("NSAutoreleasePool"),
sel_registerName("new"));
objc_msgSend((id)objc_getClass("NSApplication"),
sel_registerName("sharedApplication"));
Class __WKScriptMessageHandler = objc_allocateClassPair(
objc_getClass("NSObject"), "__WKScriptMessageHandler", 0);
class_addMethod(
__WKScriptMessageHandler,
sel_registerName("userContentController:didReceiveScriptMessage:"),
(IMP)webview_external_invoke, "v@:@@");
objc_registerClassPair(__WKScriptMessageHandler);
id scriptMessageHandler =
objc_msgSend((id)__WKScriptMessageHandler, sel_registerName("new"));
/***
_WKDownloadDelegate is an undocumented/private protocol with methods called
from WKNavigationDelegate
References:
https://github.com/WebKit/webkit/blob/master/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.h
https://github.com/WebKit/webkit/blob/master/Source/WebKit/UIProcess/API/Cocoa/_WKDownloadDelegate.h
https://github.com/WebKit/webkit/blob/master/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm
***/
Class __WKDownloadDelegate = objc_allocateClassPair(
objc_getClass("NSObject"), "__WKDownloadDelegate", 0);
class_addMethod(
__WKDownloadDelegate,
sel_registerName("_download:decideDestinationWithSuggestedFilename:"
"completionHandler:"),
(IMP)run_save_panel, "v@:@@?");
class_addMethod(__WKDownloadDelegate,
sel_registerName("_download:didFailWithError:"),
(IMP)download_failed, "v@:@@");
objc_registerClassPair(__WKDownloadDelegate);
id downloadDelegate =
objc_msgSend((id)__WKDownloadDelegate, sel_registerName("new"));
Class __WKPreferences = objc_allocateClassPair(objc_getClass("WKPreferences"),
"__WKPreferences", 0);
objc_property_attribute_t type = {"T", "c"};
objc_property_attribute_t ownership = {"N", ""};
objc_property_attribute_t attrs[] = {type, ownership};
class_replaceProperty(__WKPreferences, "developerExtrasEnabled", attrs, 2);
objc_registerClassPair(__WKPreferences);
id wkPref = objc_msgSend((id)__WKPreferences, sel_registerName("new"));
objc_msgSend(wkPref, sel_registerName("setValue:forKey:"),
objc_msgSend((id)objc_getClass("NSNumber"),
sel_registerName("numberWithBool:"), !!w->debug),
objc_msgSend((id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"),
"developerExtrasEnabled"));
id userController = objc_msgSend((id)objc_getClass("WKUserContentController"),
sel_registerName("new"));
objc_setAssociatedObject(userController, "webview", (id)(w),
OBJC_ASSOCIATION_ASSIGN);
objc_msgSend(
userController, sel_registerName("addScriptMessageHandler:name:"),
scriptMessageHandler,
objc_msgSend((id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"), "invoke"));
/***
In order to maintain compatibility with the other 'webviews' we need to
override window.external.invoke to call
webkit.messageHandlers.invoke.postMessage
***/
id windowExternalOverrideScript = objc_msgSend(
(id)objc_getClass("WKUserScript"), sel_registerName("alloc"));
objc_msgSend(
windowExternalOverrideScript,
sel_registerName("initWithSource:injectionTime:forMainFrameOnly:"),
get_nsstring("window.external = this; invoke = function(arg){ "
"webkit.messageHandlers.invoke.postMessage(arg); };"),
WKUserScriptInjectionTimeAtDocumentStart, 0);
objc_msgSend(userController, sel_registerName("addUserScript:"),
windowExternalOverrideScript);
id config = objc_msgSend((id)objc_getClass("WKWebViewConfiguration"),
sel_registerName("new"));
id processPool = objc_msgSend(config, sel_registerName("processPool"));
objc_msgSend(processPool, sel_registerName("_setDownloadDelegate:"),
downloadDelegate);
objc_msgSend(config, sel_registerName("setProcessPool:"), processPool);
objc_msgSend(config, sel_registerName("setUserContentController:"),
userController);
objc_msgSend(config, sel_registerName("setPreferences:"), wkPref);
Class __NSWindowDelegate = objc_allocateClassPair(objc_getClass("NSObject"),
"__NSWindowDelegate", 0);
class_addProtocol(__NSWindowDelegate, objc_getProtocol("NSWindowDelegate"));
class_replaceMethod(__NSWindowDelegate, sel_registerName("windowWillClose:"),
(IMP)webview_window_will_close, "v@:@");
objc_registerClassPair(__NSWindowDelegate);
w->priv.windowDelegate =
objc_msgSend((id)__NSWindowDelegate, sel_registerName("new"));
objc_setAssociatedObject(w->priv.windowDelegate, "webview", (id)(w),
OBJC_ASSOCIATION_ASSIGN);
id nsTitle =
objc_msgSend((id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"), w->title);
CGRect r = CGRectMake(0, 0, w->width, w->height);
unsigned int style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
NSWindowStyleMaskMiniaturizable;
if (w->resizable) {
style = style | NSWindowStyleMaskResizable;
}
w->priv.window =
objc_msgSend((id)objc_getClass("NSWindow"), sel_registerName("alloc"));
objc_msgSend(w->priv.window,
sel_registerName("initWithContentRect:styleMask:backing:defer:"),
r, style, NSBackingStoreBuffered, 0);
objc_msgSend(w->priv.window, sel_registerName("autorelease"));
objc_msgSend(w->priv.window, sel_registerName("setTitle:"), nsTitle);
objc_msgSend(w->priv.window, sel_registerName("setDelegate:"),
w->priv.windowDelegate);
objc_msgSend(w->priv.window, sel_registerName("center"));
Class __WKUIDelegate =
objc_allocateClassPair(objc_getClass("NSObject"), "__WKUIDelegate", 0);
class_addProtocol(__WKUIDelegate, objc_getProtocol("WKUIDelegate"));
class_addMethod(__WKUIDelegate,
sel_registerName("webView:runOpenPanelWithParameters:"
"initiatedByFrame:completionHandler:"),
(IMP)run_open_panel, "v@:@@@?");
class_addMethod(__WKUIDelegate,
sel_registerName("webView:runJavaScriptAlertPanelWithMessage:"
"initiatedByFrame:completionHandler:"),
(IMP)run_alert_panel, "v@:@@@?");
class_addMethod(
__WKUIDelegate,
sel_registerName("webView:runJavaScriptConfirmPanelWithMessage:"
"initiatedByFrame:completionHandler:"),
(IMP)run_confirmation_panel, "v@:@@@?");
objc_registerClassPair(__WKUIDelegate);
id uiDel = objc_msgSend((id)__WKUIDelegate, sel_registerName("new"));
Class __WKNavigationDelegate = objc_allocateClassPair(
objc_getClass("NSObject"), "__WKNavigationDelegate", 0);
class_addProtocol(__WKNavigationDelegate,
objc_getProtocol("WKNavigationDelegate"));
class_addMethod(
__WKNavigationDelegate,
sel_registerName(
"webView:decidePolicyForNavigationResponse:decisionHandler:"),
(IMP)make_nav_policy_decision, "v@:@@?");
objc_registerClassPair(__WKNavigationDelegate);
id navDel = objc_msgSend((id)__WKNavigationDelegate, sel_registerName("new"));
w->priv.webview =
objc_msgSend((id)objc_getClass("WKWebView"), sel_registerName("alloc"));
objc_msgSend(w->priv.webview,
sel_registerName("initWithFrame:configuration:"), r, config);
objc_msgSend(w->priv.webview, sel_registerName("setUIDelegate:"), uiDel);
objc_msgSend(w->priv.webview, sel_registerName("setNavigationDelegate:"),
navDel);
id nsURL = objc_msgSend((id)objc_getClass("NSURL"),
sel_registerName("URLWithString:"),
get_nsstring(webview_check_url(w->url)));
objc_msgSend(w->priv.webview, sel_registerName("loadRequest:"),
objc_msgSend((id)objc_getClass("NSURLRequest"),
sel_registerName("requestWithURL:"), nsURL));
objc_msgSend(w->priv.webview, sel_registerName("setAutoresizesSubviews:"), 1);
objc_msgSend(w->priv.webview, sel_registerName("setAutoresizingMask:"),
(NSViewWidthSizable | NSViewHeightSizable));
objc_msgSend(objc_msgSend(w->priv.window, sel_registerName("contentView")),
sel_registerName("addSubview:"), w->priv.webview);
objc_msgSend(w->priv.window, sel_registerName("orderFrontRegardless"));
objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"),
sel_registerName("sharedApplication")),
sel_registerName("setActivationPolicy:"),
NSApplicationActivationPolicyRegular);
objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"),
sel_registerName("sharedApplication")),
sel_registerName("finishLaunching"));
objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"),
sel_registerName("sharedApplication")),
sel_registerName("activateIgnoringOtherApps:"), 1);
id menubar =
objc_msgSend((id)objc_getClass("NSMenu"), sel_registerName("alloc"));
objc_msgSend(menubar, sel_registerName("initWithTitle:"), get_nsstring(""));
objc_msgSend(menubar, sel_registerName("autorelease"));
id appName = objc_msgSend(objc_msgSend((id)objc_getClass("NSProcessInfo"),
sel_registerName("processInfo")),
sel_registerName("processName"));
id appMenuItem =
objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc"));
objc_msgSend(appMenuItem,
sel_registerName("initWithTitle:action:keyEquivalent:"), appName,
NULL, get_nsstring(""));
id appMenu =
objc_msgSend((id)objc_getClass("NSMenu"), sel_registerName("alloc"));
objc_msgSend(appMenu, sel_registerName("initWithTitle:"), appName);
objc_msgSend(appMenu, sel_registerName("autorelease"));
objc_msgSend(appMenuItem, sel_registerName("setSubmenu:"), appMenu);
objc_msgSend(menubar, sel_registerName("addItem:"), appMenuItem);
id title =
objc_msgSend(get_nsstring("Hide "),
sel_registerName("stringByAppendingString:"), appName);
id item = create_menu_item(title, "hide:", "h");
objc_msgSend(appMenu, sel_registerName("addItem:"), item);
item = create_menu_item(get_nsstring("Hide Others"),
"hideOtherApplications:", "h");
objc_msgSend(item, sel_registerName("setKeyEquivalentModifierMask:"),
(NSEventModifierFlagOption | NSEventModifierFlagCommand));
objc_msgSend(appMenu, sel_registerName("addItem:"), item);
item =
create_menu_item(get_nsstring("Show All"), "unhideAllApplications:", "");
objc_msgSend(appMenu, sel_registerName("addItem:"), item);
objc_msgSend(appMenu, sel_registerName("addItem:"),
objc_msgSend((id)objc_getClass("NSMenuItem"),
sel_registerName("separatorItem")));
title = objc_msgSend(get_nsstring("Quit "),
sel_registerName("stringByAppendingString:"), appName);
item = create_menu_item(title, "terminate:", "q");
objc_msgSend(appMenu, sel_registerName("addItem:"), item);
id editMenuItem =
objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc"));
objc_msgSend(editMenuItem,
sel_registerName("initWithTitle:action:keyEquivalent:"), get_nsstring("Edit"),
NULL, get_nsstring(""));
/***
Edit menu
***/
id editMenu =
objc_msgSend((id)objc_getClass("NSMenu"), sel_registerName("alloc"));
objc_msgSend(editMenu, sel_registerName("initWithTitle:"), get_nsstring("Edit"));
objc_msgSend(editMenu, sel_registerName("autorelease"));
objc_msgSend(editMenuItem, sel_registerName("setSubmenu:"), editMenu);
objc_msgSend(menubar, sel_registerName("addItem:"), editMenuItem);
item = create_menu_item(get_nsstring("Undo"), "undo:", "z");
objc_msgSend(editMenu, sel_registerName("addItem:"), item);
item = create_menu_item(get_nsstring("Redo"), "redo:", "y");
objc_msgSend(editMenu, sel_registerName("addItem:"), item);
item = objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("separatorItem"));
objc_msgSend(editMenu, sel_registerName("addItem:"), item);
item = create_menu_item(get_nsstring("Cut"), "cut:", "x");
objc_msgSend(editMenu, sel_registerName("addItem:"), item);
item = create_menu_item(get_nsstring("Copy"), "copy:", "c");
objc_msgSend(editMenu, sel_registerName("addItem:"), item);
item = create_menu_item(get_nsstring("Paste"), "paste:", "v");
objc_msgSend(editMenu, sel_registerName("addItem:"), item);
item = create_menu_item(get_nsstring("Select All"), "selectAll:", "a");
objc_msgSend(editMenu, sel_registerName("addItem:"), item);
/***
Finalize menubar
***/
objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"),
sel_registerName("sharedApplication")),
sel_registerName("setMainMenu:"), menubar);
w->priv.should_exit = 0;
return 0;
}
WEBVIEW_API int webview_loop(struct webview *w, int blocking) {
id until = (blocking ? objc_msgSend((id)objc_getClass("NSDate"),
sel_registerName("distantFuture"))
: objc_msgSend((id)objc_getClass("NSDate"),
sel_registerName("distantPast")));
id event = objc_msgSend(
objc_msgSend((id)objc_getClass("NSApplication"),
sel_registerName("sharedApplication")),
sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"),
ULONG_MAX, until,
objc_msgSend((id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"),
"kCFRunLoopDefaultMode"),
true);
if (event) {
objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"),
sel_registerName("sharedApplication")),
sel_registerName("sendEvent:"), event);
}
return w->priv.should_exit;
}
WEBVIEW_API int webview_eval(struct webview *w, const char *js) {
objc_msgSend(w->priv.webview,
sel_registerName("evaluateJavaScript:completionHandler:"),
get_nsstring(js), NULL);
return 0;
}
WEBVIEW_API void webview_set_title(struct webview *w, const char *title) {
objc_msgSend(w->priv.window, sel_registerName("setTitle"),
get_nsstring(title));
}
WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) {
unsigned long windowStyleMask = (unsigned long)objc_msgSend(
w->priv.window, sel_registerName("styleMask"));
int b = (((windowStyleMask & NSWindowStyleMaskFullScreen) ==
NSWindowStyleMaskFullScreen)
? 1
: 0);
if (b != fullscreen) {
objc_msgSend(w->priv.window, sel_registerName("toggleFullScreen:"), NULL);
}
}
WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g,
uint8_t b, uint8_t a) {
id color = objc_msgSend((id)objc_getClass("NSColor"),
sel_registerName("colorWithRed:green:blue:alpha:"),
(float)r / 255.0, (float)g / 255.0, (float)b / 255.0,
(float)a / 255.0);
objc_msgSend(w->priv.window, sel_registerName("setBackgroundColor:"), color);
if (0.5 >= ((r / 255.0 * 299.0) + (g / 255.0 * 587.0) + (b / 255.0 * 114.0)) /
1000.0) {
objc_msgSend(w->priv.window, sel_registerName("setAppearance:"),
objc_msgSend((id)objc_getClass("NSAppearance"),
sel_registerName("appearanceNamed:"),
get_nsstring("NSAppearanceNameVibrantDark")));
} else {
objc_msgSend(w->priv.window, sel_registerName("setAppearance:"),
objc_msgSend((id)objc_getClass("NSAppearance"),
sel_registerName("appearanceNamed:"),
get_nsstring("NSAppearanceNameVibrantLight")));
}
objc_msgSend(w->priv.window, sel_registerName("setOpaque:"), 0);
objc_msgSend(w->priv.window,
sel_registerName("setTitlebarAppearsTransparent:"), 1);
objc_msgSend(w->priv.webview, sel_registerName("_setDrawsBackground:"), 0);
}
WEBVIEW_API void webview_dialog(struct webview *w,
enum webview_dialog_type dlgtype, int flags,
const char *title, const char *arg,
char *result, size_t resultsz) {
if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ||
dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) {
id panel = (id)objc_getClass("NSSavePanel");
if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN) {
id openPanel = objc_msgSend((id)objc_getClass("NSOpenPanel"),
sel_registerName("openPanel"));
if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY) {
objc_msgSend(openPanel, sel_registerName("setCanChooseFiles:"), 0);
objc_msgSend(openPanel, sel_registerName("setCanChooseDirectories:"),
1);
} else {
objc_msgSend(openPanel, sel_registerName("setCanChooseFiles:"), 1);
objc_msgSend(openPanel, sel_registerName("setCanChooseDirectories:"),
0);
}
objc_msgSend(openPanel, sel_registerName("setResolvesAliases:"), 0);
objc_msgSend(openPanel, sel_registerName("setAllowsMultipleSelection:"),
0);
panel = openPanel;
} else {
panel = objc_msgSend((id)objc_getClass("NSSavePanel"),
sel_registerName("savePanel"));
}
objc_msgSend(panel, sel_registerName("setCanCreateDirectories:"), 1);
objc_msgSend(panel, sel_registerName("setShowsHiddenFiles:"), 1);
objc_msgSend(panel, sel_registerName("setExtensionHidden:"), 0);
objc_msgSend(panel, sel_registerName("setCanSelectHiddenExtension:"), 0);
objc_msgSend(panel, sel_registerName("setTreatsFilePackagesAsDirectories:"),
1);
objc_msgSend(
panel, sel_registerName("beginSheetModalForWindow:completionHandler:"),
w->priv.window, ^(id result) {
objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"),
sel_registerName("sharedApplication")),
sel_registerName("stopModalWithCode:"), result);
});
if (objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"),
sel_registerName("sharedApplication")),
sel_registerName("runModalForWindow:"),
panel) == (id)NSModalResponseOK) {
id url = objc_msgSend(panel, sel_registerName("URL"));
id path = objc_msgSend(url, sel_registerName("path"));
const char *filename =
(const char *)objc_msgSend(path, sel_registerName("UTF8String"));
strlcpy(result, filename, resultsz);
}
} else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) {
id a = objc_msgSend((id)objc_getClass("NSAlert"), sel_registerName("new"));
switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) {
case WEBVIEW_DIALOG_FLAG_INFO:
objc_msgSend(a, sel_registerName("setAlertStyle:"),
NSAlertStyleInformational);
break;
case WEBVIEW_DIALOG_FLAG_WARNING:
printf("Warning\n");
objc_msgSend(a, sel_registerName("setAlertStyle:"), NSAlertStyleWarning);
break;
case WEBVIEW_DIALOG_FLAG_ERROR:
printf("Error\n");
objc_msgSend(a, sel_registerName("setAlertStyle:"), NSAlertStyleCritical);
break;
}
objc_msgSend(a, sel_registerName("setShowsHelp:"), 0);
objc_msgSend(a, sel_registerName("setShowsSuppressionButton:"), 0);
objc_msgSend(a, sel_registerName("setMessageText:"), get_nsstring(title));
objc_msgSend(a, sel_registerName("setInformativeText:"), get_nsstring(arg));
objc_msgSend(a, sel_registerName("addButtonWithTitle:"),
get_nsstring("OK"));
objc_msgSend(a, sel_registerName("runModal"));
objc_msgSend(a, sel_registerName("release"));
}
}
static void webview_dispatch_cb(void *arg) {
struct webview_dispatch_arg *context = (struct webview_dispatch_arg *)arg;
(context->fn)(context->w, context->arg);
free(context);
}
WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn,
void *arg) {
struct webview_dispatch_arg *context = (struct webview_dispatch_arg *)malloc(
sizeof(struct webview_dispatch_arg));
context->w = w;
context->arg = arg;
context->fn = fn;
dispatch_async_f(dispatch_get_main_queue(), context, webview_dispatch_cb);
}
WEBVIEW_API void webview_terminate(struct webview *w) {
w->priv.should_exit = 1;
}
WEBVIEW_API void webview_exit(struct webview *w) {
id app = objc_msgSend((id)objc_getClass("NSApplication"),
sel_registerName("sharedApplication"));
objc_msgSend(app, sel_registerName("terminate:"), app);
}
WEBVIEW_API void webview_print_log(const char *s) { printf("%s\n", s); }

14
ui/tauri-gtk-webview.h Normal file
View File

@ -0,0 +1,14 @@
#include <JavaScriptCore/JavaScript.h>
#include <gtk/gtk.h>
#include <webkit2/webkit2.h>
struct webview_priv {
GtkWidget *window;
GtkWidget *scroller;
GtkWidget *webview;
GtkWidget *inspector_window;
GAsyncQueue *queue;
int ready;
int js_busy;
int should_exit;
};

244
ui/tauri-gtk.c Normal file
View File

@ -0,0 +1,244 @@
#include "tauri-gtk-webview.h"
#include "tauri.h"
static void external_message_received_cb(WebKitUserContentManager *m,
WebKitJavascriptResult *r,
gpointer arg) {
(void)m;
struct webview *w = (struct webview *)arg;
if (w->external_invoke_cb == NULL) {
return;
}
JSGlobalContextRef context = webkit_javascript_result_get_global_context(r);
JSValueRef value = webkit_javascript_result_get_value(r);
JSStringRef js = JSValueToStringCopy(context, value, NULL);
size_t n = JSStringGetMaximumUTF8CStringSize(js);
char *s = g_new(char, n);
JSStringGetUTF8CString(js, s, n);
w->external_invoke_cb(w, s);
JSStringRelease(js);
g_free(s);
}
static void webview_load_changed_cb(WebKitWebView *webview,
WebKitLoadEvent event, gpointer arg) {
(void)webview;
struct webview *w = (struct webview *)arg;
if (event == WEBKIT_LOAD_FINISHED) {
w->priv.ready = 1;
}
}
static void webview_destroy_cb(GtkWidget *widget, gpointer arg) {
(void)widget;
struct webview *w = (struct webview *)arg;
webview_terminate(w);
}
static gboolean webview_context_menu_cb(WebKitWebView *webview,
GtkWidget *default_menu,
WebKitHitTestResult *hit_test_result,
gboolean triggered_with_keyboard,
gpointer userdata) {
(void)webview;
(void)default_menu;
(void)hit_test_result;
(void)triggered_with_keyboard;
(void)userdata;
return TRUE;
}
WEBVIEW_API int webview_init(struct webview *w) {
if (gtk_init_check(0, NULL) == FALSE) {
return -1;
}
w->priv.ready = 0;
w->priv.should_exit = 0;
w->priv.queue = g_async_queue_new();
w->priv.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(w->priv.window), w->title);
if (w->resizable) {
gtk_window_set_default_size(GTK_WINDOW(w->priv.window), w->width,
w->height);
} else {
gtk_widget_set_size_request(w->priv.window, w->width, w->height);
}
gtk_window_set_resizable(GTK_WINDOW(w->priv.window), !!w->resizable);
gtk_window_set_position(GTK_WINDOW(w->priv.window), GTK_WIN_POS_CENTER);
w->priv.scroller = gtk_scrolled_window_new(NULL, NULL);
gtk_container_add(GTK_CONTAINER(w->priv.window), w->priv.scroller);
WebKitUserContentManager *m = webkit_user_content_manager_new();
webkit_user_content_manager_register_script_message_handler(m, "external");
g_signal_connect(m, "script-message-received::external",
G_CALLBACK(external_message_received_cb), w);
w->priv.webview = webkit_web_view_new_with_user_content_manager(m);
webkit_web_view_load_uri(WEBKIT_WEB_VIEW(w->priv.webview),
webview_check_url(w->url));
g_signal_connect(G_OBJECT(w->priv.webview), "load-changed",
G_CALLBACK(webview_load_changed_cb), w);
gtk_container_add(GTK_CONTAINER(w->priv.scroller), w->priv.webview);
if (w->debug) {
WebKitSettings *settings =
webkit_web_view_get_settings(WEBKIT_WEB_VIEW(w->priv.webview));
webkit_settings_set_enable_write_console_messages_to_stdout(settings, true);
webkit_settings_set_enable_developer_extras(settings, true);
} else {
g_signal_connect(G_OBJECT(w->priv.webview), "context-menu",
G_CALLBACK(webview_context_menu_cb), w);
}
gtk_widget_show_all(w->priv.window);
webkit_web_view_run_javascript(
WEBKIT_WEB_VIEW(w->priv.webview),
"window.external={invoke:function(x){"
"window.webkit.messageHandlers.external.postMessage(x);}}",
NULL, NULL, NULL);
g_signal_connect(G_OBJECT(w->priv.window), "destroy",
G_CALLBACK(webview_destroy_cb), w);
return 0;
}
WEBVIEW_API int webview_loop(struct webview *w, int blocking) {
gtk_main_iteration_do(blocking);
return w->priv.should_exit;
}
WEBVIEW_API void webview_set_title(struct webview *w, const char *title) {
gtk_window_set_title(GTK_WINDOW(w->priv.window), title);
}
WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) {
if (fullscreen) {
gtk_window_fullscreen(GTK_WINDOW(w->priv.window));
} else {
gtk_window_unfullscreen(GTK_WINDOW(w->priv.window));
}
}
WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g,
uint8_t b, uint8_t a) {
GdkRGBA color = {r / 255.0, g / 255.0, b / 255.0, a / 255.0};
webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(w->priv.webview),
&color);
}
WEBVIEW_API void webview_dialog(struct webview *w,
enum webview_dialog_type dlgtype, int flags,
const char *title, const char *arg,
char *result, size_t resultsz) {
GtkWidget *dlg;
if (result != NULL) {
result[0] = '\0';
}
if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ||
dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) {
dlg = gtk_file_chooser_dialog_new(
title, GTK_WINDOW(w->priv.window),
(dlgtype == WEBVIEW_DIALOG_TYPE_OPEN
? (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY
? GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
: GTK_FILE_CHOOSER_ACTION_OPEN)
: GTK_FILE_CHOOSER_ACTION_SAVE),
"_Cancel", GTK_RESPONSE_CANCEL,
(dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ? "_Open" : "_Save"),
GTK_RESPONSE_ACCEPT, NULL);
gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), FALSE);
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dlg), FALSE);
gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dlg), TRUE);
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dlg), TRUE);
gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dlg), TRUE);
gint response = gtk_dialog_run(GTK_DIALOG(dlg));
if (response == GTK_RESPONSE_ACCEPT) {
gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg));
g_strlcpy(result, filename, resultsz);
g_free(filename);
}
gtk_widget_destroy(dlg);
} else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) {
GtkMessageType type = GTK_MESSAGE_OTHER;
switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) {
case WEBVIEW_DIALOG_FLAG_INFO:
type = GTK_MESSAGE_INFO;
break;
case WEBVIEW_DIALOG_FLAG_WARNING:
type = GTK_MESSAGE_WARNING;
break;
case WEBVIEW_DIALOG_FLAG_ERROR:
type = GTK_MESSAGE_ERROR;
break;
}
dlg = gtk_message_dialog_new(GTK_WINDOW(w->priv.window), GTK_DIALOG_MODAL,
type, GTK_BUTTONS_OK, "%s", title);
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dlg), "%s",
arg);
gtk_dialog_run(GTK_DIALOG(dlg));
gtk_widget_destroy(dlg);
}
}
static void webview_eval_finished(GObject *object, GAsyncResult *result,
gpointer userdata) {
(void)object;
(void)result;
struct webview *w = (struct webview *)userdata;
w->priv.js_busy = 0;
}
WEBVIEW_API int webview_eval(struct webview *w, const char *js) {
while (w->priv.ready == 0) {
g_main_context_iteration(NULL, TRUE);
}
w->priv.js_busy = 1;
webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(w->priv.webview), js, NULL,
webview_eval_finished, w);
while (w->priv.js_busy) {
g_main_context_iteration(NULL, TRUE);
}
return 0;
}
static gboolean webview_dispatch_wrapper(gpointer userdata) {
struct webview *w = (struct webview *)userdata;
for (;;) {
struct webview_dispatch_arg *arg =
(struct webview_dispatch_arg *)g_async_queue_try_pop(w->priv.queue);
if (arg == NULL) {
break;
}
(arg->fn)(w, arg->arg);
g_free(arg);
}
return FALSE;
}
WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn,
void *arg) {
struct webview_dispatch_arg *context =
(struct webview_dispatch_arg *)g_new(struct webview_dispatch_arg, 1);
context->w = w;
context->arg = arg;
context->fn = fn;
g_async_queue_lock(w->priv.queue);
g_async_queue_push_unlocked(w->priv.queue, context);
if (g_async_queue_length_unlocked(w->priv.queue) == 1) {
gdk_threads_add_idle(webview_dispatch_wrapper, w);
}
g_async_queue_unlock(w->priv.queue);
}
WEBVIEW_API void webview_terminate(struct webview *w) {
w->priv.should_exit = 1;
}
WEBVIEW_API void webview_exit(struct webview *w) { (void)w; }
WEBVIEW_API void webview_print_log(const char *s) {
fprintf(stderr, "%s\n", s);
}

View File

@ -0,0 +1,19 @@
#define CINTERFACE
#include <windows.h>
#include <commctrl.h>
#include <exdisp.h>
#include <mshtmhst.h>
#include <mshtml.h>
#include <shobjidl.h>
#include <stdio.h>
struct webview_priv {
HWND hwnd;
IOleObject **browser;
BOOL is_fullscreen;
DWORD saved_style;
DWORD saved_ex_style;
RECT saved_rect;
};

1175
ui/tauri-windows.c Normal file

File diff suppressed because it is too large Load Diff

2111
ui/tauri.h

File diff suppressed because it is too large Load Diff