diff --git a/exp/examples/kitchensink/main.go b/exp/examples/kitchensink/main.go index 9e1f16ef3..220b2970d 100644 --- a/exp/examples/kitchensink/main.go +++ b/exp/examples/kitchensink/main.go @@ -2,6 +2,7 @@ package main import ( "log" + "sync" "time" "github.com/wailsapp/wails/exp/pkg/application" @@ -63,10 +64,12 @@ func main() { println(myWindow.ID(), "WindowWillClose") }) myWindow.On(events.Mac.WindowDidResize, func() { - println(myWindow.ID(), "WindowDidResize") + w, h := myWindow.Size() + println(myWindow.ID(), "WindowDidResize", w, h) }) myWindow.On(events.Mac.WindowDidMove, func() { - println(myWindow.ID(), "WindowDidMove") + x, y := myWindow.Position() + println(myWindow.ID(), "WindowDidMove", x, y) }) myWindow.On(events.Mac.WindowDidMiniaturize, func() { println(myWindow.ID(), "WindowDidMiniaturize") @@ -112,36 +115,28 @@ func main() { }) var myWindow2 *application.Window - go func() { - time.Sleep(2 * time.Second) - myWindow2 = app.NewWindow(&options.Window{ - Title: "#2", - Width: 1024, - Height: 768, - AlwaysOnTop: false, - URL: "https://google.com", - DisableResize: true, - Mac: &options.MacWindow{ - Backdrop: options.MacBackdropTranslucent, - }, - }) - - }() - - go func() { - for { - time.Sleep(5 * time.Second) - println("window 1 is fullscreen?", myWindow.IsFullscreen()) - println("window 2 is fullscreen?", myWindow2.IsFullscreen()) - println("window 1 is maximised?", myWindow.IsMaximised()) - println("window 2 is maximised?", myWindow2.IsMaximised()) - println("window 1 is minimised?", myWindow.IsMinimised()) - println("window 2 is minimised?", myWindow2.IsMinimised()) - } - }() + var myWindow2Lock sync.RWMutex + myWindow2 = app.NewWindow(&options.Window{ + Title: "#2", + Width: 1024, + Height: 768, + AlwaysOnTop: false, + URL: "https://google.com", + Mac: &options.MacWindow{ + Backdrop: options.MacBackdropTranslucent, + }, + }) + //myWindow2.On(events.Mac.WindowDidMove, func() { + // myWindow2Lock.RLock() + // x, y := myWindow2.Position() + // println(myWindow2.ID(), "WindowDidMove: ", x, y) + // myWindow2Lock.RUnlock() + //}) + // go func() { time.Sleep(5 * time.Second) + myWindow2Lock.RLock() myWindow.SetTitle("Wooooo") myWindow.SetAlwaysOnTop(true) myWindow2.EnableDevTools() @@ -150,6 +145,7 @@ func main() { myWindow.SetMinSize(600, 600) myWindow.SetMaxSize(650, 650) myWindow.Center() + myWindow2Lock.RUnlock() }() diff --git a/exp/pkg/application/application.go b/exp/pkg/application/application.go index a2dd95210..2a64fb3ed 100644 --- a/exp/pkg/application/application.go +++ b/exp/pkg/application/application.go @@ -2,10 +2,16 @@ package application import ( "log" + "runtime" + "sync" "github.com/wailsapp/wails/exp/pkg/options" ) +func init() { + runtime.LockOSThread() +} + // Messages sent from javascript get routed here type windowMessage struct { windowId uint @@ -22,8 +28,10 @@ type App struct { options *options.Application applicationEventListeners map[uint][]func() - windows map[uint]*Window - windowAliases map[string]uint + windows map[uint]*Window + windowsLock sync.Mutex + windowAliases map[string]uint + windowAliasesLock sync.Mutex // Running running bool @@ -47,12 +55,17 @@ func (a *App) NewWindow(options *options.Window) *Window { if a.windows == nil { a.windows = make(map[uint]*Window) } + a.windowsLock.Lock() a.windows[id] = newWindow + a.windowsLock.Unlock() + if options.Alias != "" { if a.windowAliases == nil { a.windowAliases = make(map[string]uint) } + a.windowAliasesLock.Lock() a.windowAliases[options.Alias] = id + a.windowAliasesLock.Unlock() } if a.running { newWindow.Run() @@ -93,7 +106,9 @@ func (a *App) Run() error { func (a *App) handleMessage(event *windowMessage) { // Get window from window map + a.windowsLock.Lock() window, ok := a.windows[event.windowId] + a.windowsLock.Unlock() if !ok { log.Printf("Window #%d not found", event.windowId) return @@ -104,7 +119,9 @@ func (a *App) handleMessage(event *windowMessage) { func (a *App) handleWindowEvent(event *WindowEvent) { // Get window from window map + a.windowsLock.Lock() window, ok := a.windows[event.WindowID] + a.windowsLock.Unlock() if !ok { log.Printf("Window #%d not found", event.WindowID) return diff --git a/exp/pkg/application/mainthread.go b/exp/pkg/application/mainthread.go index 4e201c71e..c91fec8b0 100644 --- a/exp/pkg/application/mainthread.go +++ b/exp/pkg/application/mainthread.go @@ -6,9 +6,13 @@ package application extern void dispatch(unsigned int id); */ import "C" -import "strconv" +import ( + "os" + "sync" +) var mainThreadFuntionStore = make(map[uint]func()) +var mainThreadFuntionStoreLock sync.RWMutex func generateFunctionStoreID() uint { startID := 0 @@ -24,18 +28,23 @@ func generateFunctionStoreID() uint { } func Dispatch(fn func()) { + mainThreadFuntionStoreLock.Lock() id := generateFunctionStoreID() mainThreadFuntionStore[id] = fn + mainThreadFuntionStoreLock.Unlock() C.dispatch(C.uint(id)) } //export dispatchCallback -func dispatchCallback(id C.uint) { - - fn := mainThreadFuntionStore[uint(id)] +func dispatchCallback(callbackID C.uint) { + mainThreadFuntionStoreLock.RLock() + id := uint(callbackID) + fn := mainThreadFuntionStore[id] if fn == nil { - panic("dispatchCallback called with invalid id " + strconv.Itoa(int(id))) + println("***** dispatchCallback called with invalid id: ", id) + os.Exit(1) } + delete(mainThreadFuntionStore, id) + mainThreadFuntionStoreLock.RUnlock() fn() - delete(mainThreadFuntionStore, uint(id)) } diff --git a/exp/pkg/application/window.go b/exp/pkg/application/window.go index b62d539ae..84d83cbcc 100644 --- a/exp/pkg/application/window.go +++ b/exp/pkg/application/window.go @@ -27,14 +27,20 @@ type windowImpl interface { setBackgroundColor(color *options.RGBA) run() center() + size() (int, int) + width() int + height() int + position() (int, int) } type Window struct { - options *options.Window - impl windowImpl - id uint + options *options.Window + impl windowImpl + implLock sync.RWMutex + id uint - eventListeners map[uint][]func() + eventListeners map[uint][]func() + eventListenersLock sync.RWMutex } var windowID uint @@ -48,7 +54,6 @@ func getWindowID() uint { } func NewWindow(options *options.Window) *Window { - return &Window{ id: getWindowID(), options: options, @@ -57,6 +62,8 @@ func NewWindow(options *options.Window) *Window { } func (w *Window) SetTitle(title string) { + w.implLock.RLock() + defer w.implLock.RUnlock() if w.impl == nil { w.options.Title = title return @@ -74,7 +81,9 @@ func (w *Window) SetSize(width, height int) { } func (w *Window) Run() { + w.implLock.Lock() w.impl = newWindowImpl(w.id, w.options) + w.implLock.Unlock() w.impl.run() } @@ -191,8 +200,18 @@ func (w *Window) IsMaximised() bool { return w.impl.isMaximised() } +// Size returns the current size of the window +func (w *Window) Size() (int, int) { + if w.impl == nil { + return 0, 0 + } + return w.impl.size() +} + // IsFullscreen returns true if the window is fullscreen func (w *Window) IsFullscreen() bool { + w.implLock.RLock() + defer w.implLock.RUnlock() if w.impl == nil { return false } @@ -223,15 +242,42 @@ func (w *Window) Center() { } func (w *Window) On(eventID uint, callback func()) { + w.eventListenersLock.Lock() w.eventListeners[eventID] = append(w.eventListeners[eventID], callback) + w.eventListenersLock.Unlock() } func (w *Window) handleWindowEvent(id uint) { + w.eventListenersLock.RLock() for _, callback := range w.eventListeners[id] { - callback() + go callback() } + w.eventListenersLock.RUnlock() } func (w *Window) ID() uint { return w.id } + +func (w *Window) Width() int { + if w.impl == nil { + return 0 + } + return w.impl.width() +} + +func (w *Window) Height() int { + if w.impl == nil { + return 0 + } + return w.impl.height() +} + +func (w *Window) Position() (int, int) { + w.implLock.RLock() + defer w.implLock.RUnlock() + if w.impl == nil { + return 0, 0 + } + return w.impl.position() +} diff --git a/exp/pkg/application/window_darwin.go b/exp/pkg/application/window_darwin.go index 9c9b74761..a2eb3f5bb 100644 --- a/exp/pkg/application/window_darwin.go +++ b/exp/pkg/application/window_darwin.go @@ -394,8 +394,47 @@ void windowCenter(void* nsWindow) { }); } +// Get the current size of the window +void windowGetSize(void* nsWindow, int* width, int* height) { + // get main window + NSWindow* window = (NSWindow*)nsWindow; + // get window frame + NSRect frame = [window frame]; + // set width and height + *width = frame.size.width; + *height = frame.size.height; +} +// Get window width +int windowGetWidth(void* nsWindow) { + // get main window + NSWindow* window = (NSWindow*)nsWindow; + // get window frame + NSRect frame = [window frame]; + // return width + return frame.size.width; +} +// Get window height +int windowGetHeight(void* nsWindow) { + // get main window + NSWindow* window = (NSWindow*)nsWindow; + // get window frame + NSRect frame = [window frame]; + // return height + return frame.size.height; +} + +// Get window position +void windowGetPosition(void* nsWindow, int* x, int* y) { + // get main window + NSWindow* window = (NSWindow*)nsWindow; + // get window frame + NSRect frame = [window frame]; + // set x and y + *x = frame.origin.x; + *y = frame.origin.y; +} */ @@ -511,6 +550,41 @@ func (w *macosWindow) enableDevTools() { C.windowEnableDevTools(w.nsWindow) } +func (w *macosWindow) size() (int, int) { + var width, height C.int + var wg sync.WaitGroup + wg.Add(1) + Dispatch(func() { + C.windowGetSize(w.nsWindow, &width, &height) + wg.Done() + }) + wg.Wait() + return int(width), int(height) +} + +func (w *macosWindow) width() int { + var width C.int + var wg sync.WaitGroup + wg.Add(1) + Dispatch(func() { + width = C.windowGetWidth(w.nsWindow) + wg.Done() + }) + wg.Wait() + return int(width) +} +func (w *macosWindow) height() int { + var height C.int + var wg sync.WaitGroup + wg.Add(1) + Dispatch(func() { + height = C.windowGetHeight(w.nsWindow) + wg.Done() + }) + wg.Wait() + return int(height) +} + func (w *macosWindow) run() { Dispatch(func() { w.nsWindow = C.windowNew(C.uint(w.id), C.int(w.options.Width), C.int(w.options.Height)) @@ -575,6 +649,14 @@ func (w *macosWindow) setBackgroundColor(colour *options.RGBA) { C.webviewSetBackgroundColor(w.nsWindow, C.int(colour.Red), C.int(colour.Green), C.int(colour.Blue), C.int(colour.Alpha)) } -func (w *macosWindow) Center() { - C.windowCenter(w.nsWindow) +func (w *macosWindow) position() (int, int) { + var x, y C.int + var wg sync.WaitGroup + wg.Add(1) + go Dispatch(func() { + C.windowGetPosition(w.nsWindow, &x, &y) + wg.Done() + }) + wg.Wait() + return int(x), int(y) }