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