1 /************************************************************************
2  *
3  * Copyright 2010-2011 Jakob Leben (jakob.leben@gmail.com)
4  *
5  * This file is part of SuperCollider Qt GUI.
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  ************************************************************************/
21 
22 #include <QDebug>
23 
24 #include "QObjectProxy.h"
25 #include "QcApplication.h"
26 #include "Common.h"
27 #include "QcSignalSpy.h"
28 #include "type_codec.hpp"
29 #include "metatype.hpp"
30 
31 #include <QApplication>
32 #include <QWidget>
33 #include <QVarLengthArray>
34 #include <QThread>
35 
36 #include <PyrKernel.h>
37 #include <VMGlobals.h>
38 
39 #if defined _WIN32
40 #    include "SC_Win32Utils.h"
41 #elif defined __FreeBSD__
42 #    include <stdlib.h>
43 #else
44 #    include <alloca.h>
45 #endif
46 
47 using namespace QtCollider;
48 
49 void interpretMouseEvent(QEvent* e, QList<QVariant>& args);
50 void interpretKeyEvent(QEvent* e, QList<QVariant>& args);
51 
QObjectProxy(QObject * qObject_,PyrObject * scObject_)52 QObjectProxy::QObjectProxy(QObject* qObject_, PyrObject* scObject_):
53     qObject(qObject_),
54     _scObject(scObject_),
55     _scClassName(slotRawSymbol(&scObject_->classptr->name)->name) {
56     ProxyToken* token = new ProxyToken(this, qObject);
57     // WARNING: make sure the signal is already in normalized signature,
58     // to avoid triggering very expensive normalization!
59     connect(qObject, SIGNAL(destroyed(QObject*)), this, SLOT(invalidate()));
60     qObject->installEventFilter(this);
61     _eventHandlers.reserve(10);
62 }
63 
~QObjectProxy()64 QObjectProxy::~QObjectProxy() { qcProxyDebugMsg(1, QStringLiteral("Proxy is being deleted.")); }
65 
compareThread()66 bool QObjectProxy::compareThread() { return gMainVMGlobals->canCallOS; }
67 
invalidate()68 void QObjectProxy::invalidate() {
69     qcProxyDebugMsg(1, QStringLiteral("Object has been deleted. Invalidating proxy."));
70     mutex.lock();
71     qObject = 0;
72     mutex.unlock();
73     QApplication::postEvent(this, new QEvent((QEvent::Type)QtCollider::Event_Proxy_Release));
74 }
75 
serializeSignature(QVarLengthArray<char,512> & dst,const char * method,int argc,MetaValue * argv)76 static bool serializeSignature(QVarLengthArray<char, 512>& dst, const char* method, int argc, MetaValue* argv) {
77     // serialize method name
78     int len = qstrlen(method);
79     if (len <= 0) {
80         qcErrorMsg("Method name appears to be empty.");
81         return false;
82     }
83     dst.append(method, len);
84     dst.append('(');
85 
86     // serialize argument types
87     int i;
88     for (i = 0; i < argc; ++i) {
89         int typeId = argv[i].type()->id();
90         const char* typeName = QMetaType::typeName(typeId);
91         int len = qstrlen(typeName);
92         if (len <= 0) {
93             qcErrorMsg("Could not get argument type name.");
94             return false;
95         }
96         dst.append(typeName, len);
97         dst.append(',');
98     }
99 
100     // finalize the signature
101     if (i == 0)
102         dst.append(')');
103     else
104         dst[dst.size() - 1] = ')';
105 
106     dst.append('\0');
107 
108     return true;
109 }
110 
invokeMethod(const char * method,PyrSlot * retSlot,PyrSlot * argSlot,Qt::ConnectionType ctype)111 bool QObjectProxy::invokeMethod(const char* method, PyrSlot* retSlot, PyrSlot* argSlot, Qt::ConnectionType ctype) {
112     QMutexLocker locker(&mutex);
113 
114     if (!qObject)
115         return true;
116 
117     MetaValue args[10];
118 
119     PyrSlot* argslots;
120     int argc;
121 
122     if (isKindOfSlot(argSlot, class_array)) {
123         argslots = slotRawObject(argSlot)->slots;
124         argc = slotRawObject(argSlot)->size;
125     } else if (argSlot && NotNil(argSlot)) {
126         argslots = argSlot;
127         argc = 1;
128     } else {
129         argslots = 0;
130         argc = 0;
131     }
132 
133     // translate args
134     for (int i = 0; i < argc; ++i) {
135         MetaType* type = MetaType::find(argslots + i);
136         if (!type)
137             return false;
138 
139         void* mem = alloca(type->size());
140         if (!mem) {
141             qcErrorMsg("Could not allocate stack space for argument!");
142             return false;
143         }
144 
145         args[i].read(mem, type, argslots + i);
146     }
147 
148     // serialize signature
149     QVarLengthArray<char, 512> sig;
150     if (!serializeSignature(sig, method, argc, args))
151         return false;
152 
153     // get the meta method
154     const QMetaObject* mo = qObject->metaObject();
155 
156     int mi = mo->indexOfMethod(sig.constData());
157     if (mi < 0) {
158         QByteArray mnorm = QMetaObject::normalizedSignature(sig.constData());
159         mi = mo->indexOfMethod(mnorm.constData());
160     }
161 
162     if (mi < 0 || mi >= mo->methodCount()) {
163         qcProxyDebugMsg(1, QStringLiteral("WARNING: No such method: %1::%2").arg(mo->className()).arg(sig.constData()));
164         return false;
165     }
166 
167     QMetaMethod mm = mo->method(mi);
168 
169     // construct the return data object
170     QGenericReturnArgument rarg;
171     const char* rtype_name = mm.typeName();
172     int rtype_id = QMetaType::type(rtype_name);
173 
174     MetaValue returnVal;
175 
176     if (retSlot && rtype_id != QMetaType::Void) {
177         MetaType* type = MetaType::find(rtype_id);
178         if (!type) {
179             qcErrorMsg(QStringLiteral("No translation for return type '%1'").arg(rtype_name));
180             return false;
181         }
182 
183         void* mem = alloca(type->size());
184         if (!mem) {
185             qcErrorMsg("Could not allocate stack space for return value");
186             return false;
187         }
188 
189         returnVal.read(mem, type, 0); // default construction
190         rarg = returnVal.toGenericReturnArgument();
191     }
192 
193     // do it!
194     bool success = mm.invoke(qObject, ctype, rarg, args[0].toGenericArgument(), args[1].toGenericArgument(),
195                              args[2].toGenericArgument(), args[3].toGenericArgument(), args[4].toGenericArgument(),
196                              args[5].toGenericArgument(), args[6].toGenericArgument(), args[7].toGenericArgument(),
197                              args[8].toGenericArgument(), args[9].toGenericArgument());
198 
199     // store the return data into the return slot
200     if (success && retSlot)
201         returnVal.write(retSlot);
202 
203     // done
204     return success;
205 }
206 
invokeScMethod(PyrSymbol * method,const QList<QVariant> & args,PyrSlot * result,bool locked)207 void QObjectProxy::invokeScMethod(PyrSymbol* method, const QList<QVariant>& args, PyrSlot* result, bool locked) {
208     qcProxyDebugMsg(1, QStringLiteral("SC METHOD CALL [+++]: ") + QString(method->name));
209 
210     if (!locked) {
211         QtCollider::lockLang();
212     }
213 
214     if (_scObject) {
215         QtCollider::runLang(_scObject, method, args, result);
216     } else {
217         if (result)
218             SetNil(result);
219         qcDebugMsg(1, "WARNING: no SC object");
220     }
221 
222     if (!locked)
223         QtCollider::unlockLang();
224 
225     qcProxyDebugMsg(1, QStringLiteral("SC METHOD CALL [---]: ") + QString(method->name));
226 }
227 
customEvent(QEvent * event)228 void QObjectProxy::customEvent(QEvent* event) {
229     switch ((int)event->type()) {
230     case QtCollider::Event_ScMethodCall:
231         scMethodCallEvent(static_cast<ScMethodCallEvent*>(event));
232         return;
233     case QtCollider::Event_Proxy_SetProperty:
234         setPropertyEvent(static_cast<SetPropertyEvent*>(event));
235         return;
236     case QtCollider::Event_Proxy_Destroy:
237         destroyEvent(static_cast<DestroyEvent*>(event));
238         return;
239     case QtCollider::Event_Proxy_Release:
240         invokeScMethod(SC_SYM(prRelease));
241         return;
242     default:;
243     }
244 }
245 
setParent(QObjectProxy * parentProxy)246 bool QObjectProxy::setParent(QObjectProxy* parentProxy) {
247     if (qObject && parentProxy->object())
248         qObject->setParent(parentProxy->object());
249 
250     return true;
251 }
252 
setProperty(const char * property,const QVariant & val)253 bool QObjectProxy::setProperty(const char* property, const QVariant& val) {
254     if (!qObject)
255         return true;
256     if (!qObject->setProperty(property, val)) {
257         qcProxyDebugMsg(1, QStringLiteral("WARNING: Property '%1' not found. Setting dynamic property.").arg(property));
258     }
259     return false;
260 }
261 
setPropertyEvent(SetPropertyEvent * e)262 bool QObjectProxy::setPropertyEvent(SetPropertyEvent* e) { return setProperty(e->property->name, e->value); }
263 
property(const char * property)264 QVariant QObjectProxy::property(const char* property) { return qObject ? qObject->property(property) : QVariant(); }
265 
setEventHandler(int type,PyrSymbol * method,QtCollider::Synchronicity sync,bool enable)266 bool QObjectProxy::setEventHandler(int type, PyrSymbol* method, QtCollider::Synchronicity sync, bool enable) {
267     EventHandlerData data;
268     data.type = type;
269     data.method = method;
270     data.sync = sync;
271     data.enabled = enable;
272 
273     EventHandlerData* d = _eventHandlers.data();
274     int n = _eventHandlers.size();
275     while (n--) {
276         if (d->type == type) {
277             *d = data;
278             break;
279         }
280         ++d;
281     }
282     if (n < 0)
283         _eventHandlers.append(data);
284 
285     return true;
286 }
287 
setEventHandlerEnabled(int type,bool enabled)288 bool QObjectProxy::setEventHandlerEnabled(int type, bool enabled) {
289     EventHandlerData* d = _eventHandlers.data();
290     int n = _eventHandlers.size();
291     while (n--) {
292         if (d->type == type) {
293             d->enabled = enabled;
294             break;
295         }
296         ++d;
297     }
298 
299     return n >= 0;
300 }
301 
connectObject(const char * signal,PyrObject * object,Qt::ConnectionType ctype)302 bool QObjectProxy::connectObject(const char* signal, PyrObject* object, Qt::ConnectionType ctype) {
303     if (!qObject)
304         return true;
305 
306     QcFunctionSignalHandler* handler = new QcFunctionSignalHandler(this, signal, object, ctype);
307 
308     if (!handler->isValid()) {
309         delete handler;
310         return false;
311     }
312 
313     funcSigHandlers.append(handler);
314 
315     return true;
316 }
317 
connectMethod(const char * signal,PyrSymbol * method,Qt::ConnectionType ctype)318 bool QObjectProxy::connectMethod(const char* signal, PyrSymbol* method, Qt::ConnectionType ctype) {
319     if (!qObject)
320         return true;
321 
322     QcMethodSignalHandler* handler = new QcMethodSignalHandler(this, signal, method, ctype);
323 
324     if (handler->isValid()) {
325         methodSigHandlers.append(handler);
326         return true;
327     } else {
328         delete handler;
329         return false;
330     }
331 }
332 
disconnectObject(const char * sig,PyrObject * object)333 bool QObjectProxy::disconnectObject(const char* sig, PyrObject* object) {
334     if (!qObject)
335         return true;
336 
337     const QMetaObject* mo = qObject->metaObject();
338     QByteArray signal = QMetaObject::normalizedSignature(sig);
339     int sigId = mo->indexOfSignal(signal);
340     if (sigId < 0) {
341         qcDebugMsg(1, QStringLiteral("WARNING: No such signal: '%1'").arg(signal.constData()));
342         return false;
343     }
344 
345     for (int i = 0; i < funcSigHandlers.size(); ++i) {
346         QcFunctionSignalHandler* h = funcSigHandlers[i];
347         if (h->indexOfSignal() == sigId && h->function() == object) {
348             funcSigHandlers.removeAt(i);
349             delete h;
350             return true;
351         }
352     }
353 
354     return false;
355 }
356 
disconnectMethod(const char * sig,PyrSymbol * method)357 bool QObjectProxy::disconnectMethod(const char* sig, PyrSymbol* method) {
358     if (!qObject)
359         return true;
360 
361     const QMetaObject* mo = qObject->metaObject();
362     QByteArray signal = QMetaObject::normalizedSignature(sig);
363     int sigId = mo->indexOfSignal(signal);
364     if (sigId < 0) {
365         qcDebugMsg(1, QStringLiteral("WARNING: No such signal: '%1'").arg(signal.constData()));
366         return false;
367     }
368 
369     for (int i = 0; i < methodSigHandlers.size(); ++i) {
370         QcMethodSignalHandler* h = methodSigHandlers[i];
371         if (h->indexOfSignal() == sigId && h->method() == method) {
372             methodSigHandlers.removeAt(i);
373             delete h;
374             return true;
375         }
376     }
377 
378     return false;
379 }
380 
destroy(DestroyAction action)381 void QObjectProxy::destroy(DestroyAction action) {
382     switch (action) {
383     case DestroyObject:
384         delete qObject;
385         return;
386     case DestroyProxy:
387         delete this;
388         return;
389     case DestroyProxyAndObject:
390         delete qObject;
391         delete this;
392         return;
393     }
394 }
395 
destroyEvent(DestroyEvent * e)396 bool QObjectProxy::destroyEvent(DestroyEvent* e) {
397     destroy(e->action());
398     return true;
399 }
400 
children(PyrSymbol * className)401 QList<PyrObject*> QObjectProxy::children(PyrSymbol* className) {
402     QList<PyrObject*> scChildren;
403 
404     if (!qObject)
405         return scChildren;
406 
407     const QObjectList& children = qObject->children();
408 
409     Q_FOREACH (QObject* child, children) {
410         QObjectProxy* proxy = QObjectProxy::fromObject(child);
411         if (!proxy)
412             continue;
413 
414         PyrObject* obj = proxy->_scObject;
415 
416         if (obj) {
417             if (className && !isKindOf(obj, className->u.classobj))
418                 continue;
419             scChildren.append(obj);
420         }
421     }
422 
423     return scChildren;
424 }
425 
parent(PyrSymbol * className)426 PyrObject* QObjectProxy::parent(PyrSymbol* className) {
427     if (!qObject)
428         return 0;
429 
430     QObject* parent = qObject->parent();
431 
432     while (parent) {
433         // see if this parent has a corresponding proxy
434         QObjectProxy* proxy = QObjectProxy::fromObject(parent);
435         if (proxy) {
436             // if parent does not have a corresponding SC object (it is just
437             // being deleted) return no parent;
438             PyrObject* scobj = proxy->_scObject;
439             if (!scobj)
440                 return 0;
441 
442             // if parent SC object is of desired class (or no class specified)
443             // return it, else continue
444             if (!className || isKindOf(scobj, className->u.classobj)) {
445                 return scobj;
446             }
447         }
448 
449         // if this parent was not appropriate continue to consider the parent's parent
450         parent = parent->parent();
451     }
452 
453     return 0;
454 }
455 
eventFilter(QObject * watched,QEvent * event)456 bool QObjectProxy::eventFilter(QObject* watched, QEvent* event) {
457     int type = event->type();
458 
459     EventHandlerData* d = _eventHandlers.data();
460     int n = _eventHandlers.size();
461     while (n--) {
462         if (d->type == type)
463             break;
464         ++d;
465     }
466     if (n < 0) {
467         qcProxyDebugMsg(3, QStringLiteral("No handler for event (%1), forwarding to the widget").arg(type));
468         return false;
469     }
470 
471     QList<QVariant> args;
472 
473     if (!preProcessEvent(watched, event, *d, args)) {
474         qcProxyDebugMsg(3,
475                         QStringLiteral("Event (%1, %2) not handled, forwarding to the widget")
476                             .arg(type)
477                             .arg(event->spontaneous() ? "spontaneous" : "inspontaneous"));
478         return false;
479     }
480 
481     qcProxyDebugMsg(1,
482                     QStringLiteral("Will handle event (%1, %2) -> (%3, %4)")
483                         .arg(type)
484                         .arg(event->spontaneous() ? "spontaneous" : "inspontaneous")
485                         .arg(d->method->name)
486                         .arg(d->sync == Synchronous ? "sync" : "async"));
487 
488     bool eventHandled = invokeEventHandler(event, *d, args);
489 
490     eventHandled = postProcessEvent(watched, event, eventHandled);
491 
492     return eventHandled;
493 }
494 
invokeEventHandler(QEvent * event,EventHandlerData & eh,QList<QVariant> & args)495 bool QObjectProxy::invokeEventHandler(QEvent* event, EventHandlerData& eh, QList<QVariant>& args) {
496     PyrSymbol* method = eh.method;
497 
498     if (eh.sync == Synchronous) {
499         PyrSlot result;
500         invokeScMethod(method, args, &result);
501         if (IsTrue(&result)) {
502             qcProxyDebugMsg(2, "Event accepted");
503             event->accept();
504             return true;
505         } else if (IsFalse(&result)) {
506             qcProxyDebugMsg(2, "Event ignored");
507             event->ignore();
508             return true;
509         }
510     } else {
511         ScMethodCallEvent* e = new ScMethodCallEvent(method, args);
512         QApplication::postEvent(this, e);
513     }
514 
515     qcProxyDebugMsg(2, "Forwarding event to the system");
516     return false;
517 }
518 
preProcessEvent(QObject *,QEvent * e,EventHandlerData & eh,QList<QVariant> & args)519 bool QObjectProxy::preProcessEvent(QObject*, QEvent* e, EventHandlerData& eh, QList<QVariant>& args) {
520     return eh.enabled;
521 }
522 
scMethodCallEvent(ScMethodCallEvent * e)523 inline void QObjectProxy::scMethodCallEvent(ScMethodCallEvent* e) { invokeScMethod(e->method, e->args, 0, e->locked); }
524 
fromObject(QObject * object)525 QObjectProxy* QObjectProxy::fromObject(QObject* object) {
526     if (!object)
527         return 0;
528 
529     const QObjectList& children = object->children();
530     Q_FOREACH (QObject* child, children) {
531         ProxyToken* token = qobject_cast<QtCollider::ProxyToken*>(child);
532         if (token)
533             return token->proxy;
534     }
535     return 0;
536 }
537