diff --git a/v3/go.mod b/v3/go.mod index 5a912e016..9a8be002f 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -3,100 +3,101 @@ module github.com/wailsapp/wails/v3 go 1.21 require ( - github.com/bep/debounce v1.2.1 - github.com/ebitengine/purego v0.4.0-alpha.4 - github.com/go-git/go-git/v5 v5.3.0 - github.com/go-ole/go-ole v1.2.6 - github.com/go-task/task/v3 v3.29.1 - github.com/google/go-cmp v0.5.9 - github.com/google/uuid v1.3.0 - github.com/gorilla/pat v1.0.1 - github.com/gorilla/sessions v1.2.1 - github.com/jackmordaunt/icns/v2 v2.2.1 - github.com/json-iterator/go v1.1.12 - github.com/leaanthony/clir v1.6.0 - github.com/leaanthony/go-ansi-parser v1.6.1 - github.com/leaanthony/gosod v1.0.3 - github.com/leaanthony/winicon v1.0.0 - github.com/lmittmann/tint v1.0.0 - github.com/markbates/goth v1.77.0 - github.com/matryer/is v1.4.0 - github.com/mattn/go-colorable v0.1.13 - github.com/mattn/go-isatty v0.0.19 - github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 - github.com/pkg/errors v0.9.1 - github.com/pterm/pterm v0.12.51 - github.com/samber/lo v1.38.1 - github.com/shoenig/go-m1cpu v0.1.6 - github.com/tc-hib/winres v0.1.6 - github.com/wailsapp/go-webview2 v1.0.6-0.20230901120557-e959fdf1ccc3 - github.com/wailsapp/mimetype v1.4.1 - golang.org/x/net v0.10.0 - golang.org/x/sys v0.11.0 - modernc.org/sqlite v1.21.0 + github.com/bep/debounce v1.2.1 + github.com/ebitengine/purego v0.4.0-alpha.4 + github.com/go-git/go-git/v5 v5.3.0 + github.com/go-ole/go-ole v1.2.6 + github.com/go-task/task/v3 v3.29.1 + github.com/google/go-cmp v0.5.9 + github.com/google/uuid v1.3.0 + github.com/gorilla/pat v1.0.1 + github.com/gorilla/sessions v1.2.1 + github.com/jackmordaunt/icns/v2 v2.2.1 + github.com/json-iterator/go v1.1.12 + github.com/leaanthony/clir v1.6.0 + github.com/leaanthony/go-ansi-parser v1.6.1 + github.com/leaanthony/gosod v1.0.3 + github.com/leaanthony/winicon v1.0.0 + github.com/lmittmann/tint v1.0.0 + github.com/markbates/goth v1.77.0 + github.com/matryer/is v1.4.0 + github.com/mattn/go-colorable v0.1.13 + github.com/mattn/go-isatty v0.0.19 + github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 + github.com/pkg/errors v0.9.1 + github.com/pterm/pterm v0.12.51 + github.com/samber/lo v1.38.1 + github.com/shoenig/go-m1cpu v0.1.6 + github.com/tc-hib/winres v0.1.6 + github.com/wailsapp/go-webview2 v1.0.6-0.20230901120557-e959fdf1ccc3 + github.com/wailsapp/mimetype v1.4.1 + golang.org/x/net v0.10.0 + golang.org/x/sys v0.11.0 + modernc.org/sqlite v1.21.0 ) require ( - atomicgo.dev/cursor v0.1.1 // indirect - atomicgo.dev/keyboard v0.2.8 // indirect - github.com/Masterminds/semver/v3 v3.2.1 // indirect - github.com/Microsoft/go-winio v0.4.16 // indirect - github.com/containerd/console v1.0.3 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect - github.com/emirpasic/gods v1.12.0 // indirect - github.com/fatih/color v1.15.0 // indirect - github.com/go-git/gcfg v1.5.0 // indirect - github.com/go-git/go-billy/v5 v5.2.0 // indirect - github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect - github.com/golang/protobuf v1.4.2 // indirect - github.com/gookit/color v1.5.2 // indirect - github.com/gorilla/context v1.1.1 // indirect - github.com/gorilla/mux v1.6.2 // indirect - github.com/gorilla/securecookie v1.1.1 // indirect - github.com/imdario/mergo v0.3.12 // indirect - github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect - github.com/joho/godotenv v1.5.1 // indirect - github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect - github.com/lithammer/fuzzysearch v1.1.5 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/mattn/go-zglob v0.0.4 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect - github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect - github.com/radovskyb/watcher v1.0.7 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/rivo/uniseg v0.4.4 // indirect - github.com/sajari/fuzzy v1.0.0 // indirect - github.com/sergi/go-diff v1.2.0 // indirect - github.com/xanzy/ssh-agent v0.3.0 // indirect - github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect - golang.org/x/crypto v0.1.0 // indirect - golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect - golang.org/x/image v0.5.0 // indirect - golang.org/x/mod v0.11.0 // indirect - golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 // indirect - golang.org/x/sync v0.3.0 // indirect - golang.org/x/term v0.11.0 // indirect - golang.org/x/text v0.9.0 // indirect - golang.org/x/tools v0.6.0 // indirect - google.golang.org/appengine v1.6.6 // indirect - google.golang.org/protobuf v1.25.0 // indirect - gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - lukechampine.com/uint128 v1.2.0 // indirect - modernc.org/cc/v3 v3.40.0 // indirect - modernc.org/ccgo/v3 v3.16.13 // indirect - modernc.org/libc v1.22.3 // indirect - modernc.org/mathutil v1.5.0 // indirect - modernc.org/memory v1.5.0 // indirect - modernc.org/opt v0.1.3 // indirect - modernc.org/strutil v1.1.3 // indirect - modernc.org/token v1.0.1 // indirect - mvdan.cc/sh/v3 v3.7.0 // indirect + atomicgo.dev/cursor v0.1.1 // indirect + atomicgo.dev/keyboard v0.2.8 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/Microsoft/go-winio v0.4.16 // indirect + github.com/containerd/console v1.0.3 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect + github.com/emirpasic/gods v1.12.0 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/go-git/gcfg v1.5.0 // indirect + github.com/go-git/go-billy/v5 v5.2.0 // indirect + github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect + github.com/golang/protobuf v1.4.2 // indirect + github.com/gookit/color v1.5.2 // indirect + github.com/gorilla/context v1.1.1 // indirect + github.com/gorilla/mux v1.6.2 // indirect + github.com/gorilla/securecookie v1.1.1 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect + github.com/lithammer/fuzzysearch v1.1.5 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-zglob v0.0.4 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect + github.com/radovskyb/watcher v1.0.7 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/sajari/fuzzy v1.0.0 // indirect + github.com/sergi/go-diff v1.2.0 // indirect + github.com/xanzy/ssh-agent v0.3.0 // indirect + github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect + golang.org/x/crypto v0.1.0 // indirect + golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect + golang.org/x/image v0.5.0 // indirect + golang.org/x/mod v0.11.0 // indirect + golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/term v0.11.0 // indirect + golang.org/x/text v0.9.0 // indirect + golang.org/x/tools v0.6.0 // indirect + google.golang.org/appengine v1.6.6 // indirect + google.golang.org/protobuf v1.25.0 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + lukechampine.com/uint128 v1.2.0 // indirect + modernc.org/cc/v3 v3.40.0 // indirect + modernc.org/ccgo/v3 v3.16.13 // indirect + modernc.org/libc v1.22.3 // indirect + modernc.org/mathutil v1.5.0 // indirect + modernc.org/memory v1.5.0 // indirect + modernc.org/opt v0.1.3 // indirect + modernc.org/strutil v1.1.3 // indirect + modernc.org/token v1.0.1 // indirect + mvdan.cc/sh/v3 v3.7.0 // indirect ) -replace github.com/ebitengine/purego v0.4.0-alpha.4 => github.com/tmclane/purego v0.0.0-20230601213035-1f25e70d7b01 +// replace github.com/ebitengine/purego v0.4.0-alpha.4 => github.com/tmclane/purego v0.0.0-20230601213035-1f25e70d7b01 +replace github.com/ebitengine/purego v0.4.0-alpha.4 => /home/tmclane/Projects/P/purego diff --git a/v3/pkg/application/application.go b/v3/pkg/application/application.go index 1e8bcfc6a..8069e9e57 100644 --- a/v3/pkg/application/application.go +++ b/v3/pkg/application/application.go @@ -2,6 +2,7 @@ package application import ( "encoding/json" + "fmt" "log" "log/slog" "net/http" @@ -446,7 +447,7 @@ func (a *App) Run() error { a.runLock.Unlock() // set the application menu - if runtime.GOOS == "darwin" || runtime.GOOS == "linux" { + if runtime.GOOS == "darwin" { a.impl.setApplicationMenu(a.ApplicationMenu) } a.impl.setIcon(a.options.Icon) @@ -569,6 +570,7 @@ func (a *App) Quit() { } func (a *App) SetMenu(menu *Menu) { + fmt.Println("App.SetMenu") a.ApplicationMenu = menu if a.impl != nil { a.impl.setApplicationMenu(menu) diff --git a/v3/pkg/application/application_linux.go b/v3/pkg/application/application_linux.go index 5c82c3a9c..a899b799e 100644 --- a/v3/pkg/application/application_linux.go +++ b/v3/pkg/application/application_linux.go @@ -51,11 +51,12 @@ func (m *linuxApp) show() { func (m *linuxApp) on(eventID uint) { // TODO: What do we need to do here? - log.Println("linuxApp.on()", eventID) + // log.Println("linuxApp.on()", eventID) } func (m *linuxApp) setIcon(icon []byte) { - fmt.Println("linuxApp.setIcon", "not implemented") + + log.Println("linuxApp.setIcon", "not implemented") } func (m *linuxApp) name() string { @@ -66,17 +67,36 @@ func (m *linuxApp) getCurrentWindowID() uint { return getCurrentWindowID(m.application, m.windows) } +type rnr struct { + f func() +} + +func (r rnr) run() { + r.f() +} + +func (m *linuxApp) getApplicationMenu() pointer { + if m.applicationMenu != nilPointer { + return m.applicationMenu + } + + menu := globalApplication.ApplicationMenu + if menu != nil { + invokeSync(func() { + menu.Update() + }) + m.applicationMenu = (menu.impl).(*linuxMenu).native + } + return m.applicationMenu +} + func (m *linuxApp) setApplicationMenu(menu *Menu) { + // FIXME: How do we avoid putting a menu? if menu == nil { // Create a default menu menu = defaultApplicationMenu() + globalApplication.ApplicationMenu = menu } - globalApplication.dispatchOnMainThread(func() { - fmt.Println("setApplicationMenu") - - menu.Update() - m.applicationMenu = (menu.impl).(*linuxMenu).native - }) } func (m *linuxApp) run() error { @@ -96,8 +116,7 @@ func (m *linuxApp) destroy() { } func (m *linuxApp) isOnMainThread() bool { - // FIXME: How do we detect this properly? - return false + return isOnMainThread() } // register our window to our parent mapping diff --git a/v3/pkg/application/linux_cgo.go b/v3/pkg/application/linux_cgo.go index 867889358..a31056d98 100644 --- a/v3/pkg/application/linux_cgo.go +++ b/v3/pkg/application/linux_cgo.go @@ -129,17 +129,23 @@ type pointer unsafe.Pointer type GSList C.GSList type GSListPointer *GSList +const ( + nilPointer pointer = nil + nilRadioGroup GSListPointer = nil +) + var ( - nilRadioGroup GSListPointer = nil gtkSignalHandlers map[*C.GtkWidget]C.gulong gtkSignalToMenuItem map[*C.GtkWidget]*MenuItem + mainThreadId *C.GThread ) func init() { - fmt.Println("linux_cgo") - gtkSignalHandlers = map[*C.GtkWidget]C.gulong{} gtkSignalToMenuItem = map[*C.GtkWidget]*MenuItem{} + + mainThreadId = C.g_thread_self() + fmt.Println("init mainthread=", mainThreadId) } // mainthread stuff @@ -155,6 +161,13 @@ func dispatchOnMainThreadCallback(callbackID C.uint) { //export activateLinux func activateLinux(data pointer) { // NOOP: Callback for now + fmt.Println("activateLinux", mainThreadId) +} + +func isOnMainThread() bool { + threadId := C.g_thread_self() + // fmt.Println("isOnMainThread = ", threadId == mainThreadId) + return threadId == mainThreadId } // implementation below @@ -173,8 +186,16 @@ func appNew(name string) pointer { func appRun(app pointer) error { application := (*C.GApplication)(app) C.g_application_hold(application) // allows it to run without a window + signal := C.CString("activate") - C.g_signal_connect_data(C.gpointer(application), signal, C.GCallback(C.activateLinux), nil, nil, 0) + defer C.free(unsafe.Pointer(signal)) + C.g_signal_connect_data( + C.gpointer(application), + signal, + C.GCallback(C.activateLinux), + nil, + nil, + 0) status := C.g_application_run(application, 0, nil) C.g_application_release(application) C.g_object_unref(C.gpointer(app)) @@ -557,13 +578,16 @@ func windowMinimize(window pointer) { C.gtk_window_iconify((*C.GtkWindow)(window)) } -func windowNew(application pointer, menu pointer, windowId uint, gpuPolicy int) (window pointer, webview pointer) { +func windowNew(application pointer, menu pointer, windowId uint, gpuPolicy int) (window, webview, vbox pointer) { window = pointer(C.gtk_application_window_new((*C.GtkApplication)(application))) C.g_object_ref_sink(C.gpointer(window)) webview = windowNewWebview(windowId, gpuPolicy) - vbox := pointer(C.gtk_box_new(C.GTK_ORIENTATION_VERTICAL, 0)) - C.gtk_container_add((*C.GtkContainer)(window), (*C.GtkWidget)(vbox)) + vbox = pointer(C.gtk_box_new(C.GTK_ORIENTATION_VERTICAL, 0)) + name := C.CString("webview-box") + defer C.free(unsafe.Pointer(name)) + C.gtk_widget_set_name((*C.GtkWidget)(vbox), name) + C.gtk_container_add((*C.GtkContainer)(window), (*C.GtkWidget)(vbox)) if menu != nil { C.gtk_box_pack_start((*C.GtkBox)(vbox), (*C.GtkWidget)(menu), 0, 0, 0) } @@ -634,9 +658,20 @@ func windowShow(window pointer) { C.gtk_widget_show_all((*C.GtkWidget)(window)) } -func windowSetBackgroundColour(webview pointer, colour RGBA) { +func windowSetBackgroundColour(vbox, webview pointer, colour RGBA) { rgba := C.GdkRGBA{C.double(colour.Red) / 255.0, C.double(colour.Green) / 255.0, C.double(colour.Blue) / 255.0, C.double(colour.Alpha) / 255.0} C.webkit_web_view_set_background_color((*C.WebKitWebView)(webview), &rgba) + + colour.Alpha = 255 + cssStr := C.CString(fmt.Sprintf("#webview-box {background-color: rgba(%d, %d, %d, %1.1f);}", colour.Red, colour.Green, colour.Blue, float32(colour.Alpha)/255.0)) + provider := C.gtk_css_provider_new() + C.gtk_style_context_add_provider( + C.gtk_widget_get_style_context((*C.GtkWidget)(vbox)), + (*C.GtkStyleProvider)(unsafe.Pointer(provider)), + C.GTK_STYLE_PROVIDER_PRIORITY_USER) + C.g_object_unref(C.gpointer(provider)) + C.gtk_css_provider_load_from_data(provider, cssStr, -1, nil) + C.free(unsafe.Pointer(cssStr)) } func windowSetGeometryHints(window pointer, minWidth, minHeight, maxWidth, maxHeight int) { diff --git a/v3/pkg/application/linux_purego.go b/v3/pkg/application/linux_purego.go index 9e930bb9b..6cc7830b4 100644 --- a/v3/pkg/application/linux_purego.go +++ b/v3/pkg/application/linux_purego.go @@ -25,6 +25,10 @@ type GSList struct { type GSListPointer *GSList +const ( + nilPointer pointer = 0 +) + const ( GSourceRemove int = 0 @@ -77,6 +81,7 @@ var ( nilRadioGroup GSListPointer = nil gtkSignalHandlers map[pointer]uint = map[pointer]uint{} gtkSignalToMenuItem map[pointer]*MenuItem = map[pointer]*MenuItem{} + mainThreadId uint64 ) const ( @@ -87,9 +92,9 @@ const ( ) var ( - gtk uintptr - version int - webkit uintptr + gtk uintptr + gtkVersion int + webkit uintptr // function references gApplicationHold func(pointer) @@ -107,6 +112,7 @@ var ( gSignalConnectObject func(pointer, string, pointer, pointer, int) uint gSignalHandlerBlock func(pointer, uint) gSignalHandlerUnblock func(pointer, uint) + gThreadSelf func() uint64 // gdk functions gdkDisplayGetMonitor func(pointer, int) pointer @@ -225,7 +231,7 @@ func init() { if err != nil { panic(err) } - version = 3 + gtkVersion = 3 webkit, err = purego.Dlopen(webkit4, purego.RTLD_NOW|purego.RTLD_GLOBAL) if err != nil { @@ -249,6 +255,7 @@ func init() { purego.RegisterLibFunc(&gSignalConnectObject, gtk, "g_signal_connect_object") purego.RegisterLibFunc(&gSignalHandlerBlock, gtk, "g_signal_handler_block") purego.RegisterLibFunc(&gSignalHandlerUnblock, gtk, "g_signal_handler_unblock") + purego.RegisterLibFunc(&gThreadSelf, gtk, "g_thread_self") // GDK purego.RegisterLibFunc(&gdkDisplayGetMonitor, gtk, "gdk_display_get_monitor") @@ -352,6 +359,8 @@ func init() { // mainthread stuff func dispatchOnMainThread(id uint) { + fmt.Println("dispatchOnMainThread", gThreadSelf()) + gIdleAdd(purego.NewCallback(func(pointer) int { executeOnMainThread(id) return GSourceRemove @@ -376,6 +385,9 @@ func appNew(name string) pointer { } func appRun(application pointer) error { + mainThreadId = gThreadSelf() + fmt.Println("linux_purego: appRun threadID", mainThreadId) + app := pointer(application) activate := func() { // TODO: Do we care? @@ -765,7 +777,7 @@ func windowMinimize(window pointer) { gtkWindowMinimize(window) } -func windowNew(application pointer, menu pointer, windowId uint, gpuPolicy int) (pointer, pointer) { +func windowNew(application pointer, menu pointer, windowId uint, gpuPolicy int) (pointer, pointer, pointer) { window := gtkApplicationWindowNew(application) gObjectRefSink(window) webview := windowNewWebview(windowId, gpuPolicy) @@ -776,7 +788,7 @@ func windowNew(application pointer, menu pointer, windowId uint, gpuPolicy int) gtkBoxPackStart(vbox, menu, 0, 0, 0) } gtkBoxPackStart(vbox, webview, 1, 1, 0) - return pointer(window), pointer(webview) + return pointer(window), pointer(webview), pointer(vbox) } func windowNewWebview(parentId uint, gpuPolicy int) pointer { @@ -826,7 +838,7 @@ func windowShow(window pointer) { gtkWidgetShowAll(pointer(window)) } -func windowSetBackgroundColour(webview pointer, colour RGBA) { +func windowSetBackgroundColour(vbox, webview pointer, colour RGBA) { // FIXME: Use a struct! rgba := make([]byte, 4*8) // C.sizeof_GdkRGBA == 32 rgbaPointer := pointer(unsafe.Pointer(&rgba[0])) @@ -841,6 +853,19 @@ func windowSetBackgroundColour(webview pointer, colour RGBA) { return } webkitWebViewSetBackgroundColor(pointer(webview), rgbaPointer) + + /* + colour.Alpha = 255 + cssStr := C.CString(fmt.Sprintf("#webview-box {background-color: rgba(%d, %d, %d, %1.1f);}", colour.Red, colour.Green, colour.Blue, float32(colour.Alpha)/255.0)) + provider := C.gtk_css_provider_new() + C.gtk_style_context_add_provider( + C.gtk_widget_get_style_context((*C.GtkWidget)(vbox)), + (*C.GtkStyleProvider)(unsafe.Pointer(provider)), + C.GTK_STYLE_PROVIDER_PRIORITY_USER) + C.g_object_unref(C.gpointer(provider)) + C.gtk_css_provider_load_from_data(provider, cssStr, -1, nil) + C.free(unsafe.Pointer(cssStr)) + */ } func windowSetGeometryHints(window pointer, minWidth, minHeight, maxWidth, maxHeight int) { @@ -1151,3 +1176,7 @@ func runSaveFileDialog(dialog *SaveFileDialogStruct) (string, error) { return results[0], nil } + +func isOnMainThread() bool { + return mainThreadId == gThreadSelf() +} diff --git a/v3/pkg/application/menu_linux.go b/v3/pkg/application/menu_linux.go index 2bd8684e9..8a5592bdd 100644 --- a/v3/pkg/application/menu_linux.go +++ b/v3/pkg/application/menu_linux.go @@ -15,6 +15,10 @@ func newMenuImpl(menu *Menu) *linuxMenu { return result } +func (m *linuxMenu) run() { + m.update() +} + func (m *linuxMenu) update() { m.processMenu(m.menu) } diff --git a/v3/pkg/application/menuitem_linux.go b/v3/pkg/application/menuitem_linux.go index 8b32825f2..575c4b594 100644 --- a/v3/pkg/application/menuitem_linux.go +++ b/v3/pkg/application/menuitem_linux.go @@ -14,7 +14,7 @@ type linuxMenuItem struct { } func (l linuxMenuItem) setTooltip(tooltip string) { - globalApplication.dispatchOnMainThread(func() { + invokeSync(func(){ l.blockSignal() defer l.unBlockSignal() menuItemSetToolTip(l.native, tooltip) @@ -34,7 +34,7 @@ func (l linuxMenuItem) unBlockSignal() { } func (l linuxMenuItem) setLabel(s string) { - globalApplication.dispatchOnMainThread(func() { + invokeSync(func() { l.blockSignal() defer l.unBlockSignal() menuItemSetLabel(l.native, s) @@ -46,7 +46,7 @@ func (l linuxMenuItem) isChecked() bool { } func (l linuxMenuItem) setDisabled(disabled bool) { - globalApplication.dispatchOnMainThread(func() { + invokeSync(func() { l.blockSignal() defer l.unBlockSignal() menuItemSetDisabled(l.native, disabled) @@ -54,7 +54,7 @@ func (l linuxMenuItem) setDisabled(disabled bool) { } func (l linuxMenuItem) setChecked(checked bool) { - globalApplication.dispatchOnMainThread(func() { + invokeSync(func() { l.blockSignal() defer l.unBlockSignal() menuItemSetChecked(l.native, checked) @@ -62,7 +62,7 @@ func (l linuxMenuItem) setChecked(checked bool) { } func (l linuxMenuItem) setHidden(hidden bool) { - globalApplication.dispatchOnMainThread(func() { + invokeSync(func() { l.blockSignal() defer l.unBlockSignal() widgetSetVisible(l.native, hidden) diff --git a/v3/pkg/application/screen_linux.go b/v3/pkg/application/screen_linux.go index 6f8026a60..07b021074 100644 --- a/v3/pkg/application/screen_linux.go +++ b/v3/pkg/application/screen_linux.go @@ -16,7 +16,7 @@ func (m *linuxApp) getScreens() ([]*Screen, error) { var screens []*Screen var err error wg.Add(1) - globalApplication.dispatchOnMainThread(func() { + invokeSync(func() { screens, err = getScreens(m.application) wg.Done() }) diff --git a/v3/pkg/application/systemtray.go b/v3/pkg/application/systemtray.go index 4fd9d6ddb..bf805762d 100644 --- a/v3/pkg/application/systemtray.go +++ b/v3/pkg/application/systemtray.go @@ -2,10 +2,11 @@ package application import ( "fmt" - "github.com/wailsapp/wails/v3/pkg/events" "runtime" "sync" "time" + + "github.com/wailsapp/wails/v3/pkg/events" ) type IconPosition int @@ -149,6 +150,7 @@ func (s *SystemTray) SetDarkModeIcon(icon []byte) *SystemTray { } func (s *SystemTray) SetMenu(menu *Menu) *SystemTray { + fmt.Println("SystemTray.SetMenu", menu, s.impl) if s.impl == nil { s.menu = menu } else { diff --git a/v3/pkg/application/systemtray_linux.go b/v3/pkg/application/systemtray_linux.go index f553bc812..2f496868e 100644 --- a/v3/pkg/application/systemtray_linux.go +++ b/v3/pkg/application/systemtray_linux.go @@ -2,6 +2,8 @@ package application +import "fmt" + type linuxSystemTray struct { id uint label string @@ -17,6 +19,7 @@ func (s *linuxSystemTray) setIconPosition(position int) { } func (s *linuxSystemTray) setMenu(menu *Menu) { + fmt.Println("linuxSystemTray.SetMenu") s.menu = menu } @@ -33,7 +36,7 @@ func (s *linuxSystemTray) bounds() (*Rect, error) { } func (s *linuxSystemTray) run() { - globalApplication.dispatchOnMainThread(func() { + invokeSync(func() { // if s.nsStatusItem != nil { // Fatal("System tray '%d' already running", s.id) // } @@ -57,7 +60,7 @@ func (s *linuxSystemTray) run() { func (s *linuxSystemTray) setIcon(icon []byte) { s.icon = icon - globalApplication.dispatchOnMainThread(func() { + invokeSync(func() { // s.nsImage = unsafe.Pointer(C.imageFromBytes((*C.uchar)(&icon[0]), C.int(len(icon)))) // C.systemTraySetIcon(s.nsStatusItem, s.nsImage, C.int(s.iconPosition), C.bool(s.isTemplateIcon)) }) @@ -65,7 +68,7 @@ func (s *linuxSystemTray) setIcon(icon []byte) { func (s *linuxSystemTray) setDarkModeIcon(icon []byte) { s.icon = icon - globalApplication.dispatchOnMainThread(func() { + invokeSync(func() { // s.nsImage = unsafe.Pointer(C.imageFromBytes((*C.uchar)(&icon[0]), C.int(len(icon)))) // C.systemTraySetIcon(s.nsStatusItem, s.nsImage, C.int(s.iconPosition), C.bool(s.isTemplateIcon)) }) diff --git a/v3/pkg/application/webview_window.go b/v3/pkg/application/webview_window.go index 04d5685da..14dbb6e8e 100644 --- a/v3/pkg/application/webview_window.go +++ b/v3/pkg/application/webview_window.go @@ -3,11 +3,12 @@ package application import ( "errors" "fmt" - "github.com/samber/lo" - "github.com/wailsapp/wails/v3/pkg/events" "runtime" "strings" "sync" + + "github.com/samber/lo" + "github.com/wailsapp/wails/v3/pkg/events" ) type ( @@ -277,7 +278,7 @@ func (w *WebviewWindow) Show() *WebviewWindow { return w } if w.impl == nil { - w.run() + invokeSync(w.run) return w } invokeSync(w.impl.show) @@ -657,10 +658,12 @@ func (w *WebviewWindow) Destroy() { if w.impl == nil { return } + // Cancel the callbacks for _, cancelFunc := range w.cancellers { cancelFunc() } + invokeSync(w.impl.destroy) } diff --git a/v3/pkg/application/webview_window_linux.go b/v3/pkg/application/webview_window_linux.go index fec5948b3..d35aeae99 100644 --- a/v3/pkg/application/webview_window_linux.go +++ b/v3/pkg/application/webview_window_linux.go @@ -5,7 +5,6 @@ package application import ( "fmt" "net/url" - "sync" "unsafe" "github.com/wailsapp/wails/v3/pkg/events" @@ -104,21 +103,15 @@ func (w *linuxWebviewWindow) getScreen() (*Screen, error) { } func (w *linuxWebviewWindow) focus() { - globalApplication.dispatchOnMainThread(func() { - windowPresent(w.window) - }) + windowPresent(w.window) } func (w *linuxWebviewWindow) show() { - globalApplication.dispatchOnMainThread(func() { - windowShow(w.window) - }) + windowShow(w.window) } func (w *linuxWebviewWindow) hide() { - globalApplication.dispatchOnMainThread(func() { - windowHide(w.window) - }) + windowHide(w.window) } func (w *linuxWebviewWindow) isNormal() bool { @@ -140,26 +133,21 @@ func (w *linuxWebviewWindow) disableSizeConstraints() { } func (w *linuxWebviewWindow) unfullscreen() { - fmt.Println("unfullscreen") - globalApplication.dispatchOnMainThread(func() { - windowUnfullscreen(w.window) - w.unmaximise() - }) + windowUnfullscreen(w.window) + w.unmaximise() } func (w *linuxWebviewWindow) fullscreen() { w.maximise() w.lastWidth, w.lastHeight = w.size() - globalApplication.dispatchOnMainThread(func() { - x, y, width, height, scale := windowGetCurrentMonitorGeometry(w.window) - if x == -1 && y == -1 && width == -1 && height == -1 { - return - } - w.setMinMaxSize(0, 0, width*scale, height*scale) - w.setSize(width*scale, height*scale) - windowFullscreen(w.window) - w.setRelativePosition(0, 0) - }) + x, y, width, height, scale := windowGetCurrentMonitorGeometry(w.window) + if x == -1 && y == -1 && width == -1 && height == -1 { + return + } + w.setMinMaxSize(0, 0, width*scale, height*scale) + w.setSize(width*scale, height*scale) + windowFullscreen(w.window) + w.setRelativePosition(0, 0) } func (w *linuxWebviewWindow) unminimise() { @@ -222,19 +210,17 @@ func (w *linuxWebviewWindow) forceReload() { } func (w *linuxWebviewWindow) center() { - globalApplication.dispatchOnMainThread(func() { - x, y, width, height, _ := windowGetCurrentMonitorGeometry(w.window) - if x == -1 && y == -1 && width == -1 && height == -1 { - return - } - windowWidth, windowHeight := windowGetSize(w.window) + x, y, width, height, _ := windowGetCurrentMonitorGeometry(w.window) + if x == -1 && y == -1 && width == -1 && height == -1 { + return + } + windowWidth, windowHeight := windowGetSize(w.window) - newX := ((width - int(windowWidth)) / 2) + x - newY := ((height - int(windowHeight)) / 2) + y + newX := ((width - int(windowWidth)) / 2) + x + newY := ((height - int(windowHeight)) / 2) + y - // Place the window at the center of the monitor - windowMove(w.window, newX, newY) - }) + // Place the window at the center of the monitor + windowMove(w.window, newX, newY) } func (w *linuxWebviewWindow) isMinimised() bool { @@ -242,27 +228,11 @@ func (w *linuxWebviewWindow) isMinimised() bool { } func (w *linuxWebviewWindow) isMaximised() bool { - return w.syncMainThreadReturningBool(func() bool { - return windowIsMaximized(w.window) - }) + return windowIsMaximized(w.window) } func (w *linuxWebviewWindow) isFullscreen() bool { - return w.syncMainThreadReturningBool(func() bool { - return windowIsFullscreen(w.window) - }) -} - -func (w *linuxWebviewWindow) syncMainThreadReturningBool(fn func() bool) bool { - var wg sync.WaitGroup - wg.Add(1) - var result bool - globalApplication.dispatchOnMainThread(func() { - result = fn() - wg.Done() - }) - wg.Wait() - return result + return windowIsFullscreen(w.window) } func (w *linuxWebviewWindow) restore() { @@ -344,26 +314,12 @@ func (w *linuxWebviewWindow) toggleDevTools() { } func (w *linuxWebviewWindow) size() (int, int) { - /* var width, height C.int - var wg sync.WaitGroup - wg.Add(1) - globalApplication.dispatchOnMainThread(func() { - - C.gtk_window_get_size((*C.GtkWindow)(w.window), &width, &height) - wg.Done() - }) - wg.Wait() - return int(width), int(height) - */ - // Does this need to be guarded? return windowGetSize(w.window) } func (w *linuxWebviewWindow) setRelativePosition(x, y int) { mx, my, _, _, _ := windowGetCurrentMonitorGeometry(w.window) - globalApplication.dispatchOnMainThread(func() { - windowMove(w.window, x+mx, y+my) - }) + windowMove(w.window, x+mx, y+my) } func (w *linuxWebviewWindow) width() int { @@ -383,13 +339,7 @@ func (w *linuxWebviewWindow) setAbsolutePosition(x int, y int) { func (w *linuxWebviewWindow) absolutePosition() (int, int) { var x, y int - var wg sync.WaitGroup - wg.Add(1) - globalApplication.dispatchOnMainThread(func() { - x, y = windowGetAbsolutePosition(w.window) - wg.Done() - }) - wg.Wait() + x, y = windowGetAbsolutePosition(w.window) return x, y } @@ -399,76 +349,74 @@ func (w *linuxWebviewWindow) run() { } app := getNativeApplication() - menu := app.applicationMenu - globalApplication.dispatchOnMainThread(func() { - w.window, w.webview = windowNew(app.application, menu, w.parent.id, 1) - app.registerWindow(w.window, w.parent.id) // record our mapping - w.connectSignals() - if w.parent.options.EnableDragAndDrop { - w.enableDND() - } - w.setTitle(w.parent.options.Title) - w.setAlwaysOnTop(w.parent.options.AlwaysOnTop) - w.setResizable(!w.parent.options.DisableResize) - // only set min/max size if actually set - if w.parent.options.MinWidth != 0 && - w.parent.options.MinHeight != 0 && - w.parent.options.MaxWidth != 0 && - w.parent.options.MaxHeight != 0 { - w.setMinMaxSize( - w.parent.options.MinWidth, - w.parent.options.MinHeight, - w.parent.options.MaxWidth, - w.parent.options.MaxHeight, - ) - } - w.setSize(w.parent.options.Width, w.parent.options.Height) - w.setZoom(w.parent.options.Zoom) - w.setBackgroundColour(w.parent.options.BackgroundColour) - w.setFrameless(w.parent.options.Frameless) + menu := app.getApplicationMenu() + w.window, w.webview, w.vbox = windowNew(app.application, menu, w.parent.id, 1) + app.registerWindow(w.window, w.parent.id) // record our mapping + w.connectSignals() + if w.parent.options.EnableDragAndDrop { + w.enableDND() + } + w.setTitle(w.parent.options.Title) + w.setAlwaysOnTop(w.parent.options.AlwaysOnTop) + w.setResizable(!w.parent.options.DisableResize) + // only set min/max size if actually set + if w.parent.options.MinWidth != 0 && + w.parent.options.MinHeight != 0 && + w.parent.options.MaxWidth != 0 && + w.parent.options.MaxHeight != 0 { + w.setMinMaxSize( + w.parent.options.MinWidth, + w.parent.options.MinHeight, + w.parent.options.MaxWidth, + w.parent.options.MaxHeight, + ) + } + w.setSize(w.parent.options.Width, w.parent.options.Height) + w.setZoom(w.parent.options.Zoom) + w.setBackgroundColour(w.parent.options.BackgroundColour) + w.setFrameless(w.parent.options.Frameless) + if w.parent.options.X != 0 || w.parent.options.Y != 0 { + w.setRelativePosition(w.parent.options.X, w.parent.options.Y) + } else { + fmt.Println("attempting to set in the center") + w.center() + } + switch w.parent.options.StartState { + case WindowStateMaximised: + w.maximise() + case WindowStateMinimised: + w.minimise() + case WindowStateFullscreen: + w.fullscreen() + } + + if w.parent.options.URL != "" { + w.setURL(w.parent.options.URL) + } + // We need to wait for the HTML to load before we can execute the javascript + // FIXME: What event is this? DomReady? + w.parent.On(events.Mac.WebViewDidFinishNavigation, func(_ *WindowEvent) { + if w.parent.options.JS != "" { + w.execJS(w.parent.options.JS) + } + if w.parent.options.CSS != "" { + js := fmt.Sprintf("(function() { var style = document.createElement('style'); style.appendChild(document.createTextNode('%s')); document.head.appendChild(style); })();", w.parent.options.CSS) + w.execJS(js) + } + }) + if w.parent.options.HTML != "" { + w.setHTML(w.parent.options.HTML) + } + if !w.parent.options.Hidden { + w.show() if w.parent.options.X != 0 || w.parent.options.Y != 0 { w.setRelativePosition(w.parent.options.X, w.parent.options.Y) } else { - fmt.Println("attempting to set in the center") - w.center() + w.center() // needs to be queued until after GTK starts up! } - switch w.parent.options.StartState { - case WindowStateMaximised: - w.maximise() - case WindowStateMinimised: - w.minimise() - case WindowStateFullscreen: - w.fullscreen() - } - - if w.parent.options.URL != "" { - w.setURL(w.parent.options.URL) - } - // We need to wait for the HTML to load before we can execute the javascript - // FIXME: What event is this? DomReady? - w.parent.On(events.Mac.WebViewDidFinishNavigation, func(_ *WindowEvent) { - if w.parent.options.JS != "" { - w.execJS(w.parent.options.JS) - } - if w.parent.options.CSS != "" { - js := fmt.Sprintf("(function() { var style = document.createElement('style'); style.appendChild(document.createTextNode('%s')); document.head.appendChild(style); })();", w.parent.options.CSS) - w.execJS(js) - } - }) - if w.parent.options.HTML != "" { - w.setHTML(w.parent.options.HTML) - } - if !w.parent.options.Hidden { - w.show() - if w.parent.options.X != 0 || w.parent.options.Y != 0 { - w.setRelativePosition(w.parent.options.X, w.parent.options.Y) - } else { - w.center() // needs to be queued until after GTK starts up! - } - } - }) + } } func (w *linuxWebviewWindow) setTransparent() { @@ -479,18 +427,12 @@ func (w *linuxWebviewWindow) setBackgroundColour(colour RGBA) { if colour.Alpha < 255 { w.setTransparent() } - windowSetBackgroundColour(w.webview, colour) + windowSetBackgroundColour(w.vbox, w.webview, colour) } func (w *linuxWebviewWindow) relativePosition() (int, int) { var x, y int - var wg sync.WaitGroup - wg.Add(1) - go globalApplication.dispatchOnMainThread(func() { - x, y = windowGetRelativePosition(w.window) - wg.Done() - }) - wg.Wait() + x, y = windowGetRelativePosition(w.window) return x, y } @@ -499,9 +441,7 @@ func (w *linuxWebviewWindow) destroy() { } func (w *linuxWebviewWindow) setEnabled(enabled bool) { - globalApplication.dispatchOnMainThread(func() { - widgetSetSensitive(w.window, enabled) - }) + widgetSetSensitive(w.window, enabled) } func (w *linuxWebviewWindow) setHTML(html string) {