forked from andlabs/ui
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
143 lines (125 loc) · 3.78 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
133
134
135
136
137
138
139
140
141
142
143
// 11 december 2015
package ui
import (
"runtime"
"errors"
"sync"
"unsafe"
)
// #include "pkgui.h"
import "C"
// make sure main() runs on the first thread created by the OS
// if main() calls Main(), things will just work on macOS, where the first thread created by the OS is the only thread allowed to be the main GUI thread
// we might as well lock the OS thread for the other platforms here too (though on those it doesn't matter *which* thread we lock to)
// TODO describe the source of this trick
func init() {
runtime.LockOSThread()
}
// 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 {
opts := C.pkguiAllocInitOptions()
estr := C.uiInit(opts)
C.pkguiFreeInitOptions(opts)
if estr != nil {
err := errors.New(C.GoString(estr))
C.uiFreeInitError(estr)
return err
}
C.pkguiOnShouldQuit()
QueueMain(f)
C.uiMain()
return nil
}
func MainAdjusted(f func(), skipTheme int) error {
opts := C.pkguiAllocInitOptions()
opts.skipTheme = C.int(skipTheme)
estr := C.uiInit(opts)
C.pkguiFreeInitOptions(opts)
if estr != nil {
err := errors.New(C.GoString(estr))
C.uiFreeInitError(estr)
return err
}
C.pkguiOnShouldQuit()
QueueMain(f)
C.uiMain()
return 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.
//
// If you start a goroutine in f, it also cannot call package ui
// functions. So for instance, the following will result in
// undefined behavior:
//
// ui.QueueMain(func() {
// go ui.MsgBox(...)
// })
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.pkguiQueueMain(C.uintptr_t(n))
}
//export pkguiDoQueueMain
func pkguiDoQueueMain(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 pkguiDoOnShouldQuit
func pkguiDoOnShouldQuit(unused unsafe.Pointer) C.int {
if shouldQuitFunc == nil {
return 0
}
return frombool(shouldQuitFunc())
}
// TODO Timer?