forked from andlabs/ui
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
132 lines (114 loc) · 3.4 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// 11 december 2015
package ui
import (
"runtime"
"errors"
"sync"
"unsafe"
)
// #include "ui.h"
// extern void doQueued(void *);
// extern int doOnShouldQuit(void *);
// /* I forgot how dumb cgo is... ./main.go:73: cannot use _Cgo_ptr(_Cfpvar_fp_doQueued) (type unsafe.Pointer) as type *[0]byte in argument to _Cfunc_uiQueueMain */
// /* I'm pretty sure this worked before... */
// static inline void realQueueMain(void *x)
// {
// uiQueueMain(doQueued, x);
// }
// static inline void realOnShouldQuit(void)
// {
// uiOnShouldQuit(doOnShouldQuit, NULL);
// }
import "C"
// Main initializes package ui, runs f to set up the program,
// and executes the GUI main loop. f should set up the program's
// initial state: open the main window, create controls, and set up
// events. It should then return, at which point Main will
// process events until Quit is called, at which point Main will return
// nil. If package ui fails to initialize, Main returns an appropriate
// error.
func Main(f func()) error {
errchan := make(chan error)
go start(errchan, f)
return <-errchan
}
func start(errchan chan error, f func()) {
runtime.LockOSThread()
ensureMainThread()
// TODO HEAP SAFETY
opts := C.uiInitOptions{}
estr := C.uiInit(&opts)
if estr != nil {
errchan <- errors.New(C.GoString(estr))
C.uiFreeInitError(estr)
return
}
// set up OnShouldQuit()
C.realOnShouldQuit()
QueueMain(f)
C.uiMain()
errchan <- nil
}
// Quit queues a return from Main. It does not exit the program.
// It also does not immediately cause Main to return; Main will
// return when it next can. Quit must be called from the GUI thread.
func Quit() {
C.uiQuit()
}
// These prevent the passing of Go functions into C land.
// TODO make an actual sparse list instead of this monotonic map thingy
var (
qmmap = make(map[uintptr]func())
qmcurrent = uintptr(0)
qmlock sync.Mutex
)
// QueueMain queues f to be executed on the GUI thread when
// next possible. It returns immediately; that is, it does not wait
// for the function to actually be executed. QueueMain is the only
// function that can be called from other goroutines, and its
// primary purpose is to allow communication between other
// goroutines and the GUI thread. Calling QueueMain after Quit
// has been called results in undefined behavior.
func QueueMain(f func()) {
qmlock.Lock()
defer qmlock.Unlock()
n := uintptr(0)
for {
n = qmcurrent
qmcurrent++
if qmmap[n] == nil {
break
}
}
qmmap[n] = f
C.realQueueMain(unsafe.Pointer(n))
}
//export doQueued
func doQueued(nn unsafe.Pointer) {
qmlock.Lock()
n := uintptr(nn)
f := qmmap[n]
delete(qmmap, n)
// allow uiQueueMain() to be called by a queued function
// TODO explicitly allow this in libui too
qmlock.Unlock()
f()
}
// no need to lock this; this API is only safe on the main thread
var shouldQuitFunc func() bool
// OnShouldQuit schedules f to be exeucted when the OS wants
// the program to quit or when a Quit menu item has been clicked.
// Only one function may be registered at a time. If the function
// returns true, Quit will be called. If the function returns false, or
// if OnShouldQuit is never called. Quit will not be called and the
// OS will be told that the program needs to continue running.
func OnShouldQuit(f func() bool) {
shouldQuitFunc = f
}
//export doOnShouldQuit
func doOnShouldQuit(unused unsafe.Pointer) C.int {
if shouldQuitFunc == nil {
return 0
}
return frombool(shouldQuitFunc())
}