// guiLoop runs the main GUI thread event loop in C++ land. func guiLoop() { runtime.LockOSThread() guiLoopRef = tref.Ref() C.newGuiApplication() C.idleTimerInit(&hookWaiting) guiLoopReady.Unlock() C.applicationExec() }
// 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 := tref.Ref() == atomic.LoadUintptr(&guiPaintRef) prev, ok := engine.values[gvalue] if ok && (prev.owner == owner || 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 { prev.next = fold fold.prev = prev } else { 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 }
//export hookGoValuePaint func hookGoValuePaint(enginep, foldp unsafe.Pointer, reflectIndex C.intptr_t) { fold := ensureEngine(enginep, foldp) v := reflect.ValueOf(fold.gvalue) // 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, tref.Ref()) painter := &Painter{fold.engine, &Common{fold.cvalue, fold.engine}} method := v.Method(int(reflectIndex)) method.Call([]reflect.Value{reflect.ValueOf(painter)}) atomic.StoreUintptr(&guiPaintRef, 0) }
// gui runs f in the main GUI thread and waits for f to return. func gui(f func()) { ref := tref.Ref() if ref == guiLoopRef || 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((*int32)(unsafe.Pointer(&hookWaiting)), 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 }