QMetaObject/GObject-introspection inter-operability progress

    richard dale's picture
    2009
    15
    Feb

    I've been hacking on deriving QMetaObjects for GObject-Introspection data as I described in a recent blog and am making good progress. I've now built a complete heirarchy of QMetaObjects from the gobject-introspection Clutter module which I've been using for testing.

    I've checked the current code into playground/bindings/smoke_gobject so other people can play with it. It turns out that QMetaObject and the GObject Introspection models are pretty compatible. They both have single inheritance, properties, signals, runtime method calling (slots in Qt or or dynamic function invocation via ffi in G-I), enums and so on. So we need to add a dynamic runtime to call G-I methods as Qt slots or forward GObject signals to Qt ones. But as far as I can see it looks perfectly doable.

    In playground there is code for dumping a .gir module and for generating the QMetaObjects. There are three classes; Smoke::MetaObjectBuilder for generating the QMetaObjects from the G-I repository, Smoke::GObjectProxy for invoking methods on the GObject classes via qt_metacall(), and a Smoke::GObectNamespace class for each G-I namespace with the constructor methods as slots and all the enums and flags for a namespace.

    A dump of the Clutter QObjectNamespace instance looks like this:

        Smoke::MetaObjectBuilder::createNamespace("Clutter");
        Smoke::GObjectNamespace * gobjectNamespace = Smoke::MetaObjectBuilder::findNamespace("Clutter");
    
        const QMetaObject* metaObject = gobjectNamespace->metaObject();
        for(int i = metaObject->methodOffset(); i < metaObject->methodCount(); ++i) {
            printf("%s\n", metaObject->method(i).signature());
        }
    
    // Gives this output:
    
    Actor*Actor(Clutter::Vertex*,Clutter::ActorBox*)
    Stage*Stage()
    Stage*Stage()
    Shader*Shader()
    Timeline*Timeline(uint,uint)
    Timeline*Timeline(uint)
    Alpha*Alpha()
    Alpha*Alpha(Clutter::Timeline*,Clutter::AlphaFunc*,void*,GLib::DestroyNotify*)
    Group*Group()
    BehaviourBspline*BehaviourBspline(Clutter::Alpha*,Clutter::Knot*,uint)
    BehaviourDepth*BehaviourDepth(Clutter::Alpha*,int,int)
    ...
    

    Or dumping the enums and flags for the namespace gives this output:

        for(int i = metaObject->enumeratorOffset(); i < metaObject->enumeratorCount(); ++i) {
            QMetaEnum enumerator = metaObject->enumerator(i);
    
            printf("%s::%s\n", enumerator.scope(), enumerator.name());
            for (int j = 0; j < enumerator.keyCount(); ++j) {
                if (enumerator.isFlag()) {
                    printf("    %s = 0x%4.4x\n", enumerator.key(j), enumerator.value(j));
                } else {
                    printf("    %s = %d\n", enumerator.key(j), enumerator.value(j));
                }
            }
        }
    
    // Gives this output:
    Clutter::Gravity
        None = 0
        North = 1
        NorthEast = 2
        East = 3
        SouthEast = 4
        South = 5
        SouthWest = 6
        West = 7
        NorthWest = 8
        Center = 9
    Clutter::RotateAxis
        XAxis = 0
        YAxis = 1
        ZAxis = 2
    Clutter::RotateDirection
        Cw = 0
        Ccw = 1
    Clutter::RequestMode
        HeightForWidth = 0
        WidthForHeight = 1
    Clutter::ModifierType
        ShiftMask = 0x0001
        LockMask = 0x0002
        ControlMask = 0x0004
        Mod1Mask = 0x0008
        Mod2Mask = 0x0010
        Mod3Mask = 0x0020
        Mod4Mask = 0x0040
        Mod5Mask = 0x0080
        Button1Mask = 0x0100
        Button2Mask = 0x0200
        Button3Mask = 0x0400
        Button4Mask = 0x0800
        Button5Mask = 0x1000
        ...
    

    The other QMetaObjects have slots, signals and properties for each GObject class with the names converted to camel case and Qt types replacing the GTK ones. It looks pretty 'Qt-like' to me. I had originally been thinking of making the binding entirely dynamic, but I now think it would be nice to optionally generate a C++ class heirachy as that wouldn't be too hard. That's all for now - watch this space for more news..