From ed2590e30e1da5a4b3c53915dde60f72f3a13d2c Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Tue, 2 Apr 2019 19:26:23 +0200 Subject: [PATCH] ui/app,apps/gophers,apps/hello: revert NewWindow to CreateWindow It turns out we already support multiple windows on Android: when the activity is recreated. This reverts commit f21b5eb1df39b00631492c7c8cb2c802fa50ca74. Signed-off-by: Elias Naur --- apps/gophers/main.go | 25 ++++++++++++++--------- apps/hello/hello.go | 48 +++++++++++++++++++++++--------------------- ui/app/app.go | 13 ++++++++---- ui/app/os_android.go | 6 ++---- ui/app/os_ios.go | 6 ++---- ui/app/os_macos.go | 35 ++++++++++++++++++-------------- ui/app/os_wayland.go | 10 +++++---- ui/app/os_windows.go | 17 +++++++--------- ui/app/window.go | 10 ++++----- 9 files changed, 90 insertions(+), 80 deletions(-) diff --git a/apps/gophers/main.go b/apps/gophers/main.go index dd325d87..4bc3cb9b 100644 --- a/apps/gophers/main.go +++ b/apps/gophers/main.go @@ -121,6 +121,14 @@ func main() { fmt.Println("The quota for anonymous GitHub API access is very low. Specify a token with -token to avoid quota errors.") fmt.Println("See https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line.") } + err := app.CreateWindow(&app.WindowOptions{ + Width: ui.Dp(400), + Height: ui.Dp(800), + Title: "Gophers", + }) + if err != nil { + log.Fatal(err) + } app.Main() } @@ -141,16 +149,13 @@ func init() { fonts.italic = mustLoadFont(goitalic.TTF) fonts.mono = mustLoadFont(gomono.TTF) go func() { - w, err := app.NewWindow(&app.WindowOptions{ - Width: ui.Dp(400), - Height: ui.Dp(800), - Title: "Gophers", - }) - if err != nil { - log.Fatal(err) - } - if err := newApp(w).run(); err != nil { - log.Fatal(err) + for w := range app.Windows() { + w := w + go func() { + if err := newApp(w).run(); err != nil { + log.Fatal(err) + } + }() } }() } diff --git a/apps/hello/hello.go b/apps/hello/hello.go index 35f35058..574e19da 100644 --- a/apps/hello/hello.go +++ b/apps/hello/hello.go @@ -20,14 +20,24 @@ import ( ) func main() { - // Never called on mobile, blocks forever on - // desktop. + err := app.CreateWindow(nil) + if err != nil { + log.Fatal(err) + } app.Main() } // On iOS and Android main will never be called, so // setting up the window must run in an init function. func init() { + go func() { + for w := range app.Windows() { + go loop(w) + } + }() +} + +func loop(w *app.Window) { regular, err := sfnt.Parse(goregular.TTF) if err != nil { panic("failed to load font") @@ -35,26 +45,18 @@ func init() { var faces measure.Faces black := &image.Uniform{color.Black} face := faces.For(regular, ui.Dp(50)) - // On iOS and Android app.NewWindow blocks, waiting - // for the platform to create a window. - go func() { - w, err := app.NewWindow(nil) - if err != nil { - log.Fatal(err) + for w.IsAlive() { + e := <-w.Events() + switch e := e.(type) { + case app.Draw: + faces.Cfg = e.Config + cs := layout.ExactConstraints(w.Size()) + root, _ := (text.Label{Src: black, Face: face, Text: "Hello, World!"}).Layout(cs) + w.Draw(root) + faces.Frame() } - for w.IsAlive() { - e := <-w.Events() - switch e := e.(type) { - case app.Draw: - faces.Cfg = e.Config - cs := layout.ExactConstraints(w.Size()) - root, _ := (text.Label{Src: black, Face: face, Text: "Hello, World!"}).Layout(cs) - w.Draw(root) - faces.Frame() - } - } - if w.Err() != nil { - log.Fatal(err) - } - }() + } + if w.Err() != nil { + log.Fatal(err) + } } diff --git a/ui/app/app.go b/ui/app/app.go index 73ec9c57..814f4392 100644 --- a/ui/app/app.go +++ b/ui/app/app.go @@ -59,12 +59,13 @@ const ( // Set it with the go tool linker flag -X. var extraArgs string -// NewWindow creates a new window for a set of window +var windows = make(chan *Window) + +// CreateWindow creates a new window for a set of window // options. The options are hints; the platform is free to // ignore or adjust them. -// If the current program is running on iOS and Android, -// NewWindow the window previously created by the platform. -func NewWindow(opts *WindowOptions) (*Window, error) { +// CreateWindow is not supported on iOS and Android. +func CreateWindow(opts *WindowOptions) error { if opts == nil { opts = &WindowOptions{ Width: ui.Dp(800), @@ -78,6 +79,10 @@ func NewWindow(opts *WindowOptions) (*Window, error) { return createWindow(opts) } +func Windows() <-chan *Window { + return windows +} + func (l Stage) String() string { switch l { case StageDead: diff --git a/ui/app/os_android.go b/ui/app/os_android.go index 30a6d549..52af6ccc 100644 --- a/ui/app/os_android.go +++ b/ui/app/os_android.go @@ -55,8 +55,6 @@ type window struct { var theJVM *C.JavaVM -var windows = make(chan *Window) - var views = make(map[C.jlong]*window) func jniGetMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID { @@ -381,6 +379,6 @@ func Main() { panic("unreachable") } -func createWindow(opts *WindowOptions) (*Window, error) { - return <-windows, nil +func createWindow(opts *WindowOptions) error { + return errors.New("createWindow not supported") } diff --git a/ui/app/os_ios.go b/ui/app/os_ios.go index 7f4010f0..6a3f40f5 100644 --- a/ui/app/os_ios.go +++ b/ui/app/os_ios.go @@ -42,8 +42,6 @@ var layerFactory func() uintptr var views = make(map[C.CFTypeRef]*window) -var windows = make(chan *Window) - func init() { // Darwin requires UI operations happen on the main thread only. runtime.LockOSThread() @@ -235,8 +233,8 @@ func (w *window) setTextInput(s key.TextInputState) { } } -func createWindow(opts *WindowOptions) (*Window, error) { - return <-windows, nil +func createWindow(opts *WindowOptions) error { + panic("unsupported") } func Main() { diff --git a/ui/app/os_macos.go b/ui/app/os_macos.go index b07e5e90..63960ef3 100644 --- a/ui/app/os_macos.go +++ b/ui/app/os_macos.go @@ -15,6 +15,7 @@ import ( "errors" "image" "runtime" + "sync" "time" "unsafe" @@ -35,15 +36,13 @@ type window struct { stage Stage } -type windowError struct { - window *Window - err error +// Only support one main window for now. +var singleWindow struct { + mu sync.Mutex + hasOpts bool + opts *WindowOptions } -var windowOpts = make(chan *WindowOptions) - -var windows = make(chan windowError) - var viewFactory func() uintptr var views = make(map[C.CFTypeRef]*window) @@ -163,6 +162,7 @@ func gio_onTerminate(view C.CFTypeRef) { w := views[view] delete(views, view) w.setStage(StageDead) + close(windows) } //export gio_onHide @@ -185,23 +185,28 @@ func gio_onCreate(view C.CFTypeRef) { ow := newWindow(w) w.w = ow views[view] = w - windows <- windowError{window: ow} + windows <- ow } -func createWindow(opts *WindowOptions) (*Window, error) { - windowOpts <- opts - werr := <-windows - return werr.window, werr.err +func createWindow(opts *WindowOptions) error { + singleWindow.mu.Lock() + defer singleWindow.mu.Unlock() + if singleWindow.hasOpts { + panic("only one window supported") + } + singleWindow.opts = opts + singleWindow.hasOpts = true + return nil } func Main() { view := C.CFTypeRef(viewFactory()) if view == 0 { - windows <- windowError{err: errors.New("CreateWindow: failed to create view")} - return + // TODO: return this error from CreateWindow. + panic(errors.New("CreateWindow: failed to create view")) } cfg := getConfig() - opts := <-windowOpts + opts := singleWindow.opts w := cfg.Pixels(opts.Width) h := cfg.Pixels(opts.Height) title := C.CString(opts.Title) diff --git a/ui/app/os_wayland.go b/ui/app/os_wayland.go index adb55bb4..f83cd22f 100644 --- a/ui/app/os_wayland.go +++ b/ui/app/os_wayland.go @@ -141,28 +141,30 @@ func Main() { <-mainDone } -func createWindow(opts *WindowOptions) (*Window, error) { +func createWindow(opts *WindowOptions) error { connMu.Lock() defer connMu.Unlock() if len(winMap) > 0 { panic("multiple windows are not supported") } if err := waylandConnect(); err != nil { - return nil, err + return err } w, err := createNativeWindow(opts) if err != nil { conn.destroy() - return nil, err + return err } go func() { + windows <- w.w w.setStage(StageVisible) w.loop() w.destroy() conn.destroy() + close(windows) close(mainDone) }() - return w.w, nil + return nil } func createNativeWindow(opts *WindowOptions) (*window, error) { diff --git a/ui/app/os_windows.go b/ui/app/os_windows.go index 651d11a7..7ce3a5f2 100644 --- a/ui/app/os_windows.go +++ b/ui/app/os_windows.go @@ -157,37 +157,34 @@ func Main() { <-mainDone } -func createWindow(opts *WindowOptions) (*Window, error) { +func createWindow(opts *WindowOptions) error { onceMu.Lock() defer onceMu.Unlock() if len(winMap) > 0 { panic("multiple windows are not supported") } - type windowError struct { - window *Window - err error - } - cerr := make(chan windowError) + cerr := make(chan error) go func() { // Call win32 API from a single OS thread. runtime.LockOSThread() w, err := createNativeWindow(opts) if err != nil { - cerr <- windowError{err: err} + cerr <- err return } defer w.destroy() - cerr <- windowError{w.w, nil} + cerr <- nil + windows <- w.w showWindow(w.hwnd, _SW_SHOWDEFAULT) setForegroundWindow(w.hwnd) setFocus(w.hwnd) if err := w.loop(); err != nil { panic(err) } + close(windows) close(mainDone) }() - werr := <-cerr - return werr.window, werr.err + return <-cerr } func createNativeWindow(opts *WindowOptions) (*window, error) { diff --git a/ui/app/window.go b/ui/app/window.go index f893ca9b..18ab025a 100644 --- a/ui/app/window.go +++ b/ui/app/window.go @@ -36,7 +36,6 @@ type Window struct { mu sync.Mutex stage Stage size image.Point - skipAcks int syncGPU bool animating bool hasNextFrame bool @@ -208,9 +207,11 @@ func (w *Window) event(e Event) { case key.Event: needRedraw = true case ChangeStage: - needAck = true w.stage = e.Stage - w.syncGPU = true + if w.stage > StageDead { + needAck = true + w.syncGPU = true + } case Draw: if e.Size == (image.Point{}) { panic(errors.New("internal error: zero-sized Draw")) @@ -223,9 +224,6 @@ func (w *Window) event(e Event) { w.syncGPU = e.sync w.size = e.Size } - if !needAck { - w.skipAcks++ - } stage := w.stage w.mu.Unlock() if needRedraw {