aboutsummaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/obscuren/qml/bridge.go
diff options
context:
space:
mode:
Diffstat (limited to 'Godeps/_workspace/src/github.com/obscuren/qml/bridge.go')
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/bridge.go681
1 files changed, 0 insertions, 681 deletions
diff --git a/Godeps/_workspace/src/github.com/obscuren/qml/bridge.go b/Godeps/_workspace/src/github.com/obscuren/qml/bridge.go
deleted file mode 100644
index 77b36a566..000000000
--- a/Godeps/_workspace/src/github.com/obscuren/qml/bridge.go
+++ /dev/null
@@ -1,681 +0,0 @@
-package qml
-
-// #cgo CPPFLAGS: -I./cpp
-// #cgo CXXFLAGS: -std=c++0x -pedantic-errors -Wall -fno-strict-aliasing
-// #cgo LDFLAGS: -lstdc++
-// #cgo pkg-config: Qt5Core Qt5Widgets Qt5Quick
-//
-// #include <stdlib.h>
-//
-// #include "cpp/capi.h"
-//
-import "C"
-
-import (
- "fmt"
- "os"
- "reflect"
- "runtime"
- "sync/atomic"
- "unsafe"
-
- "gopkg.in/qml.v1/cdata"
-)
-
-var (
- guiFunc = make(chan func())
- guiDone = make(chan struct{})
- guiLock = 0
- guiMainRef uintptr
- guiPaintRef uintptr
- guiIdleRun int32
-
- initialized int32
-)
-
-func init() {
- runtime.LockOSThread()
- guiMainRef = cdata.Ref()
-}
-
-// 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
-}
-
-// Lock freezes all QML activity by blocking the main event loop.
-// Locking is necessary before updating shared data structures
-// without race conditions.
-//
-// It's safe to use qml functionality while holding a lock, as
-// long as the requests made do not depend on follow up QML
-// events to be processed before returning. If that happens, the
-// problem will be observed as the application freezing.
-//
-// The Lock function is reentrant. That means it may be called
-// multiple times, and QML activities will only be resumed after
-// Unlock is called a matching number of times.
-func Lock() {
- // TODO Better testing for this.
- RunMain(func() {
- guiLock++
- })
-}
-
-// Unlock releases the QML event loop. See Lock for details.
-func Unlock() {
- RunMain(func() {
- if guiLock == 0 {
- panic("qml.Unlock called without lock being held")
- }
- guiLock--
- })
-}
-
-// Flush synchronously flushes all pending QML activities.
-func Flush() {
- // TODO Better testing for this.
- RunMain(func() {
- C.applicationFlushAll()
- })
-}
-
-// Changed notifies all QML bindings that the given field value has changed.
-//
-// For example:
-//
-// qml.Changed(&value, &value.Field)
-//
-func Changed(value, fieldAddr interface{}) {
- valuev := reflect.ValueOf(value)
- fieldv := reflect.ValueOf(fieldAddr)
- for valuev.Kind() == reflect.Ptr {
- valuev = valuev.Elem()
- }
- if fieldv.Kind() != reflect.Ptr {
- panic("qml.Changed received non-address value as fieldAddr")
- }
- fieldv = fieldv.Elem()
- if fieldv.Type().Size() == 0 {
- panic("cannot report changes on zero-sized fields")
- }
- offset := fieldv.UnsafeAddr() - valuev.UnsafeAddr()
- if !(0 <= offset && offset < valuev.Type().Size()) {
- panic("provided field is not a member of the given value")
- }
-
- RunMain(func() {
- tinfo := typeInfo(value)
- for _, engine := range engines {
- fold := engine.values[value]
- for fold != nil {
- C.goValueActivate(fold.cvalue, tinfo, C.int(offset))
- fold = fold.next
- }
- // TODO typeNew might also be a linked list keyed by the gvalue.
- // This would prevent the iteration and the deferrals.
- for fold, _ = range typeNew {
- if fold.gvalue == value {
- // Activate these later so they don't get recursively moved
- // out of typeNew while the iteration is still happening.
- defer C.goValueActivate(fold.cvalue, tinfo, C.int(offset))
- }
- }
- }
- })
-}
-
-// hookIdleTimer is run once per iteration of the Qt event loop,
-// within the main GUI thread, but only if at least one goroutine
-// has atomically incremented guiIdleRun.
-//
-//export hookIdleTimer
-func hookIdleTimer() {
- var f func()
- for {
- select {
- case f = <-guiFunc:
- default:
- if guiLock > 0 {
- f = <-guiFunc
- } else {
- return
- }
- }
- f()
- guiDone <- struct{}{}
- atomic.AddInt32(&guiIdleRun, -1)
- }
-}
-
-type valueFold struct {
- engine *Engine
- gvalue interface{}
- cvalue unsafe.Pointer
- init reflect.Value
- prev *valueFold
- next *valueFold
- owner valueOwner
-}
-
-type valueOwner uint8
-
-const (
- cppOwner = 1 << iota
- jsOwner
-)
-
-// 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
-}
-
-func addrOf(gvalue interface{}) uintptr {
- return reflect.ValueOf(gvalue).Pointer()
-}
-
-// typeNew holds fold values that are created by registered types.
-// These values are special in two senses: first, they don't have a
-// reference to an engine before they are used in a context that can
-// set the reference; second, these values always hold a new cvalue,
-// because they are created as a side-effect of the registered type
-// being instantiated (it's too late to reuse an existent cvalue).
-//
-// For these reasons, typeNew holds the fold for these values until
-// their engine is known, and once it's known they may have to be
-// added to the linked list, since mulitple references for the same
-// gvalue may occur.
-var typeNew = make(map[*valueFold]bool)
-
-//export hookGoValueTypeNew
-func hookGoValueTypeNew(cvalue unsafe.Pointer, specp unsafe.Pointer) (foldp unsafe.Pointer) {
- // Initialization is postponed until the engine is available, so that
- // we can hand Init the qml.Object that represents the object.
- init := reflect.ValueOf((*TypeSpec)(specp).Init)
- fold := &valueFold{
- init: init,
- gvalue: reflect.New(init.Type().In(0).Elem()).Interface(),
- cvalue: cvalue,
- owner: jsOwner,
- }
- typeNew[fold] = true
- //fmt.Printf("[DEBUG] value alive (type-created): cvalue=%x gvalue=%x/%#v\n", fold.cvalue, addrOf(fold.gvalue), fold.gvalue)
- stats.valuesAlive(+1)
- return unsafe.Pointer(fold)
-}
-
-//export hookGoValueDestroyed
-func hookGoValueDestroyed(enginep unsafe.Pointer, foldp unsafe.Pointer) {
- fold := (*valueFold)(foldp)
- engine := fold.engine
- if engine == nil {
- before := len(typeNew)
- delete(typeNew, fold)
- if len(typeNew) == before {
- panic("destroying value without an associated engine; who created the value?")
- }
- } else if engines[engine.addr] == nil {
- // Must never do that. The engine holds memory references that C++ depends on.
- panic(fmt.Sprintf("engine %p was released from global list while its values were still alive", engine.addr))
- } else {
- switch {
- case fold.prev != nil:
- fold.prev.next = fold.next
- if fold.next != nil {
- fold.next.prev = fold.prev
- }
- case fold.next != nil:
- fold.next.prev = fold.prev
- if fold.prev != nil {
- fold.prev.next = fold.next
- } else {
- fold.engine.values[fold.gvalue] = fold.next
- }
- default:
- before := len(engine.values)
- delete(engine.values, fold.gvalue)
- if len(engine.values) == before {
- panic("destroying value that knows about the engine, but the engine doesn't know about the value; who cleared the engine?")
- }
- if engine.destroyed && len(engine.values) == 0 {
- delete(engines, engine.addr)
- }
- }
- }
- //fmt.Printf("[DEBUG] value destroyed: cvalue=%x gvalue=%x/%#v\n", fold.cvalue, addrOf(fold.gvalue), fold.gvalue)
- stats.valuesAlive(-1)
-}
-
-func deref(value reflect.Value) reflect.Value {
- for {
- switch value.Kind() {
- case reflect.Ptr, reflect.Interface:
- value = value.Elem()
- continue
- }
- return value
- }
- panic("cannot happen")
-}
-
-//export hookGoValueReadField
-func hookGoValueReadField(enginep, foldp unsafe.Pointer, reflectIndex, getIndex, setIndex C.int, resultdv *C.DataValue) {
- fold := ensureEngine(enginep, foldp)
-
- var field reflect.Value
- if getIndex >= 0 {
- field = reflect.ValueOf(fold.gvalue).Method(int(getIndex)).Call(nil)[0]
- } else {
- field = deref(reflect.ValueOf(fold.gvalue)).Field(int(reflectIndex))
- }
- field = deref(field)
-
- // Cannot compare Type directly as field may be invalid (nil).
- if field.Kind() == reflect.Slice && field.Type() == typeObjSlice {
- // TODO Handle getters that return []qml.Object.
- // TODO Handle other GoValue slices (!= []qml.Object).
- resultdv.dataType = C.DTListProperty
- *(*unsafe.Pointer)(unsafe.Pointer(&resultdv.data)) = C.newListProperty(foldp, C.intptr_t(reflectIndex), C.intptr_t(setIndex))
- return
- }
-
- fieldk := field.Kind()
- if fieldk == reflect.Slice || fieldk == reflect.Struct && field.Type() != typeRGBA {
- if field.CanAddr() {
- field = field.Addr()
- } else if !hashable(field.Interface()) {
- t := reflect.ValueOf(fold.gvalue).Type()
- for t.Kind() == reflect.Ptr {
- t = t.Elem()
- }
- panic(fmt.Sprintf("cannot access unaddressable and unhashable struct value on interface field %s.%s; value: %#v", t.Name(), t.Field(int(reflectIndex)).Name, field.Interface()))
- }
- }
- var gvalue interface{}
- if field.IsValid() {
- gvalue = field.Interface()
- }
-
- // TODO Strings are being passed in an unsafe manner here. There is a
- // small chance that the field is changed and the garbage collector is run
- // before C++ has a chance to look at the data. We can solve this problem
- // by queuing up values in a stack, and cleaning the stack when the
- // idle timer fires next.
- packDataValue(gvalue, resultdv, fold.engine, jsOwner)
-}
-
-//export hookGoValueWriteField
-func hookGoValueWriteField(enginep, foldp unsafe.Pointer, reflectIndex, setIndex C.int, assigndv *C.DataValue) {
- fold := ensureEngine(enginep, foldp)
- v := reflect.ValueOf(fold.gvalue)
- ve := v
- for ve.Type().Kind() == reflect.Ptr {
- ve = ve.Elem()
- }
- var field, setMethod reflect.Value
- if reflectIndex >= 0 {
- // It's a real field rather than a getter.
- field = ve.Field(int(reflectIndex))
- }
- if setIndex >= 0 {
- // It has a setter.
- setMethod = v.Method(int(setIndex))
- }
-
- assign := unpackDataValue(assigndv, fold.engine)
-
- // TODO Return false to the call site if it fails. That's how Qt seems to handle it internally.
- err := convertAndSet(field, reflect.ValueOf(assign), setMethod)
- if err != nil {
- panic(err.Error())
- }
-}
-
-func convertAndSet(to, from reflect.Value, setMethod reflect.Value) (err error) {
- var toType reflect.Type
- if setMethod.IsValid() {
- toType = setMethod.Type().In(0)
- } else {
- toType = to.Type()
- }
- fromType := from.Type()
- defer func() {
- // TODO This is catching more than it should. There are calls
- // to custom code below that should be isolated.
- if v := recover(); v != nil {
- err = fmt.Errorf("cannot use %s as a %s", fromType, toType)
- }
- }()
- if fromType == typeList && toType.Kind() == reflect.Slice {
- list := from.Interface().(*List)
- from = reflect.MakeSlice(toType, len(list.data), len(list.data))
- elemType := toType.Elem()
- for i, elem := range list.data {
- from.Index(i).Set(reflect.ValueOf(elem).Convert(elemType))
- }
- } else if fromType == typeMap && toType.Kind() == reflect.Map {
- qmap := from.Interface().(*Map)
- from = reflect.MakeMap(toType)
- elemType := toType.Elem()
- for i := 0; i < len(qmap.data); i += 2 {
- key := reflect.ValueOf(qmap.data[i])
- val := reflect.ValueOf(qmap.data[i+1])
- if val.Type() != elemType {
- val = val.Convert(elemType)
- }
- from.SetMapIndex(key, val)
- }
- } else if toType != fromType {
- from = from.Convert(toType)
- }
- if setMethod.IsValid() {
- setMethod.Call([]reflect.Value{from})
- } else {
- to.Set(from)
- }
- return nil
-}
-
-var (
- dataValueSize = uintptr(unsafe.Sizeof(C.DataValue{}))
- dataValueArray [C.MaxParams]C.DataValue
-)
-
-//export hookGoValueCallMethod
-func hookGoValueCallMethod(enginep, foldp unsafe.Pointer, reflectIndex C.int, args *C.DataValue) {
- fold := ensureEngine(enginep, foldp)
- v := reflect.ValueOf(fold.gvalue)
-
- // TODO Must assert that v is necessarily a pointer here, but we shouldn't have to manipulate
- // gvalue here for that. This should happen in a sensible place in the wrapping functions
- // that can still error out to the user in due time.
-
- method := v.Method(int(reflectIndex))
- methodt := method.Type()
- methodName := v.Type().Method(int(reflectIndex)).Name
-
- // TODO Ensure methods with more parameters than this are not registered.
- var params [C.MaxParams]reflect.Value
- var err error
-
- numIn := methodt.NumIn()
- for i := 0; i < numIn; i++ {
- paramdv := (*C.DataValue)(unsafe.Pointer(uintptr(unsafe.Pointer(args)) + (uintptr(i)+1)*dataValueSize))
- param := reflect.ValueOf(unpackDataValue(paramdv, fold.engine))
- if argt := methodt.In(i); param.Type() != argt {
- param, err = convertParam(methodName, i, param, argt)
- if err != nil {
- panic(err.Error())
- }
- }
- params[i] = param
- }
-
- result := method.Call(params[:numIn])
-
- if len(result) == 1 {
- packDataValue(result[0].Interface(), args, fold.engine, jsOwner)
- } else if len(result) > 1 {
- if len(result) > len(dataValueArray) {
- panic("function has too many results")
- }
- for i, v := range result {
- packDataValue(v.Interface(), &dataValueArray[i], fold.engine, jsOwner)
- }
- args.dataType = C.DTVariantList
- *(*unsafe.Pointer)(unsafe.Pointer(&args.data)) = C.newVariantList(&dataValueArray[0], C.int(len(result)))
- }
-}
-
-func convertParam(methodName string, index int, param reflect.Value, argt reflect.Type) (reflect.Value, error) {
- out := reflect.New(argt).Elem()
- err := convertAndSet(out, param, reflect.Value{})
- if err != nil {
- err = fmt.Errorf("cannot convert parameter %d of method %s from %s to %s; provided value: %#v",
- index, methodName, param.Type(), argt, param.Interface())
- return reflect.Value{}, err
- }
- return out, nil
-}
-
-func printPaintPanic() {
- if v := recover(); v != nil {
- buf := make([]byte, 8192)
- runtime.Stack(buf, false)
- fmt.Fprintf(os.Stderr, "panic while painting: %s\n\n%s", v, buf)
- }
-}
-
-//export hookGoValuePaint
-func hookGoValuePaint(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 ensureEngine(enginep, foldp unsafe.Pointer) *valueFold {
- fold := (*valueFold)(foldp)
- if fold.engine != nil {
- if fold.init.IsValid() {
- initGoType(fold)
- }
- return fold
- }
-
- if enginep == nilPtr {
- panic("accessing value without an engine pointer; who created the value?")
- }
- engine := engines[enginep]
- if engine == nil {
- panic("unknown engine pointer; who created the engine?")
- }
- fold.engine = engine
- prev := engine.values[fold.gvalue]
- if prev != nil {
- for prev.next != nil {
- prev = prev.next
- }
- prev.next = fold
- fold.prev = prev
- } else {
- engine.values[fold.gvalue] = fold
- }
- before := len(typeNew)
- delete(typeNew, fold)
- if len(typeNew) == before {
- panic("value had no engine, but was not created by a registered type; who created the value?")
- }
- initGoType(fold)
- return fold
-}
-
-func initGoType(fold *valueFold) {
- if cdata.Ref() == atomic.LoadUintptr(&guiPaintRef) {
- go RunMain(func() { _initGoType(fold, true) })
- } else {
- _initGoType(fold, false)
- }
-}
-
-func _initGoType(fold *valueFold, schedulePaint bool) {
- if !fold.init.IsValid() {
- return
- }
- // TODO Would be good to preserve identity on the Go side. See unpackDataValue as well.
- obj := &Common{engine: fold.engine, addr: fold.cvalue}
- fold.init.Call([]reflect.Value{reflect.ValueOf(fold.gvalue), reflect.ValueOf(obj)})
- fold.init = reflect.Value{}
- if schedulePaint {
- obj.Call("update")
- }
-}
-
-//export hookPanic
-func hookPanic(message *C.char) {
- defer C.free(unsafe.Pointer(message))
- panic(C.GoString(message))
-}
-
-func listSlice(fold *valueFold, reflectIndex C.intptr_t) *[]Object {
- field := deref(reflect.ValueOf(fold.gvalue)).Field(int(reflectIndex))
- return field.Addr().Interface().(*[]Object)
-}
-
-//export hookListPropertyAt
-func hookListPropertyAt(foldp unsafe.Pointer, reflectIndex, setIndex C.intptr_t, index C.int) (objp unsafe.Pointer) {
- fold := (*valueFold)(foldp)
- slice := listSlice(fold, reflectIndex)
- return (*slice)[int(index)].Common().addr
-}
-
-//export hookListPropertyCount
-func hookListPropertyCount(foldp unsafe.Pointer, reflectIndex, setIndex C.intptr_t) C.int {
- fold := (*valueFold)(foldp)
- slice := listSlice(fold, reflectIndex)
- return C.int(len(*slice))
-}
-
-//export hookListPropertyAppend
-func hookListPropertyAppend(foldp unsafe.Pointer, reflectIndex, setIndex C.intptr_t, objp unsafe.Pointer) {
- fold := (*valueFold)(foldp)
- slice := listSlice(fold, reflectIndex)
- var objdv C.DataValue
- objdv.dataType = C.DTObject
- *(*unsafe.Pointer)(unsafe.Pointer(&objdv.data)) = objp
- newslice := append(*slice, unpackDataValue(&objdv, fold.engine).(Object))
- if setIndex >= 0 {
- reflect.ValueOf(fold.gvalue).Method(int(setIndex)).Call([]reflect.Value{reflect.ValueOf(newslice)})
- } else {
- *slice = newslice
- }
-}
-
-//export hookListPropertyClear
-func hookListPropertyClear(foldp unsafe.Pointer, reflectIndex, setIndex C.intptr_t) {
- fold := (*valueFold)(foldp)
- slice := listSlice(fold, reflectIndex)
- newslice := (*slice)[0:0]
- if setIndex >= 0 {
- reflect.ValueOf(fold.gvalue).Method(int(setIndex)).Call([]reflect.Value{reflect.ValueOf(newslice)})
- } else {
- for i := range *slice {
- (*slice)[i] = nil
- }
- *slice = newslice
- }
-}