func initGoType(fold *valueFold) { if cdata.Ref() == atomic.LoadUintptr(&guiPaintRef) { go RunMain(func() { _initGoType(fold, true) }) } else { _initGoType(fold, false) } }
// wrapGoValue creates a new GoValue object in C++ land wrapping // the Go value contained in the given interface. // // This must be run from the main GUI thread. func wrapGoValue(engine *Engine, gvalue interface{}, owner valueOwner) (cvalue unsafe.Pointer) { gvaluev := reflect.ValueOf(gvalue) gvaluek := gvaluev.Kind() if gvaluek == reflect.Struct && !hashable(gvalue) { name := gvaluev.Type().Name() if name != "" { name = " (" + name + ")" } panic("cannot hand an unhashable struct value" + name + " to QML logic; use its address instead") } if gvaluek == reflect.Ptr && gvaluev.Elem().Kind() == reflect.Ptr { panic("cannot hand pointer of pointer to QML logic; use a simple pointer instead") } painting := cdata.Ref() == atomic.LoadUintptr(&guiPaintRef) // Cannot reuse a jsOwner because the QML runtime may choose to destroy // the value _after_ we hand it a new reference to the same value. // See issue #68 for details. prev, ok := engine.values[gvalue] if ok && (prev.owner == cppOwner || painting) { return prev.cvalue } if painting { panic("cannot allocate new objects while painting") } parent := nilPtr if owner == cppOwner { parent = engine.addr } fold := &valueFold{ engine: engine, gvalue: gvalue, owner: owner, } fold.cvalue = C.newGoValue(unsafe.Pointer(fold), typeInfo(gvalue), parent) if prev != nil { // Put new fold first so the single cppOwner, if any, is always the first entry. fold.next = prev prev.prev = fold } engine.values[gvalue] = fold //fmt.Printf("[DEBUG] value alive (wrapped): cvalue=%x gvalue=%x/%#v\n", fold.cvalue, addrOf(fold.gvalue), fold.gvalue) stats.valuesAlive(+1) C.engineSetContextForObject(engine.addr, fold.cvalue) switch owner { case cppOwner: C.engineSetOwnershipCPP(engine.addr, fold.cvalue) case jsOwner: C.engineSetOwnershipJS(engine.addr, fold.cvalue) } return fold.cvalue }
// Run runs the main QML event loop, runs f, and then terminates the // event loop once f returns. // // Most functions from the qml package block until Run is called. // // The Run function must necessarily be called from the same goroutine as // the main function or the application may fail when running on Mac OS. func Run(f func() error) error { if cdata.Ref() != guiMainRef { panic("Run must be called on the initial goroutine so apps are portable to Mac OS") } if !atomic.CompareAndSwapInt32(&initialized, 0, 1) { panic("qml.Run called more than once") } C.newGuiApplication() C.idleTimerInit((*C.int32_t)(&guiIdleRun)) done := make(chan error, 1) go func() { RunMain(func() {}) // Block until the event loop is running. done <- f() C.applicationExit() }() C.applicationExec() return <-done }
// RunMain runs f in the main QML thread and waits for f to return. // // This is meant to be used by extensions that integrate directly with the // underlying QML logic. func RunMain(f func()) { ref := cdata.Ref() if ref == guiMainRef || ref == atomic.LoadUintptr(&guiPaintRef) { // Already within the GUI or render threads. Attempting to wait would deadlock. f() return } // Tell Qt we're waiting for the idle hook to be called. if atomic.AddInt32(&guiIdleRun, 1) == 1 { C.idleTimerStart() } // Send f to be executed by the idle hook in the main GUI thread. guiFunc <- f // Wait until f is done executing. <-guiDone }
//export cgoHookGoValuePaint func cgoHookGoValuePaint(enginep, foldp unsafe.Pointer, reflectIndex C.intptr_t) { // Besides a convenience this is a workaround for http://golang.org/issue/8588 defer printPaintPanic() defer atomic.StoreUintptr(&guiPaintRef, 0) // The main GUI thread is mutex-locked while paint methods are called, // so no two paintings should be happening at the same time. atomic.StoreUintptr(&guiPaintRef, cdata.Ref()) fold := ensureEngine(enginep, foldp) if fold.init.IsValid() { return } painter := &Painter{engine: fold.engine, obj: &Common{fold.cvalue, fold.engine}} v := reflect.ValueOf(fold.gvalue) method := v.Method(int(reflectIndex)) method.Call([]reflect.Value{reflect.ValueOf(painter)}) }
func init() { runtime.LockOSThread() guiMainRef = cdata.Ref() }