mirror of
https://github.com/taigrr/wails.git
synced 2026-04-02 13:19:00 -07:00
Compare commits
5 Commits
v2.0.0-bet
...
update-web
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f73506c496 | ||
|
|
cf18086b84 | ||
|
|
91269bc4a0 | ||
|
|
b3061156a8 | ||
|
|
7df86cc8e6 |
27
lib/renderer/webview/memory.go
Normal file
27
lib/renderer/webview/memory.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package webview
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdlib.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
// This is our allocated C Memory
|
||||||
|
var memory []unsafe.Pointer
|
||||||
|
|
||||||
|
func saveMemoryReference(mem unsafe.Pointer) {
|
||||||
|
memory = append(memory, mem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func freeMemory() {
|
||||||
|
for _, mem := range memory {
|
||||||
|
C.free(mem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func string2CString(str string) *C.char {
|
||||||
|
result := C.CString(str)
|
||||||
|
saveMemoryReference(unsafe.Pointer(result))
|
||||||
|
return result
|
||||||
|
}
|
||||||
356
lib/renderer/webview/webview.go
Executable file → Normal file
356
lib/renderer/webview/webview.go
Executable file → Normal file
@@ -1,166 +1,20 @@
|
|||||||
// Package webview implements Go bindings to https://github.com/zserge/webview C library.
|
|
||||||
// It is a modified version of webview.go from that repository
|
|
||||||
// Bindings closely repeat the C APIs and include both, a simplified
|
|
||||||
// single-function API to just open a full-screen webview window, and a more
|
|
||||||
// advanced and featureful set of APIs, including Go-to-JavaScript bindings.
|
|
||||||
//
|
|
||||||
// The library uses gtk-webkit, Cocoa/Webkit and MSHTML (IE8..11) as a browser
|
|
||||||
// engine and supports Linux, MacOS and Windows 7..10 respectively.
|
|
||||||
//
|
|
||||||
package webview
|
package webview
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux openbsd freebsd CFLAGS: -DWEBVIEW_GTK=1 -Wno-deprecated-declarations
|
|
||||||
#cgo linux openbsd freebsd pkg-config: gtk+-3.0 webkit2gtk-4.0
|
|
||||||
|
|
||||||
#cgo windows CFLAGS: -DWEBVIEW_WINAPI=1 -std=c99
|
|
||||||
#cgo windows LDFLAGS: -lole32 -lcomctl32 -loleaut32 -luuid -lgdi32
|
|
||||||
|
|
||||||
#cgo darwin CFLAGS: -DWEBVIEW_COCOA=1 -x objective-c
|
|
||||||
#cgo darwin LDFLAGS: -framework Cocoa -framework WebKit
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#define WEBVIEW_STATIC
|
|
||||||
#define WEBVIEW_IMPLEMENTATION
|
|
||||||
#include "webview.h"
|
|
||||||
|
|
||||||
extern void _webviewExternalInvokeCallback(void *, void *);
|
|
||||||
|
|
||||||
static inline void CgoWebViewFree(void *w) {
|
|
||||||
free((void *)((struct webview *)w)->title);
|
|
||||||
free((void *)((struct webview *)w)->url);
|
|
||||||
free(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void *CgoWebViewCreate(int width, int height, char *title, char *url, int resizable, int debug) {
|
|
||||||
struct webview *w = (struct webview *) calloc(1, sizeof(*w));
|
|
||||||
w->width = width;
|
|
||||||
w->height = height;
|
|
||||||
w->title = title;
|
|
||||||
w->url = url;
|
|
||||||
w->resizable = resizable;
|
|
||||||
w->debug = debug;
|
|
||||||
w->external_invoke_cb = (webview_external_invoke_cb_t) _webviewExternalInvokeCallback;
|
|
||||||
if (webview_init(w) != 0) {
|
|
||||||
CgoWebViewFree(w);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return (void *)w;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int CgoWebViewLoop(void *w, int blocking) {
|
|
||||||
return webview_loop((struct webview *)w, blocking);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void CgoWebViewTerminate(void *w) {
|
|
||||||
webview_terminate((struct webview *)w);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void CgoWebViewExit(void *w) {
|
|
||||||
webview_exit((struct webview *)w);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void CgoWebViewSetTitle(void *w, char *title) {
|
|
||||||
webview_set_title((struct webview *)w, title);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void CgoWebViewSetFullscreen(void *w, int fullscreen) {
|
|
||||||
webview_set_fullscreen((struct webview *)w, fullscreen);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void CgoWebViewSetColor(void *w, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
|
||||||
webview_set_color((struct webview *)w, r, g, b, a);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void CgoDialog(void *w, int dlgtype, int flags,
|
|
||||||
char *title, char *arg, char *res, size_t ressz, char *filter) {
|
|
||||||
webview_dialog(w, dlgtype, flags,
|
|
||||||
(const char*)title, (const char*) arg, res, ressz, filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int CgoWebViewEval(void *w, char *js) {
|
|
||||||
return webview_eval((struct webview *)w, js);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void CgoWebViewInjectCSS(void *w, char *css) {
|
|
||||||
webview_inject_css((struct webview *)w, css);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern void _webviewDispatchGoCallback(void *);
|
|
||||||
static inline void _webview_dispatch_cb(struct webview *w, void *arg) {
|
|
||||||
_webviewDispatchGoCallback(arg);
|
|
||||||
}
|
|
||||||
static inline void CgoWebViewDispatch(void *w, uintptr_t arg) {
|
|
||||||
webview_dispatch((struct webview *)w, _webview_dispatch_cb, (void *)arg);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
import "C"
|
import "C"
|
||||||
import (
|
|
||||||
"errors"
|
// DialogType is an enumeration of all supported system dialog types
|
||||||
"runtime"
|
type DialogType int
|
||||||
"sync"
|
|
||||||
"unsafe"
|
const (
|
||||||
|
// DialogTypeOpen is a system file open dialog
|
||||||
|
DialogTypeOpen DialogType = iota
|
||||||
|
// DialogTypeSave is a system file save dialog
|
||||||
|
DialogTypeSave
|
||||||
|
// DialogTypeAlert is a system alert dialog (message box)
|
||||||
|
DialogTypeAlert
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
// WebView is our webview interface
|
||||||
// Ensure that main.main is called from the main thread
|
|
||||||
runtime.LockOSThread()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open is a simplified API to open a single native window with a full-size webview in
|
|
||||||
// it. It can be helpful if you want to communicate with the core app using XHR
|
|
||||||
// or WebSockets (as opposed to using JavaScript bindings).
|
|
||||||
//
|
|
||||||
// Window appearance can be customized using title, width, height and resizable parameters.
|
|
||||||
// URL must be provided and can user either a http or https protocol, or be a
|
|
||||||
// local file:// URL. On some platforms "data:" URLs are also supported
|
|
||||||
// (Linux/MacOS).
|
|
||||||
func Open(title, url string, w, h int, resizable bool) error {
|
|
||||||
titleStr := C.CString(title)
|
|
||||||
defer C.free(unsafe.Pointer(titleStr))
|
|
||||||
urlStr := C.CString(url)
|
|
||||||
defer C.free(unsafe.Pointer(urlStr))
|
|
||||||
resize := C.int(0)
|
|
||||||
if resizable {
|
|
||||||
resize = C.int(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
r := C.webview(titleStr, urlStr, C.int(w), C.int(h), resize)
|
|
||||||
if r != 0 {
|
|
||||||
return errors.New("failed to create webview")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExternalInvokeCallbackFunc is a function type that is called every time
|
|
||||||
// "window.external.invoke()" is called from JavaScript. Data is the only
|
|
||||||
// obligatory string parameter passed into the "invoke(data)" function from
|
|
||||||
// JavaScript. To pass more complex data serialized JSON or base64 encoded
|
|
||||||
// string can be used.
|
|
||||||
type ExternalInvokeCallbackFunc func(w WebView, data string)
|
|
||||||
|
|
||||||
// Settings is a set of parameters to customize the initial WebView appearance
|
|
||||||
// and behavior. It is passed into the webview.New() constructor.
|
|
||||||
type Settings struct {
|
|
||||||
// WebView main window title
|
|
||||||
Title string
|
|
||||||
// URL to open in a webview
|
|
||||||
URL string
|
|
||||||
// Window width in pixels
|
|
||||||
Width int
|
|
||||||
// Window height in pixels
|
|
||||||
Height int
|
|
||||||
// Allows/disallows window resizing
|
|
||||||
Resizable bool
|
|
||||||
// Enable debugging tools (Linux/BSD/MacOS, on Windows use Firebug)
|
|
||||||
Debug bool
|
|
||||||
// A callback that is executed when JavaScript calls "window.external.invoke()"
|
|
||||||
ExternalInvokeCallback ExternalInvokeCallbackFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebView is an interface that wraps the basic methods for controlling the UI
|
|
||||||
// loop, handling multithreading and providing JavaScript bindings.
|
|
||||||
type WebView interface {
|
type WebView interface {
|
||||||
// Run() starts the main UI loop until the user closes the webview window or
|
// Run() starts the main UI loop until the user closes the webview window or
|
||||||
// Terminate() is called.
|
// Terminate() is called.
|
||||||
@@ -199,175 +53,21 @@ type WebView interface {
|
|||||||
Exit()
|
Exit()
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialogType is an enumeration of all supported system dialog types
|
type ExternalInvokeCallbackFunc func(w WebView, data string)
|
||||||
type DialogType int
|
|
||||||
|
|
||||||
const (
|
type Settings struct {
|
||||||
// DialogTypeOpen is a system file open dialog
|
// WebView main window title
|
||||||
DialogTypeOpen DialogType = iota
|
Title string
|
||||||
// DialogTypeSave is a system file save dialog
|
// URL to open in a webview
|
||||||
DialogTypeSave
|
URL string
|
||||||
// DialogTypeAlert is a system alert dialog (message box)
|
// Window width in pixels
|
||||||
DialogTypeAlert
|
Width int
|
||||||
)
|
// Window height in pixels
|
||||||
|
Height int
|
||||||
const (
|
// Allows/disallows window resizing
|
||||||
// DialogFlagFile is a normal file picker dialog
|
Resizable bool
|
||||||
DialogFlagFile = C.WEBVIEW_DIALOG_FLAG_FILE
|
// Enable debugging tools (Linux/BSD/MacOS, on Windows use Firebug)
|
||||||
// DialogFlagDirectory is an open directory dialog
|
Debug bool
|
||||||
DialogFlagDirectory = C.WEBVIEW_DIALOG_FLAG_DIRECTORY
|
// A callback that is executed when JavaScript calls "window.external.invoke()"
|
||||||
// DialogFlagInfo is an info alert dialog
|
ExternalInvokeCallback ExternalInvokeCallbackFunc
|
||||||
DialogFlagInfo = C.WEBVIEW_DIALOG_FLAG_INFO
|
|
||||||
// DialogFlagWarning is a warning alert dialog
|
|
||||||
DialogFlagWarning = C.WEBVIEW_DIALOG_FLAG_WARNING
|
|
||||||
// DialogFlagError is an error dialog
|
|
||||||
DialogFlagError = C.WEBVIEW_DIALOG_FLAG_ERROR
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
m sync.Mutex
|
|
||||||
index uintptr
|
|
||||||
fns = map[uintptr]func(){}
|
|
||||||
cbs = map[WebView]ExternalInvokeCallbackFunc{}
|
|
||||||
)
|
|
||||||
|
|
||||||
type webview struct {
|
|
||||||
w unsafe.Pointer
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ WebView = &webview{}
|
|
||||||
|
|
||||||
func boolToInt(b bool) int {
|
|
||||||
if b {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWebview creates and opens a new webview window using the given settings. The
|
|
||||||
// returned object implements the WebView interface. This function returns nil
|
|
||||||
// if a window can not be created.
|
|
||||||
func NewWebview(settings Settings) WebView {
|
|
||||||
if settings.Width == 0 {
|
|
||||||
settings.Width = 640
|
|
||||||
}
|
|
||||||
if settings.Height == 0 {
|
|
||||||
settings.Height = 480
|
|
||||||
}
|
|
||||||
if settings.Title == "" {
|
|
||||||
settings.Title = "WebView"
|
|
||||||
}
|
|
||||||
w := &webview{}
|
|
||||||
w.w = C.CgoWebViewCreate(C.int(settings.Width), C.int(settings.Height),
|
|
||||||
C.CString(settings.Title), C.CString(settings.URL),
|
|
||||||
C.int(boolToInt(settings.Resizable)), C.int(boolToInt(settings.Debug)))
|
|
||||||
m.Lock()
|
|
||||||
if settings.ExternalInvokeCallback != nil {
|
|
||||||
cbs[w] = settings.ExternalInvokeCallback
|
|
||||||
} else {
|
|
||||||
cbs[w] = func(w WebView, data string) {}
|
|
||||||
}
|
|
||||||
m.Unlock()
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *webview) Loop(blocking bool) bool {
|
|
||||||
block := C.int(0)
|
|
||||||
if blocking {
|
|
||||||
block = 1
|
|
||||||
}
|
|
||||||
return C.CgoWebViewLoop(w.w, block) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *webview) Run() {
|
|
||||||
for w.Loop(true) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *webview) Exit() {
|
|
||||||
C.CgoWebViewExit(w.w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *webview) Dispatch(f func()) {
|
|
||||||
m.Lock()
|
|
||||||
for ; fns[index] != nil; index++ {
|
|
||||||
}
|
|
||||||
fns[index] = f
|
|
||||||
m.Unlock()
|
|
||||||
C.CgoWebViewDispatch(w.w, C.uintptr_t(index))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *webview) SetTitle(title string) {
|
|
||||||
p := C.CString(title)
|
|
||||||
defer C.free(unsafe.Pointer(p))
|
|
||||||
C.CgoWebViewSetTitle(w.w, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *webview) SetColor(r, g, b, a uint8) {
|
|
||||||
C.CgoWebViewSetColor(w.w, C.uint8_t(r), C.uint8_t(g), C.uint8_t(b), C.uint8_t(a))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *webview) SetFullscreen(fullscreen bool) {
|
|
||||||
C.CgoWebViewSetFullscreen(w.w, C.int(boolToInt(fullscreen)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *webview) Dialog(dlgType DialogType, flags int, title string, arg string, filter string) string {
|
|
||||||
const maxPath = 4096
|
|
||||||
titlePtr := C.CString(title)
|
|
||||||
defer C.free(unsafe.Pointer(titlePtr))
|
|
||||||
argPtr := C.CString(arg)
|
|
||||||
defer C.free(unsafe.Pointer(argPtr))
|
|
||||||
resultPtr := (*C.char)(C.calloc((C.size_t)(unsafe.Sizeof((*C.char)(nil))), (C.size_t)(maxPath)))
|
|
||||||
defer C.free(unsafe.Pointer(resultPtr))
|
|
||||||
filterPtr := C.CString(filter)
|
|
||||||
defer C.free(unsafe.Pointer(filterPtr))
|
|
||||||
C.CgoDialog(w.w, C.int(dlgType), C.int(flags), titlePtr,
|
|
||||||
argPtr, resultPtr, C.size_t(maxPath), filterPtr)
|
|
||||||
return C.GoString(resultPtr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *webview) Eval(js string) error {
|
|
||||||
p := C.CString(js)
|
|
||||||
defer C.free(unsafe.Pointer(p))
|
|
||||||
switch C.CgoWebViewEval(w.w, p) {
|
|
||||||
case -1:
|
|
||||||
return errors.New("evaluation failed")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *webview) InjectCSS(css string) {
|
|
||||||
p := C.CString(css)
|
|
||||||
defer C.free(unsafe.Pointer(p))
|
|
||||||
C.CgoWebViewInjectCSS(w.w, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *webview) Terminate() {
|
|
||||||
C.CgoWebViewTerminate(w.w)
|
|
||||||
}
|
|
||||||
|
|
||||||
//export _webviewDispatchGoCallback
|
|
||||||
func _webviewDispatchGoCallback(index unsafe.Pointer) {
|
|
||||||
var f func()
|
|
||||||
m.Lock()
|
|
||||||
f = fns[uintptr(index)]
|
|
||||||
delete(fns, uintptr(index))
|
|
||||||
m.Unlock()
|
|
||||||
f()
|
|
||||||
}
|
|
||||||
|
|
||||||
//export _webviewExternalInvokeCallback
|
|
||||||
func _webviewExternalInvokeCallback(w unsafe.Pointer, data unsafe.Pointer) {
|
|
||||||
m.Lock()
|
|
||||||
var (
|
|
||||||
cb ExternalInvokeCallbackFunc
|
|
||||||
wv WebView
|
|
||||||
)
|
|
||||||
for wv, cb = range cbs {
|
|
||||||
if wv.(*webview).w == w {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m.Unlock()
|
|
||||||
cb(wv, C.GoString((*C.char)(data)))
|
|
||||||
}
|
}
|
||||||
|
|||||||
57
lib/renderer/webview/webview_darwin.go
Executable file
57
lib/renderer/webview/webview_darwin.go
Executable file
@@ -0,0 +1,57 @@
|
|||||||
|
package webview
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "webview_darwin.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DialogFlagFile is a normal file picker dialog
|
||||||
|
DialogFlagFile = C.WEBVIEW_DIALOG_FLAG_FILE
|
||||||
|
// DialogFlagDirectory is an open directory dialog
|
||||||
|
DialogFlagDirectory = C.WEBVIEW_DIALOG_FLAG_DIRECTORY
|
||||||
|
// DialogFlagInfo is an info alert dialog
|
||||||
|
DialogFlagInfo = C.WEBVIEW_DIALOG_FLAG_INFO
|
||||||
|
// DialogFlagWarning is a warning alert dialog
|
||||||
|
DialogFlagWarning = C.WEBVIEW_DIALOG_FLAG_WARNING
|
||||||
|
// DialogFlagError is an error dialog
|
||||||
|
DialogFlagError = C.WEBVIEW_DIALOG_FLAG_ERROR
|
||||||
|
)
|
||||||
|
|
||||||
|
type MacWebView struct {
|
||||||
|
app unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWebview(settings Settings) WebView {
|
||||||
|
return &MacWebView{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *MacWebView) Dialog(dlgType DialogType, flags int, title string, arg string, filter string) string {
|
||||||
|
// TBD
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *MacWebView) Dispatch(func()) {
|
||||||
|
// TBD
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *MacWebView) Eval(js string) error {
|
||||||
|
// TBD
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *MacWebView) Exit() {
|
||||||
|
// TBD
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *MacWebView) Run() {}
|
||||||
|
func (w *MacWebView) Loop(blocking bool) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
func (w *MacWebView) SetTitle(title string) {}
|
||||||
|
func (w *MacWebView) SetFullscreen(fullscreen bool) {}
|
||||||
|
func (w *MacWebView) SetColor(r, g, b, a uint8) {}
|
||||||
|
func (w *MacWebView) InjectCSS(css string) {}
|
||||||
|
func (w *MacWebView) Terminate() {}
|
||||||
13
lib/renderer/webview/webview_darwin.h
Normal file
13
lib/renderer/webview/webview_darwin.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
#ifndef WEBVIEW_DARWIN
|
||||||
|
#define WEBVIEW_DARWIN
|
||||||
|
|
||||||
|
#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)
|
||||||
|
|
||||||
|
#endif
|
||||||
386
lib/renderer/webview/webview_linux.go
Executable file
386
lib/renderer/webview/webview_linux.go
Executable file
@@ -0,0 +1,386 @@
|
|||||||
|
// Package webview implements Go bindings to https://github.com/zserge/webview C library.
|
||||||
|
// It is a modified version of webview.go from that repository
|
||||||
|
// Bindings closely repeat the C APIs and include both, a simplified
|
||||||
|
// single-function API to just open a full-screen webview window, and a more
|
||||||
|
// advanced and featureful set of APIs, including Go-to-JavaScript bindings.
|
||||||
|
//
|
||||||
|
// The library uses gtk-webkit, Cocoa/Webkit and MSHTML (IE8..11) as a browser
|
||||||
|
// engine and supports Linux, MacOS and Windows 7..10 respectively.
|
||||||
|
//
|
||||||
|
package webview
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo linux openbsd freebsd CFLAGS: -DWEBVIEW_GTK=1 -Wno-deprecated-declarations
|
||||||
|
#cgo linux openbsd freebsd pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||||
|
|
||||||
|
#cgo windows CFLAGS: -DWEBVIEW_WINAPI=1 -std=c99
|
||||||
|
#cgo windows LDFLAGS: -lole32 -lcomctl32 -loleaut32 -luuid -lgdi32
|
||||||
|
|
||||||
|
#cgo darwin CFLAGS: -DWEBVIEW_COCOA=1 -x objective-c
|
||||||
|
#cgo darwin LDFLAGS: -framework Cocoa -framework WebKit
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#define WEBVIEW_STATIC
|
||||||
|
#define WEBVIEW_IMPLEMENTATION
|
||||||
|
|
||||||
|
#ifdef WEBVIEW_GTK
|
||||||
|
#include "webview_linux.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WEBVIEW_COCOA
|
||||||
|
#include "webview_darwin.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WEBVIEW_COCOA
|
||||||
|
#include "webview_darwin.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
extern void _webviewExternalInvokeCallback(void *, void *);
|
||||||
|
|
||||||
|
static inline void CgoWebViewFree(void *w) {
|
||||||
|
free((void *)((struct webview *)w)->title);
|
||||||
|
free((void *)((struct webview *)w)->url);
|
||||||
|
free(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *CgoWebViewCreate(int width, int height, char *title, char *url, int resizable, int debug) {
|
||||||
|
struct webview *w = (struct webview *) calloc(1, sizeof(*w));
|
||||||
|
w->width = width;
|
||||||
|
w->height = height;
|
||||||
|
w->title = title;
|
||||||
|
w->url = url;
|
||||||
|
w->resizable = resizable;
|
||||||
|
w->debug = debug;
|
||||||
|
w->external_invoke_cb = (webview_external_invoke_cb_t) _webviewExternalInvokeCallback;
|
||||||
|
if (webview_init(w) != 0) {
|
||||||
|
CgoWebViewFree(w);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return (void *)w;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int CgoWebViewLoop(void *w, int blocking) {
|
||||||
|
return webview_loop((struct webview *)w, blocking);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewTerminate(void *w) {
|
||||||
|
webview_terminate((struct webview *)w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewExit(void *w) {
|
||||||
|
webview_exit((struct webview *)w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewSetTitle(void *w, char *title) {
|
||||||
|
webview_set_title((struct webview *)w, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewSetFullscreen(void *w, int fullscreen) {
|
||||||
|
webview_set_fullscreen((struct webview *)w, fullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewSetColor(void *w, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||||
|
webview_set_color((struct webview *)w, r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoDialog(void *w, int dlgtype, int flags,
|
||||||
|
char *title, char *arg, char *res, size_t ressz, char *filter) {
|
||||||
|
webview_dialog(w, dlgtype, flags,
|
||||||
|
(const char*)title, (const char*) arg, res, ressz, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int CgoWebViewEval(void *w, char *js) {
|
||||||
|
return webview_eval((struct webview *)w, js);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewInjectCSS(void *w, char *css) {
|
||||||
|
webview_inject_css((struct webview *)w, css);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void _webviewDispatchGoCallback(void *);
|
||||||
|
static inline void _webview_dispatch_cb(struct webview *w, void *arg) {
|
||||||
|
_webviewDispatchGoCallback(arg);
|
||||||
|
}
|
||||||
|
static inline void CgoWebViewDispatch(void *w, uintptr_t arg) {
|
||||||
|
webview_dispatch((struct webview *)w, _webview_dispatch_cb, (void *)arg);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Ensure that main.main is called from the main thread
|
||||||
|
runtime.LockOSThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open is a simplified API to open a single native window with a full-size webview in
|
||||||
|
// it. It can be helpful if you want to communicate with the core app using XHR
|
||||||
|
// or WebSockets (as opposed to using JavaScript bindings).
|
||||||
|
//
|
||||||
|
// Window appearance can be customized using title, width, height and resizable parameters.
|
||||||
|
// URL must be provided and can user either a http or https protocol, or be a
|
||||||
|
// local file:// URL. On some platforms "data:" URLs are also supported
|
||||||
|
// (Linux/MacOS).
|
||||||
|
func Open(title, url string, w, h int, resizable bool) error {
|
||||||
|
titleStr := C.CString(title)
|
||||||
|
defer C.free(unsafe.Pointer(titleStr))
|
||||||
|
urlStr := C.CString(url)
|
||||||
|
defer C.free(unsafe.Pointer(urlStr))
|
||||||
|
resize := C.int(0)
|
||||||
|
if resizable {
|
||||||
|
resize = C.int(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := C.webview(titleStr, urlStr, C.int(w), C.int(h), resize)
|
||||||
|
if r != 0 {
|
||||||
|
return errors.New("failed to create webview")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalInvokeCallbackFunc is a function type that is called every time
|
||||||
|
// "window.external.invoke()" is called from JavaScript. Data is the only
|
||||||
|
// obligatory string parameter passed into the "invoke(data)" function from
|
||||||
|
// JavaScript. To pass more complex data serialized JSON or base64 encoded
|
||||||
|
// string can be used.
|
||||||
|
type ExternalInvokeCallbackFunc func(w WebView, data string)
|
||||||
|
|
||||||
|
// Settings is a set of parameters to customize the initial WebView appearance
|
||||||
|
// and behavior. It is passed into the webview.New() constructor.
|
||||||
|
type Settings struct {
|
||||||
|
// WebView main window title
|
||||||
|
Title string
|
||||||
|
// URL to open in a webview
|
||||||
|
URL string
|
||||||
|
// Window width in pixels
|
||||||
|
Width int
|
||||||
|
// Window height in pixels
|
||||||
|
Height int
|
||||||
|
// Allows/disallows window resizing
|
||||||
|
Resizable bool
|
||||||
|
// Enable debugging tools (Linux/BSD/MacOS, on Windows use Firebug)
|
||||||
|
Debug bool
|
||||||
|
// A callback that is executed when JavaScript calls "window.external.invoke()"
|
||||||
|
ExternalInvokeCallback ExternalInvokeCallbackFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebView is an interface that wraps the basic methods for controlling the UI
|
||||||
|
// loop, handling multithreading and providing JavaScript bindings.
|
||||||
|
type WebView interface {
|
||||||
|
// Run() starts the main UI loop until the user closes the webview window or
|
||||||
|
// Terminate() is called.
|
||||||
|
Run()
|
||||||
|
// Loop() runs a single iteration of the main UI.
|
||||||
|
Loop(blocking bool) bool
|
||||||
|
// SetTitle() changes window title. This method must be called from the main
|
||||||
|
// thread only. See Dispatch() for more details.
|
||||||
|
SetTitle(title string)
|
||||||
|
// SetFullscreen() controls window full-screen mode. This method must be
|
||||||
|
// called from the main thread only. See Dispatch() for more details.
|
||||||
|
SetFullscreen(fullscreen bool)
|
||||||
|
// SetColor() changes window background color. This method must be called from
|
||||||
|
// the main thread only. See Dispatch() for more details.
|
||||||
|
SetColor(r, g, b, a uint8)
|
||||||
|
// Eval() evaluates an arbitrary JS code inside the webview. This method must
|
||||||
|
// be called from the main thread only. See Dispatch() for more details.
|
||||||
|
Eval(js string) error
|
||||||
|
// InjectJS() injects an arbitrary block of CSS code using the JS API. This
|
||||||
|
// method must be called from the main thread only. See Dispatch() for more
|
||||||
|
// details.
|
||||||
|
InjectCSS(css string)
|
||||||
|
// Dialog() opens a system dialog of the given type and title. String
|
||||||
|
// argument can be provided for certain dialogs, such as alert boxes. For
|
||||||
|
// alert boxes argument is a message inside the dialog box.
|
||||||
|
Dialog(dlgType DialogType, flags int, title string, arg string, filter string) string
|
||||||
|
// Terminate() breaks the main UI loop. This method must be called from the main thread
|
||||||
|
// only. See Dispatch() for more details.
|
||||||
|
Terminate()
|
||||||
|
// Dispatch() schedules some arbitrary function to be executed on the main UI
|
||||||
|
// thread. This may be helpful if you want to run some JavaScript from
|
||||||
|
// background threads/goroutines, or to terminate the app.
|
||||||
|
Dispatch(func())
|
||||||
|
// Exit() closes the window and cleans up the resources. Use Terminate() to
|
||||||
|
// forcefully break out of the main UI loop.
|
||||||
|
Exit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialogType is an enumeration of all supported system dialog types
|
||||||
|
type DialogType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DialogTypeOpen is a system file open dialog
|
||||||
|
DialogTypeOpen DialogType = iota
|
||||||
|
// DialogTypeSave is a system file save dialog
|
||||||
|
DialogTypeSave
|
||||||
|
// DialogTypeAlert is a system alert dialog (message box)
|
||||||
|
DialogTypeAlert
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DialogFlagFile is a normal file picker dialog
|
||||||
|
DialogFlagFile = C.WEBVIEW_DIALOG_FLAG_FILE
|
||||||
|
// DialogFlagDirectory is an open directory dialog
|
||||||
|
DialogFlagDirectory = C.WEBVIEW_DIALOG_FLAG_DIRECTORY
|
||||||
|
// DialogFlagInfo is an info alert dialog
|
||||||
|
DialogFlagInfo = C.WEBVIEW_DIALOG_FLAG_INFO
|
||||||
|
// DialogFlagWarning is a warning alert dialog
|
||||||
|
DialogFlagWarning = C.WEBVIEW_DIALOG_FLAG_WARNING
|
||||||
|
// DialogFlagError is an error dialog
|
||||||
|
DialogFlagError = C.WEBVIEW_DIALOG_FLAG_ERROR
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
m sync.Mutex
|
||||||
|
index uintptr
|
||||||
|
fns = map[uintptr]func(){}
|
||||||
|
cbs = map[WebView]ExternalInvokeCallbackFunc{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type webview struct {
|
||||||
|
w unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ WebView = &webview{}
|
||||||
|
|
||||||
|
func boolToInt(b bool) int {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWebview creates and opens a new webview window using the given settings. The
|
||||||
|
// returned object implements the WebView interface. This function returns nil
|
||||||
|
// if a window can not be created.
|
||||||
|
func NewWebview(settings Settings) WebView {
|
||||||
|
if settings.Width == 0 {
|
||||||
|
settings.Width = 640
|
||||||
|
}
|
||||||
|
if settings.Height == 0 {
|
||||||
|
settings.Height = 480
|
||||||
|
}
|
||||||
|
if settings.Title == "" {
|
||||||
|
settings.Title = "WebView"
|
||||||
|
}
|
||||||
|
w := &webview{}
|
||||||
|
w.w = C.CgoWebViewCreate(C.int(settings.Width), C.int(settings.Height),
|
||||||
|
C.CString(settings.Title), C.CString(settings.URL),
|
||||||
|
C.int(boolToInt(settings.Resizable)), C.int(boolToInt(settings.Debug)))
|
||||||
|
m.Lock()
|
||||||
|
if settings.ExternalInvokeCallback != nil {
|
||||||
|
cbs[w] = settings.ExternalInvokeCallback
|
||||||
|
} else {
|
||||||
|
cbs[w] = func(w WebView, data string) {}
|
||||||
|
}
|
||||||
|
m.Unlock()
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Loop(blocking bool) bool {
|
||||||
|
block := C.int(0)
|
||||||
|
if blocking {
|
||||||
|
block = 1
|
||||||
|
}
|
||||||
|
return C.CgoWebViewLoop(w.w, block) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Run() {
|
||||||
|
for w.Loop(true) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Exit() {
|
||||||
|
C.CgoWebViewExit(w.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Dispatch(f func()) {
|
||||||
|
m.Lock()
|
||||||
|
for ; fns[index] != nil; index++ {
|
||||||
|
}
|
||||||
|
fns[index] = f
|
||||||
|
m.Unlock()
|
||||||
|
C.CgoWebViewDispatch(w.w, C.uintptr_t(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) SetTitle(title string) {
|
||||||
|
p := C.CString(title)
|
||||||
|
defer C.free(unsafe.Pointer(p))
|
||||||
|
C.CgoWebViewSetTitle(w.w, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) SetColor(r, g, b, a uint8) {
|
||||||
|
C.CgoWebViewSetColor(w.w, C.uint8_t(r), C.uint8_t(g), C.uint8_t(b), C.uint8_t(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) SetFullscreen(fullscreen bool) {
|
||||||
|
C.CgoWebViewSetFullscreen(w.w, C.int(boolToInt(fullscreen)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Dialog(dlgType DialogType, flags int, title string, arg string, filter string) string {
|
||||||
|
const maxPath = 4096
|
||||||
|
titlePtr := C.CString(title)
|
||||||
|
defer C.free(unsafe.Pointer(titlePtr))
|
||||||
|
argPtr := C.CString(arg)
|
||||||
|
defer C.free(unsafe.Pointer(argPtr))
|
||||||
|
resultPtr := (*C.char)(C.calloc((C.size_t)(unsafe.Sizeof((*C.char)(nil))), (C.size_t)(maxPath)))
|
||||||
|
defer C.free(unsafe.Pointer(resultPtr))
|
||||||
|
filterPtr := C.CString(filter)
|
||||||
|
defer C.free(unsafe.Pointer(filterPtr))
|
||||||
|
C.CgoDialog(w.w, C.int(dlgType), C.int(flags), titlePtr,
|
||||||
|
argPtr, resultPtr, C.size_t(maxPath), filterPtr)
|
||||||
|
return C.GoString(resultPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Eval(js string) error {
|
||||||
|
p := C.CString(js)
|
||||||
|
defer C.free(unsafe.Pointer(p))
|
||||||
|
switch C.CgoWebViewEval(w.w, p) {
|
||||||
|
case -1:
|
||||||
|
return errors.New("evaluation failed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) InjectCSS(css string) {
|
||||||
|
p := C.CString(css)
|
||||||
|
defer C.free(unsafe.Pointer(p))
|
||||||
|
C.CgoWebViewInjectCSS(w.w, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Terminate() {
|
||||||
|
C.CgoWebViewTerminate(w.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export _webviewDispatchGoCallback
|
||||||
|
func _webviewDispatchGoCallback(index unsafe.Pointer) {
|
||||||
|
var f func()
|
||||||
|
m.Lock()
|
||||||
|
f = fns[uintptr(index)]
|
||||||
|
delete(fns, uintptr(index))
|
||||||
|
m.Unlock()
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
|
//export _webviewExternalInvokeCallback
|
||||||
|
func _webviewExternalInvokeCallback(w unsafe.Pointer, data unsafe.Pointer) {
|
||||||
|
m.Lock()
|
||||||
|
var (
|
||||||
|
cb ExternalInvokeCallbackFunc
|
||||||
|
wv WebView
|
||||||
|
)
|
||||||
|
for wv, cb = range cbs {
|
||||||
|
if wv.(*webview).w == w {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.Unlock()
|
||||||
|
cb(wv, C.GoString((*C.char)(data)))
|
||||||
|
}
|
||||||
522
lib/renderer/webview/webview_linux.h
Normal file
522
lib/renderer/webview/webview_linux.h
Normal file
@@ -0,0 +1,522 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WEBVIEW_STATIC
|
||||||
|
#define WEBVIEW_API static
|
||||||
|
#else
|
||||||
|
#define WEBVIEW_API extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct webview;
|
||||||
|
|
||||||
|
typedef void (*webview_external_invoke_cb_t)(struct webview *w,
|
||||||
|
const char *arg);
|
||||||
|
|
||||||
|
struct webview
|
||||||
|
{
|
||||||
|
const char *url;
|
||||||
|
const char *title;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int resizable;
|
||||||
|
int transparentTitlebar;
|
||||||
|
int debug;
|
||||||
|
webview_external_invoke_cb_t external_invoke_cb;
|
||||||
|
struct webview_priv priv;
|
||||||
|
void *userdata;
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
typedef void (*webview_dispatch_fn)(struct webview *w, void *arg);
|
||||||
|
|
||||||
|
struct webview_dispatch_arg
|
||||||
|
{
|
||||||
|
webview_dispatch_fn fn;
|
||||||
|
struct webview *w;
|
||||||
|
void *arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DEFAULT_URL \
|
||||||
|
"data:text/" \
|
||||||
|
"html,%3C%21DOCTYPE%20html%3E%0A%3Chtml%20lang=%22en%22%3E%0A%3Chead%3E%" \
|
||||||
|
"3Cmeta%20charset=%22utf-8%22%3E%3Cmeta%20http-equiv=%22IE=edge%22%" \
|
||||||
|
"20content=%22IE=edge%22%3E%3C%2Fhead%3E%0A%3Cbody%3E%3Cdiv%20id=%22app%22%" \
|
||||||
|
"3E%3C%2Fdiv%3E%3Cscript%20type=%22text%2Fjavascript%22%3E%3C%2Fscript%3E%" \
|
||||||
|
"3C%2Fbody%3E%0A%3C%2Fhtml%3E"
|
||||||
|
|
||||||
|
#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 const char *webview_check_url(const char *url)
|
||||||
|
{
|
||||||
|
if (url == NULL || strlen(url) == 0)
|
||||||
|
{
|
||||||
|
return DEFAULT_URL;
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
WEBVIEW_API int webview(const char *title, const char *url, int width,
|
||||||
|
int height, int resizable);
|
||||||
|
|
||||||
|
WEBVIEW_API int webview_init(struct webview *w);
|
||||||
|
WEBVIEW_API int webview_loop(struct webview *w, int blocking);
|
||||||
|
WEBVIEW_API int webview_eval(struct webview *w, const char *js);
|
||||||
|
WEBVIEW_API int webview_inject_css(struct webview *w, const char *css);
|
||||||
|
WEBVIEW_API void webview_set_title(struct webview *w, const char *title);
|
||||||
|
WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen);
|
||||||
|
WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g,
|
||||||
|
uint8_t b, uint8_t a);
|
||||||
|
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, char *filter);
|
||||||
|
WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn,
|
||||||
|
void *arg);
|
||||||
|
WEBVIEW_API void webview_terminate(struct webview *w);
|
||||||
|
WEBVIEW_API void webview_exit(struct webview *w);
|
||||||
|
WEBVIEW_API void webview_debug(const char *format, ...);
|
||||||
|
WEBVIEW_API void webview_print_log(const char *s);
|
||||||
|
|
||||||
|
WEBVIEW_API int webview(const char *title, const char *url, int width,
|
||||||
|
int height, int resizable)
|
||||||
|
{
|
||||||
|
struct webview webview;
|
||||||
|
memset(&webview, 0, sizeof(webview));
|
||||||
|
webview.title = title;
|
||||||
|
webview.url = url;
|
||||||
|
webview.width = width;
|
||||||
|
webview.height = height;
|
||||||
|
webview.resizable = resizable;
|
||||||
|
int r = webview_init(&webview);
|
||||||
|
if (r != 0)
|
||||||
|
{
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
while (webview_loop(&webview, 1) == 0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
webview_exit(&webview);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
WEBVIEW_API void webview_debug(const char *format, ...)
|
||||||
|
{
|
||||||
|
char buf[4096];
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
vsnprintf(buf, sizeof(buf), format, ap);
|
||||||
|
webview_print_log(buf);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(struct webview *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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, char *filter)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
if (filter[0] != '\0') {
|
||||||
|
GtkFileFilter *file_filter = gtk_file_filter_new();
|
||||||
|
gchar **filters = g_strsplit(filter, ",", -1);
|
||||||
|
gint i;
|
||||||
|
for(i = 0; filters && filters[i]; i++) {
|
||||||
|
gtk_file_filter_add_pattern(file_filter, filters[i]);
|
||||||
|
}
|
||||||
|
gtk_file_filter_set_name(file_filter, filter);
|
||||||
|
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dlg), file_filter);
|
||||||
|
g_strfreev(filters);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* WEBVIEW_H */
|
||||||
368
lib/renderer/webview/webview_windows.go
Executable file
368
lib/renderer/webview/webview_windows.go
Executable file
@@ -0,0 +1,368 @@
|
|||||||
|
// Package webview implements Go bindings to https://github.com/zserge/webview C library.
|
||||||
|
// It is a modified version of webview.go from that repository
|
||||||
|
// Bindings closely repeat the C APIs and include both, a simplified
|
||||||
|
// single-function API to just open a full-screen webview window, and a more
|
||||||
|
// advanced and featureful set of APIs, including Go-to-JavaScript bindings.
|
||||||
|
//
|
||||||
|
// The library uses gtk-webkit, Cocoa/Webkit and MSHTML (IE8..11) as a browser
|
||||||
|
// engine and supports Linux, MacOS and Windows 7..10 respectively.
|
||||||
|
//
|
||||||
|
package webview
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo windows CFLAGS: -DWEBVIEW_WINAPI=1 -std=c99
|
||||||
|
#cgo windows LDFLAGS: -lole32 -lcomctl32 -loleaut32 -luuid -lgdi32
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#define WEBVIEW_STATIC
|
||||||
|
#define WEBVIEW_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include "webview_windows.h"
|
||||||
|
|
||||||
|
extern void _webviewExternalInvokeCallback(void *, void *);
|
||||||
|
|
||||||
|
static inline void CgoWebViewFree(void *w) {
|
||||||
|
free((void *)((struct webview *)w)->title);
|
||||||
|
free((void *)((struct webview *)w)->url);
|
||||||
|
free(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *CgoWebViewCreate(int width, int height, char *title, char *url, int resizable, int debug) {
|
||||||
|
struct webview *w = (struct webview *) calloc(1, sizeof(*w));
|
||||||
|
w->width = width;
|
||||||
|
w->height = height;
|
||||||
|
w->title = title;
|
||||||
|
w->url = url;
|
||||||
|
w->resizable = resizable;
|
||||||
|
w->debug = debug;
|
||||||
|
w->external_invoke_cb = (webview_external_invoke_cb_t) _webviewExternalInvokeCallback;
|
||||||
|
if (webview_init(w) != 0) {
|
||||||
|
CgoWebViewFree(w);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return (void *)w;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int CgoWebViewLoop(void *w, int blocking) {
|
||||||
|
return webview_loop((struct webview *)w, blocking);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewTerminate(void *w) {
|
||||||
|
webview_terminate((struct webview *)w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewExit(void *w) {
|
||||||
|
webview_exit((struct webview *)w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewSetTitle(void *w, char *title) {
|
||||||
|
webview_set_title((struct webview *)w, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewSetFullscreen(void *w, int fullscreen) {
|
||||||
|
webview_set_fullscreen((struct webview *)w, fullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewSetColor(void *w, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||||
|
webview_set_color((struct webview *)w, r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoDialog(void *w, int dlgtype, int flags,
|
||||||
|
char *title, char *arg, char *res, size_t ressz, char *filter) {
|
||||||
|
webview_dialog(w, dlgtype, flags,
|
||||||
|
(const char*)title, (const char*) arg, res, ressz, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int CgoWebViewEval(void *w, char *js) {
|
||||||
|
return webview_eval((struct webview *)w, js);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewInjectCSS(void *w, char *css) {
|
||||||
|
webview_inject_css((struct webview *)w, css);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void _webviewDispatchGoCallback(void *);
|
||||||
|
static inline void _webview_dispatch_cb(struct webview *w, void *arg) {
|
||||||
|
_webviewDispatchGoCallback(arg);
|
||||||
|
}
|
||||||
|
static inline void CgoWebViewDispatch(void *w, uintptr_t arg) {
|
||||||
|
webview_dispatch((struct webview *)w, _webview_dispatch_cb, (void *)arg);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Ensure that main.main is called from the main thread
|
||||||
|
runtime.LockOSThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open is a simplified API to open a single native window with a full-size webview in
|
||||||
|
// it. It can be helpful if you want to communicate with the core app using XHR
|
||||||
|
// or WebSockets (as opposed to using JavaScript bindings).
|
||||||
|
//
|
||||||
|
// Window appearance can be customized using title, width, height and resizable parameters.
|
||||||
|
// URL must be provided and can user either a http or https protocol, or be a
|
||||||
|
// local file:// URL. On some platforms "data:" URLs are also supported
|
||||||
|
// (Linux/MacOS).
|
||||||
|
func Open(title, url string, w, h int, resizable bool) error {
|
||||||
|
titleStr := C.CString(title)
|
||||||
|
defer C.free(unsafe.Pointer(titleStr))
|
||||||
|
urlStr := C.CString(url)
|
||||||
|
defer C.free(unsafe.Pointer(urlStr))
|
||||||
|
resize := C.int(0)
|
||||||
|
if resizable {
|
||||||
|
resize = C.int(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := C.webview(titleStr, urlStr, C.int(w), C.int(h), resize)
|
||||||
|
if r != 0 {
|
||||||
|
return errors.New("failed to create webview")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalInvokeCallbackFunc is a function type that is called every time
|
||||||
|
// "window.external.invoke()" is called from JavaScript. Data is the only
|
||||||
|
// obligatory string parameter passed into the "invoke(data)" function from
|
||||||
|
// JavaScript. To pass more complex data serialized JSON or base64 encoded
|
||||||
|
// string can be used.
|
||||||
|
type ExternalInvokeCallbackFunc func(w WebView, data string)
|
||||||
|
|
||||||
|
// Settings is a set of parameters to customize the initial WebView appearance
|
||||||
|
// and behavior. It is passed into the webview.New() constructor.
|
||||||
|
type Settings struct {
|
||||||
|
// WebView main window title
|
||||||
|
Title string
|
||||||
|
// URL to open in a webview
|
||||||
|
URL string
|
||||||
|
// Window width in pixels
|
||||||
|
Width int
|
||||||
|
// Window height in pixels
|
||||||
|
Height int
|
||||||
|
// Allows/disallows window resizing
|
||||||
|
Resizable bool
|
||||||
|
// Enable debugging tools (Linux/BSD/MacOS, on Windows use Firebug)
|
||||||
|
Debug bool
|
||||||
|
// A callback that is executed when JavaScript calls "window.external.invoke()"
|
||||||
|
ExternalInvokeCallback ExternalInvokeCallbackFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebView is an interface that wraps the basic methods for controlling the UI
|
||||||
|
// loop, handling multithreading and providing JavaScript bindings.
|
||||||
|
type WebView interface {
|
||||||
|
// Run() starts the main UI loop until the user closes the webview window or
|
||||||
|
// Terminate() is called.
|
||||||
|
Run()
|
||||||
|
// Loop() runs a single iteration of the main UI.
|
||||||
|
Loop(blocking bool) bool
|
||||||
|
// SetTitle() changes window title. This method must be called from the main
|
||||||
|
// thread only. See Dispatch() for more details.
|
||||||
|
SetTitle(title string)
|
||||||
|
// SetFullscreen() controls window full-screen mode. This method must be
|
||||||
|
// called from the main thread only. See Dispatch() for more details.
|
||||||
|
SetFullscreen(fullscreen bool)
|
||||||
|
// SetColor() changes window background color. This method must be called from
|
||||||
|
// the main thread only. See Dispatch() for more details.
|
||||||
|
SetColor(r, g, b, a uint8)
|
||||||
|
// Eval() evaluates an arbitrary JS code inside the webview. This method must
|
||||||
|
// be called from the main thread only. See Dispatch() for more details.
|
||||||
|
Eval(js string) error
|
||||||
|
// InjectJS() injects an arbitrary block of CSS code using the JS API. This
|
||||||
|
// method must be called from the main thread only. See Dispatch() for more
|
||||||
|
// details.
|
||||||
|
InjectCSS(css string)
|
||||||
|
// Dialog() opens a system dialog of the given type and title. String
|
||||||
|
// argument can be provided for certain dialogs, such as alert boxes. For
|
||||||
|
// alert boxes argument is a message inside the dialog box.
|
||||||
|
Dialog(dlgType DialogType, flags int, title string, arg string, filter string) string
|
||||||
|
// Terminate() breaks the main UI loop. This method must be called from the main thread
|
||||||
|
// only. See Dispatch() for more details.
|
||||||
|
Terminate()
|
||||||
|
// Dispatch() schedules some arbitrary function to be executed on the main UI
|
||||||
|
// thread. This may be helpful if you want to run some JavaScript from
|
||||||
|
// background threads/goroutines, or to terminate the app.
|
||||||
|
Dispatch(func())
|
||||||
|
// Exit() closes the window and cleans up the resources. Use Terminate() to
|
||||||
|
// forcefully break out of the main UI loop.
|
||||||
|
Exit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialogType is an enumeration of all supported system dialog types
|
||||||
|
type DialogType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DialogTypeOpen is a system file open dialog
|
||||||
|
DialogTypeOpen DialogType = iota
|
||||||
|
// DialogTypeSave is a system file save dialog
|
||||||
|
DialogTypeSave
|
||||||
|
// DialogTypeAlert is a system alert dialog (message box)
|
||||||
|
DialogTypeAlert
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DialogFlagFile is a normal file picker dialog
|
||||||
|
DialogFlagFile = C.WEBVIEW_DIALOG_FLAG_FILE
|
||||||
|
// DialogFlagDirectory is an open directory dialog
|
||||||
|
DialogFlagDirectory = C.WEBVIEW_DIALOG_FLAG_DIRECTORY
|
||||||
|
// DialogFlagInfo is an info alert dialog
|
||||||
|
DialogFlagInfo = C.WEBVIEW_DIALOG_FLAG_INFO
|
||||||
|
// DialogFlagWarning is a warning alert dialog
|
||||||
|
DialogFlagWarning = C.WEBVIEW_DIALOG_FLAG_WARNING
|
||||||
|
// DialogFlagError is an error dialog
|
||||||
|
DialogFlagError = C.WEBVIEW_DIALOG_FLAG_ERROR
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
m sync.Mutex
|
||||||
|
index uintptr
|
||||||
|
fns = map[uintptr]func(){}
|
||||||
|
cbs = map[WebView]ExternalInvokeCallbackFunc{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type webview struct {
|
||||||
|
w unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ WebView = &webview{}
|
||||||
|
|
||||||
|
func boolToInt(b bool) int {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWebview creates and opens a new webview window using the given settings. The
|
||||||
|
// returned object implements the WebView interface. This function returns nil
|
||||||
|
// if a window can not be created.
|
||||||
|
func NewWebview(settings Settings) WebView {
|
||||||
|
if settings.Width == 0 {
|
||||||
|
settings.Width = 640
|
||||||
|
}
|
||||||
|
if settings.Height == 0 {
|
||||||
|
settings.Height = 480
|
||||||
|
}
|
||||||
|
if settings.Title == "" {
|
||||||
|
settings.Title = "WebView"
|
||||||
|
}
|
||||||
|
w := &webview{}
|
||||||
|
w.w = C.CgoWebViewCreate(C.int(settings.Width), C.int(settings.Height),
|
||||||
|
C.CString(settings.Title), C.CString(settings.URL),
|
||||||
|
C.int(boolToInt(settings.Resizable)), C.int(boolToInt(settings.Debug)))
|
||||||
|
m.Lock()
|
||||||
|
if settings.ExternalInvokeCallback != nil {
|
||||||
|
cbs[w] = settings.ExternalInvokeCallback
|
||||||
|
} else {
|
||||||
|
cbs[w] = func(w WebView, data string) {}
|
||||||
|
}
|
||||||
|
m.Unlock()
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Loop(blocking bool) bool {
|
||||||
|
block := C.int(0)
|
||||||
|
if blocking {
|
||||||
|
block = 1
|
||||||
|
}
|
||||||
|
return C.CgoWebViewLoop(w.w, block) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Run() {
|
||||||
|
for w.Loop(true) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Exit() {
|
||||||
|
C.CgoWebViewExit(w.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Dispatch(f func()) {
|
||||||
|
m.Lock()
|
||||||
|
for ; fns[index] != nil; index++ {
|
||||||
|
}
|
||||||
|
fns[index] = f
|
||||||
|
m.Unlock()
|
||||||
|
C.CgoWebViewDispatch(w.w, C.uintptr_t(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) SetTitle(title string) {
|
||||||
|
p := C.CString(title)
|
||||||
|
defer C.free(unsafe.Pointer(p))
|
||||||
|
C.CgoWebViewSetTitle(w.w, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) SetColor(r, g, b, a uint8) {
|
||||||
|
C.CgoWebViewSetColor(w.w, C.uint8_t(r), C.uint8_t(g), C.uint8_t(b), C.uint8_t(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) SetFullscreen(fullscreen bool) {
|
||||||
|
C.CgoWebViewSetFullscreen(w.w, C.int(boolToInt(fullscreen)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Dialog(dlgType DialogType, flags int, title string, arg string, filter string) string {
|
||||||
|
const maxPath = 4096
|
||||||
|
titlePtr := C.CString(title)
|
||||||
|
defer C.free(unsafe.Pointer(titlePtr))
|
||||||
|
argPtr := C.CString(arg)
|
||||||
|
defer C.free(unsafe.Pointer(argPtr))
|
||||||
|
resultPtr := (*C.char)(C.calloc((C.size_t)(unsafe.Sizeof((*C.char)(nil))), (C.size_t)(maxPath)))
|
||||||
|
defer C.free(unsafe.Pointer(resultPtr))
|
||||||
|
filterPtr := C.CString(filter)
|
||||||
|
defer C.free(unsafe.Pointer(filterPtr))
|
||||||
|
C.CgoDialog(w.w, C.int(dlgType), C.int(flags), titlePtr,
|
||||||
|
argPtr, resultPtr, C.size_t(maxPath), filterPtr)
|
||||||
|
return C.GoString(resultPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Eval(js string) error {
|
||||||
|
p := C.CString(js)
|
||||||
|
defer C.free(unsafe.Pointer(p))
|
||||||
|
switch C.CgoWebViewEval(w.w, p) {
|
||||||
|
case -1:
|
||||||
|
return errors.New("evaluation failed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) InjectCSS(css string) {
|
||||||
|
p := C.CString(css)
|
||||||
|
defer C.free(unsafe.Pointer(p))
|
||||||
|
C.CgoWebViewInjectCSS(w.w, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Terminate() {
|
||||||
|
C.CgoWebViewTerminate(w.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export _webviewDispatchGoCallback
|
||||||
|
func _webviewDispatchGoCallback(index unsafe.Pointer) {
|
||||||
|
var f func()
|
||||||
|
m.Lock()
|
||||||
|
f = fns[uintptr(index)]
|
||||||
|
delete(fns, uintptr(index))
|
||||||
|
m.Unlock()
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
|
//export _webviewExternalInvokeCallback
|
||||||
|
func _webviewExternalInvokeCallback(w unsafe.Pointer, data unsafe.Pointer) {
|
||||||
|
m.Lock()
|
||||||
|
var (
|
||||||
|
cb ExternalInvokeCallbackFunc
|
||||||
|
wv WebView
|
||||||
|
)
|
||||||
|
for wv, cb = range cbs {
|
||||||
|
if wv.(*webview).w == w {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.Unlock()
|
||||||
|
cb(wv, C.GoString((*C.char)(data)))
|
||||||
|
}
|
||||||
@@ -39,23 +39,6 @@ extern "C"
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#if defined(WEBVIEW_GTK)
|
|
||||||
#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;
|
|
||||||
};
|
|
||||||
#elif defined(WEBVIEW_WINAPI)
|
|
||||||
#define CINTERFACE
|
#define CINTERFACE
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
@@ -76,22 +59,6 @@ struct webview_priv
|
|||||||
DWORD saved_ex_style;
|
DWORD saved_ex_style;
|
||||||
RECT saved_rect;
|
RECT saved_rect;
|
||||||
};
|
};
|
||||||
#elif defined(WEBVIEW_COCOA)
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#import <WebKit/WebKit.h>
|
|
||||||
#import <objc/runtime.h>
|
|
||||||
|
|
||||||
struct webview_priv
|
|
||||||
{
|
|
||||||
NSAutoreleasePool *pool;
|
|
||||||
NSWindow *window;
|
|
||||||
WebView *webview;
|
|
||||||
id delegate;
|
|
||||||
int should_exit;
|
|
||||||
};
|
|
||||||
#else
|
|
||||||
#error "Define one of: WEBVIEW_GTK, WEBVIEW_COCOA or WEBVIEW_WINAPI"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct webview;
|
struct webview;
|
||||||
|
|
||||||
@@ -182,9 +149,6 @@ struct webview_priv
|
|||||||
WEBVIEW_API void webview_debug(const char *format, ...);
|
WEBVIEW_API void webview_debug(const char *format, ...);
|
||||||
WEBVIEW_API void webview_print_log(const char *s);
|
WEBVIEW_API void webview_print_log(const char *s);
|
||||||
|
|
||||||
#ifdef WEBVIEW_IMPLEMENTATION
|
|
||||||
#undef WEBVIEW_IMPLEMENTATION
|
|
||||||
|
|
||||||
WEBVIEW_API int webview(const char *title, const char *url, int width,
|
WEBVIEW_API int webview(const char *title, const char *url, int width,
|
||||||
int height, int resizable)
|
int height, int resizable)
|
||||||
{
|
{
|
||||||
@@ -264,301 +228,6 @@ struct webview_priv
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(WEBVIEW_GTK)
|
|
||||||
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, char *filter)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
if (filter[0] != '\0') {
|
|
||||||
GtkFileFilter *file_filter = gtk_file_filter_new();
|
|
||||||
gchar **filters = g_strsplit(filter, ",", -1);
|
|
||||||
gint i;
|
|
||||||
for(i = 0; filters && filters[i]; i++) {
|
|
||||||
gtk_file_filter_add_pattern(file_filter, filters[i]);
|
|
||||||
}
|
|
||||||
gtk_file_filter_set_name(file_filter, filter);
|
|
||||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dlg), file_filter);
|
|
||||||
g_strfreev(filters);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* WEBVIEW_GTK */
|
|
||||||
|
|
||||||
#if defined(WEBVIEW_WINAPI)
|
#if defined(WEBVIEW_WINAPI)
|
||||||
|
|
||||||
#pragma comment(lib, "user32.lib")
|
#pragma comment(lib, "user32.lib")
|
||||||
@@ -1930,433 +1599,6 @@ struct webview_priv
|
|||||||
WEBVIEW_API void webview_exit(struct webview *w) { OleUninitialize(); }
|
WEBVIEW_API void webview_exit(struct webview *w) { OleUninitialize(); }
|
||||||
WEBVIEW_API void webview_print_log(const char *s) { OutputDebugString(s); }
|
WEBVIEW_API void webview_print_log(const char *s) { OutputDebugString(s); }
|
||||||
|
|
||||||
#endif /* WEBVIEW_WINAPI */
|
|
||||||
|
|
||||||
#if defined(WEBVIEW_COCOA)
|
|
||||||
#if (!defined MAC_OS_X_VERSION_10_12) || \
|
|
||||||
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
|
|
||||||
#define NSAlertStyleWarning NSWarningAlertStyle
|
|
||||||
#define NSAlertStyleCritical NSCriticalAlertStyle
|
|
||||||
#define NSWindowStyleMaskResizable NSResizableWindowMask
|
|
||||||
#define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask
|
|
||||||
#define NSWindowStyleMaskTitled NSTitledWindowMask
|
|
||||||
#define NSWindowStyleMaskClosable NSClosableWindowMask
|
|
||||||
#define NSWindowStyleMaskFullScreen NSFullScreenWindowMask
|
|
||||||
#define NSEventMaskAny NSAnyEventMask
|
|
||||||
#define NSEventModifierFlagCommand NSCommandKeyMask
|
|
||||||
#define NSEventModifierFlagOption NSAlternateKeyMask
|
|
||||||
#define NSAlertStyleInformational NSInformationalAlertStyle
|
|
||||||
#endif /* MAC_OS_X_VERSION_10_12 */
|
|
||||||
#if (!defined MAC_OS_X_VERSION_10_13) || \
|
|
||||||
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13
|
|
||||||
#define NSModalResponseOK NSFileHandlingPanelOKButton
|
|
||||||
#endif /* MAC_OS_X_VERSION_10_12, MAC_OS_X_VERSION_10_13 */
|
|
||||||
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 BOOL webview_is_selector_excluded_from_web_script(id self, SEL cmd,
|
|
||||||
SEL selector)
|
|
||||||
{
|
|
||||||
return selector != @selector(invoke:);
|
|
||||||
}
|
|
||||||
|
|
||||||
static NSString *webview_webscript_name_for_selector(id self, SEL cmd,
|
|
||||||
SEL selector)
|
|
||||||
{
|
|
||||||
return selector == @selector(invoke:) ? @"invoke" : nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void webview_did_clear_window_object(id self, SEL cmd, id webview,
|
|
||||||
id script, id frame)
|
|
||||||
{
|
|
||||||
[script setValue:self forKey:@"external"];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void webview_run_input_open_panel(id self, SEL cmd, id webview,
|
|
||||||
id listener, BOOL allowMultiple)
|
|
||||||
{
|
|
||||||
char filename[256] = "";
|
|
||||||
struct webview *w =
|
|
||||||
(struct webview *)objc_getAssociatedObject(self, "webview");
|
|
||||||
|
|
||||||
webview_dialog(w, WEBVIEW_DIALOG_TYPE_OPEN, WEBVIEW_DIALOG_FLAG_FILE, "", "",
|
|
||||||
filename, 255, "");
|
|
||||||
filename[255] = '\0';
|
|
||||||
if (strlen(filename) > 0)
|
|
||||||
{
|
|
||||||
[listener chooseFilename:[NSString stringWithUTF8String:filename]];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[listener cancel];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void webview_external_invoke(id self, SEL cmd, id arg)
|
|
||||||
{
|
|
||||||
struct webview *w =
|
|
||||||
(struct webview *)objc_getAssociatedObject(self, "webview");
|
|
||||||
if (w == NULL || w->external_invoke_cb == NULL)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ([arg isKindOfClass:[NSString class]] == NO)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
w->external_invoke_cb(w, [(NSString *)(arg) UTF8String]);
|
|
||||||
}
|
|
||||||
|
|
||||||
WEBVIEW_API int webview_init(struct webview *w)
|
|
||||||
{
|
|
||||||
w->priv.pool = [[NSAutoreleasePool alloc] init];
|
|
||||||
[NSApplication sharedApplication];
|
|
||||||
|
|
||||||
Class webViewDelegateClass =
|
|
||||||
objc_allocateClassPair([NSObject class], "WebViewDelegate", 0);
|
|
||||||
class_addMethod(webViewDelegateClass, sel_registerName("windowWillClose:"),
|
|
||||||
(IMP)webview_window_will_close, "v@:@");
|
|
||||||
class_addMethod(object_getClass(webViewDelegateClass),
|
|
||||||
sel_registerName("isSelectorExcludedFromWebScript:"),
|
|
||||||
(IMP)webview_is_selector_excluded_from_web_script, "c@::");
|
|
||||||
class_addMethod(object_getClass(webViewDelegateClass),
|
|
||||||
sel_registerName("webScriptNameForSelector:"),
|
|
||||||
(IMP)webview_webscript_name_for_selector, "c@::");
|
|
||||||
class_addMethod(webViewDelegateClass,
|
|
||||||
sel_registerName("webView:didClearWindowObject:forFrame:"),
|
|
||||||
(IMP)webview_did_clear_window_object, "v@:@@@");
|
|
||||||
class_addMethod(
|
|
||||||
webViewDelegateClass,
|
|
||||||
sel_registerName("webView:runOpenPanelForFileButtonWithResultListener:"
|
|
||||||
"allowMultipleFiles:"),
|
|
||||||
(IMP)webview_run_input_open_panel, "v@:@@c");
|
|
||||||
class_addMethod(webViewDelegateClass, sel_registerName("invoke:"),
|
|
||||||
(IMP)webview_external_invoke, "v@:@");
|
|
||||||
objc_registerClassPair(webViewDelegateClass);
|
|
||||||
|
|
||||||
w->priv.delegate = [[webViewDelegateClass alloc] init];
|
|
||||||
objc_setAssociatedObject(w->priv.delegate, "webview", (id)(w),
|
|
||||||
OBJC_ASSOCIATION_ASSIGN);
|
|
||||||
|
|
||||||
NSRect r = NSMakeRect(0, 0, w->width, w->height);
|
|
||||||
NSUInteger style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
|
|
||||||
NSWindowStyleMaskMiniaturizable;
|
|
||||||
if (w->resizable)
|
|
||||||
{
|
|
||||||
style = style | NSWindowStyleMaskResizable;
|
|
||||||
// style = style | NSTexturedBackgroundWindowMask;
|
|
||||||
// style = style | NSUnifiedTitleAndToolbarWindowMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transparent title bar
|
|
||||||
// if (w->transparentTitlebar) {
|
|
||||||
// style = style | NSFullSizeContentViewWindowMask | NSUnifiedTitleAndToolbarWindowMask | NSTexturedBackgroundWindowMask;
|
|
||||||
// }
|
|
||||||
|
|
||||||
w->priv.window = [[NSWindow alloc] initWithContentRect:r
|
|
||||||
styleMask:style
|
|
||||||
backing:NSBackingStoreBuffered
|
|
||||||
defer:NO];
|
|
||||||
[w->priv.window autorelease];
|
|
||||||
|
|
||||||
// Title
|
|
||||||
NSString *nsTitle = [NSString stringWithUTF8String:w->title];
|
|
||||||
[w->priv.window setTitle:nsTitle];
|
|
||||||
|
|
||||||
[w->priv.window setDelegate:w->priv.delegate];
|
|
||||||
[w->priv.window center];
|
|
||||||
|
|
||||||
// NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"wat"];
|
|
||||||
// toolbar.showsBaselineSeparator = NO;
|
|
||||||
// [w->priv.window setToolbar:toolbar];
|
|
||||||
|
|
||||||
// if (w->transparentTitlebar) {
|
|
||||||
|
|
||||||
// // Configure window look with hidden toolbar
|
|
||||||
// [w->priv.window setTitlebarAppearsTransparent:YES];
|
|
||||||
// [w->priv.window setTitleVisibility:NSWindowTitleHidden];
|
|
||||||
// // w->priv.window.isMovableByWindowBackground = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:!!w->debug
|
|
||||||
forKey:@"WebKitDeveloperExtras"];
|
|
||||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
||||||
w->priv.webview =
|
|
||||||
[[WebView alloc] initWithFrame:r
|
|
||||||
frameName:@"WebView"
|
|
||||||
groupName:nil];
|
|
||||||
NSURL *nsURL = [NSURL
|
|
||||||
URLWithString:[NSString stringWithUTF8String:webview_check_url(w->url)]];
|
|
||||||
[[w->priv.webview mainFrame] loadRequest:[NSURLRequest requestWithURL:nsURL]];
|
|
||||||
|
|
||||||
[w->priv.webview setAutoresizesSubviews:YES];
|
|
||||||
[w->priv.webview
|
|
||||||
setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
|
||||||
w->priv.webview.frameLoadDelegate = w->priv.delegate;
|
|
||||||
w->priv.webview.UIDelegate = w->priv.delegate;
|
|
||||||
[[w->priv.window contentView] addSubview:w->priv.webview];
|
|
||||||
[w->priv.window orderFrontRegardless];
|
|
||||||
|
|
||||||
// Disable scrolling - make this configurable
|
|
||||||
// [[[w->priv.webview mainFrame] frameView] setAllowsScrolling:NO];
|
|
||||||
|
|
||||||
// ----> Enables WebGL but won't pass the app store guidelines
|
|
||||||
//
|
|
||||||
// WebPreferences *p = [w->priv.webview preferences];
|
|
||||||
// if ([p respondsToSelector:@selector(setWebGLEnabled:)]) {
|
|
||||||
// [p setWebGLEnabled:YES];
|
|
||||||
// }
|
|
||||||
|
|
||||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
|
||||||
[NSApp finishLaunching];
|
|
||||||
[NSApp activateIgnoringOtherApps:YES];
|
|
||||||
|
|
||||||
NSMenu *menubar = [[[NSMenu alloc] initWithTitle:@""] autorelease];
|
|
||||||
|
|
||||||
NSMenuItem *appMenuItem =
|
|
||||||
[[[NSMenuItem alloc] initWithTitle:@"wails app" action:NULL keyEquivalent:@""]
|
|
||||||
autorelease];
|
|
||||||
NSMenu *appMenu = [[[NSMenu alloc] initWithTitle:@"wails app"] autorelease];
|
|
||||||
[appMenuItem setSubmenu:appMenu];
|
|
||||||
[menubar addItem:appMenuItem];
|
|
||||||
|
|
||||||
NSMenuItem *item = [[[NSMenuItem alloc] initWithTitle:@"Hide"
|
|
||||||
action:@selector(hide:)
|
|
||||||
keyEquivalent:@"h"] autorelease];
|
|
||||||
[appMenu addItem:item];
|
|
||||||
item = [[[NSMenuItem alloc] initWithTitle:@"Hide Others"
|
|
||||||
action:@selector(hideOtherApplications:)
|
|
||||||
keyEquivalent:@"h"] autorelease];
|
|
||||||
[item setKeyEquivalentModifierMask:(NSEventModifierFlagOption |
|
|
||||||
NSEventModifierFlagCommand)];
|
|
||||||
[appMenu addItem:item];
|
|
||||||
item = [[[NSMenuItem alloc] initWithTitle:@"Show All"
|
|
||||||
action:@selector(unhideAllApplications:)
|
|
||||||
keyEquivalent:@""] autorelease];
|
|
||||||
[appMenu addItem:item];
|
|
||||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
|
||||||
|
|
||||||
NSMenuItem *editMenuItem =
|
|
||||||
[[[NSMenuItem alloc] initWithTitle:@"Edit" action:NULL keyEquivalent:@""]
|
|
||||||
autorelease];
|
|
||||||
NSMenu *editMenu = [[[NSMenu alloc] initWithTitle:@"Edit"] autorelease];
|
|
||||||
[editMenuItem setSubmenu:editMenu];
|
|
||||||
[menubar addItem:editMenuItem];
|
|
||||||
|
|
||||||
item = [[[NSMenuItem alloc] initWithTitle:@"Cut"
|
|
||||||
action:@selector(cut:)
|
|
||||||
keyEquivalent:@"x"] autorelease];
|
|
||||||
[editMenu addItem:item];
|
|
||||||
|
|
||||||
item = [[[NSMenuItem alloc] initWithTitle:@"Copy"
|
|
||||||
action:@selector(copy:)
|
|
||||||
keyEquivalent:@"c"] autorelease];
|
|
||||||
[editMenu addItem:item];
|
|
||||||
|
|
||||||
item = [[[NSMenuItem alloc] initWithTitle:@"Paste"
|
|
||||||
action:@selector(paste:)
|
|
||||||
keyEquivalent:@"v"] autorelease];
|
|
||||||
[editMenu addItem:item];
|
|
||||||
|
|
||||||
item = [[[NSMenuItem alloc] initWithTitle:@"Select All"
|
|
||||||
action:@selector(selectAll:)
|
|
||||||
keyEquivalent:@"a"] autorelease];
|
|
||||||
[editMenu addItem:item];
|
|
||||||
|
|
||||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
|
||||||
|
|
||||||
item = [[[NSMenuItem alloc] initWithTitle:@"Quit"
|
|
||||||
action:@selector(terminate:)
|
|
||||||
keyEquivalent:@"q"] autorelease];
|
|
||||||
[appMenu addItem:item];
|
|
||||||
|
|
||||||
[NSApp setMainMenu:menubar];
|
|
||||||
|
|
||||||
w->priv.should_exit = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
WEBVIEW_API int webview_loop(struct webview *w, int blocking)
|
|
||||||
{
|
|
||||||
NSDate *until = (blocking ? [NSDate distantFuture] : [NSDate distantPast]);
|
|
||||||
NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny
|
|
||||||
untilDate:until
|
|
||||||
inMode:NSDefaultRunLoopMode
|
|
||||||
dequeue:YES];
|
|
||||||
if (event)
|
|
||||||
{
|
|
||||||
[NSApp sendEvent:event];
|
|
||||||
}
|
|
||||||
return w->priv.should_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
WEBVIEW_API int webview_eval(struct webview *w, const char *js)
|
|
||||||
{
|
|
||||||
NSString *nsJS = [NSString stringWithUTF8String:js];
|
|
||||||
[[w->priv.webview windowScriptObject] evaluateWebScript:nsJS];
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
WEBVIEW_API void webview_set_title(struct webview *w, const char *title)
|
|
||||||
{
|
|
||||||
NSString *nsTitle = [NSString stringWithUTF8String:title];
|
|
||||||
[w->priv.window setTitle:nsTitle];
|
|
||||||
}
|
|
||||||
|
|
||||||
WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen)
|
|
||||||
{
|
|
||||||
int b = ((([w->priv.window styleMask] & NSWindowStyleMaskFullScreen) ==
|
|
||||||
NSWindowStyleMaskFullScreen)
|
|
||||||
? 1
|
|
||||||
: 0);
|
|
||||||
if (b != fullscreen)
|
|
||||||
{
|
|
||||||
[w->priv.window toggleFullScreen:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g,
|
|
||||||
uint8_t b, uint8_t a)
|
|
||||||
{
|
|
||||||
[w->priv.window setBackgroundColor:[NSColor colorWithRed:(CGFloat)r / 255.0
|
|
||||||
green:(CGFloat)g / 255.0
|
|
||||||
blue:(CGFloat)b / 255.0
|
|
||||||
alpha:(CGFloat)a / 255.0]];
|
|
||||||
if (0.5 >= ((r / 255.0 * 299.0) + (g / 255.0 * 587.0) + (b / 255.0 * 114.0)) /
|
|
||||||
1000.0)
|
|
||||||
{
|
|
||||||
[w->priv.window
|
|
||||||
setAppearance:[NSAppearance
|
|
||||||
appearanceNamed:NSAppearanceNameVibrantDark]];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[w->priv.window
|
|
||||||
setAppearance:[NSAppearance
|
|
||||||
appearanceNamed:NSAppearanceNameVibrantLight]];
|
|
||||||
}
|
|
||||||
[w->priv.window setOpaque:NO];
|
|
||||||
[w->priv.window setTitlebarAppearsTransparent:YES];
|
|
||||||
[w->priv.webview setDrawsBackground:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
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, char *filter)
|
|
||||||
{
|
|
||||||
if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ||
|
|
||||||
dlgtype == WEBVIEW_DIALOG_TYPE_SAVE)
|
|
||||||
{
|
|
||||||
NSSavePanel *panel;
|
|
||||||
NSString *filter_str = [NSString stringWithUTF8String:filter];
|
|
||||||
filter_str = [filter_str stringByReplacingOccurrencesOfString:@"*."
|
|
||||||
withString:@""];
|
|
||||||
NSArray *fileTypes = [filter_str componentsSeparatedByString:@","];
|
|
||||||
if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN)
|
|
||||||
{
|
|
||||||
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
|
|
||||||
if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY)
|
|
||||||
{
|
|
||||||
[openPanel setCanChooseFiles:NO];
|
|
||||||
[openPanel setCanChooseDirectories:YES];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[openPanel setCanChooseFiles:YES];
|
|
||||||
[openPanel setCanChooseDirectories:NO];
|
|
||||||
if(filter[0] != NULL)
|
|
||||||
{
|
|
||||||
[openPanel setAllowedFileTypes:fileTypes];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[openPanel setResolvesAliases:NO];
|
|
||||||
[openPanel setAllowsMultipleSelection:NO];
|
|
||||||
panel = openPanel;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
panel = [NSSavePanel savePanel];
|
|
||||||
}
|
|
||||||
[panel setCanCreateDirectories:YES];
|
|
||||||
[panel setShowsHiddenFiles:YES];
|
|
||||||
[panel setExtensionHidden:NO];
|
|
||||||
[panel setCanSelectHiddenExtension:NO];
|
|
||||||
if(filter[0] != NULL)
|
|
||||||
{
|
|
||||||
[panel setAllowedFileTypes:fileTypes];
|
|
||||||
}
|
|
||||||
[panel setTreatsFilePackagesAsDirectories:YES];
|
|
||||||
[panel beginSheetModalForWindow:w->priv.window
|
|
||||||
completionHandler:^(NSInteger result) {
|
|
||||||
[NSApp stopModalWithCode:result];
|
|
||||||
}];
|
|
||||||
if ([NSApp runModalForWindow:panel] == NSModalResponseOK)
|
|
||||||
{
|
|
||||||
const char *filename = [[[panel URL] path] UTF8String];
|
|
||||||
strlcpy(result, filename, resultsz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT)
|
|
||||||
{
|
|
||||||
NSAlert *a = [NSAlert new];
|
|
||||||
switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK)
|
|
||||||
{
|
|
||||||
case WEBVIEW_DIALOG_FLAG_INFO:
|
|
||||||
[a setAlertStyle:NSAlertStyleInformational];
|
|
||||||
break;
|
|
||||||
case WEBVIEW_DIALOG_FLAG_WARNING:
|
|
||||||
NSLog(@"warning");
|
|
||||||
[a setAlertStyle:NSAlertStyleWarning];
|
|
||||||
break;
|
|
||||||
case WEBVIEW_DIALOG_FLAG_ERROR:
|
|
||||||
NSLog(@"error");
|
|
||||||
[a setAlertStyle:NSAlertStyleCritical];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
[a setShowsHelp:NO];
|
|
||||||
[a setShowsSuppressionButton:NO];
|
|
||||||
[a setMessageText:[NSString stringWithUTF8String:title]];
|
|
||||||
[a setInformativeText:[NSString stringWithUTF8String:arg]];
|
|
||||||
[a addButtonWithTitle:@"OK"];
|
|
||||||
[a runModal];
|
|
||||||
[a 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) { [NSApp terminate:NSApp]; }
|
|
||||||
WEBVIEW_API void webview_print_log(const char *s) { NSLog(@"%s", s); }
|
|
||||||
|
|
||||||
#endif /* WEBVIEW_COCOA */
|
|
||||||
|
|
||||||
#endif /* WEBVIEW_IMPLEMENTATION */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
Reference in New Issue
Block a user