2 * Copyright (c) 2007 Mauro Iazzi
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use,
8 * copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
31 #include <QMetaObject>
32 #include <QMetaProperty>
35 #include <QGenericArgument>
38 #ifdef QOBJECT_HANDLER
39 static int lqt_handler__gc (lua_State
*L
);
40 #endif // QOBJECT_HANDLER
42 static int lqt_qpointer__gc (lua_State
*L
);
43 static int method_index (const QMetaObject
*M
, QByteArray name
, int start
= 0);
45 // #define luaL_checkqobject(L, index) (*(QObject**)luaL_checkudata(L, index, QOBJECT_METATABLE))
47 // int lua_pushqvariant (lua_State *L, QVariant *V);
48 // QVariant lua_toqvariant (lua_State *L, int index);
50 static int lua_isint (lua_State
*L
, int index
) {
51 int i
= lua_tointeger(L
, index
);
52 double d
= lua_tonumber(L
, index
);
56 static bool signal_slot_names (QObject
*obj
, QByteArray
& name
) {
57 const QMetaObject
*M
= NULL
;
60 name
= QMetaObject::normalizedSignature(name
);
62 M
= obj
->metaObject();
63 index
= M
->indexOfMethod(name
);
67 switch (M
->method(index
).methodType()) {
68 case QMetaMethod::Signal
:
71 case QMetaMethod::Slot
:
81 int lqt_connect (lua_State
*L
) {
83 QObject
*obj1
= luaL_checkqobject(L
, 1);
84 QByteArray func1
= QMetaObject::normalizedSignature(luaL_checkstring(L
, 2));
85 QObject
*obj2
= luaL_checkqobject(L
, 3);
86 QByteArray func2
= QMetaObject::normalizedSignature(luaL_checkstring(L
, 4));
87 // check for QObjects validity
89 const QMetaObject
*M1
= obj1
->metaObject();
90 const QMetaObject
*M2
= obj2
->metaObject();
91 int index1
= M1
->indexOfMethod(func1
);
92 int index2
= M2
->indexOfMethod(func2
);
94 if ( index1
!=-1 && index2
!=-1 ) {
96 } else if ( index1
!=-1 && index2
==-1 ) {
98 for (index2
= method_index(M2
, func2
, 0);index2
!=-1;index2
=method_index(M2
, func2
, index2
+1)) {
99 if ( (M2
->method(index2
).methodType()==QMetaMethod::Signal
||
100 M2
->method(index2
).methodType()==QMetaMethod::Slot
) &&
101 QMetaObject::checkConnectArgs(M2
->method(index2
).signature(), func1
))
104 if (index2
==-1) qDebug() << "no matching slot of name" << func2
<< "for signal" << func1
;
105 else func2
= M2
->method(index2
).signature();
106 // slot has been filled at best
107 } else if ( index1
==-1 && index2
!=-1 ) {
109 for (index1
= method_index(M1
, func1
, 0);index1
!=-1;index1
=method_index(M1
, func1
, index1
+1)) {
110 if (M1
->method(index1
).methodType()==QMetaMethod::Signal
&&
111 QMetaObject::checkConnectArgs(M1
->method(index1
).signature(), func2
))
114 if (index1
==-1) qDebug() << "no matching signal of name" << func1
<< "for slot" << func2
;
115 else func1
= M1
->method(index1
).signature();
116 // signal has been filled at best
117 } else if ( index1
==-1 && index2
==-1 ) {
120 signal_slot_names(obj1
, func1
);
121 signal_slot_names(obj2
, func2
);
123 // if (!signal_slot_names(obj1, func1)) {
125 // if (!signal_slot_names(obj2, func2)) {
127 // // find if a signal has same signature of slot
129 // for (m1 = method_index(M1, func1, 0);m1!=-1;m1=method_index(M1, func1, m1+1)) {
130 // if (M1->method(m1).methodType()==QMetaMethod::Signal &&
131 // QMetaObject::checkConnectArgs(M1->method(m1).signature(), func2))
134 // if (m1==-1) qDebug() << "no matching signal of name" << func1 << "for slot" << func2;
138 // if (!signal_slot_names(obj2, func2)) {
139 // // find if a slot has same signature of signal
141 // for (m2 = method_index(M2, func2, 0);m2!=-1;m2=method_index(M2, func2, m2+1)) {
142 // qDebug() << M2->method(m2).signature() << func1;
143 // if ( (M2->method(m2).methodType()==QMetaMethod::Signal ||
144 // M2->method(m2).methodType()==QMetaMethod::Slot ) &&
145 // QMetaObject::checkConnectArgs(M2->method(m2).signature(), func1))
148 // if (m2==-1) qDebug() << "no matching slot of name" << func2 << "for signal" << func1;
153 qDebug() << "trying to connect signal" << func1
<< "to slot" << func2
;
154 lua_pushboolean(L
, QObject::connect(obj1
, func1
, obj2
, func2
));
159 int lqt_disconnect (lua_State
*L
) {
161 QObject
*obj1
= luaL_checkqobject(L
, 1);
162 QByteArray func1
= luaL_checkstring(L
, 2);
163 QObject
*obj2
= luaL_checkqobject(L
, 3);
164 QByteArray func2
= luaL_checkstring(L
, 4);
165 // check fo QObjects validity
167 if (!signal_slot_names(obj1
, func1
)) {
168 // TODO: handle method missing
170 if (!signal_slot_names(obj2
, func2
)) {
171 // TODO: handle method missing
174 lua_pushboolean(L
, QObject::disconnect(obj1
, func1
, obj2
, func2
));
179 static void method_see (const QMetaObject
*M
) {
180 if (M
->superClass()) method_see(M
->superClass());
181 qDebug() << M
->className() << "methods:";
182 for (int m
=0;m
<M
->methodCount();m
++) {
183 qDebug() << "" << M
->method(m
).typeName() << M
->method(m
).signature();
187 int methods_of (lua_State
*L
) {
189 QObject
*Q
= luaL_checkqobject(L
, 1);
192 method_see(Q
->metaObject());
199 static int method_index (const QMetaObject
*M
, QByteArray name
, int start
) {
202 int ret
= M
->indexOfMethod(name
);
204 if (ret
>= start
) { return ret
; }
209 for (int m
=start
;m
<M
->methodCount();m
++) {
210 if ( QByteArray(M
->method(m
).signature()).startsWith(name
) ) {
218 // void delete_genericret (int type, void *data) {
219 // QMetaType::destroy(type, data);
223 // QGenericReturnArgument lua_togenericret (int type, void **data) {
224 // QGenericReturnArgument ret = QGenericReturnArgument();
226 // *data = QMetaType::construct(type);
227 // if (*data) ret = QGenericReturnArgument(QMetaType::typeName(type), *data);
232 // void delete_genericarg (lua_State *L, int index, void *data) {
233 // if (!data) return;
234 // QVariant var = lua_toqvariant(L, index);
235 // QMetaType::destroy(var.type(), data);
240 // TODO: should be done better (e.g. reentrant)
241 #define HANDLE_CASE_OF(T) case (QVariant:: T): { \
242 static typeof(V.to##T ()) my##T = V.to##T (); return &my##T; \
244 static const void *data_copy(const QVariant
& V
, int t
) {
246 HANDLE_CASE_OF(Bool
);
248 HANDLE_CASE_OF(Double
);
249 HANDLE_CASE_OF(String
);
253 return (const void*)NULL
;
255 #undef HANDLE_CASE_OF
258 // QGenericArgument lua_togenericarg (lua_State *L, int index, void **data) {
259 // // qDebug() << "passing QVariant";
260 // QVariant var = lua_toqvariant(L, index);
261 // *data = QMetaType::construct(var.type(), var.data());
262 // QGenericArgument ret = QGenericArgument(var.typeName(), *data);
266 static int lqt_invoke (lua_State
*L
) {
268 QGenericArgument args
[10];
269 QGenericReturnArgument ret
;
271 // void *args_data[10];
273 // int ret_type = -1;
275 QObject
*Q
= luaL_checkqobject(L
, 1);
276 const QMetaObject
*M
= Q
->metaObject();
278 QByteArray method
= luaL_checkstring(L
, lua_upvalueindex(1));
280 // ret_type = QMetaType::type(luaL_checkstring(L, lua_upvalueindex(2)));
282 // qDebug() << "calling" << method << "on object" << Q << "with return type" << QMetaType::typeName(ret_type);
283 // QMetaObject::invokeMethod(Q, "show");
285 for (int i
=0;i
<10;i
++) {
286 qvargs
[i
] = lua_toqvariant(L
, i
+2);
287 // args[i] = lua_togenericarg(L, i+2, &args_data[i]);
289 // ret = lua_togenericret(ret_type, &ret_data);
292 // DONE: find for sure which method the user wants
293 int method_number
= -1;
294 int method_result
= -1;
295 for (int m
= method_index(M
, method
, 0);m
!=-1;m
=method_index(M
, method
, m
+1)) {
296 // qDebug() << "checking for match" << M->method(m).signature();
298 QList
<QByteArray
> sig
= M
->method(m
).parameterTypes();
299 for (int i
= 0; i
< sig
.size(); ++i
) {
300 QVariant::Type this_type
= QVariant::nameToType(sig
.at(i
));
301 // qDebug() << "checking for match" << sig.at(i) << qvargs[i].typeName();
302 if (qvargs
[i
].type()==this_type
) {
304 } else if (qvargs
[i
].canConvert(this_type
)) {
311 if (corresponds
>method_result
) {
313 method_result
= corresponds
;
316 if (method_number
!=-1) {
317 qDebug() << "I found best method is" << M
->method(method_number
).signature();
320 QMetaMethod mmeth
= M
->method(method_number
);
321 QByteArray method_name
= mmeth
.signature();
322 method_name
.truncate(method_name
.indexOf('('));
323 // qDebug() << method_name;
324 QList
<QByteArray
> sig
= mmeth
.parameterTypes();
325 for (int i
= 0; i
< sig
.size(); ++i
) {
326 QVariant::Type this_type
= QVariant::nameToType(sig
.at(i
));
327 qvargs
[i
].convert(this_type
);
328 args
[i
] = QGenericArgument(qvargs
[i
].typeName(), qvargs
[i
].data());
332 qvret
= QVariant(QVariant::nameToType(mmeth
.typeName()));
333 ret
= QGenericReturnArgument(qvret
.typeName(), qvret
.data());
334 qDebug() << "method call with ret=" << qvret
;
336 if ( QMetaObject::invokeMethod(Q
, method_name
, Qt::AutoConnection
, ret
,
337 args
[0], args
[1], args
[2], args
[3], args
[4],
338 args
[5], args
[6], args
[7], args
[8], args
[9] ) )
340 qDebug() << "method call returned:" << qvret
;
341 // QVariant r(ret_type, ret.data());
342 // qDebug() << "method call returned:" << r;
343 lua_pushqvariant(L
, &qvret
);
346 qDebug() << "method call failed";
348 // for (int i = 0;i<10;i++) {
349 // delete_genericarg( L, i+2, args_data[i] );
351 // delete_genericret( ret_type, ret_data );
352 qDebug() << "method call finished";
356 // static int canConserve (const QVariant *V, QVariant::Type type) {
357 // QVariant copy(*V);
358 // QVariant::Type oldtype = copy.type();
359 // if (!copy.canConvert(type)) return 0;
360 // copy.convert(type);
361 // if (!copy.canConvert(oldtype)) return 0;
362 // copy.convert(oldtype);
363 // return (copy==*V)?1:0;
367 static int lqt__index (lua_State
*L
) {
369 const char *name
= NULL
;
372 QObject
**qp
= (QObject
**)luaL_checkudata(L
, 1, QOBJECT_METATABLE
);
373 QObject
*Q
= qp
?*qp
:NULL
;
375 // TODO: find if object has method overwritten
377 name
= lua_tolstring(L
, 2, &len
);
378 qDebug() << "requested member: " << lua_tostring(L
, 2);
380 const QMetaObject
*M
= Q
->metaObject();
382 // for (int m=0;m<M->methodCount();m++){
383 // qDebug() << M->method(m).signature();
386 if ( (index
=method_index(M
, name
, len
)) != -1 ) {
387 qDebug() << "method requested";
389 // lua_pushstring(L, M->method(index).typeName());
390 lua_pushcclosure(L
, lqt_invoke
, 1);
392 } else if ( (index
=M
->indexOfProperty(name
)) != -1) {
393 QVariant
value(M
->property(index
).read(Q
));
394 return lua_pushqvariant(L
, &value
);
395 }/* else if ( (index=M->indexOfEnumerator(name)) != -1) {
399 qDebug() << "NULL Object indexed";
401 lua_pushstring(L
, "QObject destroyed");
407 static int lqt__newindex (lua_State
*L
) {
409 const char *name
= NULL
;
413 QObject
**qp
= (QObject
**)luaL_checkudata(L
, 1, QOBJECT_METATABLE
);
414 QObject
*Q
= qp
?*qp
:NULL
;
416 name
= lua_tolstring(L
, 2, &len
);
417 val
= lua_toqvariant(L
, 3);
419 const QMetaObject
*M
= Q
->metaObject();
421 qDebug() << "setting property: " << name
;
423 index
=M
->indexOfProperty(name
);
424 if ( (index
=M
->indexOfProperty(name
)) != -1) {
425 if (!M
->property(index
).write(Q
, val
)) {
427 lua_pushstring(L
, "could not set property ");
428 lua_pushstring(L
, name
);
429 lua_pushstring(L
, " for object of type ");
430 lua_pushstring(L
, M
->className());
435 // qDebug() << "NULL Object assigned";
437 lua_pushstring(L
, "QObject does not have this property");
442 lua_pushstring(L
, "QObject destroyed");
448 static struct luaL_Reg lqt_metatable
[] = {
449 { "__newindex", lqt__newindex
},
450 { "__index", lqt__index
},
451 { "__gc", lqt_qpointer__gc
},
458 /************* HANDLER ****************/
460 static int lqt_qpointer__gc (lua_State
*L
) {
461 QPointer
<QObject
> *qp
= (QPointer
<QObject
> *)lua_touserdata(L
, 1);
463 if (lua_istable(L
, -1)) {
464 lua_getfield(L
, -1, "__gc");
465 if (lua_isfunction(L
, -1)) {
466 qDebug() << "found a __gc";
474 *qp
= NULL
; // hoping this unsets the guard by Qt
475 // qDebug() << "Garbage collecting qp =" << qp << ", *qp =" << *qp;
481 * This is __gc for the handler, which is not managed by Qt
484 #ifdef QOBJECT_HANDLER
485 static int lqt_handler__gc (lua_State
*L
) {
486 lua_getfield(L
, LUA_REGISTRYINDEX
, QOBJECT_HANDLER
);
487 QObject
**qp1
= (QObject
**)lua_touserdata(L
, 1);
488 QObject
**qp2
= (QObject
**)lua_touserdata(L
, -1);
492 qDebug() << "destroying handler";
497 void QLuaHandler::destroyed (QObject
* obj
) {
498 luaL_checkstack(L
, 2, "Cannot grow stack to destroy QObject");
499 lua_pushlightuserdata(L
, obj
);
500 lua_gettable(L
, LUA_REGISTRYINDEX
);
501 QObject
**op
= (QObject
**)lua_touserdata(L
, -1);
502 qDebug() << "voiding object: " << *op
;
503 lua_pushlightuserdata(L
, obj
);
505 lua_settable(L
, LUA_REGISTRYINDEX
);
509 #endif // QOBJECT_HANDLER
511 /************* STACK FUNCTIONS ****************/
513 int lua_pushqobject (lua_State
*L
, QObject
*Q
) {
514 // QPointer<QObject> obj = Q;
515 int ot
= lua_gettop(L
); /* old stack top */
516 // qDebug() << "creating object: " << Q;
519 luaL_checkstack(L
, 3, "Cannot grow stack to push QObject");
523 * if we already have it don't rebuild
525 // lua_pushlightuserdata(L, Q);
526 // lua_gettable(L, LUA_REGISTRYINDEX);
527 // if (lua_isqobject(L, -1)) {
533 * we don't have it: let's create.
534 * push the new userdata (QObject*)
536 QPointer
<QObject
> *op
= (QPointer
<QObject
> *)lua_newuserdata(L
, sizeof(QPointer
<QObject
>));
537 *op
= NULL
; /* if we fail later, do not leave invalid pointers */
538 // qDebug() << "Creating qp =" << op << ", *qp =" << *op;
539 /* insert in register indexed by its content */
540 // lua_pushlightuserdata(L, Q);
541 // lua_pushvalue(L, ot+1);
542 // lua_settable(L, LUA_REGISTRYINDEX);
544 /* get the metatable */
545 if (luaL_newmetatable(L
, QOBJECT_METATABLE
)) {
546 /* create metatable if it did not exist */
547 // DONE: check validity of metatable
548 luaL_register(L
, NULL
, lqt_metatable
);
549 lua_pushvalue(L
, -1);
550 lua_setmetatable(L
, -2);/* it is its own metatable */
551 lua_pushvalue(L
, -1);
552 lua_pushstring(L
, QOBJECT_METATABLE
);
553 lua_settable(L
, LUA_REGISTRYINDEX
);
555 lua_setmetatable(L
, -2);
557 #ifdef QOBJECT_HANDLER
558 lua_getfield(L
, LUA_REGISTRYINDEX
, QOBJECT_HANDLER
);
559 // DONE: check presence of handler
560 if (lua_isnil(L
, -1)) {
562 QLuaHandler
**qhp
= (QLuaHandler
**)lua_newuserdata(L
, sizeof(*qhp
));
564 lua_pushcfunction(L
, lqt_handler__gc
);
565 lua_setfield(L
, -2, "__gc");
566 lua_setmetatable(L
, -2);
568 lua_pushvalue(L
, -1);
569 lua_setfield(L
, LUA_REGISTRYINDEX
, QOBJECT_HANDLER
);
570 *qhp
= new QLuaHandler(L
);
572 QObject
*H
= *(QObject
**)lua_touserdata(L
, -1);
574 #endif // QOBJECT_HANDLER
578 #ifdef QOBJECT_HANDLER
579 if (Q
) QObject::connect( Q
, SIGNAL(destroyed(QObject
*)), H
, SLOT(destroyed(QObject
*)) );
580 #endif // QOBJECT_HANDLER
585 int lua_isqobject (lua_State
*L
, int index
) {
587 if (lua_isuserdata(L
, index
)) {
588 lua_getmetatable(L
, index
);
589 luaL_getmetatable(L
, QOBJECT_METATABLE
);
590 ret
= lua_rawequal(L
, -1, -2);
596 QObject
*lua_toqobject (lua_State
*L
, int index
) {
597 if (lua_isqobject(L
, index
)) {
598 QPointer
<QObject
> *qp
= (QPointer
<QObject
> *)lua_touserdata(L
, index
);
604 QVariant
lua_toqvariant (lua_State
*L
, int index
) {
606 switch (lua_type(L
, index
)) {
608 if (lua_isint(L
, index
)) {
609 // qDebug() << "getting int" << lua_tointeger(L, index);
610 ret
= QVariant((int)lua_tointeger(L
, index
));
612 // qDebug() << "getting double" << lua_tonumber(L, index);
613 ret
= QVariant(lua_tonumber(L
, index
));
617 // qDebug() << "getting bool" << (bool)lua_toboolean(L, index);
618 ret
= QVariant((bool)lua_toboolean(L
, index
));
621 // qDebug() << "getting string" << lua_tostring(L, index);
622 ret
= QVariant(lua_tostring(L
, index
));
625 lua_getmetatable(L
, index
);
626 lua_gettable(L
, LUA_REGISTRYINDEX
);
627 // qDebug() << "getting userdata";
628 if (lua_isstring(L
, -1)) {
629 int type
= QMetaType::type(lua_tostring(L
, -1));
631 const void *obj
= (const void*)lua_touserdata(L
, index
);
632 // qDebug() << "getting userdata" << obj << "of type" << tname;
633 ret
= QVariant(type
, obj
);
642 case LUA_TLIGHTUSERDATA
:
649 int lua_pushqvariant (lua_State
*L
, QVariant
*V
) {
650 // qDebug() << "pushing val: " << *V;
652 case QVariant::Bool
: lua_pushboolean(L
, (int)V
->toBool()); break;
653 case QVariant::Int
: lua_pushinteger(L
, V
->toInt()); break;
654 case QVariant::LongLong
: lua_pushinteger(L
, V
->toLongLong()); break;
655 case QVariant::UInt
: lua_pushinteger(L
, V
->toUInt()); break;
656 case QVariant::ULongLong
: lua_pushinteger(L
, V
->toULongLong()); break;
657 case QVariant::Double
: lua_pushnumber (L
, V
->toDouble()); break;
658 case QVariant::String
: lua_pushstring (L
, (const char*)V
->toString().toAscii().data()); break;
659 case QVariant::KeySequence
: lua_pushstring (L
, (const char*)V
->toString().toAscii().data()); break;
660 case QVariant::Invalid
:
662 lua_pushstring(L
, "invalid value can't be pushed");
666 if (V
->canConvert(QVariant::Bool
)) {
667 lua_pushboolean(L
, (int)V
->toBool());
668 } else if (V
->canConvert(QVariant::Double
)) {
669 lua_pushnumber (L
, V
->toDouble());
670 } else if (V
->canConvert(QVariant::Int
)) {
671 lua_pushinteger(L
, V
->toInt());
672 } else if (V
->canConvert(QVariant::String
)) {
673 lua_pushstring (L
, (const char*)V
->toString().toAscii().data());
675 // lua_getfield(L, LUA_REGISTRYINDEX, V->typeName()); ...
677 lua_pushstring(L
, "QVariant type ");
678 lua_pushstring(L
, V
->typeName());
679 lua_pushstring(L
, " can't be pushed");