mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 09:55:40 +00:00
ui/app: create windows directly
Replace CreateWindow with NewWindow that immediately creates a Window ready to use. Drop the Windows channel of windows created by the system. For iOS and Android where the system creates the windows, let them rendezvous with the window created in the first NewWindow call. Android is further changed so that destroying and re-creating the Java Activity simply reconnects with the original Window. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+38
-24
@@ -3,6 +3,7 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"image"
|
"image"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
@@ -47,6 +48,17 @@ type CommandEvent struct {
|
|||||||
type Stage uint8
|
type Stage uint8
|
||||||
type CommandType uint8
|
type CommandType uint8
|
||||||
|
|
||||||
|
type windowRendezvous struct {
|
||||||
|
in chan windowAndOptions
|
||||||
|
out chan windowAndOptions
|
||||||
|
errs chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
type windowAndOptions struct {
|
||||||
|
window *Window
|
||||||
|
opts *WindowOptions
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StageDead Stage = iota
|
StageDead Stage = iota
|
||||||
StagePaused
|
StagePaused
|
||||||
@@ -79,30 +91,6 @@ const (
|
|||||||
// Set with the go linker flag -X.
|
// Set with the go linker flag -X.
|
||||||
var extraArgs string
|
var extraArgs string
|
||||||
|
|
||||||
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.
|
|
||||||
// CreateWindow is not supported on iOS and Android.
|
|
||||||
func CreateWindow(opts *WindowOptions) error {
|
|
||||||
if opts == nil {
|
|
||||||
opts = &WindowOptions{
|
|
||||||
Width: ui.Dp(800),
|
|
||||||
Height: ui.Dp(600),
|
|
||||||
Title: "Gio program",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if opts.Width.V <= 0 || opts.Height.V <= 0 {
|
|
||||||
panic("window width and height must be larger than 0")
|
|
||||||
}
|
|
||||||
return createWindow(opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Windows() <-chan *Window {
|
|
||||||
return windows
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l Stage) String() string {
|
func (l Stage) String() string {
|
||||||
switch l {
|
switch l {
|
||||||
case StageDead:
|
case StageDead:
|
||||||
@@ -164,3 +152,29 @@ func (c *Config) Px(v ui.Value) int {
|
|||||||
}
|
}
|
||||||
return int(math.Round(float64(r)))
|
return int(math.Round(float64(r)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newWindowRendezvous() *windowRendezvous {
|
||||||
|
wr := &windowRendezvous{
|
||||||
|
in: make(chan windowAndOptions),
|
||||||
|
out: make(chan windowAndOptions),
|
||||||
|
errs: make(chan error),
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
var main windowAndOptions
|
||||||
|
var out chan windowAndOptions
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case w := <-wr.in:
|
||||||
|
var err error
|
||||||
|
if main.window != nil {
|
||||||
|
err = errors.New("multiple windows are not supported")
|
||||||
|
}
|
||||||
|
wr.errs <- err
|
||||||
|
main = w
|
||||||
|
out = wr.out
|
||||||
|
case out <- main:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return wr
|
||||||
|
}
|
||||||
|
|||||||
+11
-8
@@ -58,6 +58,8 @@ var theJVM *C.JavaVM
|
|||||||
|
|
||||||
var views = make(map[C.jlong]*window)
|
var views = make(map[C.jlong]*window)
|
||||||
|
|
||||||
|
var mainWindow = newWindowRendezvous()
|
||||||
|
|
||||||
func jniGetMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID {
|
func jniGetMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID {
|
||||||
m := C.CString(method)
|
m := C.CString(method)
|
||||||
defer C.free(unsafe.Pointer(m))
|
defer C.free(unsafe.Pointer(m))
|
||||||
@@ -91,12 +93,12 @@ func onCreateView(env *C.JNIEnv, class C.jclass, view C.jobject) C.jlong {
|
|||||||
mpostFrameCallback: jniGetMethodID(env, class, "postFrameCallback", "()V"),
|
mpostFrameCallback: jniGetMethodID(env, class, "postFrameCallback", "()V"),
|
||||||
mpostFrameCallbackOnMainThread: jniGetMethodID(env, class, "postFrameCallbackOnMainThread", "()V"),
|
mpostFrameCallbackOnMainThread: jniGetMethodID(env, class, "postFrameCallbackOnMainThread", "()V"),
|
||||||
}
|
}
|
||||||
ow := newWindow(w)
|
wopts := <-mainWindow.out
|
||||||
w.Window = ow
|
w.Window = wopts.window
|
||||||
|
w.Window.setDriver(w)
|
||||||
handle := C.jlong(view)
|
handle := C.jlong(view)
|
||||||
views[handle] = w
|
views[handle] = w
|
||||||
w.loadConfig(env, class)
|
w.loadConfig(env, class)
|
||||||
windows <- ow
|
|
||||||
w.setStage(StagePaused)
|
w.setStage(StagePaused)
|
||||||
return handle
|
return handle
|
||||||
}
|
}
|
||||||
@@ -104,8 +106,8 @@ func onCreateView(env *C.JNIEnv, class C.jclass, view C.jobject) C.jlong {
|
|||||||
//export onDestroyView
|
//export onDestroyView
|
||||||
func onDestroyView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
|
func onDestroyView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
|
||||||
w := views[handle]
|
w := views[handle]
|
||||||
|
w.setDriver(nil)
|
||||||
delete(views, handle)
|
delete(views, handle)
|
||||||
w.setStage(StageDead)
|
|
||||||
C.gio_jni_DeleteGlobalRef(env, w.view)
|
C.gio_jni_DeleteGlobalRef(env, w.view)
|
||||||
w.view = 0
|
w.view = 0
|
||||||
}
|
}
|
||||||
@@ -408,10 +410,11 @@ func (w *window) setTextInput(s key.TextInputState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Main() {
|
func Main() {
|
||||||
// Android runs in c-shared mode where is never reached.
|
// Android runs in c-shared mode where main is never reached.
|
||||||
panic("unreachable")
|
panic("call to Main from outside main")
|
||||||
}
|
}
|
||||||
|
|
||||||
func createWindow(opts *WindowOptions) error {
|
func createWindow(window *Window, opts *WindowOptions) error {
|
||||||
return errors.New("createWindow not supported")
|
mainWindow.in <- windowAndOptions{window, opts}
|
||||||
|
return <-mainWindow.errs
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-6
@@ -38,6 +38,8 @@ type window struct {
|
|||||||
pointerMap []C.CFTypeRef
|
pointerMap []C.CFTypeRef
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mainWindow = newWindowRendezvous()
|
||||||
|
|
||||||
var layerFactory func() uintptr
|
var layerFactory func() uintptr
|
||||||
|
|
||||||
var views = make(map[C.CFTypeRef]*window)
|
var views = make(map[C.CFTypeRef]*window)
|
||||||
@@ -52,13 +54,13 @@ func onCreate(view C.CFTypeRef) {
|
|||||||
w := &window{
|
w := &window{
|
||||||
view: view,
|
view: view,
|
||||||
}
|
}
|
||||||
ow := newWindow(w)
|
wopts := <-mainWindow.out
|
||||||
w.w = ow
|
w.w = wopts.window
|
||||||
|
w.w.setDriver(w)
|
||||||
w.visible.Store(false)
|
w.visible.Store(false)
|
||||||
w.layer = C.CFTypeRef(layerFactory())
|
w.layer = C.CFTypeRef(layerFactory())
|
||||||
C.gio_addLayerToView(view, w.layer)
|
C.gio_addLayerToView(view, w.layer)
|
||||||
views[view] = w
|
views[view] = w
|
||||||
windows <- ow
|
|
||||||
w.w.event(StageEvent{StagePaused})
|
w.w.event(StageEvent{StagePaused})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,11 +247,12 @@ func (w *window) setTextInput(s key.TextInputState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createWindow(opts *WindowOptions) error {
|
func createWindow(win *Window, opts *WindowOptions) error {
|
||||||
panic("unsupported")
|
mainWindow.in <- windowAndOptions{win, opts}
|
||||||
|
return <-mainWindow.errs
|
||||||
}
|
}
|
||||||
|
|
||||||
func Main() {
|
func Main() {
|
||||||
// iOS runs in c-archive mode, so this is never reached.
|
// iOS runs in c-archive mode, so this is never reached.
|
||||||
panic("unreachable")
|
panic("call to Main from outside main")
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -31,7 +31,7 @@ type window struct {
|
|||||||
|
|
||||||
var mainDone = make(chan struct{})
|
var mainDone = make(chan struct{})
|
||||||
|
|
||||||
func createWindow(opts *WindowOptions) error {
|
func createWindow(win *Window, opts *WindowOptions) error {
|
||||||
doc := js.Global().Get("document")
|
doc := js.Global().Get("document")
|
||||||
parent := doc.Call("getElementById", "giowindow")
|
parent := doc.Call("getElementById", "giowindow")
|
||||||
if parent == js.Null() {
|
if parent == js.Null() {
|
||||||
@@ -52,9 +52,9 @@ func createWindow(opts *WindowOptions) error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
w.addEventListeners()
|
w.addEventListeners()
|
||||||
w.w = newWindow(w)
|
w.w = win
|
||||||
go func() {
|
go func() {
|
||||||
windows <- w.w
|
w.w.setDriver(w)
|
||||||
w.focus()
|
w.focus()
|
||||||
w.w.event(StageEvent{StageRunning})
|
w.w.event(StageEvent{StageRunning})
|
||||||
w.draw(true)
|
w.draw(true)
|
||||||
|
|||||||
+9
-21
@@ -15,7 +15,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"image"
|
"image"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
@@ -35,12 +34,7 @@ type window struct {
|
|||||||
stage Stage
|
stage Stage
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only support one main window for now.
|
var mainWindow = newWindowRendezvous()
|
||||||
var singleWindow struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
hasOpts bool
|
|
||||||
opts *WindowOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
var viewFactory func() C.CFTypeRef
|
var viewFactory func() C.CFTypeRef
|
||||||
|
|
||||||
@@ -170,7 +164,6 @@ func gio_onTerminate(view C.CFTypeRef) {
|
|||||||
w := views[view]
|
w := views[view]
|
||||||
delete(views, view)
|
delete(views, view)
|
||||||
w.setStage(StageDead)
|
w.setStage(StageDead)
|
||||||
close(windows)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onHide
|
//export gio_onHide
|
||||||
@@ -190,31 +183,26 @@ func gio_onCreate(view C.CFTypeRef) {
|
|||||||
w := &window{
|
w := &window{
|
||||||
view: view,
|
view: view,
|
||||||
}
|
}
|
||||||
ow := newWindow(w)
|
wopts := <-mainWindow.out
|
||||||
w.w = ow
|
w.w = wopts.window
|
||||||
|
w.w.setDriver(w)
|
||||||
views[view] = w
|
views[view] = w
|
||||||
windows <- ow
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createWindow(opts *WindowOptions) error {
|
func createWindow(win *Window, opts *WindowOptions) error {
|
||||||
singleWindow.mu.Lock()
|
mainWindow.in <- windowAndOptions{win, opts}
|
||||||
defer singleWindow.mu.Unlock()
|
return <-mainWindow.errs
|
||||||
if singleWindow.hasOpts {
|
|
||||||
panic("only one window supported")
|
|
||||||
}
|
|
||||||
singleWindow.opts = opts
|
|
||||||
singleWindow.hasOpts = true
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Main() {
|
func Main() {
|
||||||
|
wopts := <-mainWindow.out
|
||||||
view := viewFactory()
|
view := viewFactory()
|
||||||
if view == 0 {
|
if view == 0 {
|
||||||
// TODO: return this error from CreateWindow.
|
// TODO: return this error from CreateWindow.
|
||||||
panic(errors.New("CreateWindow: failed to create view"))
|
panic(errors.New("CreateWindow: failed to create view"))
|
||||||
}
|
}
|
||||||
cfg := getConfig()
|
cfg := getConfig()
|
||||||
opts := singleWindow.opts
|
opts := wopts.opts
|
||||||
w := cfg.Px(opts.Width)
|
w := cfg.Px(opts.Width)
|
||||||
h := cfg.Px(opts.Height)
|
h := cfg.Px(opts.Height)
|
||||||
title := C.CString(opts.Title)
|
title := C.CString(opts.Title)
|
||||||
|
|||||||
@@ -155,11 +155,11 @@ func Main() {
|
|||||||
<-mainDone
|
<-mainDone
|
||||||
}
|
}
|
||||||
|
|
||||||
func createWindow(opts *WindowOptions) error {
|
func createWindow(window *Window, opts *WindowOptions) error {
|
||||||
connMu.Lock()
|
connMu.Lock()
|
||||||
defer connMu.Unlock()
|
defer connMu.Unlock()
|
||||||
if len(winMap) > 0 {
|
if len(winMap) > 0 {
|
||||||
panic("multiple windows are not supported")
|
return errors.New("multiple windows are not supported")
|
||||||
}
|
}
|
||||||
if err := waylandConnect(); err != nil {
|
if err := waylandConnect(); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -169,13 +169,13 @@ func createWindow(opts *WindowOptions) error {
|
|||||||
conn.destroy()
|
conn.destroy()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
w.w = window
|
||||||
go func() {
|
go func() {
|
||||||
windows <- w.w
|
w.w.setDriver(w)
|
||||||
w.setStage(StageRunning)
|
w.setStage(StageRunning)
|
||||||
w.loop()
|
w.loop()
|
||||||
w.destroy()
|
w.destroy()
|
||||||
conn.destroy()
|
conn.destroy()
|
||||||
close(windows)
|
|
||||||
close(mainDone)
|
close(mainDone)
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
@@ -250,8 +250,6 @@ func createNativeWindow(opts *WindowOptions) (*window, error) {
|
|||||||
}
|
}
|
||||||
w.updateOpaqueRegion()
|
w.updateOpaqueRegion()
|
||||||
C.wl_surface_commit(w.surf)
|
C.wl_surface_commit(w.surf)
|
||||||
ow := newWindow(w)
|
|
||||||
w.w = ow
|
|
||||||
winMap[w.topLvl] = w
|
winMap[w.topLvl] = w
|
||||||
winMap[w.surf] = w
|
winMap[w.surf] = w
|
||||||
winMap[w.wmSurf] = w
|
winMap[w.wmSurf] = w
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -159,11 +160,11 @@ func Main() {
|
|||||||
<-mainDone
|
<-mainDone
|
||||||
}
|
}
|
||||||
|
|
||||||
func createWindow(opts *WindowOptions) error {
|
func createWindow(window *Window, opts *WindowOptions) error {
|
||||||
onceMu.Lock()
|
onceMu.Lock()
|
||||||
defer onceMu.Unlock()
|
defer onceMu.Unlock()
|
||||||
if len(winMap) > 0 {
|
if len(winMap) > 0 {
|
||||||
panic("multiple windows are not supported")
|
return errors.New("multiple windows are not supported")
|
||||||
}
|
}
|
||||||
cerr := make(chan error)
|
cerr := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
@@ -176,14 +177,14 @@ func createWindow(opts *WindowOptions) error {
|
|||||||
}
|
}
|
||||||
defer w.destroy()
|
defer w.destroy()
|
||||||
cerr <- nil
|
cerr <- nil
|
||||||
windows <- w.w
|
w.w = window
|
||||||
|
w.w.setDriver(w)
|
||||||
showWindow(w.hwnd, _SW_SHOWDEFAULT)
|
showWindow(w.hwnd, _SW_SHOWDEFAULT)
|
||||||
setForegroundWindow(w.hwnd)
|
setForegroundWindow(w.hwnd)
|
||||||
setFocus(w.hwnd)
|
setFocus(w.hwnd)
|
||||||
if err := w.loop(); err != nil {
|
if err := w.loop(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
close(windows)
|
|
||||||
close(mainDone)
|
close(mainDone)
|
||||||
}()
|
}()
|
||||||
return <-cerr
|
return <-cerr
|
||||||
@@ -248,7 +249,6 @@ func createNativeWindow(opts *WindowOptions) (*window, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
w.w = newWindow(w)
|
|
||||||
return w, nil
|
return w, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+37
-6
@@ -13,8 +13,8 @@ import (
|
|||||||
"gioui.org/ui/app/internal/gpu"
|
"gioui.org/ui/app/internal/gpu"
|
||||||
iinput "gioui.org/ui/app/internal/input"
|
iinput "gioui.org/ui/app/internal/input"
|
||||||
"gioui.org/ui/input"
|
"gioui.org/ui/input"
|
||||||
"gioui.org/ui/system"
|
|
||||||
"gioui.org/ui/key"
|
"gioui.org/ui/key"
|
||||||
|
"gioui.org/ui/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WindowOptions struct {
|
type WindowOptions struct {
|
||||||
@@ -47,6 +47,12 @@ type Window struct {
|
|||||||
router iinput.Router
|
router iinput.Router
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// driverEvent is sent when a new native driver
|
||||||
|
// is available for the Window.
|
||||||
|
type driverEvent struct {
|
||||||
|
driver *window
|
||||||
|
}
|
||||||
|
|
||||||
// driver is the interface for the platform implementation
|
// driver is the interface for the platform implementation
|
||||||
// of a Window.
|
// of a Window.
|
||||||
var _ interface {
|
var _ interface {
|
||||||
@@ -59,13 +65,31 @@ var _ interface {
|
|||||||
|
|
||||||
var ackEvent Event
|
var ackEvent Event
|
||||||
|
|
||||||
func newWindow(nw *window) *Window {
|
// NewWindow 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 returns the window previously by the platform.
|
||||||
|
func NewWindow(opts *WindowOptions) (*Window, error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &WindowOptions{
|
||||||
|
Width: ui.Dp(800),
|
||||||
|
Height: ui.Dp(600),
|
||||||
|
Title: "Gio program",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opts.Width.V <= 0 || opts.Height.V <= 0 {
|
||||||
|
panic("window width and height must be larger than 0")
|
||||||
|
}
|
||||||
|
|
||||||
w := &Window{
|
w := &Window{
|
||||||
driver: nw,
|
|
||||||
events: make(chan Event),
|
events: make(chan Event),
|
||||||
stage: StagePaused,
|
stage: StagePaused,
|
||||||
}
|
}
|
||||||
return w
|
if err := createWindow(w, opts); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return w, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) Events() <-chan Event {
|
func (w *Window) Events() <-chan Event {
|
||||||
@@ -103,8 +127,9 @@ func (w *Window) Draw(root *ui.Ops) {
|
|||||||
w.syncGPU = false
|
w.syncGPU = false
|
||||||
alive := w.isAlive()
|
alive := w.isAlive()
|
||||||
size := w.size
|
size := w.size
|
||||||
|
driver := w.driver
|
||||||
w.mu.Unlock()
|
w.mu.Unlock()
|
||||||
if !alive || stage < StageRunning {
|
if !alive || stage < StageRunning || driver == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if w.gpu != nil {
|
if w.gpu != nil {
|
||||||
@@ -117,7 +142,7 @@ func (w *Window) Draw(root *ui.Ops) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if w.gpu == nil {
|
if w.gpu == nil {
|
||||||
ctx, err := newContext(w.driver)
|
ctx, err := newContext(driver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.err = err
|
w.err = err
|
||||||
return
|
return
|
||||||
@@ -205,6 +230,12 @@ func (w *Window) contextDriver() interface{} {
|
|||||||
return w.driver
|
return w.driver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Window) setDriver(d *window) {
|
||||||
|
w.mu.Lock()
|
||||||
|
defer w.mu.Unlock()
|
||||||
|
w.driver = d
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Window) event(e Event) {
|
func (w *Window) event(e Event) {
|
||||||
w.eventLock.Lock()
|
w.eventLock.Lock()
|
||||||
defer w.eventLock.Unlock()
|
defer w.eventLock.Unlock()
|
||||||
|
|||||||
Reference in New Issue
Block a user