app,internal/window: extract native window code to separate package

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2019-10-14 16:00:26 +02:00
parent 36d1cd90f2
commit 10c1b2cb8d
41 changed files with 312 additions and 253 deletions
+2 -83
View File
@@ -3,37 +3,10 @@
package app package app
import ( import (
"errors"
"math"
"os" "os"
"strings" "strings"
"time"
"gioui.org/unit" "gioui.org/app/internal/window"
)
type windowRendezvous struct {
in chan windowAndOptions
out chan windowAndOptions
errs chan error
}
type windowAndOptions struct {
window *Window
opts *windowOptions
}
const (
inchPrDp = 1.0 / 160
mmPrDp = 25.4 / 160
// monitorScale is the extra scale applied to
// monitor outputs to compensate for the extra
// viewing distance compared to phone and tables.
monitorScale = 1.20
// minDensity is the minimum pixels per dp to
// ensure font and ui legibility on low-dpi
// screens.
minDensity = 1.25
) )
// extraArgs contains extra arguments to append to // extraArgs contains extra arguments to append to
@@ -69,59 +42,5 @@ func DataDir() (string, error) {
// require control of the main thread of the program for // require control of the main thread of the program for
// running windows. // running windows.
func Main() { func Main() {
main() window.Main()
}
// config implements the system.Config interface.
type config struct {
// Device pixels per dp.
pxPerDp float32
// Device pixels per sp.
pxPerSp float32
now time.Time
}
func (c *config) Now() time.Time {
return c.now
}
func (c *config) Px(v unit.Value) int {
var r float32
switch v.U {
case unit.UnitPx:
r = v.V
case unit.UnitDp:
r = c.pxPerDp * v.V
case unit.UnitSp:
r = c.pxPerSp * v.V
default:
panic("unknown unit")
}
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
} }
+7 -7
View File
@@ -5,21 +5,21 @@
package app package app
import "C" import "C"
import "sync"
import (
"sync"
"gioui.org/app/internal/window"
)
var ( var (
dataDirOnce sync.Once dataDirOnce sync.Once
dataDirChan = make(chan string, 1)
dataPath string dataPath string
) )
func dataDir() (string, error) { func dataDir() (string, error) {
dataDirOnce.Do(func() { dataDirOnce.Do(func() {
dataPath = <-dataDirChan dataPath = window.GetDataDir()
}) })
return dataPath, nil return dataPath, nil
} }
func setDataDir(dir string) {
dataDirChan <- dir
}
+1 -1
View File
@@ -2,7 +2,7 @@
// +build linux windows // +build linux windows
package app package window
import ( import (
"errors" "errors"
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
package app package window
/* /*
#include <EGL/egl.h> #include <EGL/egl.h>
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
package app package window
/* /*
#cgo LDFLAGS: -lEGL #cgo LDFLAGS: -lEGL
@@ -13,6 +13,8 @@ package app
*/ */
import "C" import "C"
import "gioui.org/app/internal/gl"
type ( type (
_EGLint = C.EGLint _EGLint = C.EGLint
_EGLDisplay = C.EGLDisplay _EGLDisplay = C.EGLDisplay
@@ -93,3 +95,7 @@ func eglCreateWindowSurface(disp _EGLDisplay, conf _EGLConfig, win _EGLNativeWin
eglSurf := C.eglCreateWindowSurface(disp, conf, win, &attribs[0]) eglSurf := C.eglCreateWindowSurface(disp, conf, win, &attribs[0])
return eglSurf return eglSurf
} }
func (w *window) NewContext() (gl.Context, error) {
return newContext(w)
}
@@ -2,7 +2,7 @@
// +build linux,!android // +build linux,!android
package app package window
import ( import (
"errors" "errors"
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
package app package window
import ( import (
"os" "os"
@@ -154,3 +154,7 @@ func (w *window) eglWindow(visID int) (_EGLNativeWindowType, int, int, error) {
hwnd, width, height := w.HWND() hwnd, width, height := w.HWND()
return _EGLNativeWindowType(hwnd), width, height, nil return _EGLNativeWindowType(hwnd), width, height, nil
} }
func (w *window) NewContext() (gl.Context, error) {
return newContext(w)
}
@@ -2,7 +2,7 @@
// +build darwin,ios // +build darwin,ios
package app package window
/* /*
#cgo CFLAGS: -fmodules -fobjc-arc -x objective-c #cgo CFLAGS: -fmodules -fobjc-arc -x objective-c
@@ -122,3 +122,7 @@ func (c *context) MakeCurrent() error {
} }
return nil return nil
} }
func (w *window) NewContext() (gl.Context, error) {
return newContext(w)
}
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
package app package window
import ( import (
"errors" "errors"
@@ -89,3 +89,7 @@ func (c *context) MakeCurrent() error {
} }
return nil return nil
} }
func (w *window) NewContext() (gl.Context, error) {
return newContext(w)
}
@@ -2,7 +2,7 @@
// +build darwin,!ios // +build darwin,!ios
package app package window
import ( import (
"gioui.org/app/internal/gl" "gioui.org/app/internal/gl"
@@ -72,3 +72,7 @@ func (c *context) MakeCurrent() error {
C.gio_makeCurrentContext(c.ctx) C.gio_makeCurrentContext(c.ctx)
return nil return nil
} }
func (w *window) NewContext() (gl.Context, error) {
return newContext(w)
}
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
package app package window
/* /*
#cgo LDFLAGS: -landroid #cgo LDFLAGS: -landroid
@@ -32,7 +32,7 @@ import (
) )
type window struct { type window struct {
*Window callbacks Callbacks
view C.jobject view C.jobject
@@ -55,6 +55,8 @@ type window struct {
mpostFrameCallbackOnMainThread C.jmethodID mpostFrameCallbackOnMainThread C.jmethodID
} }
var dataDirChan = make(chan string, 1)
var theJVM *C.JavaVM var theJVM *C.JavaVM
var views = make(map[C.jlong]*window) var views = make(map[C.jlong]*window)
@@ -85,11 +87,15 @@ func runGoMain(env *C.JNIEnv, class C.jclass, jdataDir C.jbyteArray) {
} }
n := C.gio_jni_GetArrayLength(env, jdataDir) n := C.gio_jni_GetArrayLength(env, jdataDir)
dataDir := C.GoStringN((*C.char)(unsafe.Pointer(dirBytes)), n) dataDir := C.GoStringN((*C.char)(unsafe.Pointer(dirBytes)), n)
setDataDir(dataDir) dataDirChan <- dataDir
C.gio_jni_ReleaseByteArrayElements(env, jdataDir, dirBytes) C.gio_jni_ReleaseByteArrayElements(env, jdataDir, dirBytes)
runMain() runMain()
} }
func GetDataDir() string {
return <-dataDirChan
}
//export setJVM //export setJVM
func setJVM(vm *C.JavaVM) { func setJVM(vm *C.JavaVM) {
theJVM = vm theJVM = vm
@@ -108,8 +114,8 @@ func onCreateView(env *C.JNIEnv, class C.jclass, view C.jobject) C.jlong {
mpostFrameCallbackOnMainThread: jniGetMethodID(env, class, "postFrameCallbackOnMainThread", "()V"), mpostFrameCallbackOnMainThread: jniGetMethodID(env, class, "postFrameCallbackOnMainThread", "()V"),
} }
wopts := <-mainWindow.out wopts := <-mainWindow.out
w.Window = wopts.window w.callbacks = wopts.window
w.Window.setDriver(w) w.callbacks.SetDriver(w)
handle := C.jlong(view) handle := C.jlong(view)
views[handle] = w views[handle] = w
w.loadConfig(env, class) w.loadConfig(env, class)
@@ -120,7 +126,7 @@ 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) w.callbacks.SetDriver(nil)
delete(views, handle) delete(views, handle)
C.gio_jni_DeleteGlobalRef(env, w.view) C.gio_jni_DeleteGlobalRef(env, w.view)
w.view = 0 w.view = 0
@@ -201,7 +207,7 @@ func onFrameCallback(env *C.JNIEnv, class C.jclass, view C.jlong, nanos C.jlong)
func onBack(env *C.JNIEnv, class C.jclass, view C.jlong) C.jboolean { func onBack(env *C.JNIEnv, class C.jclass, view C.jlong) C.jboolean {
w := views[view] w := views[view]
ev := &system.CommandEvent{Type: system.CommandBack} ev := &system.CommandEvent{Type: system.CommandBack}
w.event(ev) w.callbacks.Event(ev)
if ev.Cancel { if ev.Cancel {
return C.JNI_TRUE return C.JNI_TRUE
} }
@@ -211,7 +217,7 @@ func onBack(env *C.JNIEnv, class C.jclass, view C.jlong) C.jboolean {
//export onFocusChange //export onFocusChange
func onFocusChange(env *C.JNIEnv, class C.jclass, view C.jlong, focus C.jboolean) { func onFocusChange(env *C.JNIEnv, class C.jclass, view C.jlong, focus C.jboolean) {
w := views[view] w := views[view]
w.event(key.FocusEvent{Focus: focus == C.JNI_TRUE}) w.callbacks.Event(key.FocusEvent{Focus: focus == C.JNI_TRUE})
} }
//export onWindowInsets //export onWindowInsets
@@ -243,7 +249,7 @@ func (w *window) setStage(stage system.Stage) {
return return
} }
w.stage = stage w.stage = stage
w.event(system.StageEvent{stage}) w.callbacks.Event(system.StageEvent{stage})
} }
func (w *window) nativeWindow(visID int) (*C.ANativeWindow, int, int) { func (w *window) nativeWindow(visID int) (*C.ANativeWindow, int, int) {
@@ -279,7 +285,7 @@ func (w *window) loadConfig(env *C.JNIEnv, class C.jclass) {
} }
} }
func (w *window) setAnimating(anim bool) { func (w *window) SetAnimating(anim bool) {
w.mu.Lock() w.mu.Lock()
w.animating = anim w.animating = anim
w.mu.Unlock() w.mu.Unlock()
@@ -297,7 +303,7 @@ func (w *window) draw(sync bool) {
return return
} }
ppdp := float32(w.dpi) * inchPrDp ppdp := float32(w.dpi) * inchPrDp
w.event(frameEvent{ w.callbacks.Event(FrameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: system.FrameEvent{
Size: image.Point{ Size: image.Point{
X: int(width), X: int(width),
@@ -310,7 +316,7 @@ func (w *window) draw(sync bool) {
now: time.Now(), now: time.Now(),
}, },
}, },
sync: sync, Sync: sync,
}) })
} }
@@ -364,10 +370,10 @@ func convertKeyCode(code C.jint) (rune, bool) {
func onKeyEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, keyCode, r C.jint, t C.jlong) { func onKeyEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, keyCode, r C.jint, t C.jlong) {
w := views[handle] w := views[handle]
if n, ok := convertKeyCode(keyCode); ok { if n, ok := convertKeyCode(keyCode); ok {
w.event(key.Event{Name: n}) w.callbacks.Event(key.Event{Name: n})
} }
if r != 0 { if r != 0 {
w.event(key.EditEvent{Text: string(rune(r))}) w.callbacks.Event(key.EditEvent{Text: string(rune(r))})
} }
} }
@@ -396,7 +402,7 @@ func onTouchEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, action, pointer
default: default:
return return
} }
w.event(pointer.Event{ w.callbacks.Event(pointer.Event{
Type: typ, Type: typ,
Source: src, Source: src,
PointerID: pointer.ID(pointerID), PointerID: pointer.ID(pointerID),
@@ -405,7 +411,7 @@ func onTouchEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, action, pointer
}) })
} }
func (w *window) showTextInput(show bool) { func (w *window) ShowTextInput(show bool) {
if w.view == 0 { if w.view == 0 {
return return
} }
@@ -418,10 +424,10 @@ func (w *window) showTextInput(show bool) {
}) })
} }
func main() { func Main() {
} }
func createWindow(window *Window, opts *windowOptions) error { func NewWindow(window Callbacks, opts *Options) error {
mainWindow.in <- windowAndOptions{window, opts} mainWindow.in <- windowAndOptions{window, opts}
return <-mainWindow.errs return <-mainWindow.errs
} }
+17 -17
View File
@@ -2,7 +2,7 @@
// +build darwin,ios // +build darwin,ios
package app package window
/* /*
#cgo CFLAGS: -fmodules -fobjc-arc -x objective-c #cgo CFLAGS: -fmodules -fobjc-arc -x objective-c
@@ -31,7 +31,7 @@ import (
type window struct { type window struct {
view C.CFTypeRef view C.CFTypeRef
w *Window w Callbacks
layer C.CFTypeRef layer C.CFTypeRef
visible atomic.Value visible atomic.Value
@@ -57,12 +57,12 @@ func onCreate(view C.CFTypeRef) {
} }
wopts := <-mainWindow.out wopts := <-mainWindow.out
w.w = wopts.window w.w = wopts.window
w.w.setDriver(w) 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
w.w.event(system.StageEvent{Stage: system.StagePaused}) w.w.Event(system.StageEvent{Stage: system.StagePaused})
} }
//export onDraw //export onDraw
@@ -75,13 +75,13 @@ func onDraw(view C.CFTypeRef, dpi, sdpi, width, height C.CGFloat, sync C.int, to
w.visible.Store(true) w.visible.Store(true)
C.gio_updateView(view, w.layer) C.gio_updateView(view, w.layer)
if !wasVisible { if !wasVisible {
w.w.event(system.StageEvent{Stage: system.StageRunning}) w.w.Event(system.StageEvent{Stage: system.StageRunning})
} }
isSync := false isSync := false
if sync != 0 { if sync != 0 {
isSync = true isSync = true
} }
w.w.event(frameEvent{ w.w.Event(FrameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: system.FrameEvent{
Size: image.Point{ Size: image.Point{
X: int(width + .5), X: int(width + .5),
@@ -99,7 +99,7 @@ func onDraw(view C.CFTypeRef, dpi, sdpi, width, height C.CGFloat, sync C.int, to
now: time.Now(), now: time.Now(),
}, },
}, },
sync: isSync, Sync: isSync,
}) })
} }
@@ -107,14 +107,14 @@ func onDraw(view C.CFTypeRef, dpi, sdpi, width, height C.CGFloat, sync C.int, to
func onStop(view C.CFTypeRef) { func onStop(view C.CFTypeRef) {
w := views[view] w := views[view]
w.visible.Store(false) w.visible.Store(false)
w.w.event(system.StageEvent{Stage: system.StagePaused}) w.w.Event(system.StageEvent{Stage: system.StagePaused})
} }
//export onDestroy //export onDestroy
func onDestroy(view C.CFTypeRef) { func onDestroy(view C.CFTypeRef) {
w := views[view] w := views[view]
delete(views, view) delete(views, view)
w.w.event(system.DestroyEvent{}) w.w.Event(system.DestroyEvent{})
C.gio_removeLayer(w.layer) C.gio_removeLayer(w.layer)
C.CFRelease(w.layer) C.CFRelease(w.layer)
w.layer = 0 w.layer = 0
@@ -124,7 +124,7 @@ func onDestroy(view C.CFTypeRef) {
//export onFocus //export onFocus
func onFocus(view C.CFTypeRef, focus int) { func onFocus(view C.CFTypeRef, focus int) {
w := views[view] w := views[view]
w.w.event(key.FocusEvent{Focus: focus != 0}) w.w.Event(key.FocusEvent{Focus: focus != 0})
} }
//export onLowMemory //export onLowMemory
@@ -161,7 +161,7 @@ func onDeleteBackward(view C.CFTypeRef) {
//export onText //export onText
func onText(view C.CFTypeRef, str *C.char) { func onText(view C.CFTypeRef, str *C.char) {
w := views[view] w := views[view]
w.w.event(key.EditEvent{ w.w.Event(key.EditEvent{
Text: C.GoString(str), Text: C.GoString(str),
}) })
} }
@@ -184,7 +184,7 @@ func onTouch(last C.int, view, touchRef C.CFTypeRef, phase C.NSInteger, x, y C.C
w := views[view] w := views[view]
t := time.Duration(float64(ti) * float64(time.Second)) t := time.Duration(float64(ti) * float64(time.Second))
p := f32.Point{X: float32(x), Y: float32(y)} p := f32.Point{X: float32(x), Y: float32(y)}
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: typ, Type: typ,
Source: pointer.Touch, Source: pointer.Touch,
PointerID: w.lookupTouch(last != 0, touchRef), PointerID: w.lookupTouch(last != 0, touchRef),
@@ -193,7 +193,7 @@ func onTouch(last C.int, view, touchRef C.CFTypeRef, phase C.NSInteger, x, y C.C
}) })
} }
func (w *window) setAnimating(anim bool) { func (w *window) SetAnimating(anim bool) {
if w.view == 0 { if w.view == 0 {
return return
} }
@@ -205,7 +205,7 @@ func (w *window) setAnimating(anim bool) {
} }
func (w *window) onKeyCommand(name rune) { func (w *window) onKeyCommand(name rune) {
w.w.event(key.Event{ w.w.Event(key.Event{
Name: name, Name: name,
}) })
} }
@@ -238,7 +238,7 @@ func (w *window) isVisible() bool {
return w.visible.Load().(bool) return w.visible.Load().(bool)
} }
func (w *window) showTextInput(show bool) { func (w *window) ShowTextInput(show bool) {
if w.view == 0 { if w.view == 0 {
return return
} }
@@ -249,10 +249,10 @@ func (w *window) showTextInput(show bool) {
} }
} }
func createWindow(win *Window, opts *windowOptions) error { func NewWindow(win Callbacks, opts *Options) error {
mainWindow.in <- windowAndOptions{win, opts} mainWindow.in <- windowAndOptions{win, opts}
return <-mainWindow.errs return <-mainWindow.errs
} }
func main() { func Main() {
} }
+17 -17
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
package app package window
import ( import (
"image" "image"
@@ -18,7 +18,7 @@ type window struct {
window js.Value window js.Value
cnv js.Value cnv js.Value
tarea js.Value tarea js.Value
w *Window w Callbacks
redraw js.Func redraw js.Func
requestAnimationFrame js.Value requestAnimationFrame js.Value
cleanfuncs []func() cleanfuncs []func()
@@ -32,7 +32,7 @@ type window struct {
var mainDone = make(chan struct{}) var mainDone = make(chan struct{})
func createWindow(win *Window, opts *windowOptions) error { func NewWindow(win Callbacks, opts *Options) error {
doc := js.Global().Get("document") doc := js.Global().Get("document")
cont := getContainer(doc) cont := getContainer(doc)
cnv := createCanvas(doc) cnv := createCanvas(doc)
@@ -52,9 +52,9 @@ func createWindow(win *Window, opts *windowOptions) error {
w.addEventListeners() w.addEventListeners()
w.w = win w.w = win
go func() { go func() {
w.w.setDriver(w) w.w.SetDriver(w)
w.focus() w.focus()
w.w.event(system.StageEvent{Stage: system.StageRunning}) w.w.Event(system.StageEvent{Stage: system.StageRunning})
w.draw(true) w.draw(true)
select {} select {}
w.cleanup() w.cleanup()
@@ -156,18 +156,18 @@ func (w *window) addEventListeners() {
w.touches[i] = js.Null() w.touches[i] = js.Null()
} }
w.touches = w.touches[:0] w.touches = w.touches[:0]
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: pointer.Cancel, Type: pointer.Cancel,
Source: pointer.Touch, Source: pointer.Touch,
}) })
return nil return nil
}) })
w.addEventListener(w.tarea, "focus", func(this js.Value, args []js.Value) interface{} { w.addEventListener(w.tarea, "focus", func(this js.Value, args []js.Value) interface{} {
w.w.event(key.FocusEvent{Focus: true}) w.w.Event(key.FocusEvent{Focus: true})
return nil return nil
}) })
w.addEventListener(w.tarea, "blur", func(this js.Value, args []js.Value) interface{} { w.addEventListener(w.tarea, "blur", func(this js.Value, args []js.Value) interface{} {
w.w.event(key.FocusEvent{Focus: false}) w.w.Event(key.FocusEvent{Focus: false})
return nil return nil
}) })
w.addEventListener(w.tarea, "keydown", func(this js.Value, args []js.Value) interface{} { w.addEventListener(w.tarea, "keydown", func(this js.Value, args []js.Value) interface{} {
@@ -195,7 +195,7 @@ func (w *window) addEventListeners() {
func (w *window) flushInput() { func (w *window) flushInput() {
val := w.tarea.Get("value").String() val := w.tarea.Get("value").String()
w.tarea.Set("value", "") w.tarea.Set("value", "")
w.w.event(key.EditEvent{Text: string(val)}) w.w.Event(key.EditEvent{Text: string(val)})
} }
func (w *window) blur() { func (w *window) blur() {
@@ -216,7 +216,7 @@ func (w *window) keyEvent(e js.Value) {
if e.Call("getModifierState", "Shift").Bool() { if e.Call("getModifierState", "Shift").Bool() {
cmd.Modifiers |= key.ModShift cmd.Modifiers |= key.ModShift
} }
w.w.event(cmd) w.w.Event(cmd)
} }
} }
@@ -239,7 +239,7 @@ func (w *window) touchEvent(typ pointer.Type, e js.Value) {
X: float32(x) * scale, X: float32(x) * scale,
Y: float32(y) * scale, Y: float32(y) * scale,
} }
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: typ, Type: typ,
Source: pointer.Touch, Source: pointer.Touch,
Position: pos, Position: pos,
@@ -279,7 +279,7 @@ func (w *window) pointerEvent(typ pointer.Type, dx, dy float32, e js.Value) {
Y: dy * scale, Y: dy * scale,
} }
t := time.Duration(e.Get("timeStamp").Int()) * time.Millisecond t := time.Duration(e.Get("timeStamp").Int()) * time.Millisecond
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: typ, Type: typ,
Source: pointer.Mouse, Source: pointer.Mouse,
Position: pos, Position: pos,
@@ -316,7 +316,7 @@ func (w *window) animCallback() {
} }
} }
func (w *window) setAnimating(anim bool) { func (w *window) SetAnimating(anim bool) {
w.mu.Lock() w.mu.Lock()
defer w.mu.Unlock() defer w.mu.Unlock()
if anim && !w.animating { if anim && !w.animating {
@@ -325,7 +325,7 @@ func (w *window) setAnimating(anim bool) {
w.animating = anim w.animating = anim
} }
func (w *window) showTextInput(show bool) { func (w *window) ShowTextInput(show bool) {
// Run in a goroutine to avoid a deadlock if the // Run in a goroutine to avoid a deadlock if the
// focus change result in an event. // focus change result in an event.
go func() { go func() {
@@ -346,7 +346,7 @@ func (w *window) draw(sync bool) {
w.scale = float32(scale) w.scale = float32(scale)
w.mu.Unlock() w.mu.Unlock()
cfg.now = time.Now() cfg.now = time.Now()
w.w.event(frameEvent{ w.w.Event(FrameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: system.FrameEvent{
Size: image.Point{ Size: image.Point{
X: width, X: width,
@@ -354,7 +354,7 @@ func (w *window) draw(sync bool) {
}, },
Config: &cfg, Config: &cfg,
}, },
sync: sync, Sync: sync,
}) })
} }
@@ -377,7 +377,7 @@ func (w *window) config() (int, int, float32, config) {
} }
} }
func main() { func Main() {
<-mainDone <-mainDone
} }
@@ -2,7 +2,7 @@
// +build darwin,!ios // +build darwin,!ios
package app package window
import ( import (
"errors" "errors"
@@ -33,7 +33,7 @@ func init() {
type window struct { type window struct {
view C.CFTypeRef view C.CFTypeRef
w *Window w Callbacks
stage system.Stage stage system.Stage
ppdp float32 ppdp float32
scale float32 scale float32
@@ -81,9 +81,9 @@ func (w *window) contextView() C.CFTypeRef {
return w.view return w.view
} }
func (w *window) showTextInput(show bool) {} func (w *window) ShowTextInput(show bool) {}
func (w *window) setAnimating(anim bool) { func (w *window) SetAnimating(anim bool) {
var animb C.BOOL var animb C.BOOL
if anim { if anim {
animb = 1 animb = 1
@@ -96,7 +96,7 @@ func (w *window) setStage(stage system.Stage) {
return return
} }
w.stage = stage w.stage = stage
w.w.event(system.StageEvent{Stage: stage}) w.w.Event(system.StageEvent{Stage: stage})
} }
// Use a top level func for onFrameCallback to avoid // Use a top level func for onFrameCallback to avoid
@@ -129,7 +129,7 @@ func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger)
w := views[view] w := views[view]
for _, k := range str { for _, k := range str {
if n, ok := convertKey(k); ok { if n, ok := convertKey(k); ok {
w.w.event(key.Event{Name: n, Modifiers: kmods}) w.w.Event(key.Event{Name: n, Modifiers: kmods})
} }
} }
}) })
@@ -140,7 +140,7 @@ func gio_onText(view C.CFTypeRef, cstr *C.char) {
str := C.GoString(cstr) str := C.GoString(cstr)
viewDo(view, func(views viewMap, view C.CFTypeRef) { viewDo(view, func(views viewMap, view C.CFTypeRef) {
w := views[view] w := views[view]
w.w.event(key.EditEvent{Text: str}) w.w.Event(key.EditEvent{Text: str})
}) })
} }
@@ -162,7 +162,7 @@ func gio_onMouse(view C.CFTypeRef, cdir C.int, x, y, dx, dy C.CGFloat, ti C.doub
w := views[view] w := views[view]
x, y := float32(x)*w.scale, float32(y)*w.scale x, y := float32(x)*w.scale, float32(y)*w.scale
dx, dy := float32(dx)*w.scale, float32(dy)*w.scale dx, dy := float32(dx)*w.scale, float32(dy)*w.scale
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: typ, Type: typ,
Source: pointer.Mouse, Source: pointer.Mouse,
Time: t, Time: t,
@@ -185,7 +185,7 @@ func gio_onDraw(view C.CFTypeRef) {
func gio_onFocus(view C.CFTypeRef, focus C.BOOL) { func gio_onFocus(view C.CFTypeRef, focus C.BOOL) {
viewDo(view, func(views viewMap, view C.CFTypeRef) { viewDo(view, func(views viewMap, view C.CFTypeRef) {
w := views[view] w := views[view]
w.w.event(key.FocusEvent{Focus: focus == C.YES}) w.w.Event(key.FocusEvent{Focus: focus == C.YES})
}) })
} }
@@ -200,7 +200,7 @@ func (w *window) draw(sync bool) {
cfg := configFor(w.ppdp, w.scale) cfg := configFor(w.ppdp, w.scale)
cfg.now = time.Now() cfg.now = time.Now()
w.setStage(system.StageRunning) w.setStage(system.StageRunning)
w.w.event(frameEvent{ w.w.Event(FrameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: system.FrameEvent{
Size: image.Point{ Size: image.Point{
X: width, X: width,
@@ -208,7 +208,7 @@ func (w *window) draw(sync bool) {
}, },
Config: &cfg, Config: &cfg,
}, },
sync: sync, Sync: sync,
}) })
} }
@@ -234,7 +234,7 @@ func gio_onTerminate(view C.CFTypeRef) {
viewDo(view, func(views viewMap, view C.CFTypeRef) { viewDo(view, func(views viewMap, view C.CFTypeRef) {
w := views[view] w := views[view]
delete(views, view) delete(views, view)
w.w.event(system.DestroyEvent{}) w.w.Event(system.DestroyEvent{})
}) })
} }
@@ -265,17 +265,17 @@ func gio_onCreate(view C.CFTypeRef) {
} }
wopts := <-mainWindow.out wopts := <-mainWindow.out
w.w = wopts.window w.w = wopts.window
w.w.setDriver(w) w.w.SetDriver(w)
views[view] = w views[view] = w
}) })
} }
func createWindow(win *Window, opts *windowOptions) error { func NewWindow(win Callbacks, opts *Options) error {
mainWindow.in <- windowAndOptions{win, opts} mainWindow.in <- windowAndOptions{win, opts}
return <-mainWindow.errs return <-mainWindow.errs
} }
func main() { func Main() {
wopts := <-mainWindow.out wopts := <-mainWindow.out
view := viewFactory() view := viewFactory()
if view == 0 { if view == 0 {
@@ -2,7 +2,7 @@
// +build linux,!android // +build linux,!android
package app package window
import ( import (
"bytes" "bytes"
@@ -80,7 +80,7 @@ type repeatState struct {
delay time.Duration delay time.Duration
key uint32 key uint32
win *Window win Callbacks
stopC chan struct{} stopC chan struct{}
start time.Duration start time.Duration
@@ -90,7 +90,7 @@ type repeatState struct {
} }
type window struct { type window struct {
w *Window w Callbacks
disp *C.struct_wl_display disp *C.struct_wl_display
surf *C.struct_wl_surface surf *C.struct_wl_surface
wmSurf *C.struct_xdg_surface wmSurf *C.struct_xdg_surface
@@ -153,11 +153,11 @@ var (
outputConfig = make(map[*C.struct_wl_output]*wlOutput) outputConfig = make(map[*C.struct_wl_output]*wlOutput)
) )
func main() { func Main() {
<-mainDone <-mainDone
} }
func createWindow(window *Window, opts *windowOptions) error { func NewWindow(window Callbacks, opts *Options) error {
connMu.Lock() connMu.Lock()
defer connMu.Unlock() defer connMu.Unlock()
if len(winMap) > 0 { if len(winMap) > 0 {
@@ -173,7 +173,7 @@ func createWindow(window *Window, opts *windowOptions) error {
} }
w.w = window w.w = window
go func() { go func() {
w.w.setDriver(w) w.w.SetDriver(w)
w.loop() w.loop()
w.destroy() w.destroy()
conn.destroy() conn.destroy()
@@ -182,7 +182,7 @@ func createWindow(window *Window, opts *windowOptions) error {
return nil return nil
} }
func createNativeWindow(opts *windowOptions) (*window, error) { func createNativeWindow(opts *Options) (*window, error) {
pipe := make([]int, 2) pipe := make([]int, 2)
if err := syscall.Pipe2(pipe, syscall.O_NONBLOCK|syscall.O_CLOEXEC); err != nil { if err := syscall.Pipe2(pipe, syscall.O_NONBLOCK|syscall.O_CLOEXEC); err != nil {
return nil, fmt.Errorf("createNativeWindow: failed to create pipe: %v", err) return nil, fmt.Errorf("createNativeWindow: failed to create pipe: %v", err)
@@ -449,7 +449,7 @@ func gio_onTouchDown(data unsafe.Pointer, touch *C.struct_wl_touch, serial, t C.
X: fromFixed(x) * float32(w.scale), X: fromFixed(x) * float32(w.scale),
Y: fromFixed(y) * float32(w.scale), Y: fromFixed(y) * float32(w.scale),
} }
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: pointer.Press, Type: pointer.Press,
Source: pointer.Touch, Source: pointer.Touch,
Position: w.lastTouch, Position: w.lastTouch,
@@ -461,7 +461,7 @@ func gio_onTouchDown(data unsafe.Pointer, touch *C.struct_wl_touch, serial, t C.
//export gio_onTouchUp //export gio_onTouchUp
func gio_onTouchUp(data unsafe.Pointer, touch *C.struct_wl_touch, serial, t C.uint32_t, id C.int32_t) { func gio_onTouchUp(data unsafe.Pointer, touch *C.struct_wl_touch, serial, t C.uint32_t, id C.int32_t) {
w := winMap[touch] w := winMap[touch]
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: pointer.Release, Type: pointer.Release,
Source: pointer.Touch, Source: pointer.Touch,
Position: w.lastTouch, Position: w.lastTouch,
@@ -477,7 +477,7 @@ func gio_onTouchMotion(data unsafe.Pointer, touch *C.struct_wl_touch, t C.uint32
X: fromFixed(x) * float32(w.scale), X: fromFixed(x) * float32(w.scale),
Y: fromFixed(y) * float32(w.scale), Y: fromFixed(y) * float32(w.scale),
} }
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: pointer.Move, Type: pointer.Move,
Position: w.lastTouch, Position: w.lastTouch,
Source: pointer.Touch, Source: pointer.Touch,
@@ -493,7 +493,7 @@ func gio_onTouchFrame(data unsafe.Pointer, touch *C.struct_wl_touch) {
//export gio_onTouchCancel //export gio_onTouchCancel
func gio_onTouchCancel(data unsafe.Pointer, touch *C.struct_wl_touch) { func gio_onTouchCancel(data unsafe.Pointer, touch *C.struct_wl_touch) {
w := winMap[touch] w := winMap[touch]
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: pointer.Cancel, Type: pointer.Cancel,
Source: pointer.Touch, Source: pointer.Touch,
}) })
@@ -544,7 +544,7 @@ func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t,
} }
w.flushScroll() w.flushScroll()
w.resetFling() w.resetFling()
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: typ, Type: typ,
Source: pointer.Mouse, Source: pointer.Mouse,
Position: w.lastPos, Position: w.lastPos,
@@ -645,14 +645,14 @@ func gio_onKeyboardEnter(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, se
conn.repeat.Stop(0) conn.repeat.Stop(0)
w := winMap[surf] w := winMap[surf]
winMap[keyboard] = w winMap[keyboard] = w
w.w.event(key.FocusEvent{Focus: true}) w.w.Event(key.FocusEvent{Focus: true})
} }
//export gio_onKeyboardLeave //export gio_onKeyboardLeave
func gio_onKeyboardLeave(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, serial C.uint32_t, surf *C.struct_wl_surface) { func gio_onKeyboardLeave(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, serial C.uint32_t, surf *C.struct_wl_surface) {
conn.repeat.Stop(0) conn.repeat.Stop(0)
w := winMap[keyboard] w := winMap[keyboard]
w.w.event(key.FocusEvent{Focus: false}) w.w.Event(key.FocusEvent{Focus: false})
} }
//export gio_onKeyboardKey //export gio_onKeyboardKey
@@ -666,7 +666,7 @@ func gio_onKeyboardKey(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, seri
} }
kc := uint32(keyCode) kc := uint32(keyCode)
for _, e := range conn.xkb.DispatchKey(kc) { for _, e := range conn.xkb.DispatchKey(kc) {
w.w.event(e) w.w.Event(e)
} }
if conn.xkb.IsRepeatKey(kc) { if conn.xkb.IsRepeatKey(kc) {
conn.repeat.Start(w, kc, t) conn.repeat.Start(w, kc, t)
@@ -739,7 +739,7 @@ func (r *repeatState) Repeat() {
break break
} }
for _, e := range conn.xkb.DispatchKey(r.key) { for _, e := range conn.xkb.DispatchKey(r.key) {
r.win.event(e) r.win.Event(e)
} }
r.last += delay r.last += delay
} }
@@ -773,7 +773,7 @@ loop:
break break
} }
if w.dead { if w.dead {
w.w.event(system.DestroyEvent{Err: w.pendingErr}) w.w.Event(system.DestroyEvent{Err: w.pendingErr})
break break
} }
// Clear poll events. // Clear poll events.
@@ -809,7 +809,7 @@ loop:
} }
} }
func (w *window) setAnimating(anim bool) { func (w *window) SetAnimating(anim bool) {
w.mu.Lock() w.mu.Lock()
w.animating = anim w.animating = anim
animating := w.isAnimating() animating := w.isAnimating()
@@ -924,7 +924,7 @@ func (w *window) flushScroll() {
if total == (f32.Point{}) { if total == (f32.Point{}) {
return return
} }
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: pointer.Move, Type: pointer.Move,
Source: pointer.Mouse, Source: pointer.Mouse,
Position: w.lastPos, Position: w.lastPos,
@@ -945,7 +945,7 @@ func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) {
X: fromFixed(x) * float32(w.scale), X: fromFixed(x) * float32(w.scale),
Y: fromFixed(y) * float32(w.scale), Y: fromFixed(y) * float32(w.scale),
} }
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: pointer.Move, Type: pointer.Move,
Position: w.lastPos, Position: w.lastPos,
Source: pointer.Mouse, Source: pointer.Mouse,
@@ -1025,7 +1025,7 @@ func (w *window) draw(sync bool) {
C.gio_wl_callback_add_listener(w.lastFrameCallback, unsafe.Pointer(w.surf)) C.gio_wl_callback_add_listener(w.lastFrameCallback, unsafe.Pointer(w.surf))
} }
cfg.now = time.Now() cfg.now = time.Now()
w.w.event(frameEvent{ w.w.Event(FrameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: system.FrameEvent{
Size: image.Point{ Size: image.Point{
X: width, X: width,
@@ -1033,7 +1033,7 @@ func (w *window) draw(sync bool) {
}, },
Config: &cfg, Config: &cfg,
}, },
sync: sync, Sync: sync,
}) })
} }
@@ -1042,7 +1042,7 @@ func (w *window) setStage(s system.Stage) {
return return
} }
w.stage = s w.stage = s
w.w.event(system.StageEvent{s}) w.w.Event(system.StageEvent{s})
} }
func (w *window) display() *C.struct_wl_display { func (w *window) display() *C.struct_wl_display {
@@ -1064,7 +1064,7 @@ func (w *window) surface() (*C.struct_wl_surface, int, int) {
return w.surf, width * scale, height * scale return w.surf, width * scale, height * scale
} }
func (w *window) showTextInput(show bool) {} func (w *window) ShowTextInput(show bool) {}
// detectFontScale reports current font scale, or 1.0 // detectFontScale reports current font scale, or 1.0
// if it fails. // if it fails.
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
package app package window
import ( import (
"errors" "errors"
@@ -58,7 +58,7 @@ type point struct {
type window struct { type window struct {
hwnd syscall.Handle hwnd syscall.Handle
hdc syscall.Handle hdc syscall.Handle
w *Window w Callbacks
width int width int
height int height int
stage system.Stage stage system.Stage
@@ -159,11 +159,11 @@ const _WM_REDRAW = _WM_USER + 0
var onceMu sync.Mutex var onceMu sync.Mutex
var mainDone = make(chan struct{}) var mainDone = make(chan struct{})
func main() { func Main() {
<-mainDone <-mainDone
} }
func createWindow(window *Window, opts *windowOptions) error { func NewWindow(window Callbacks, opts *Options) error {
onceMu.Lock() onceMu.Lock()
defer onceMu.Unlock() defer onceMu.Unlock()
if len(winMap) > 0 { if len(winMap) > 0 {
@@ -183,8 +183,8 @@ func createWindow(window *Window, opts *windowOptions) error {
winMap[w.hwnd] = w winMap[w.hwnd] = w
defer delete(winMap, w.hwnd) defer delete(winMap, w.hwnd)
w.w = window w.w = window
w.w.setDriver(w) w.w.SetDriver(w)
defer w.w.event(system.DestroyEvent{}) defer w.w.Event(system.DestroyEvent{})
showWindow(w.hwnd, _SW_SHOWDEFAULT) showWindow(w.hwnd, _SW_SHOWDEFAULT)
setForegroundWindow(w.hwnd) setForegroundWindow(w.hwnd)
setFocus(w.hwnd) setFocus(w.hwnd)
@@ -196,7 +196,7 @@ func createWindow(window *Window, opts *windowOptions) error {
return <-cerr return <-cerr
} }
func createNativeWindow(opts *windowOptions) (*window, error) { func createNativeWindow(opts *Options) (*window, error) {
setProcessDPIAware() setProcessDPIAware()
screenDC, err := getDC(0) screenDC, err := getDC(0)
if err != nil { if err != nil {
@@ -266,7 +266,7 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
fallthrough fallthrough
case _WM_CHAR: case _WM_CHAR:
if r := rune(wParam); unicode.IsPrint(r) { if r := rune(wParam); unicode.IsPrint(r) {
w.w.event(key.EditEvent{Text: string(r)}) w.w.Event(key.EditEvent{Text: string(r)})
} }
// The message is processed. // The message is processed.
return 1 return 1
@@ -279,31 +279,31 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
if getKeyState(_VK_SHIFT)&0x1000 != 0 { if getKeyState(_VK_SHIFT)&0x1000 != 0 {
cmd.Modifiers |= key.ModShift cmd.Modifiers |= key.ModShift
} }
w.w.event(cmd) w.w.Event(cmd)
} }
case _WM_LBUTTONDOWN: case _WM_LBUTTONDOWN:
setCapture(w.hwnd) setCapture(w.hwnd)
x, y := coordsFromlParam(lParam) x, y := coordsFromlParam(lParam)
p := f32.Point{X: float32(x), Y: float32(y)} p := f32.Point{X: float32(x), Y: float32(y)}
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: pointer.Press, Type: pointer.Press,
Source: pointer.Mouse, Source: pointer.Mouse,
Position: p, Position: p,
Time: getMessageTime(), Time: getMessageTime(),
}) })
case _WM_CANCELMODE: case _WM_CANCELMODE:
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: pointer.Cancel, Type: pointer.Cancel,
}) })
case _WM_SETFOCUS: case _WM_SETFOCUS:
w.w.event(key.FocusEvent{Focus: true}) w.w.Event(key.FocusEvent{Focus: true})
case _WM_KILLFOCUS: case _WM_KILLFOCUS:
w.w.event(key.FocusEvent{Focus: false}) w.w.Event(key.FocusEvent{Focus: false})
case _WM_LBUTTONUP: case _WM_LBUTTONUP:
releaseCapture() releaseCapture()
x, y := coordsFromlParam(lParam) x, y := coordsFromlParam(lParam)
p := f32.Point{X: float32(x), Y: float32(y)} p := f32.Point{X: float32(x), Y: float32(y)}
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: pointer.Release, Type: pointer.Release,
Source: pointer.Mouse, Source: pointer.Mouse,
Position: p, Position: p,
@@ -312,7 +312,7 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
case _WM_MOUSEMOVE: case _WM_MOUSEMOVE:
x, y := coordsFromlParam(lParam) x, y := coordsFromlParam(lParam)
p := f32.Point{X: float32(x), Y: float32(y)} p := f32.Point{X: float32(x), Y: float32(y)}
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: pointer.Move, Type: pointer.Move,
Source: pointer.Mouse, Source: pointer.Mouse,
Position: p, Position: p,
@@ -350,7 +350,7 @@ func (w *window) scrollEvent(wParam, lParam uintptr) {
screenToClient(w.hwnd, &np) screenToClient(w.hwnd, &np)
p := f32.Point{X: float32(np.x), Y: float32(np.y)} p := f32.Point{X: float32(np.x), Y: float32(np.y)}
dist := float32(int16(wParam >> 16)) dist := float32(int16(wParam >> 16))
w.w.event(pointer.Event{ w.w.Event(pointer.Event{
Type: pointer.Move, Type: pointer.Move,
Source: pointer.Mouse, Source: pointer.Mouse,
Position: p, Position: p,
@@ -381,7 +381,7 @@ func (w *window) loop() error {
return nil return nil
} }
func (w *window) setAnimating(anim bool) { func (w *window) SetAnimating(anim bool) {
w.mu.Lock() w.mu.Lock()
w.animating = anim w.animating = anim
w.mu.Unlock() w.mu.Unlock()
@@ -398,7 +398,7 @@ func (w *window) postRedraw() {
func (w *window) setStage(s system.Stage) { func (w *window) setStage(s system.Stage) {
w.stage = s w.stage = s
w.w.event(system.StageEvent{Stage: s}) w.w.Event(system.StageEvent{Stage: s})
} }
func (w *window) draw(sync bool) { func (w *window) draw(sync bool) {
@@ -408,7 +408,7 @@ func (w *window) draw(sync bool) {
w.height = int(r.bottom - r.top) w.height = int(r.bottom - r.top)
cfg := configForDC(w.hdc) cfg := configForDC(w.hdc)
cfg.now = time.Now() cfg.now = time.Now()
w.w.event(frameEvent{ w.w.Event(FrameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: system.FrameEvent{
Size: image.Point{ Size: image.Point{
X: w.width, X: w.width,
@@ -416,7 +416,7 @@ func (w *window) draw(sync bool) {
}, },
Config: &cfg, Config: &cfg,
}, },
sync: sync, Sync: sync,
}) })
} }
@@ -431,7 +431,7 @@ func (w *window) destroy() {
} }
} }
func (w *window) showTextInput(show bool) {} func (w *window) ShowTextInput(show bool) {}
func (w *window) HDC() syscall.Handle { func (w *window) HDC() syscall.Handle {
return w.hdc return w.hdc
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: Unlicense OR MIT
// +build android darwin,ios // +build android darwin,ios
package app package window
// Android only supports non-Java programs as c-shared libraries. // Android only supports non-Java programs as c-shared libraries.
// Unfortunately, Go does not run a program's main function in // Unfortunately, Go does not run a program's main function in
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: Unlicense OR MIT
// +build darwin,ios // +build darwin,ios
package app package window
import "C" import "C"
+121
View File
@@ -0,0 +1,121 @@
// SPDX-License-Identifier: Unlicense OR MIT
// Package window implements platform specific windows
// and GPU contexts.
package window
import (
"errors"
"math"
"time"
"gioui.org/app/internal/gl"
"gioui.org/io/event"
"gioui.org/io/system"
"gioui.org/unit"
)
type Options struct {
Width, Height unit.Value
Title string
}
type FrameEvent struct {
system.FrameEvent
Sync bool
}
type Callbacks interface {
SetDriver(d Driver)
Event(e event.Event)
}
// Driver is the interface for the platform implementation
// of a window.
type Driver interface {
// SetAnimating sets the animation flag. When the window is animating,
// FrameEvents are delivered as fast as the display can handle them.
SetAnimating(anim bool)
// ShowTextInput updates the virtual keyboard state.
ShowTextInput(show bool)
NewContext() (gl.Context, error)
}
type windowRendezvous struct {
in chan windowAndOptions
out chan windowAndOptions
errs chan error
}
type windowAndOptions struct {
window Callbacks
opts *Options
}
// config implements the system.Config interface.
type config struct {
// Device pixels per dp.
pxPerDp float32
// Device pixels per sp.
pxPerSp float32
now time.Time
}
func (c *config) Now() time.Time {
return c.now
}
func (c *config) Px(v unit.Value) int {
var r float32
switch v.U {
case unit.UnitPx:
r = v.V
case unit.UnitDp:
r = c.pxPerDp * v.V
case unit.UnitSp:
r = c.pxPerSp * v.V
default:
panic("unknown unit")
}
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
}
const (
inchPrDp = 1.0 / 160
mmPrDp = 25.4 / 160
// monitorScale is the extra scale applied to
// monitor outputs to compensate for the extra
// viewing distance compared to phone and tables.
monitorScale = 1.20
// minDensity is the minimum pixels per dp to
// ensure font and ui legibility on low-dpi
// screens.
minDensity = 1.25
)
+28 -41
View File
@@ -10,6 +10,7 @@ import (
"gioui.org/app/internal/gpu" "gioui.org/app/internal/gpu"
"gioui.org/app/internal/input" "gioui.org/app/internal/input"
"gioui.org/app/internal/window"
"gioui.org/io/event" "gioui.org/io/event"
"gioui.org/io/profile" "gioui.org/io/profile"
"gioui.org/io/system" "gioui.org/io/system"
@@ -18,22 +19,11 @@ import (
) )
// WindowOption configures a Window. // WindowOption configures a Window.
type Option func(opts *windowOptions) type Option func(opts *window.Options)
type windowOptions struct {
Width, Height unit.Value
Title string
}
type frameEvent struct {
system.FrameEvent
sync bool
}
// Window represents an operating system window. // Window represents an operating system window.
type Window struct { type Window struct {
driver *window driver window.Driver
lastFrame time.Time lastFrame time.Time
drawStart time.Time drawStart time.Time
gpu *gpu.GPU gpu *gpu.GPU
@@ -51,6 +41,12 @@ type Window struct {
delayedDraw *time.Timer delayedDraw *time.Timer
queue Queue queue Queue
callbacks callbacks
}
type callbacks struct {
w *Window
} }
// Queue is an event.Queue implementation that distributes system events // Queue is an event.Queue implementation that distributes system events
@@ -62,19 +58,9 @@ type Queue struct {
// driverEvent is sent when a new native driver // driverEvent is sent when a new native driver
// is available for the Window. // is available for the Window.
type driverEvent struct { type driverEvent struct {
driver *window driver window.Driver
} }
// driver is the interface for the platform implementation
// of a Window.
var _ interface {
// setAnimating sets the animation flag. When the window is animating,
// FrameEvents are delivered as fast as the display can handle them.
setAnimating(anim bool)
// showTextInput updates the virtual keyboard state.
showTextInput(show bool)
} = (*window)(nil)
// Pre-allocate the ack event to avoid garbage. // Pre-allocate the ack event to avoid garbage.
var ackEvent event.Event var ackEvent event.Event
@@ -90,7 +76,7 @@ var ackEvent event.Event
// //
// BUG: Calling NewWindow more than once is not yet supported. // BUG: Calling NewWindow more than once is not yet supported.
func NewWindow(options ...Option) *Window { func NewWindow(options ...Option) *Window {
opts := &windowOptions{ opts := &window.Options{
Width: unit.Dp(800), Width: unit.Dp(800),
Height: unit.Dp(600), Height: unit.Dp(600),
Title: "Gio", Title: "Gio",
@@ -107,6 +93,7 @@ func NewWindow(options ...Option) *Window {
invalidates: make(chan struct{}, 1), invalidates: make(chan struct{}, 1),
frames: make(chan *op.Ops), frames: make(chan *op.Ops),
} }
w.callbacks.w = w
go w.run(opts) go w.run(opts)
return w return w
} }
@@ -141,9 +128,9 @@ func (w *Window) draw(size image.Point, frame *op.Ops) {
now := time.Now() now := time.Now()
switch w.queue.q.TextInputState() { switch w.queue.q.TextInputState() {
case input.TextInputOpen: case input.TextInputOpen:
w.driver.showTextInput(true) w.driver.ShowTextInput(true)
case input.TextInputClose: case input.TextInputClose:
w.driver.showTextInput(false) w.driver.ShowTextInput(false)
} }
frameDur := now.Sub(w.lastFrame) frameDur := now.Sub(w.lastFrame)
frameDur = frameDur.Truncate(100 * time.Microsecond) frameDur = frameDur.Truncate(100 * time.Microsecond)
@@ -186,7 +173,7 @@ func (w *Window) updateAnimation() {
} }
if animate != w.animating { if animate != w.animating {
w.animating = animate w.animating = animate
w.driver.setAnimating(animate) w.driver.SetAnimating(animate)
} }
} }
@@ -197,13 +184,13 @@ func (w *Window) setNextFrame(at time.Time) {
} }
} }
func (w *Window) setDriver(d *window) { func (c *callbacks) SetDriver(d window.Driver) {
w.event(driverEvent{d}) c.Event(driverEvent{d})
} }
func (w *Window) event(e event.Event) { func (c *callbacks) Event(e event.Event) {
w.in <- e c.w.in <- e
<-w.ack <-c.w.ack
} }
func (w *Window) waitAck() { func (w *Window) waitAck() {
@@ -226,10 +213,10 @@ func (w *Window) destroy(err error) {
} }
} }
func (w *Window) run(opts *windowOptions) { func (w *Window) run(opts *window.Options) {
defer close(w.in) defer close(w.in)
defer close(w.out) defer close(w.out)
if err := createWindow(w, opts); err != nil { if err := window.NewWindow(&w.callbacks, opts); err != nil {
w.out <- system.DestroyEvent{Err: err} w.out <- system.DestroyEvent{Err: err}
return return
} }
@@ -260,7 +247,7 @@ func (w *Window) run(opts *windowOptions) {
w.updateAnimation() w.updateAnimation()
w.out <- e w.out <- e
w.waitAck() w.waitAck()
case frameEvent: case window.FrameEvent:
if e2.Size == (image.Point{}) { if e2.Size == (image.Point{}) {
panic(errors.New("internal error: zero-sized Draw")) panic(errors.New("internal error: zero-sized Draw"))
} }
@@ -280,7 +267,7 @@ func (w *Window) run(opts *windowOptions) {
case w.out <- ackEvent: case w.out <- ackEvent:
} }
if w.gpu != nil { if w.gpu != nil {
if e2.sync { if e2.Sync {
w.gpu.Refresh() w.gpu.Refresh()
} }
if err := w.gpu.Flush(); err != nil { if err := w.gpu.Flush(); err != nil {
@@ -290,7 +277,7 @@ func (w *Window) run(opts *windowOptions) {
return return
} }
} else { } else {
ctx, err := newContext(w.driver) ctx, err := w.driver.NewContext()
if err != nil { if err != nil {
w.destroy(err) w.destroy(err)
return return
@@ -302,7 +289,7 @@ func (w *Window) run(opts *windowOptions) {
} }
} }
w.draw(e2.Size, frame) w.draw(e2.Size, frame)
if e2.sync { if e2.Sync {
if err := w.gpu.Flush(); err != nil { if err := w.gpu.Flush(); err != nil {
w.gpu.Release() w.gpu.Release()
w.gpu = nil w.gpu = nil
@@ -337,7 +324,7 @@ func (q *Queue) Events(k event.Key) []event.Event {
// Title sets the title of the window. // Title sets the title of the window.
func Title(t string) Option { func Title(t string) Option {
return func(opts *windowOptions) { return func(opts *window.Options) {
opts.Title = t opts.Title = t
} }
} }
@@ -350,7 +337,7 @@ func Size(w, h unit.Value) Option {
if h.V <= 0 { if h.V <= 0 {
panic("height must be larger than or equal to 0") panic("height must be larger than or equal to 0")
} }
return func(opts *windowOptions) { return func(opts *window.Options) {
opts.Width = w opts.Width = w
opts.Height = h opts.Height = h
} }