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
import (
"errors"
"math"
"os"
"strings"
"time"
"gioui.org/unit"
)
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
"gioui.org/app/internal/window"
)
// 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
// running windows.
func Main() {
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
window.Main()
}
+7 -7
View File
@@ -5,21 +5,21 @@
package app
import "C"
import "sync"
import (
"sync"
"gioui.org/app/internal/window"
)
var (
dataDirOnce sync.Once
dataDirChan = make(chan string, 1)
dataPath string
)
func dataDir() (string, error) {
dataDirOnce.Do(func() {
dataPath = <-dataDirChan
dataPath = window.GetDataDir()
})
return dataPath, nil
}
func setDataDir(dir string) {
dataDirChan <- dir
}
+1 -1
View File
@@ -2,7 +2,7 @@
// +build linux windows
package app
package window
import (
"errors"
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT
package app
package window
/*
#include <EGL/egl.h>
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT
package app
package window
/*
#cgo LDFLAGS: -lEGL
@@ -13,6 +13,8 @@ package app
*/
import "C"
import "gioui.org/app/internal/gl"
type (
_EGLint = C.EGLint
_EGLDisplay = C.EGLDisplay
@@ -93,3 +95,7 @@ func eglCreateWindowSurface(disp _EGLDisplay, conf _EGLConfig, win _EGLNativeWin
eglSurf := C.eglCreateWindowSurface(disp, conf, win, &attribs[0])
return eglSurf
}
func (w *window) NewContext() (gl.Context, error) {
return newContext(w)
}
@@ -2,7 +2,7 @@
// +build linux,!android
package app
package window
import (
"errors"
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT
package app
package window
import (
"os"
@@ -154,3 +154,7 @@ func (w *window) eglWindow(visID int) (_EGLNativeWindowType, int, int, error) {
hwnd, width, height := w.HWND()
return _EGLNativeWindowType(hwnd), width, height, nil
}
func (w *window) NewContext() (gl.Context, error) {
return newContext(w)
}
@@ -2,7 +2,7 @@
// +build darwin,ios
package app
package window
/*
#cgo CFLAGS: -fmodules -fobjc-arc -x objective-c
@@ -122,3 +122,7 @@ func (c *context) MakeCurrent() error {
}
return nil
}
func (w *window) NewContext() (gl.Context, error) {
return newContext(w)
}
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT
package app
package window
import (
"errors"
@@ -89,3 +89,7 @@ func (c *context) MakeCurrent() error {
}
return nil
}
func (w *window) NewContext() (gl.Context, error) {
return newContext(w)
}
@@ -2,7 +2,7 @@
// +build darwin,!ios
package app
package window
import (
"gioui.org/app/internal/gl"
@@ -72,3 +72,7 @@ func (c *context) MakeCurrent() error {
C.gio_makeCurrentContext(c.ctx)
return nil
}
func (w *window) NewContext() (gl.Context, error) {
return newContext(w)
}
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT
package app
package window
/*
#cgo LDFLAGS: -landroid
@@ -32,7 +32,7 @@ import (
)
type window struct {
*Window
callbacks Callbacks
view C.jobject
@@ -55,6 +55,8 @@ type window struct {
mpostFrameCallbackOnMainThread C.jmethodID
}
var dataDirChan = make(chan string, 1)
var theJVM *C.JavaVM
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)
dataDir := C.GoStringN((*C.char)(unsafe.Pointer(dirBytes)), n)
setDataDir(dataDir)
dataDirChan <- dataDir
C.gio_jni_ReleaseByteArrayElements(env, jdataDir, dirBytes)
runMain()
}
func GetDataDir() string {
return <-dataDirChan
}
//export setJVM
func setJVM(vm *C.JavaVM) {
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"),
}
wopts := <-mainWindow.out
w.Window = wopts.window
w.Window.setDriver(w)
w.callbacks = wopts.window
w.callbacks.SetDriver(w)
handle := C.jlong(view)
views[handle] = w
w.loadConfig(env, class)
@@ -120,7 +126,7 @@ func onCreateView(env *C.JNIEnv, class C.jclass, view C.jobject) C.jlong {
//export onDestroyView
func onDestroyView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
w := views[handle]
w.setDriver(nil)
w.callbacks.SetDriver(nil)
delete(views, handle)
C.gio_jni_DeleteGlobalRef(env, w.view)
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 {
w := views[view]
ev := &system.CommandEvent{Type: system.CommandBack}
w.event(ev)
w.callbacks.Event(ev)
if ev.Cancel {
return C.JNI_TRUE
}
@@ -211,7 +217,7 @@ func onBack(env *C.JNIEnv, class C.jclass, view C.jlong) C.jboolean {
//export onFocusChange
func onFocusChange(env *C.JNIEnv, class C.jclass, view C.jlong, focus C.jboolean) {
w := views[view]
w.event(key.FocusEvent{Focus: focus == C.JNI_TRUE})
w.callbacks.Event(key.FocusEvent{Focus: focus == C.JNI_TRUE})
}
//export onWindowInsets
@@ -243,7 +249,7 @@ func (w *window) setStage(stage system.Stage) {
return
}
w.stage = stage
w.event(system.StageEvent{stage})
w.callbacks.Event(system.StageEvent{stage})
}
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.animating = anim
w.mu.Unlock()
@@ -297,7 +303,7 @@ func (w *window) draw(sync bool) {
return
}
ppdp := float32(w.dpi) * inchPrDp
w.event(frameEvent{
w.callbacks.Event(FrameEvent{
FrameEvent: system.FrameEvent{
Size: image.Point{
X: int(width),
@@ -310,7 +316,7 @@ func (w *window) draw(sync bool) {
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) {
w := views[handle]
if n, ok := convertKeyCode(keyCode); ok {
w.event(key.Event{Name: n})
w.callbacks.Event(key.Event{Name: n})
}
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:
return
}
w.event(pointer.Event{
w.callbacks.Event(pointer.Event{
Type: typ,
Source: src,
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 {
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}
return <-mainWindow.errs
}
+17 -17
View File
@@ -2,7 +2,7 @@
// +build darwin,ios
package app
package window
/*
#cgo CFLAGS: -fmodules -fobjc-arc -x objective-c
@@ -31,7 +31,7 @@ import (
type window struct {
view C.CFTypeRef
w *Window
w Callbacks
layer C.CFTypeRef
visible atomic.Value
@@ -57,12 +57,12 @@ func onCreate(view C.CFTypeRef) {
}
wopts := <-mainWindow.out
w.w = wopts.window
w.w.setDriver(w)
w.w.SetDriver(w)
w.visible.Store(false)
w.layer = C.CFTypeRef(layerFactory())
C.gio_addLayerToView(view, w.layer)
views[view] = w
w.w.event(system.StageEvent{Stage: system.StagePaused})
w.w.Event(system.StageEvent{Stage: system.StagePaused})
}
//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)
C.gio_updateView(view, w.layer)
if !wasVisible {
w.w.event(system.StageEvent{Stage: system.StageRunning})
w.w.Event(system.StageEvent{Stage: system.StageRunning})
}
isSync := false
if sync != 0 {
isSync = true
}
w.w.event(frameEvent{
w.w.Event(FrameEvent{
FrameEvent: system.FrameEvent{
Size: image.Point{
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(),
},
},
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) {
w := views[view]
w.visible.Store(false)
w.w.event(system.StageEvent{Stage: system.StagePaused})
w.w.Event(system.StageEvent{Stage: system.StagePaused})
}
//export onDestroy
func onDestroy(view C.CFTypeRef) {
w := views[view]
delete(views, view)
w.w.event(system.DestroyEvent{})
w.w.Event(system.DestroyEvent{})
C.gio_removeLayer(w.layer)
C.CFRelease(w.layer)
w.layer = 0
@@ -124,7 +124,7 @@ func onDestroy(view C.CFTypeRef) {
//export onFocus
func onFocus(view C.CFTypeRef, focus int) {
w := views[view]
w.w.event(key.FocusEvent{Focus: focus != 0})
w.w.Event(key.FocusEvent{Focus: focus != 0})
}
//export onLowMemory
@@ -161,7 +161,7 @@ func onDeleteBackward(view C.CFTypeRef) {
//export onText
func onText(view C.CFTypeRef, str *C.char) {
w := views[view]
w.w.event(key.EditEvent{
w.w.Event(key.EditEvent{
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]
t := time.Duration(float64(ti) * float64(time.Second))
p := f32.Point{X: float32(x), Y: float32(y)}
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: typ,
Source: pointer.Touch,
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 {
return
}
@@ -205,7 +205,7 @@ func (w *window) setAnimating(anim bool) {
}
func (w *window) onKeyCommand(name rune) {
w.w.event(key.Event{
w.w.Event(key.Event{
Name: name,
})
}
@@ -238,7 +238,7 @@ func (w *window) isVisible() bool {
return w.visible.Load().(bool)
}
func (w *window) showTextInput(show bool) {
func (w *window) ShowTextInput(show bool) {
if w.view == 0 {
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}
return <-mainWindow.errs
}
func main() {
func Main() {
}
+17 -17
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT
package app
package window
import (
"image"
@@ -18,7 +18,7 @@ type window struct {
window js.Value
cnv js.Value
tarea js.Value
w *Window
w Callbacks
redraw js.Func
requestAnimationFrame js.Value
cleanfuncs []func()
@@ -32,7 +32,7 @@ type window 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")
cont := getContainer(doc)
cnv := createCanvas(doc)
@@ -52,9 +52,9 @@ func createWindow(win *Window, opts *windowOptions) error {
w.addEventListeners()
w.w = win
go func() {
w.w.setDriver(w)
w.w.SetDriver(w)
w.focus()
w.w.event(system.StageEvent{Stage: system.StageRunning})
w.w.Event(system.StageEvent{Stage: system.StageRunning})
w.draw(true)
select {}
w.cleanup()
@@ -156,18 +156,18 @@ func (w *window) addEventListeners() {
w.touches[i] = js.Null()
}
w.touches = w.touches[:0]
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: pointer.Cancel,
Source: pointer.Touch,
})
return nil
})
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
})
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
})
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() {
val := w.tarea.Get("value").String()
w.tarea.Set("value", "")
w.w.event(key.EditEvent{Text: string(val)})
w.w.Event(key.EditEvent{Text: string(val)})
}
func (w *window) blur() {
@@ -216,7 +216,7 @@ func (w *window) keyEvent(e js.Value) {
if e.Call("getModifierState", "Shift").Bool() {
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,
Y: float32(y) * scale,
}
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: typ,
Source: pointer.Touch,
Position: pos,
@@ -279,7 +279,7 @@ func (w *window) pointerEvent(typ pointer.Type, dx, dy float32, e js.Value) {
Y: dy * scale,
}
t := time.Duration(e.Get("timeStamp").Int()) * time.Millisecond
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: typ,
Source: pointer.Mouse,
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()
defer w.mu.Unlock()
if anim && !w.animating {
@@ -325,7 +325,7 @@ func (w *window) setAnimating(anim bool) {
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
// focus change result in an event.
go func() {
@@ -346,7 +346,7 @@ func (w *window) draw(sync bool) {
w.scale = float32(scale)
w.mu.Unlock()
cfg.now = time.Now()
w.w.event(frameEvent{
w.w.Event(FrameEvent{
FrameEvent: system.FrameEvent{
Size: image.Point{
X: width,
@@ -354,7 +354,7 @@ func (w *window) draw(sync bool) {
},
Config: &cfg,
},
sync: sync,
Sync: sync,
})
}
@@ -377,7 +377,7 @@ func (w *window) config() (int, int, float32, config) {
}
}
func main() {
func Main() {
<-mainDone
}
@@ -2,7 +2,7 @@
// +build darwin,!ios
package app
package window
import (
"errors"
@@ -33,7 +33,7 @@ func init() {
type window struct {
view C.CFTypeRef
w *Window
w Callbacks
stage system.Stage
ppdp float32
scale float32
@@ -81,9 +81,9 @@ func (w *window) contextView() C.CFTypeRef {
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
if anim {
animb = 1
@@ -96,7 +96,7 @@ func (w *window) setStage(stage system.Stage) {
return
}
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
@@ -129,7 +129,7 @@ func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger)
w := views[view]
for _, k := range str {
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)
viewDo(view, func(views viewMap, view C.CFTypeRef) {
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]
x, y := float32(x)*w.scale, float32(y)*w.scale
dx, dy := float32(dx)*w.scale, float32(dy)*w.scale
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: typ,
Source: pointer.Mouse,
Time: t,
@@ -185,7 +185,7 @@ func gio_onDraw(view C.CFTypeRef) {
func gio_onFocus(view C.CFTypeRef, focus C.BOOL) {
viewDo(view, func(views viewMap, view C.CFTypeRef) {
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.now = time.Now()
w.setStage(system.StageRunning)
w.w.event(frameEvent{
w.w.Event(FrameEvent{
FrameEvent: system.FrameEvent{
Size: image.Point{
X: width,
@@ -208,7 +208,7 @@ func (w *window) draw(sync bool) {
},
Config: &cfg,
},
sync: sync,
Sync: sync,
})
}
@@ -234,7 +234,7 @@ func gio_onTerminate(view C.CFTypeRef) {
viewDo(view, func(views viewMap, view C.CFTypeRef) {
w := 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
w.w = wopts.window
w.w.setDriver(w)
w.w.SetDriver(w)
views[view] = w
})
}
func createWindow(win *Window, opts *windowOptions) error {
func NewWindow(win Callbacks, opts *Options) error {
mainWindow.in <- windowAndOptions{win, opts}
return <-mainWindow.errs
}
func main() {
func Main() {
wopts := <-mainWindow.out
view := viewFactory()
if view == 0 {
@@ -2,7 +2,7 @@
// +build linux,!android
package app
package window
import (
"bytes"
@@ -80,7 +80,7 @@ type repeatState struct {
delay time.Duration
key uint32
win *Window
win Callbacks
stopC chan struct{}
start time.Duration
@@ -90,7 +90,7 @@ type repeatState struct {
}
type window struct {
w *Window
w Callbacks
disp *C.struct_wl_display
surf *C.struct_wl_surface
wmSurf *C.struct_xdg_surface
@@ -153,11 +153,11 @@ var (
outputConfig = make(map[*C.struct_wl_output]*wlOutput)
)
func main() {
func Main() {
<-mainDone
}
func createWindow(window *Window, opts *windowOptions) error {
func NewWindow(window Callbacks, opts *Options) error {
connMu.Lock()
defer connMu.Unlock()
if len(winMap) > 0 {
@@ -173,7 +173,7 @@ func createWindow(window *Window, opts *windowOptions) error {
}
w.w = window
go func() {
w.w.setDriver(w)
w.w.SetDriver(w)
w.loop()
w.destroy()
conn.destroy()
@@ -182,7 +182,7 @@ func createWindow(window *Window, opts *windowOptions) error {
return nil
}
func createNativeWindow(opts *windowOptions) (*window, error) {
func createNativeWindow(opts *Options) (*window, error) {
pipe := make([]int, 2)
if err := syscall.Pipe2(pipe, syscall.O_NONBLOCK|syscall.O_CLOEXEC); err != nil {
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),
Y: fromFixed(y) * float32(w.scale),
}
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: pointer.Press,
Source: pointer.Touch,
Position: w.lastTouch,
@@ -461,7 +461,7 @@ func gio_onTouchDown(data unsafe.Pointer, touch *C.struct_wl_touch, serial, t C.
//export gio_onTouchUp
func gio_onTouchUp(data unsafe.Pointer, touch *C.struct_wl_touch, serial, t C.uint32_t, id C.int32_t) {
w := winMap[touch]
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: pointer.Release,
Source: pointer.Touch,
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),
Y: fromFixed(y) * float32(w.scale),
}
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: pointer.Move,
Position: w.lastTouch,
Source: pointer.Touch,
@@ -493,7 +493,7 @@ func gio_onTouchFrame(data unsafe.Pointer, touch *C.struct_wl_touch) {
//export gio_onTouchCancel
func gio_onTouchCancel(data unsafe.Pointer, touch *C.struct_wl_touch) {
w := winMap[touch]
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: pointer.Cancel,
Source: pointer.Touch,
})
@@ -544,7 +544,7 @@ func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t,
}
w.flushScroll()
w.resetFling()
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: typ,
Source: pointer.Mouse,
Position: w.lastPos,
@@ -645,14 +645,14 @@ func gio_onKeyboardEnter(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, se
conn.repeat.Stop(0)
w := winMap[surf]
winMap[keyboard] = w
w.w.event(key.FocusEvent{Focus: true})
w.w.Event(key.FocusEvent{Focus: true})
}
//export gio_onKeyboardLeave
func gio_onKeyboardLeave(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, serial C.uint32_t, surf *C.struct_wl_surface) {
conn.repeat.Stop(0)
w := winMap[keyboard]
w.w.event(key.FocusEvent{Focus: false})
w.w.Event(key.FocusEvent{Focus: false})
}
//export gio_onKeyboardKey
@@ -666,7 +666,7 @@ func gio_onKeyboardKey(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, seri
}
kc := uint32(keyCode)
for _, e := range conn.xkb.DispatchKey(kc) {
w.w.event(e)
w.w.Event(e)
}
if conn.xkb.IsRepeatKey(kc) {
conn.repeat.Start(w, kc, t)
@@ -739,7 +739,7 @@ func (r *repeatState) Repeat() {
break
}
for _, e := range conn.xkb.DispatchKey(r.key) {
r.win.event(e)
r.win.Event(e)
}
r.last += delay
}
@@ -773,7 +773,7 @@ loop:
break
}
if w.dead {
w.w.event(system.DestroyEvent{Err: w.pendingErr})
w.w.Event(system.DestroyEvent{Err: w.pendingErr})
break
}
// Clear poll events.
@@ -809,7 +809,7 @@ loop:
}
}
func (w *window) setAnimating(anim bool) {
func (w *window) SetAnimating(anim bool) {
w.mu.Lock()
w.animating = anim
animating := w.isAnimating()
@@ -924,7 +924,7 @@ func (w *window) flushScroll() {
if total == (f32.Point{}) {
return
}
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: pointer.Move,
Source: pointer.Mouse,
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),
Y: fromFixed(y) * float32(w.scale),
}
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: pointer.Move,
Position: w.lastPos,
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))
}
cfg.now = time.Now()
w.w.event(frameEvent{
w.w.Event(FrameEvent{
FrameEvent: system.FrameEvent{
Size: image.Point{
X: width,
@@ -1033,7 +1033,7 @@ func (w *window) draw(sync bool) {
},
Config: &cfg,
},
sync: sync,
Sync: sync,
})
}
@@ -1042,7 +1042,7 @@ func (w *window) setStage(s system.Stage) {
return
}
w.stage = s
w.w.event(system.StageEvent{s})
w.w.Event(system.StageEvent{s})
}
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
}
func (w *window) showTextInput(show bool) {}
func (w *window) ShowTextInput(show bool) {}
// detectFontScale reports current font scale, or 1.0
// if it fails.
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT
package app
package window
import (
"errors"
@@ -58,7 +58,7 @@ type point struct {
type window struct {
hwnd syscall.Handle
hdc syscall.Handle
w *Window
w Callbacks
width int
height int
stage system.Stage
@@ -159,11 +159,11 @@ const _WM_REDRAW = _WM_USER + 0
var onceMu sync.Mutex
var mainDone = make(chan struct{})
func main() {
func Main() {
<-mainDone
}
func createWindow(window *Window, opts *windowOptions) error {
func NewWindow(window Callbacks, opts *Options) error {
onceMu.Lock()
defer onceMu.Unlock()
if len(winMap) > 0 {
@@ -183,8 +183,8 @@ func createWindow(window *Window, opts *windowOptions) error {
winMap[w.hwnd] = w
defer delete(winMap, w.hwnd)
w.w = window
w.w.setDriver(w)
defer w.w.event(system.DestroyEvent{})
w.w.SetDriver(w)
defer w.w.Event(system.DestroyEvent{})
showWindow(w.hwnd, _SW_SHOWDEFAULT)
setForegroundWindow(w.hwnd)
setFocus(w.hwnd)
@@ -196,7 +196,7 @@ func createWindow(window *Window, opts *windowOptions) error {
return <-cerr
}
func createNativeWindow(opts *windowOptions) (*window, error) {
func createNativeWindow(opts *Options) (*window, error) {
setProcessDPIAware()
screenDC, err := getDC(0)
if err != nil {
@@ -266,7 +266,7 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
fallthrough
case _WM_CHAR:
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.
return 1
@@ -279,31 +279,31 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
if getKeyState(_VK_SHIFT)&0x1000 != 0 {
cmd.Modifiers |= key.ModShift
}
w.w.event(cmd)
w.w.Event(cmd)
}
case _WM_LBUTTONDOWN:
setCapture(w.hwnd)
x, y := coordsFromlParam(lParam)
p := f32.Point{X: float32(x), Y: float32(y)}
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: pointer.Press,
Source: pointer.Mouse,
Position: p,
Time: getMessageTime(),
})
case _WM_CANCELMODE:
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: pointer.Cancel,
})
case _WM_SETFOCUS:
w.w.event(key.FocusEvent{Focus: true})
w.w.Event(key.FocusEvent{Focus: true})
case _WM_KILLFOCUS:
w.w.event(key.FocusEvent{Focus: false})
w.w.Event(key.FocusEvent{Focus: false})
case _WM_LBUTTONUP:
releaseCapture()
x, y := coordsFromlParam(lParam)
p := f32.Point{X: float32(x), Y: float32(y)}
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: pointer.Release,
Source: pointer.Mouse,
Position: p,
@@ -312,7 +312,7 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
case _WM_MOUSEMOVE:
x, y := coordsFromlParam(lParam)
p := f32.Point{X: float32(x), Y: float32(y)}
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: pointer.Move,
Source: pointer.Mouse,
Position: p,
@@ -350,7 +350,7 @@ func (w *window) scrollEvent(wParam, lParam uintptr) {
screenToClient(w.hwnd, &np)
p := f32.Point{X: float32(np.x), Y: float32(np.y)}
dist := float32(int16(wParam >> 16))
w.w.event(pointer.Event{
w.w.Event(pointer.Event{
Type: pointer.Move,
Source: pointer.Mouse,
Position: p,
@@ -381,7 +381,7 @@ func (w *window) loop() error {
return nil
}
func (w *window) setAnimating(anim bool) {
func (w *window) SetAnimating(anim bool) {
w.mu.Lock()
w.animating = anim
w.mu.Unlock()
@@ -398,7 +398,7 @@ func (w *window) postRedraw() {
func (w *window) setStage(s system.Stage) {
w.stage = s
w.w.event(system.StageEvent{Stage: s})
w.w.Event(system.StageEvent{Stage: s})
}
func (w *window) draw(sync bool) {
@@ -408,7 +408,7 @@ func (w *window) draw(sync bool) {
w.height = int(r.bottom - r.top)
cfg := configForDC(w.hdc)
cfg.now = time.Now()
w.w.event(frameEvent{
w.w.Event(FrameEvent{
FrameEvent: system.FrameEvent{
Size: image.Point{
X: w.width,
@@ -416,7 +416,7 @@ func (w *window) draw(sync bool) {
},
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 {
return w.hdc
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: Unlicense OR MIT
// +build android darwin,ios
package app
package window
// Android only supports non-Java programs as c-shared libraries.
// Unfortunately, Go does not run a program's main function in
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: Unlicense OR MIT
// +build darwin,ios
package app
package window
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/input"
"gioui.org/app/internal/window"
"gioui.org/io/event"
"gioui.org/io/profile"
"gioui.org/io/system"
@@ -18,22 +19,11 @@ import (
)
// WindowOption configures a Window.
type Option func(opts *windowOptions)
type windowOptions struct {
Width, Height unit.Value
Title string
}
type frameEvent struct {
system.FrameEvent
sync bool
}
type Option func(opts *window.Options)
// Window represents an operating system window.
type Window struct {
driver *window
driver window.Driver
lastFrame time.Time
drawStart time.Time
gpu *gpu.GPU
@@ -51,6 +41,12 @@ type Window struct {
delayedDraw *time.Timer
queue Queue
callbacks callbacks
}
type callbacks struct {
w *Window
}
// 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
// is available for the Window.
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.
var ackEvent event.Event
@@ -90,7 +76,7 @@ var ackEvent event.Event
//
// BUG: Calling NewWindow more than once is not yet supported.
func NewWindow(options ...Option) *Window {
opts := &windowOptions{
opts := &window.Options{
Width: unit.Dp(800),
Height: unit.Dp(600),
Title: "Gio",
@@ -107,6 +93,7 @@ func NewWindow(options ...Option) *Window {
invalidates: make(chan struct{}, 1),
frames: make(chan *op.Ops),
}
w.callbacks.w = w
go w.run(opts)
return w
}
@@ -141,9 +128,9 @@ func (w *Window) draw(size image.Point, frame *op.Ops) {
now := time.Now()
switch w.queue.q.TextInputState() {
case input.TextInputOpen:
w.driver.showTextInput(true)
w.driver.ShowTextInput(true)
case input.TextInputClose:
w.driver.showTextInput(false)
w.driver.ShowTextInput(false)
}
frameDur := now.Sub(w.lastFrame)
frameDur = frameDur.Truncate(100 * time.Microsecond)
@@ -186,7 +173,7 @@ func (w *Window) updateAnimation() {
}
if animate != w.animating {
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) {
w.event(driverEvent{d})
func (c *callbacks) SetDriver(d window.Driver) {
c.Event(driverEvent{d})
}
func (w *Window) event(e event.Event) {
w.in <- e
<-w.ack
func (c *callbacks) Event(e event.Event) {
c.w.in <- e
<-c.w.ack
}
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.out)
if err := createWindow(w, opts); err != nil {
if err := window.NewWindow(&w.callbacks, opts); err != nil {
w.out <- system.DestroyEvent{Err: err}
return
}
@@ -260,7 +247,7 @@ func (w *Window) run(opts *windowOptions) {
w.updateAnimation()
w.out <- e
w.waitAck()
case frameEvent:
case window.FrameEvent:
if e2.Size == (image.Point{}) {
panic(errors.New("internal error: zero-sized Draw"))
}
@@ -280,7 +267,7 @@ func (w *Window) run(opts *windowOptions) {
case w.out <- ackEvent:
}
if w.gpu != nil {
if e2.sync {
if e2.Sync {
w.gpu.Refresh()
}
if err := w.gpu.Flush(); err != nil {
@@ -290,7 +277,7 @@ func (w *Window) run(opts *windowOptions) {
return
}
} else {
ctx, err := newContext(w.driver)
ctx, err := w.driver.NewContext()
if err != nil {
w.destroy(err)
return
@@ -302,7 +289,7 @@ func (w *Window) run(opts *windowOptions) {
}
}
w.draw(e2.Size, frame)
if e2.sync {
if e2.Sync {
if err := w.gpu.Flush(); err != nil {
w.gpu.Release()
w.gpu = nil
@@ -337,7 +324,7 @@ func (q *Queue) Events(k event.Key) []event.Event {
// Title sets the title of the window.
func Title(t string) Option {
return func(opts *windowOptions) {
return func(opts *window.Options) {
opts.Title = t
}
}
@@ -350,7 +337,7 @@ func Size(w, h unit.Value) Option {
if h.V <= 0 {
panic("height must be larger than or equal to 0")
}
return func(opts *windowOptions) {
return func(opts *window.Options) {
opts.Width = w
opts.Height = h
}