diff options
| author | Pietro Gagliardi <[email protected]> | 2018-08-12 09:17:01 -0400 |
|---|---|---|
| committer | Pietro Gagliardi <[email protected]> | 2018-08-12 09:17:01 -0400 |
| commit | c91b665da35ce987c2143ebbc141c7d04b6c27c5 (patch) | |
| tree | c161d8a73e81740e9329e18a553ceda49247c884 /main.go | |
| parent | f0fb0f4f7e36b70c61a6cbcc6a2875946a6beec2 (diff) | |
Migrated main.go. Migrated link_darwin_amd64.go and removed -DMACOSX_DEPLOYMENT_TARGET (see libui commit aa28904408fb75ae8042c616982c13cbe2a5a784). This should be two separate commits but I screwed up.
Diffstat (limited to 'main.go')
| -rw-r--r-- | main.go | 130 |
1 files changed, 130 insertions, 0 deletions
@@ -0,0 +1,130 @@ +// 11 december 2015 + +package ui + +import ( + "runtime" + "errors" + "sync" + "unsafe" +) + +// #include "ui.h" +// extern void doQueueMain(void *); +// extern int doOnShouldQuit(void *); +// extern int doOnTimer(void *); +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 { + // TODO HEAP SAFETY + opts := C.uiInitOptions{} + estr := C.uiInit(&opts) + if estr != nil { + err := errors.New(C.GoString(estr)) + C.uiFreeInitError(estr) + return err + } + C.uiOnShouldQuit(C.doOnShouldQuit, nil) + 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.uiQueueMain(C.doQueueMain, unsafe.Pointer(n)) +} + +//export doQueueMain +func doQueueMain(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()) +} + +// TODO Timer? |
