diff -ruN qml-1/bridge.go /home/mersdk/src/gopkg.in/qml.v1/bridge.go --- qml-1/bridge.go 2015-02-09 14:10:31.000000000 +0000 +++ /home/mersdk/src/gopkg.in/qml.v1/bridge.go 2016-04-05 13:16:40.219952082 +0000 @@ -1,9 +1,9 @@ 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 +// #cgo CPPFLAGS: -I./cpp -I/usr/include/sailfishapp +// #cgo CXXFLAGS: -std=c++0x -Wall -fno-strict-aliasing +// #cgo LDFLAGS: -lstdc++ -L/usr/lib/ -lsailfishapp -lmdeclarativecache5 +// #cgo pkg-config: Qt5Core Qt5Quick // // #include // @@ -23,12 +23,12 @@ ) var ( - guiFunc = make(chan func()) - guiDone = make(chan struct{}) - guiLock = 0 - guiMainRef uintptr - guiPaintRef uintptr - guiIdleRun int32 + guiFunc = make(chan func()) + guiDone = make(chan struct{}) + guiLock = 0 + guiMainRef uintptr + guiPaintRef uintptr + guiIdleRun int32 initialized int32 ) @@ -38,6 +38,38 @@ guiMainRef = cdata.Ref() } +// foldRefs holds all fold values that are created. Since Go Pointer +// are not allowed to be held by cgo. We need a lookup table to +// interface the two space. +var foldRefs = make(map[uintptr]*valueFold) + +// Special Run() function to launch Jolla SailfishOS applications. +// 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 SailfishRun(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.sailfishnewGuiApplication() + 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.sailfishapplicationExit() + }() + C.sailfishapplicationExec() + return <-done +} + // Run runs the main QML event loop, runs f, and then terminates the // event loop once f returns. // @@ -117,6 +149,14 @@ }) } +// Flush synchronously flushes all pending QML activities for Sailfish application +func SailfishFlush() { + // TODO Better testing for this. + RunMain(func() { + C.sailfishapplicationFlushAll() + }) +} + // Flush synchronously flushes all pending QML activities. func Flush() { // TODO Better testing for this. @@ -210,6 +250,16 @@ jsOwner ) +func storeFold(fold *valueFold) C.GoRef { + foldRef := uintptr(unsafe.Pointer(fold)) + foldRefs[foldRef] = fold + return C.GoRef(foldRef) +} + +func restoreFold(ref uintptr) *valueFold { + return foldRefs[ref] +} + // wrapGoValue creates a new GoValue object in C++ land wrapping // the Go value contained in the given interface. // @@ -251,7 +301,7 @@ gvalue: gvalue, owner: owner, } - fold.cvalue = C.newGoValue(unsafe.Pointer(fold), typeInfo(gvalue), parent) + fold.cvalue = C.newGoValue(storeFold(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 @@ -289,7 +339,7 @@ var typeNew = make(map[*valueFold]bool) //export hookGoValueTypeNew -func hookGoValueTypeNew(cvalue unsafe.Pointer, specp unsafe.Pointer) (foldp unsafe.Pointer) { +func hookGoValueTypeNew(cvalue unsafe.Pointer, specp unsafe.Pointer) (foldr C.GoRef) { // 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) @@ -299,15 +349,17 @@ 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) + return storeFold(fold) } //export hookGoValueDestroyed -func hookGoValueDestroyed(enginep unsafe.Pointer, foldp unsafe.Pointer) { - fold := (*valueFold)(foldp) +func hookGoValueDestroyed(enginep unsafe.Pointer, foldr uintptr) { + fold := restoreFold(foldr) + engine := fold.engine if engine == nil { before := len(typeNew) @@ -360,8 +412,8 @@ } //export hookGoValueReadField -func hookGoValueReadField(enginep, foldp unsafe.Pointer, reflectIndex, getIndex, setIndex C.int, resultdv *C.DataValue) { - fold := ensureEngine(enginep, foldp) +func hookGoValueReadField(enginep unsafe.Pointer, foldr uintptr, reflectIndex, getIndex, setIndex C.int, resultdv *C.DataValue) { + fold := ensureEngine(enginep, foldr) var field reflect.Value if getIndex >= 0 { @@ -376,7 +428,7 @@ // 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)) + *(*unsafe.Pointer)(unsafe.Pointer(&resultdv.data)) = C.newListProperty(C.GoRef(foldr), C.intptr_t(reflectIndex), C.intptr_t(setIndex)) return } @@ -406,8 +458,8 @@ } //export hookGoValueWriteField -func hookGoValueWriteField(enginep, foldp unsafe.Pointer, reflectIndex, setIndex C.int, assigndv *C.DataValue) { - fold := ensureEngine(enginep, foldp) +func hookGoValueWriteField(enginep unsafe.Pointer, foldr uintptr, reflectIndex, setIndex C.int, assigndv *C.DataValue) { + fold := ensureEngine(enginep, foldr) v := reflect.ValueOf(fold.gvalue) ve := v for ve.Type().Kind() == reflect.Ptr { @@ -483,8 +535,8 @@ ) //export hookGoValueCallMethod -func hookGoValueCallMethod(enginep, foldp unsafe.Pointer, reflectIndex C.int, args *C.DataValue) { - fold := ensureEngine(enginep, foldp) +func hookGoValueCallMethod(enginep unsafe.Pointer, foldr uintptr, reflectIndex C.int, args *C.DataValue) { + fold := ensureEngine(enginep, foldr) v := reflect.ValueOf(fold.gvalue) // TODO Must assert that v is necessarily a pointer here, but we shouldn't have to manipulate @@ -548,7 +600,7 @@ } //export hookGoValuePaint -func hookGoValuePaint(enginep, foldp unsafe.Pointer, reflectIndex C.intptr_t) { +func hookGoValuePaint(enginep unsafe.Pointer, foldr uintptr, reflectIndex C.intptr_t) { // Besides a convenience this is a workaround for http://golang.org/issue/8588 defer printPaintPanic() defer atomic.StoreUintptr(&guiPaintRef, 0) @@ -557,7 +609,7 @@ // so no two paintings should be happening at the same time. atomic.StoreUintptr(&guiPaintRef, cdata.Ref()) - fold := ensureEngine(enginep, foldp) + fold := ensureEngine(enginep, foldr) if fold.init.IsValid() { return } @@ -568,8 +620,8 @@ method.Call([]reflect.Value{reflect.ValueOf(painter)}) } -func ensureEngine(enginep, foldp unsafe.Pointer) *valueFold { - fold := (*valueFold)(foldp) +func ensureEngine(enginep unsafe.Pointer, foldr uintptr) *valueFold { + fold := restoreFold(foldr) if fold.engine != nil { if fold.init.IsValid() { initGoType(fold) @@ -637,22 +689,22 @@ } //export hookListPropertyAt -func hookListPropertyAt(foldp unsafe.Pointer, reflectIndex, setIndex C.intptr_t, index C.int) (objp unsafe.Pointer) { - fold := (*valueFold)(foldp) +func hookListPropertyAt(foldr uintptr, reflectIndex, setIndex C.intptr_t, index C.int) (objp unsafe.Pointer) { + fold := restoreFold(foldr) 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) +func hookListPropertyCount(foldr uintptr, reflectIndex, setIndex C.intptr_t) C.int { + fold := restoreFold(foldr) 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) +func hookListPropertyAppend(foldr uintptr, reflectIndex, setIndex C.intptr_t, objp unsafe.Pointer) { + fold := restoreFold(foldr) slice := listSlice(fold, reflectIndex) var objdv C.DataValue objdv.dataType = C.DTObject @@ -666,8 +718,8 @@ } //export hookListPropertyClear -func hookListPropertyClear(foldp unsafe.Pointer, reflectIndex, setIndex C.intptr_t) { - fold := (*valueFold)(foldp) +func hookListPropertyClear(foldr uintptr, reflectIndex, setIndex C.intptr_t) { + fold := restoreFold(foldr) slice := listSlice(fold, reflectIndex) newslice := (*slice)[0:0] if setIndex >= 0 { diff -ruN qml-1/cpp/capi.cpp /home/mersdk/src/gopkg.in/qml.v1/cpp/capi.cpp --- qml-1/cpp/capi.cpp 2015-02-09 14:10:31.000000000 +0000 +++ /home/mersdk/src/gopkg.in/qml.v1/cpp/capi.cpp 2016-04-05 12:53:20.650583203 +0000 @@ -1,10 +1,14 @@ -#include +#include #include #include #include #include #include +#include + +#include + #include #include "govalue.h" @@ -12,6 +16,21 @@ #include "connector.h" #include "capi.h" +QGuiApplication *app; +QQuickView *sfview; + +void newTranslator(QString_ *i18nroot) +{ + QString *root = reinterpret_cast(i18nroot); + QTranslator *translator = new QTranslator; + if (translator->load(QLatin1String("qml_") + QLocale::system().name(), *root)) { + QCoreApplication::installTranslator(translator); + qDebug() << "Found i18n for " + QLocale::system().name(); + } else { + qDebug() << "No translations found for language " + QLatin1String("qml_") + QLocale::system().name(); + } +} + static char *local_strdup(const char *str) { char *strcopy = 0; @@ -43,27 +62,55 @@ hookPanic(local_strdup(ba.constData())); } +void sailfishnewGuiApplication() +{ + static char empty[1] = {0}; + static char *argv[] = {empty, 0}; + static int argc = 1; + + app = SailfishApp::application(argc, argv); + + // The event loop should never die. + app->setQuitOnLastWindowClosed(false); +} + void newGuiApplication() { static char empty[1] = {0}; static char *argv[] = {empty, 0}; static int argc = 1; - new QApplication(argc, argv); + + new QGuiApplication(argc, argv); // The event loop should never die. qApp->setQuitOnLastWindowClosed(false); } +void sailfishapplicationExec() +{ + app->exec(); +} + void applicationExec() { qApp->exec(); } +void sailfishapplicationExit() +{ + app->exit(0); +} + void applicationExit() { qApp->exit(0); } +void sailfishapplicationFlushAll() +{ + app->processEvents(); +} + void applicationFlushAll() { qApp->processEvents(); @@ -79,6 +126,12 @@ return QCoreApplication::instance()->thread(); } +QQmlEngine_ *newSailfishEngine() +{ + sfview = SailfishApp::createView(); + return sfview->engine(); +} + QQmlEngine_ *newEngine(QObject_ *parent) { return new QQmlEngine(reinterpret_cast(parent)); @@ -163,6 +216,13 @@ qengine->addImageProvider(*qproviderId, new GoImageProvider(imageFunc)); } +void sailfishSetSource(const char *url, int urlLen) +{ + QByteArray qurl(url, urlLen); + QString qsurl = QString::fromUtf8(qurl); + sfview->setSource(SailfishApp::pathTo(qsurl)); +} + void componentLoadURL(QQmlComponent_ *component, const char *url, int urlLen) { QByteArray qurl(url, urlLen); @@ -202,6 +262,11 @@ return qcomponent->create(qcontext); } +QQuickWindow_ *sailfishCreateWindow() +{ + return sfview; +} + QQuickWindow_ *componentCreateWindow(QQmlComponent_ *component, QQmlContext_ *context) { QQmlComponent *qcomponent = reinterpret_cast(component); @@ -229,6 +294,11 @@ } }; +void sailfishwindowShow() +{ + sfview->show(); +} + void windowShow(QQuickWindow_ *win) { reinterpret_cast(win)->show(); @@ -244,12 +314,43 @@ return reinterpret_cast(win)->winId(); } +void destroyMe(QQuickCloseEvent *close) +{ + qDebug() << "Ok, called."; +} + + +class CloseEventFilter : public QObject +{ + Q_OBJECT +public: + CloseEventFilter(QObject *parent) : QObject(parent) {} + +protected: + bool eventFilter(QObject *obj, QEvent *event) + { + if (event->type() == QEvent::Close) + { + hookWindowHidden(obj); + } + + return QObject::eventFilter(obj, event); + } +}; + + void windowConnectHidden(QQuickWindow_ *win) { QQuickWindow *qwin = reinterpret_cast(win); + // Test + CloseEventFilter *closeFilter = new CloseEventFilter(qwin); + qwin->installEventFilter(closeFilter); + // Test End QObject::connect(qwin, &QWindow::visibleChanged, [=](bool visible){ if (!visible) { - hookWindowHidden(win); + // Disabled this because on SailfishOS switching to cover screen will exit application! + //hookWindowHidden(win); + //qDebug() << "hookWindowHidden() called"; } }); } @@ -403,7 +504,7 @@ } else { int varType = var.userType(); QVariant saved = var; - if (propType != varType && !var.convert(propType)) { + if (propType != varType && !var.convert(propType) && propType != QMetaType::QUrl) { if (varType == QMetaType::QObjectStar) { return errorf("cannot set property \"%s\" with type %s to value of %s*", name, QMetaType::typeName(propType), saved.value()->metaObject()->className()); @@ -436,7 +537,6 @@ if (paramsLen > 10) { panicf("fix the parameter dispatching"); } - const QMetaObject *metaObject = qobject->metaObject(); // Walk backwards so descendants have priority. for (int i = metaObject->methodCount()-1; i >= 0; i--) { @@ -452,10 +552,10 @@ bool ok; if (metaMethod.returnType() == QMetaType::Void) { - ok = metaMethod.invoke(qobject, Qt::DirectConnection, + ok = QMetaObject::invokeMethod(qobject, metaMethod.name(), Qt::DirectConnection, arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7], arg[8], arg[9]); } else { - ok = metaMethod.invoke(qobject, Qt::DirectConnection, Q_RETURN_ARG(QVariant, result), + ok = QMetaObject::invokeMethod(qobject, metaMethod.name(), Qt::DirectConnection, Q_RETURN_ARG(QVariant, result), arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7], arg[8], arg[9]); } if (!ok) { @@ -475,7 +575,7 @@ { QObject *qobject = reinterpret_cast(object); QString *qname = reinterpret_cast(name); - + QVariant var; QObject *result = qobject->findChild(*qname); if (result) { @@ -543,17 +643,17 @@ return dynamic_cast(qobject) ? 1 : 0; } -error *objectGoAddr(QObject_ *object, GoAddr **addr) +error *objectGoRef(QObject_ *object, GoRef *ref) { QObject *qobject = static_cast(object); GoValue *goValue = dynamic_cast(qobject); if (goValue) { - *addr = goValue->addr; + *ref = goValue->ref; return 0; } GoPaintedValue *goPaintedValue = dynamic_cast(qobject); if (goPaintedValue) { - *addr = goPaintedValue->addr; + *ref= goPaintedValue->ref; return 0; } return errorf("QML object is not backed by a Go value"); @@ -571,13 +671,13 @@ delete reinterpret_cast(s); } -GoValue_ *newGoValue(GoAddr *addr, GoTypeInfo *typeInfo, QObject_ *parent) +GoValue_ *newGoValue(GoRef ref, GoTypeInfo *typeInfo, QObject_ *parent) { QObject *qparent = reinterpret_cast(parent); if (typeInfo->paint) { - return new GoPaintedValue(addr, typeInfo, qparent); + return new GoPaintedValue(ref, typeInfo, qparent); } - return new GoValue(addr, typeInfo, qparent); + return new GoValue(ref, typeInfo, qparent); } void goValueActivate(GoValue_ *value, GoTypeInfo *typeInfo, int addrOffset) @@ -629,6 +729,9 @@ case DTColor: *qvar = QColor::fromRgba(*(QRgb*)(value->data)); break; + case DTDateTime: + *qvar = QDateTime::fromTime_t(*(quint32*)(value->data)); + break; case DTVariantList: *qvar = **(QVariantList**)(value->data); delete *(QVariantList**)(value->data); @@ -709,6 +812,21 @@ value->dataType = DTColor; *(unsigned int*)(value->data) = qvar->value().rgba(); break; + case QMetaType::QDateTime: + value->dataType = DTDateTime; + *(quint32*)(value->data) = qvar->toUInt(); + break; + case QMetaType::User: + { + if (qvar->userType() == 1034) { + auto var = qvar->value().toVariant(); + packDataValue(&var, value); + } else { + qDebug() << "user-type = " << qvar->userType() << " name = " << QVariant::typeToName(qvar->userType()); + } + } + break; + case QMetaType::QVariantList: { QVariantList varlist = qvar->toList(); @@ -749,13 +867,13 @@ GoValue *goValue = dynamic_cast(qobject); if (goValue) { value->dataType = DTGoAddr; - *(void **)(value->data) = goValue->addr; + *(uintptr_t*)(value->data) = goValue->ref; break; } GoPaintedValue *goPaintedValue = dynamic_cast(qobject); if (goPaintedValue) { value->dataType = DTGoAddr; - *(void **)(value->data) = goPaintedValue->addr; + *(uintptr_t*)(value->data) = goPaintedValue->ref; break; } value->dataType = DTObject; @@ -813,28 +931,28 @@ QObject *listPropertyAt(QQmlListProperty *list, int i) { - return reinterpret_cast(hookListPropertyAt(list->data, (intptr_t)list->dummy1, (intptr_t)list->dummy2, i)); + return reinterpret_cast(hookListPropertyAt((uintptr_t)list->data, (intptr_t)list->dummy1, (intptr_t)list->dummy2, i)); } int listPropertyCount(QQmlListProperty *list) { - return hookListPropertyCount(list->data, (intptr_t)list->dummy1, (intptr_t)list->dummy2); + return hookListPropertyCount((uintptr_t)list->data, (intptr_t)list->dummy1, (intptr_t)list->dummy2); } void listPropertyAppend(QQmlListProperty *list, QObject *obj) { - hookListPropertyAppend(list->data, (intptr_t)list->dummy1, (intptr_t)list->dummy2, obj); + hookListPropertyAppend((uintptr_t)list->data, (intptr_t)list->dummy1, (intptr_t)list->dummy2, obj); } void listPropertyClear(QQmlListProperty *list) { - hookListPropertyClear(list->data, (intptr_t)list->dummy1, (intptr_t)list->dummy2); + hookListPropertyClear((uintptr_t)list->data, (intptr_t)list->dummy1, (intptr_t)list->dummy2); } -QQmlListProperty_ *newListProperty(GoAddr *addr, intptr_t reflectIndex, intptr_t setIndex) +QQmlListProperty_ *newListProperty(GoRef ref, intptr_t reflectIndex, intptr_t setIndex) { QQmlListProperty *list = new QQmlListProperty(); - list->data = addr; + list->data = (void*)ref; list->dummy1 = (void*)reflectIndex; list->dummy2 = (void*)setIndex; list->at = listPropertyAt; diff -ruN qml-1/cpp/capi.h /home/mersdk/src/gopkg.in/qml.v1/cpp/capi.h --- qml-1/cpp/capi.h 2015-02-09 14:10:31.000000000 +0000 +++ /home/mersdk/src/gopkg.in/qml.v1/cpp/capi.h 2016-04-05 12:53:20.649583202 +0000 @@ -29,6 +29,7 @@ typedef void QImage_; typedef void GoValue_; typedef void GoAddr; +typedef uintptr_t GoRef; typedef void GoTypeSpec_; typedef char error; @@ -49,6 +50,7 @@ DTFloat64 = 17, DTFloat32 = 18, DTColor = 19, + DTDateTime = 20, DTGoAddr = 100, DTObject = 101, @@ -106,9 +108,13 @@ } LogMessage; void newGuiApplication(); +void sailfishnewGuiApplication(); void applicationExec(); +void sailfishapplicationExec(); void applicationExit(); +void sailfishapplicationExit(); void applicationFlushAll(); +void sailfishapplicationFlushAll(); void idleTimerInit(int32_t *guiIdleRun); void idleTimerStart(); @@ -116,7 +122,9 @@ void *currentThread(); void *appThread(); +void newTranslator(QString_ *translatorRoot); QQmlEngine_ *newEngine(QObject_ *parent); +QQmlEngine_ *newSailfishEngine(); QQmlContext_ *engineRootContext(QQmlEngine_ *engine); void engineSetOwnershipCPP(QQmlEngine_ *engine, QObject_ *object); void engineSetOwnershipJS(QQmlEngine_ *engine, QObject_ *object); @@ -141,19 +149,23 @@ int objectIsWindow(QObject_ *object); int objectIsView(QObject_ *object); error *objectConnect(QObject_ *object, const char *signal, int signalLen, QQmlEngine_ *engine, void *func, int argsLen); -error *objectGoAddr(QObject_ *object, GoAddr **addr); +error *objectGoRef(QObject_ *object, GoRef *ref); QQmlComponent_ *newComponent(QQmlEngine_ *engine, QObject_ *parent); void componentLoadURL(QQmlComponent_ *component, const char *url, int urlLen); +void sailfishSetSource(const char *url, int urlLen); void componentSetData(QQmlComponent_ *component, const char *data, int dataLen, const char *url, int urlLen); char *componentErrorString(QQmlComponent_ *component); QObject_ *componentCreate(QQmlComponent_ *component, QQmlContext_ *context); QQuickWindow_ *componentCreateWindow(QQmlComponent_ *component, QQmlContext_ *context); +QQuickWindow_ *sailfishCreateWindow(); void windowShow(QQuickWindow_ *win); +void sailfishwindowShow(); void windowHide(QQuickWindow_ *win); uintptr_t windowPlatformId(QQuickWindow_ *win); void windowConnectHidden(QQuickWindow_ *win); +void windowConnectDestroy(QQuickWindow_ *win); QObject_ *windowRootObject(QQuickWindow_ *win); QImage_ *windowGrabWindow(QQuickWindow_ *win); @@ -166,7 +178,7 @@ QString_ *newString(const char *data, int len); void delString(QString_ *s); -GoValue_ *newGoValue(GoAddr *addr, GoTypeInfo *typeInfo, QObject_ *parent); +GoValue_ *newGoValue(GoRef ref, GoTypeInfo *typeInfo, QObject_ *parent); void goValueActivate(GoValue_ *value, GoTypeInfo *typeInfo, int addrOffset); void packDataValue(QVariant_ *var, DataValue *result); @@ -174,7 +186,7 @@ QVariantList_ *newVariantList(DataValue *list, int len); -QQmlListProperty_ *newListProperty(GoAddr *addr, intptr_t reflectIndex, intptr_t setIndex); +QQmlListProperty_ *newListProperty(GoRef ref, intptr_t reflectIndex, intptr_t setIndex); int registerType(char *location, int major, int minor, char *name, GoTypeInfo *typeInfo, GoTypeSpec_ *spec); int registerSingleton(char *location, int major, int minor, char *name, GoTypeInfo *typeInfo, GoTypeSpec_ *spec); @@ -183,21 +195,21 @@ void hookIdleTimer(); void hookLogHandler(LogMessage *message); -void hookGoValueReadField(QQmlEngine_ *engine, GoAddr *addr, int memberIndex, int getIndex, int setIndex, DataValue *result); -void hookGoValueWriteField(QQmlEngine_ *engine, GoAddr *addr, int memberIndex, int setIndex, DataValue *assign); -void hookGoValueCallMethod(QQmlEngine_ *engine, GoAddr *addr, int memberIndex, DataValue *result); -void hookGoValueDestroyed(QQmlEngine_ *engine, GoAddr *addr); -void hookGoValuePaint(QQmlEngine_ *engine, GoAddr *addr, intptr_t reflextIndex); +void hookGoValueReadField(QQmlEngine_ *engine, GoRef ref, int memberIndex, int getIndex, int setIndex, DataValue *result); +void hookGoValueWriteField(QQmlEngine_ *engine, GoRef ref, int memberIndex, int setIndex, DataValue *assign); +void hookGoValueCallMethod(QQmlEngine_ *engine, GoRef ref, int memberIndex, DataValue *result); +void hookGoValueDestroyed(QQmlEngine_ *engine, GoRef ref); +void hookGoValuePaint(QQmlEngine_ *engine, GoRef ref, intptr_t reflextIndex); QImage_ *hookRequestImage(void *imageFunc, char *id, int idLen, int width, int height); -GoAddr *hookGoValueTypeNew(GoValue_ *value, GoTypeSpec_ *spec); +uintptr_t hookGoValueTypeNew(GoValue_ *value, GoTypeSpec_ *spec); void hookWindowHidden(QObject_ *addr); void hookSignalCall(QQmlEngine_ *engine, void *func, DataValue *params); void hookSignalDisconnect(void *func); void hookPanic(char *message); -int hookListPropertyCount(GoAddr *addr, intptr_t reflectIndex, intptr_t setIndex); -QObject_ *hookListPropertyAt(GoAddr *addr, intptr_t reflectIndex, intptr_t setIndex, int i); -void hookListPropertyAppend(GoAddr *addr, intptr_t reflectIndex, intptr_t setIndex, QObject_ *obj); -void hookListPropertyClear(GoAddr *addr, intptr_t reflectIndex, intptr_t setIndex); +int hookListPropertyCount(GoRef ref, intptr_t reflectIndex, intptr_t setIndex); +QObject_ *hookListPropertyAt(GoRef ref, intptr_t reflectIndex, intptr_t setIndex, int i); +void hookListPropertyAppend(GoRef ref, intptr_t reflectIndex, intptr_t setIndex, QObject_ *obj); +void hookListPropertyClear(GoRef ref, intptr_t reflectIndex, intptr_t setIndex); void registerResourceData(int version, char *tree, char *name, char *data); void unregisterResourceData(int version, char *tree, char *name, char *data); diff -ruN qml-1/cpp/govalue.cpp /home/mersdk/src/gopkg.in/qml.v1/cpp/govalue.cpp --- qml-1/cpp/govalue.cpp 2015-02-09 14:10:31.000000000 +0000 +++ /home/mersdk/src/gopkg.in/qml.v1/cpp/govalue.cpp 2016-04-05 12:53:20.662583214 +0000 @@ -13,7 +13,7 @@ class GoValueMetaObject : public QAbstractDynamicMetaObject { public: - GoValueMetaObject(QObject* value, GoAddr *addr, GoTypeInfo *typeInfo); + GoValueMetaObject(QObject* value, GoRef ref, GoTypeInfo *typeInfo); void activatePropIndex(int propIndex); @@ -22,12 +22,12 @@ private: QObject *value; - GoAddr *addr; + GoRef ref; GoTypeInfo *typeInfo; }; -GoValueMetaObject::GoValueMetaObject(QObject *value, GoAddr *addr, GoTypeInfo *typeInfo) - : value(value), addr(addr), typeInfo(typeInfo) +GoValueMetaObject::GoValueMetaObject(QObject *value, GoRef ref, GoTypeInfo *typeInfo) + : value(value), ref(ref), typeInfo(typeInfo) { //d->parent = static_cast(priv->metaObject); *static_cast(this) = *metaObjectFor(typeInfo); @@ -53,7 +53,7 @@ if (memberInfo->metaIndex == idx) { if (c == QMetaObject::ReadProperty) { DataValue result; - hookGoValueReadField(qmlEngine(value), addr, memberInfo->reflectIndex, memberInfo->reflectGetIndex, memberInfo->reflectSetIndex, &result); + hookGoValueReadField(qmlEngine(value), ref, memberInfo->reflectIndex, memberInfo->reflectGetIndex, memberInfo->reflectSetIndex, &result); if (memberInfo->memberType == DTListProperty) { if (result.dataType != DTListProperty) { panicf("reading DTListProperty field returned non-DTListProperty result"); @@ -71,7 +71,7 @@ DataValue assign; QVariant *in = reinterpret_cast(a[0]); packDataValue(in, &assign); - hookGoValueWriteField(qmlEngine(value), addr, memberInfo->reflectIndex, memberInfo->reflectSetIndex, &assign); + hookGoValueWriteField(qmlEngine(value), ref, memberInfo->reflectIndex, memberInfo->reflectSetIndex, &assign); activate(value, methodOffset() + (idx - propOffset), 0); } return -1; @@ -95,7 +95,7 @@ for (int i = 1; i < memberInfo->numIn+1; i++) { packDataValue(reinterpret_cast(a[i]), &args[i]); } - hookGoValueCallMethod(qmlEngine(value), addr, memberInfo->reflectIndex, args); + hookGoValueCallMethod(qmlEngine(value), ref, memberInfo->reflectIndex, args); if (memberInfo->numOut > 0) { unpackDataValue(&args[0], reinterpret_cast(a[0])); } @@ -121,16 +121,16 @@ activate(value, methodOffset() + relativeIndex, 0); } -GoValue::GoValue(GoAddr *addr, GoTypeInfo *typeInfo, QObject *parent) - : addr(addr), typeInfo(typeInfo) +GoValue::GoValue(GoRef ref, GoTypeInfo *typeInfo, QObject *parent) + : ref(ref), typeInfo(typeInfo) { - valueMeta = new GoValueMetaObject(this, addr, typeInfo); + valueMeta = new GoValueMetaObject(this, ref, typeInfo); setParent(parent); } GoValue::~GoValue() { - hookGoValueDestroyed(qmlEngine(this), addr); + hookGoValueDestroyed(qmlEngine(this), ref); } void GoValue::activate(int propIndex) @@ -138,10 +138,10 @@ valueMeta->activatePropIndex(propIndex); } -GoPaintedValue::GoPaintedValue(GoAddr *addr, GoTypeInfo *typeInfo, QObject *parent) - : addr(addr), typeInfo(typeInfo) +GoPaintedValue::GoPaintedValue(GoRef ref, GoTypeInfo *typeInfo, QObject *parent) + : ref(ref), typeInfo(typeInfo) { - valueMeta = new GoValueMetaObject(this, addr, typeInfo); + valueMeta = new GoValueMetaObject(this, ref, typeInfo); setParent(parent); QQuickItem::setFlag(QQuickItem::ItemHasContents, true); @@ -150,7 +150,7 @@ GoPaintedValue::~GoPaintedValue() { - hookGoValueDestroyed(qmlEngine(this), addr); + hookGoValueDestroyed(qmlEngine(this), ref); } void GoPaintedValue::activate(int propIndex) @@ -161,7 +161,7 @@ void GoPaintedValue::paint(QPainter *painter) { painter->beginNativePainting(); - hookGoValuePaint(qmlEngine(this), addr, typeInfo->paint->reflectIndex); + hookGoValuePaint(qmlEngine(this), ref, typeInfo->paint->reflectIndex); painter->endNativePainting(); } diff -ruN qml-1/cpp/govalue.h /home/mersdk/src/gopkg.in/qml.v1/cpp/govalue.h --- qml-1/cpp/govalue.h 2015-02-09 14:10:31.000000000 +0000 +++ /home/mersdk/src/gopkg.in/qml.v1/cpp/govalue.h 2016-04-05 12:53:20.665583217 +0000 @@ -20,10 +20,10 @@ Q_OBJECT public: - GoAddr *addr; + GoRef ref; GoTypeInfo *typeInfo; - GoValue(GoAddr *addr, GoTypeInfo *typeInfo, QObject *parent); + GoValue(GoRef ref, GoTypeInfo *typeInfo, QObject *parent); virtual ~GoValue(); void activate(int propIndex); @@ -37,10 +37,10 @@ Q_OBJECT public: - GoAddr *addr; + GoRef ref; GoTypeInfo *typeInfo; - GoPaintedValue(GoAddr *addr, GoTypeInfo *typeInfo, QObject *parent); + GoPaintedValue(GoRef ref, GoTypeInfo *typeInfo, QObject *parent); virtual ~GoPaintedValue(); void activate(int propIndex); diff -ruN qml-1/cpp/moc_all.cpp /home/mersdk/src/gopkg.in/qml.v1/cpp/moc_all.cpp --- qml-1/cpp/moc_all.cpp 2015-02-09 14:10:31.000000000 +0000 +++ /home/mersdk/src/gopkg.in/qml.v1/cpp/moc_all.cpp 2016-04-05 13:07:40.260614966 +0000 @@ -1,4 +1,5 @@ // This file is automatically generated by cpp/update-moc.sh +#include "cpp/moc_capi.cpp" #include "cpp/moc_connector.cpp" #include "cpp/moc_govalue.cpp" #include "cpp/moc_idletimer.cpp" diff -ruN qml-1/cpp/moc_capi.cpp /home/mersdk/src/gopkg.in/qml.v1/cpp/moc_capi.cpp --- qml-1/cpp/moc_capi.cpp 1970-01-01 00:00:00.000000000 +0000 +++ /home/mersdk/src/gopkg.in/qml.v1/cpp/moc_capi.cpp 2016-04-05 13:07:40.241614773 +0000 @@ -0,0 +1,87 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'capi.cpp' +** +** Created by: The Qt Meta Object Compiler version 67 (Qt 5.2.2) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include +#include +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'capi.cpp' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 67 +#error "This file was generated using the moc from 5.2.2. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +struct qt_meta_stringdata_CloseEventFilter_t { + QByteArrayData data[1]; + char stringdata[18]; +}; +#define QT_MOC_LITERAL(idx, ofs, len) \ + Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ + offsetof(qt_meta_stringdata_CloseEventFilter_t, stringdata) + ofs \ + - idx * sizeof(QByteArrayData) \ + ) +static const qt_meta_stringdata_CloseEventFilter_t qt_meta_stringdata_CloseEventFilter = { + { +QT_MOC_LITERAL(0, 0, 16) + }, + "CloseEventFilter\0" +}; +#undef QT_MOC_LITERAL + +static const uint qt_meta_data_CloseEventFilter[] = { + + // content: + 7, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + 0 // eod +}; + +void CloseEventFilter::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + Q_UNUSED(_o); + Q_UNUSED(_id); + Q_UNUSED(_c); + Q_UNUSED(_a); +} + +const QMetaObject CloseEventFilter::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_CloseEventFilter.data, + qt_meta_data_CloseEventFilter, qt_static_metacall, 0, 0} +}; + + +const QMetaObject *CloseEventFilter::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; +} + +void *CloseEventFilter::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_CloseEventFilter.stringdata)) + return static_cast(const_cast< CloseEventFilter*>(this)); + return QObject::qt_metacast(_clname); +} + +int CloseEventFilter::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + return _id; +} +QT_END_MOC_NAMESPACE diff -ruN qml-1/cpp/moc_connector.cpp /home/mersdk/src/gopkg.in/qml.v1/cpp/moc_connector.cpp --- qml-1/cpp/moc_connector.cpp 2015-02-09 14:10:31.000000000 +0000 +++ /home/mersdk/src/gopkg.in/qml.v1/cpp/moc_connector.cpp 2016-04-05 13:07:40.248614903 +0000 @@ -1,7 +1,7 @@ /**************************************************************************** ** Meta object code from reading C++ file 'connector.h' ** -** Created by: The Qt Meta Object Compiler version 67 (Qt 5.2.1) +** Created by: The Qt Meta Object Compiler version 67 (Qt 5.2.2) ** ** WARNING! All changes made in this file will be lost! *****************************************************************************/ @@ -12,7 +12,7 @@ #if !defined(Q_MOC_OUTPUT_REVISION) #error "The header file 'connector.h' doesn't include ." #elif Q_MOC_OUTPUT_REVISION != 67 -#error "This file was generated using the moc from 5.2.1. It" +#error "This file was generated using the moc from 5.2.2. It" #error "cannot be used with the include files from this version of Qt." #error "(The moc has changed too much.)" #endif diff -ruN qml-1/cpp/moc_govalue.cpp /home/mersdk/src/gopkg.in/qml.v1/cpp/moc_govalue.cpp --- qml-1/cpp/moc_govalue.cpp 2015-02-09 14:10:31.000000000 +0000 +++ /home/mersdk/src/gopkg.in/qml.v1/cpp/moc_govalue.cpp 2016-04-05 13:07:40.254614971 +0000 @@ -1,7 +1,7 @@ /**************************************************************************** ** Meta object code from reading C++ file 'govalue.h' ** -** Created by: The Qt Meta Object Compiler version 67 (Qt 5.2.1) +** Created by: The Qt Meta Object Compiler version 67 (Qt 5.2.2) ** ** WARNING! All changes made in this file will be lost! *****************************************************************************/ @@ -12,7 +12,7 @@ #if !defined(Q_MOC_OUTPUT_REVISION) #error "The header file 'govalue.h' doesn't include ." #elif Q_MOC_OUTPUT_REVISION != 67 -#error "This file was generated using the moc from 5.2.1. It" +#error "This file was generated using the moc from 5.2.2. It" #error "cannot be used with the include files from this version of Qt." #error "(The moc has changed too much.)" #endif diff -ruN qml-1/cpp/moc_idletimer.cpp /home/mersdk/src/gopkg.in/qml.v1/cpp/moc_idletimer.cpp --- qml-1/cpp/moc_idletimer.cpp 2015-02-09 14:10:31.000000000 +0000 +++ /home/mersdk/src/gopkg.in/qml.v1/cpp/moc_idletimer.cpp 2016-04-05 13:07:40.259614964 +0000 @@ -1,7 +1,7 @@ /**************************************************************************** ** Meta object code from reading C++ file 'idletimer.cpp' ** -** Created by: The Qt Meta Object Compiler version 67 (Qt 5.2.1) +** Created by: The Qt Meta Object Compiler version 67 (Qt 5.2.2) ** ** WARNING! All changes made in this file will be lost! *****************************************************************************/ @@ -11,7 +11,7 @@ #if !defined(Q_MOC_OUTPUT_REVISION) #error "The header file 'idletimer.cpp' doesn't include ." #elif Q_MOC_OUTPUT_REVISION != 67 -#error "This file was generated using the moc from 5.2.1. It" +#error "This file was generated using the moc from 5.2.2. It" #error "cannot be used with the include files from this version of Qt." #error "(The moc has changed too much.)" #endif diff -ruN qml-1/datatype.go /home/mersdk/src/gopkg.in/qml.v1/datatype.go --- qml-1/datatype.go 2015-02-09 14:10:31.000000000 +0000 +++ /home/mersdk/src/gopkg.in/qml.v1/datatype.go 2016-04-05 12:53:20.542583113 +0000 @@ -10,6 +10,7 @@ "image/color" "reflect" "strings" + "time" "unicode" "unsafe" ) @@ -37,6 +38,7 @@ typePainter = reflect.TypeOf(&Painter{}) typeList = reflect.TypeOf(&List{}) typeMap = reflect.TypeOf(&Map{}) + typeDateTime = reflect.TypeOf(&time.Time{}) typeGenericMap = reflect.TypeOf(map[string]interface{}(nil)) ) @@ -108,6 +110,9 @@ case color.RGBA: dvalue.dataType = C.DTColor *(*uint32)(datap) = uint32(value.A)<<24 | uint32(value.R)<<16 | uint32(value.G)<<8 | uint32(value.B) + case time.Time: + dvalue.dataType = C.DTDateTime + *(*uint32)(datap) = uint32(value.Unix()) default: dvalue.dataType = C.DTObject if obj, ok := value.(Object); ok { @@ -152,11 +157,13 @@ case C.DTColor: var c uint32 = *(*uint32)(datap) return color.RGBA{byte(c >> 16), byte(c >> 8), byte(c), byte(c >> 24)} + case C.DTDateTime: + var d int64 = int64(*(*uint32)(datap)) + return time.Unix(d, 0) case C.DTGoAddr: // ObjectByName also does this fold conversion, to have access // to the cvalue. Perhaps the fold should be returned. - fold := (*(**valueFold)(datap)) - ensureEngine(engine.addr, unsafe.Pointer(fold)) + fold := ensureEngine(engine.addr, uintptr(datap)) return fold.gvalue case C.DTInvalid: return nil @@ -222,6 +229,8 @@ return C.DTAny case typeRGBA: return C.DTColor + case typeDateTime: + return C.DTDateTime case typeObjSlice: return C.DTListProperty } diff -ruN qml-1/qml.go /home/mersdk/src/gopkg.in/qml.v1/qml.go --- qml-1/qml.go 2015-02-09 14:10:31.000000000 +0000 +++ /home/mersdk/src/gopkg.in/qml.v1/qml.go 2016-04-05 12:53:20.562583132 +0000 @@ -33,6 +33,22 @@ var engines = make(map[unsafe.Pointer]*Engine) +// NewSailfishEngine returns a new Sailfish QML engine. +// +// The Destory method must be called to finalize the engine and +// release any resources used. +func SailfishNewEngine() *Engine { + engine := &Engine{values: make(map[interface{}]*valueFold)} + RunMain(func() { + engine.addr = C.newSailfishEngine() + engine.engine = engine + engine.imageProviders = make(map[string]*func(imageId string, width, height int) image.Image) + engines[engine.addr] = engine + stats.enginesAlive(+1) + }) + return engine +} + // NewEngine returns a new QML engine. // // The Destory method must be called to finalize the engine and @@ -124,7 +140,7 @@ cloc, cloclen := unsafeStringData(location) comp := &Common{engine: e} RunMain(func() { - // TODO The component's parent should probably be the engine. + //TODO The component's parent should probably be the engine. comp.addr = C.newComponent(e.addr, nilPtr) if qrc { C.componentLoadURL(comp.addr, cloc, cloclen) @@ -143,6 +159,16 @@ return comp, nil } +// Set the Sailfish applications view source to the given QML file from the applications +// root shared folder. +func (e *Engine) SailfishSetSource(path string) (Object, error) { + cloc, cloclen := unsafeStringData(path) + RunMain(func() { + C.sailfishSetSource(cloc, cloclen) + }) + return &Common{engine: e}, nil +} + // LoadFile loads a component from the provided QML file. // Resources referenced by the QML content will be resolved relative to its path. // @@ -241,6 +267,15 @@ }) } +func (e *Engine) Translator(translatorRoot string) { + ctranslatorRoot, ctranslatorLen := unsafeStringData(translatorRoot) + RunMain(func() { + qtranslatorRoot := C.newString(ctranslatorRoot, ctranslatorLen) + defer C.delString(qtranslatorRoot) + C.newTranslator(qtranslatorRoot) + }) +} + //export hookRequestImage func hookRequestImage(imageFunc unsafe.Pointer, cid *C.char, cidLen, cwidth, cheight C.int) unsafe.Pointer { f := *(*func(imgId string, width, height int) image.Image)(imageFunc) @@ -368,6 +403,7 @@ Call(method string, params ...interface{}) interface{} Create(ctx *Context) Object CreateWindow(ctx *Context) *Window + SailfishCreateWindow() *Window Destroy() On(signal string, function interface{}) } @@ -484,8 +520,9 @@ var result interface{} var cerr *C.error RunMain(func() { - var fold *valueFold - if cerr = C.objectGoAddr(obj.addr, (*unsafe.Pointer)(unsafe.Pointer(&fold))); cerr == nil { + var foldr C.GoRef + if cerr = C.objectGoRef(obj.addr, &foldr); cerr == nil { + fold := restoreFold(uintptr(foldr)) result = fold.gvalue } }) @@ -729,6 +766,26 @@ return &root } +// Returns the root object after Sailfish view had been created +func (e *Engine) SailfishGetWindowRoot() *Common { + var obj Common + obj.engine = e.engine + RunMain(func() { + obj.addr = C.windowRootObject(C.sailfishCreateWindow()) + }) + return &obj +} + +// Returns a window structure for Sailfish application QQuickView +func (obj *Common) SailfishCreateWindow() *Window { + var win Window + win.engine = obj.engine + RunMain(func() { + win.addr = C.sailfishCreateWindow() + }) + return &win +} + // CreateWindow creates a new instance of the component held by obj, // and creates a new window holding the instance as its root object. // The component instance runs under the ctx context. If ctx is nil, @@ -859,6 +916,13 @@ Common } +// Show exposes the Sailfish application window. +func (win *Window) SailfishShow() { + RunMain(func() { + C.sailfishwindowShow() + }) +} + // Show exposes the window. func (win *Window) Show() { RunMain(func() { @@ -903,9 +967,11 @@ var m sync.Mutex m.Lock() RunMain(func() { - // TODO Must be able to wait for the same Window from multiple goroutines. + // TODO Must be able to wait for the same Window from multiple goroutines. // TODO If the window is not visible, must return immediately. waitingWindows[win.addr] = &m + // BUG: Exiting on window hidden will fail on SailfishOS when cover image is shown app will exit + // Solved this by implementing closeEvent filter for QWindows C.windowConnectHidden(win.addr) }) m.Lock() @@ -1088,9 +1154,9 @@ } else if len(r.bdata) > 0 { base = *(*unsafe.Pointer)(unsafe.Pointer(&r.bdata)) } - tree := (*C.char)(unsafe.Pointer(uintptr(base)+uintptr(r.treeOffset))) - name := (*C.char)(unsafe.Pointer(uintptr(base)+uintptr(r.nameOffset))) - data := (*C.char)(unsafe.Pointer(uintptr(base)+uintptr(r.dataOffset))) + tree := (*C.char)(unsafe.Pointer(uintptr(base) + uintptr(r.treeOffset))) + name := (*C.char)(unsafe.Pointer(uintptr(base) + uintptr(r.nameOffset))) + data := (*C.char)(unsafe.Pointer(uintptr(base) + uintptr(r.dataOffset))) C.registerResourceData(C.int(r.version), tree, name, data) } @@ -1102,8 +1168,8 @@ } else if len(r.bdata) > 0 { base = *(*unsafe.Pointer)(unsafe.Pointer(&r.bdata)) } - tree := (*C.char)(unsafe.Pointer(uintptr(base)+uintptr(r.treeOffset))) - name := (*C.char)(unsafe.Pointer(uintptr(base)+uintptr(r.nameOffset))) - data := (*C.char)(unsafe.Pointer(uintptr(base)+uintptr(r.dataOffset))) + tree := (*C.char)(unsafe.Pointer(uintptr(base) + uintptr(r.treeOffset))) + name := (*C.char)(unsafe.Pointer(uintptr(base) + uintptr(r.nameOffset))) + data := (*C.char)(unsafe.Pointer(uintptr(base) + uintptr(r.dataOffset))) C.unregisterResourceData(C.int(r.version), tree, name, data) }