mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-07 02:15:34 +00:00
ui/app: introduce DestroyEvent for ending the event loop
Replace the StageDead stage with DestroyEvent dedicated to ending the event loop and, for premature window closes, the error. Drop the error return from NewWindow; any errors in window creation will appear as an immediate DestroyEvent with its Err field set. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+16
-8
@@ -17,6 +17,8 @@ type Event interface {
|
|||||||
ImplementsEvent()
|
ImplementsEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DrawEvent is sent when a Window's Draw
|
||||||
|
// method must be called.
|
||||||
type DrawEvent struct {
|
type DrawEvent struct {
|
||||||
Config Config
|
Config Config
|
||||||
Size image.Point
|
Size image.Point
|
||||||
@@ -27,6 +29,14 @@ type DrawEvent struct {
|
|||||||
sync bool
|
sync bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DestroyEvent is the last event sent through
|
||||||
|
// a window event channel.
|
||||||
|
type DestroyEvent struct {
|
||||||
|
// Err is nil for normal window closures. If a
|
||||||
|
// window is prematurely closed, Err is the cause.
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
// Insets is the space taken up by
|
// Insets is the space taken up by
|
||||||
// system decoration such as translucent
|
// system decoration such as translucent
|
||||||
// system bars and software keyboards.
|
// system bars and software keyboards.
|
||||||
@@ -60,8 +70,7 @@ type windowAndOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StageDead Stage = iota
|
StagePaused Stage = iota
|
||||||
StagePaused
|
|
||||||
StageRunning
|
StageRunning
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -93,8 +102,6 @@ var extraArgs string
|
|||||||
|
|
||||||
func (l Stage) String() string {
|
func (l Stage) String() string {
|
||||||
switch l {
|
switch l {
|
||||||
case StageDead:
|
|
||||||
return "StageDead"
|
|
||||||
case StagePaused:
|
case StagePaused:
|
||||||
return "StagePaused"
|
return "StagePaused"
|
||||||
case StageRunning:
|
case StageRunning:
|
||||||
@@ -104,10 +111,6 @@ func (l Stage) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_ DrawEvent) ImplementsEvent() {}
|
|
||||||
func (_ StageEvent) ImplementsEvent() {}
|
|
||||||
func (_ *CommandEvent) ImplementsEvent() {}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
args := strings.Split(extraArgs, "|")
|
args := strings.Split(extraArgs, "|")
|
||||||
os.Args = append(os.Args, args...)
|
os.Args = append(os.Args, args...)
|
||||||
@@ -178,3 +181,8 @@ func newWindowRendezvous() *windowRendezvous {
|
|||||||
}()
|
}()
|
||||||
return wr
|
return wr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (_ DrawEvent) ImplementsEvent() {}
|
||||||
|
func (_ StageEvent) ImplementsEvent() {}
|
||||||
|
func (_ *CommandEvent) ImplementsEvent() {}
|
||||||
|
func (_ DestroyEvent) ImplementsEvent() {}
|
||||||
|
|||||||
+1
-1
@@ -111,7 +111,7 @@ func onStop(view C.CFTypeRef) {
|
|||||||
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(StageEvent{StageDead})
|
w.w.event(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
|
||||||
|
|||||||
+1
-1
@@ -163,7 +163,7 @@ func getConfig() Config {
|
|||||||
func gio_onTerminate(view C.CFTypeRef) {
|
func gio_onTerminate(view C.CFTypeRef) {
|
||||||
w := views[view]
|
w := views[view]
|
||||||
delete(views, view)
|
delete(views, view)
|
||||||
w.setStage(StageDead)
|
w.w.event(DestroyEvent{})
|
||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onHide
|
//export gio_onHide
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ type window struct {
|
|||||||
lastTouch f32.Point
|
lastTouch f32.Point
|
||||||
|
|
||||||
stage Stage
|
stage Stage
|
||||||
|
dead bool
|
||||||
lastFrameCallback *C.struct_wl_callback
|
lastFrameCallback *C.struct_wl_callback
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
@@ -308,7 +309,8 @@ func gio_onXdgSurfaceConfigure(data unsafe.Pointer, wmSurf *C.struct_xdg_surface
|
|||||||
//export gio_onToplevelClose
|
//export gio_onToplevelClose
|
||||||
func gio_onToplevelClose(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel) {
|
func gio_onToplevelClose(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel) {
|
||||||
w := winMap[topLvl]
|
w := winMap[topLvl]
|
||||||
w.setStage(StageDead)
|
w.dead = true
|
||||||
|
w.w.event(DestroyEvent{})
|
||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onToplevelConfigure
|
//export gio_onToplevelConfigure
|
||||||
@@ -769,7 +771,7 @@ loop:
|
|||||||
if ret := C.wl_display_flush(conn.disp); ret < 0 {
|
if ret := C.wl_display_flush(conn.disp); ret < 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if w.stage == StageDead {
|
if w.dead {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Clear poll events.
|
// Clear poll events.
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ type window struct {
|
|||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
stage Stage
|
stage Stage
|
||||||
|
dead bool
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
animating bool
|
animating bool
|
||||||
@@ -319,7 +320,8 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
|
|||||||
w.scrollEvent(wParam, lParam)
|
w.scrollEvent(wParam, lParam)
|
||||||
case _WM_DESTROY:
|
case _WM_DESTROY:
|
||||||
delete(winMap, hwnd)
|
delete(winMap, hwnd)
|
||||||
w.setStage(StageDead)
|
w.dead = true
|
||||||
|
w.w.event(DestroyEvent{})
|
||||||
case _WM_REDRAW:
|
case _WM_REDRAW:
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
anim := w.animating
|
anim := w.animating
|
||||||
@@ -368,7 +370,7 @@ func (w *window) scrollEvent(wParam, lParam uintptr) {
|
|||||||
// Adapted from https://blogs.msdn.microsoft.com/oldnewthing/20060126-00/?p=32513/
|
// Adapted from https://blogs.msdn.microsoft.com/oldnewthing/20060126-00/?p=32513/
|
||||||
func (w *window) loop() error {
|
func (w *window) loop() error {
|
||||||
loop:
|
loop:
|
||||||
for w.stage > StageDead {
|
for !w.dead {
|
||||||
var msg msg
|
var msg msg
|
||||||
// Since posted messages are always returned before system messages,
|
// Since posted messages are always returned before system messages,
|
||||||
// but we want our WM_REDRAW to always come last, just like WM_PAINT.
|
// but we want our WM_REDRAW to always come last, just like WM_PAINT.
|
||||||
|
|||||||
+25
-16
@@ -29,7 +29,6 @@ type Window struct {
|
|||||||
drawStart time.Time
|
drawStart time.Time
|
||||||
gpu *gpu.GPU
|
gpu *gpu.GPU
|
||||||
inputState key.TextInputState
|
inputState key.TextInputState
|
||||||
err error
|
|
||||||
|
|
||||||
events chan Event
|
events chan Event
|
||||||
|
|
||||||
@@ -70,7 +69,7 @@ var ackEvent Event
|
|||||||
// ignore or adjust them.
|
// ignore or adjust them.
|
||||||
// If the current program is running on iOS and Android,
|
// If the current program is running on iOS and Android,
|
||||||
// NewWindow returns the window previously by the platform.
|
// NewWindow returns the window previously by the platform.
|
||||||
func NewWindow(opts *WindowOptions) (*Window, error) {
|
func NewWindow(opts *WindowOptions) *Window {
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = &WindowOptions{
|
opts = &WindowOptions{
|
||||||
Width: ui.Dp(800),
|
Width: ui.Dp(800),
|
||||||
@@ -87,9 +86,11 @@ func NewWindow(opts *WindowOptions) (*Window, error) {
|
|||||||
stage: StagePaused,
|
stage: StagePaused,
|
||||||
}
|
}
|
||||||
if err := createWindow(w, opts); err != nil {
|
if err := createWindow(w, opts); err != nil {
|
||||||
return nil, err
|
// For simplicity, NewWindow always succeeds. Send
|
||||||
|
// an immediate DestroyEvent instead of returning the error.
|
||||||
|
w.destroy(err)
|
||||||
}
|
}
|
||||||
return w, nil
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) Events() <-chan Event {
|
func (w *Window) Events() <-chan Event {
|
||||||
@@ -107,10 +108,6 @@ func (w *Window) setTextInput(s key.TextInputState) {
|
|||||||
w.inputState = s
|
w.inputState = s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) Err() error {
|
|
||||||
return w.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Window) Queue() input.Queue {
|
func (w *Window) Queue() input.Queue {
|
||||||
return &w.router
|
return &w.router
|
||||||
}
|
}
|
||||||
@@ -144,12 +141,12 @@ func (w *Window) Draw(root *ui.Ops) {
|
|||||||
if w.gpu == nil {
|
if w.gpu == nil {
|
||||||
ctx, err := newContext(driver)
|
ctx, err := newContext(driver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.err = err
|
w.destroy(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.gpu, err = gpu.NewGPU(ctx)
|
w.gpu, err = gpu.NewGPU(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.err = err
|
w.destroy(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,6 +187,9 @@ func (w *Window) updateAnimation() {
|
|||||||
w.delayedDraw.Stop()
|
w.delayedDraw.Stop()
|
||||||
w.delayedDraw = nil
|
w.delayedDraw = nil
|
||||||
}
|
}
|
||||||
|
if !w.isAlive() {
|
||||||
|
return
|
||||||
|
}
|
||||||
if w.stage >= StageRunning && w.hasNextFrame {
|
if w.stage >= StageRunning && w.hasNextFrame {
|
||||||
if dt := time.Until(w.nextFrame); dt <= 0 {
|
if dt := time.Until(w.nextFrame); dt <= 0 {
|
||||||
animate = true
|
animate = true
|
||||||
@@ -223,7 +223,7 @@ func (w *Window) Stage() Stage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) isAlive() bool {
|
func (w *Window) isAlive() bool {
|
||||||
return w.stage > StageDead && w.err == nil
|
return w.driver != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) contextDriver() interface{} {
|
func (w *Window) contextDriver() interface{} {
|
||||||
@@ -236,10 +236,18 @@ func (w *Window) setDriver(d *window) {
|
|||||||
w.driver = d
|
w.driver = d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Window) destroy(err error) {
|
||||||
|
w.setDriver(nil)
|
||||||
|
go func() {
|
||||||
|
w.event(DestroyEvent{err})
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
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()
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
|
died := false
|
||||||
needAck := false
|
needAck := false
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case input.Event:
|
case input.Event:
|
||||||
@@ -248,12 +256,13 @@ func (w *Window) event(e Event) {
|
|||||||
}
|
}
|
||||||
case *CommandEvent:
|
case *CommandEvent:
|
||||||
needAck = true
|
needAck = true
|
||||||
|
case DestroyEvent:
|
||||||
|
w.driver = nil
|
||||||
|
died = true
|
||||||
case StageEvent:
|
case StageEvent:
|
||||||
w.stage = e.Stage
|
w.stage = e.Stage
|
||||||
if w.stage > StageDead {
|
needAck = true
|
||||||
needAck = true
|
w.syncGPU = true
|
||||||
w.syncGPU = true
|
|
||||||
}
|
|
||||||
case DrawEvent:
|
case DrawEvent:
|
||||||
if e.Size == (image.Point{}) {
|
if e.Size == (image.Point{}) {
|
||||||
panic(errors.New("internal error: zero-sized Draw"))
|
panic(errors.New("internal error: zero-sized Draw"))
|
||||||
@@ -290,7 +299,7 @@ func (w *Window) event(e Event) {
|
|||||||
w.gpu.Refresh()
|
w.gpu.Refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if stage == StageDead {
|
if died {
|
||||||
close(w.events)
|
close(w.events)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user