QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskMetaInvokable.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskMetaInvokable.h"
7#include "QskMetaFunction.h"
8
9#include <qmetaobject.h>
10#include <qobject.h>
11#include <qcoreapplication.h>
12#include <qthread.h>
13
14#if QT_CONFIG(thread)
15#include <qsemaphore.h>
16#endif
17
18QSK_QT_PRIVATE_BEGIN
19#include <private/qobject_p.h>
20QSK_QT_PRIVATE_END
21
22static void qskRegisterMetaInvokable()
23{
24 qRegisterMetaType< QskMetaInvokable >();
25}
26
27Q_CONSTRUCTOR_FUNCTION( qskRegisterMetaInvokable )
28
29namespace
30{
31 using CallFunction = QObjectPrivate::StaticMetaCallFunction;
32
33 class Function : public QskMetaFunction
34 {
35 public:
36 inline Function( void* functionCall )
37 : QskMetaFunction( static_cast< FunctionCall* >( functionCall ) )
38 {
39 }
40
41 static inline void ref( void* functionCall )
42 {
43 if ( functionCall )
44 static_cast< FunctionCall* >( functionCall )->ref();
45 }
46
47 static inline void deref( void* functionCall )
48 {
49 if ( functionCall )
50 static_cast< FunctionCall* >( functionCall )->destroyIfLastRef();
51 }
52 };
53
54 class MetaCallEvent final : public QMetaCallEvent
55 {
56 public:
57 MetaCallEvent( QMetaObject::Call call, const QMetaObject* metaObject,
58 ushort offset, ushort index, void* args[], QSemaphore* semaphore )
59 : QMetaCallEvent( offset, index,
60 metaObject->d.static_metacall, nullptr, -1, args, semaphore )
61 , m_call( call )
62 , m_callFunction( metaObject->d.static_metacall )
63 , m_index( index )
64 {
65 }
66
67 MetaCallEvent( QMetaObject::Call call, const QMetaObject* metaObject,
68 ushort offset, ushort index, int argc )
69 : QMetaCallEvent( offset, index,
70 metaObject->d.static_metacall, nullptr, -1, argc )
71 , m_call( call )
72 , m_callFunction( metaObject->d.static_metacall )
73 , m_index( index )
74 {
75 }
76
77 void placeMetaCall( QObject* object ) override
78 {
79 m_callFunction( object, m_call, m_index, args() );
80 }
81
82 private:
83 const QMetaObject::Call m_call;
84
85 // as those members from QMetaCallEvent are not accessible
86 CallFunction m_callFunction;
87 const ushort m_index;
88 };
89}
90
91QMetaMethod qskMetaMethod( const QObject* object, const char* methodName )
92{
93 return object ? qskMetaMethod( object->metaObject(), methodName ) : QMetaMethod();
94}
95
96QMetaMethod qskMetaMethod( const QMetaObject* metaObject, const char* methodName )
97{
98 if ( metaObject == nullptr || methodName == nullptr )
99 return QMetaMethod();
100
101 constexpr char signalIndicator = '0' + QSIGNAL_CODE;
102 constexpr char slotIndicator = '0' + QSLOT_CODE;
103
104 int index = -1;
105
106 if ( methodName[ 0 ] == signalIndicator )
107 {
108 auto signature = QMetaObject::normalizedSignature( methodName + 1 );
109 index = metaObject->indexOfSignal( signature );
110 }
111 else if ( methodName[ 0 ] == slotIndicator )
112 {
113 auto signature = QMetaObject::normalizedSignature( methodName + 1 );
114 index = metaObject->indexOfSlot( signature );
115 }
116 else
117 {
118 auto signature = QMetaObject::normalizedSignature( methodName );
119 index = metaObject->indexOfMethod( signature );
120 }
121
122 return ( index >= 0 ) ? metaObject->method( index ) : QMetaMethod();
123}
124
125QMetaMethod qskNotifySignal( const QObject* object, const char* propertyName )
126{
127 return object ? qskNotifySignal( object->metaObject(), propertyName ) : QMetaMethod();
128}
129
130QMetaMethod qskNotifySignal( const QMetaObject* metaObject, const char* propertyName )
131{
132 if ( metaObject == nullptr || propertyName == nullptr )
133 return QMetaMethod();
134
135 const int propertyIndex = metaObject->indexOfProperty( propertyName );
136 if ( propertyIndex )
137 {
138 const auto property = metaObject->property( propertyIndex );
139 return property.notifySignal();
140 }
141
142 return QMetaMethod();
143}
144
145static void qskInvokeMetaCall(
146 QObject* object, const QMetaObject* metaObject,
147 QMetaObject::Call call, ushort offset, ushort index, void* args[],
148 Qt::ConnectionType connectionType )
149{
150 QPointer< QObject > receiver( object );
151
152 int invokeType = connectionType & 0x3;
153
154 if ( invokeType == Qt::AutoConnection )
155 {
156 invokeType = ( object && object->thread() != QThread::currentThread() )
157 ? Qt::QueuedConnection : Qt::DirectConnection;
158 }
159
160 switch ( invokeType )
161 {
162 case Qt::DirectConnection:
163 {
164 if ( receiver.isNull() )
165 {
166#if 1
167 // do we really always need an object, what about Q_GADGET ???
168 return;
169#endif
170 }
171
172 /*
173 QMetaObject::metacall seems to be made for situations we don't have.
174 Need to dive deeper into the Qt code to be 100% sure TODO ...
175 */
176
177 metaObject->d.static_metacall( receiver, call, index, args );
178 break;
179 }
180 case Qt::BlockingQueuedConnection:
181 {
182 if ( receiver.isNull() ||
183 ( receiver->thread() == QThread::currentThread() ) )
184 {
185 // We would end up in a deadlock, better do nothing
186 return;
187 }
188
189#if QT_CONFIG(thread)
190 QSemaphore semaphore;
191
192 auto event = new MetaCallEvent( call, metaObject,
193 offset, index, args, &semaphore );
194
195#else
196 auto event = new MetaCallEvent( call, metaObject,
197 offset, index, args, nullptr );
198#endif
199
200 QCoreApplication::postEvent( receiver, event );
201
202#if QT_CONFIG(thread)
203 semaphore.acquire();
204#endif
205
206 break;
207 }
208 case Qt::QueuedConnection:
209 {
210 if ( receiver == nullptr )
211 return;
212
213 MetaCallEvent* event = nullptr;
214
215 if ( call == QMetaObject::InvokeMetaMethod )
216 {
217#if 1
218 // should be doable without QMetaMethod. TODO ...
219 const auto method = metaObject->method( offset + index );
220#endif
221 const int argc = method.parameterCount() + 1;
222
223 event = new MetaCallEvent( call, metaObject, offset, index, argc );
224
225 /*
226 The first one is the return type, one that is always
227 invalid for Queued Connections.
228 */
229
230 auto types = event->types();
231 auto arguments = event->args();
232
233#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
234 types[0] = QMetaType();
235 arguments[ 0 ] = nullptr;
236#else
237 types[0] = 0;
238 arguments[ 0 ] = nullptr;
239#endif
240 for ( int i = 1; i < argc; i++ )
241 {
242 if ( args[ i ] == nullptr )
243 {
244 Q_ASSERT( args[ i ] != nullptr );
245 receiver = nullptr;
246 break;
247 }
248
249 const auto type = method.parameterType( i - 1 );
250 const auto arg = args[ i ];
251
252#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
253 types[ i ] = QMetaType( type );
254 arguments[ i ] = QMetaType( type ).create( arg );
255#else
256 types[ i ] = type;
257 arguments[ i ] = QMetaType::create( type, arg );
258#endif
259 }
260 }
261 else
262 {
263 // should be doable without QMetaMethod. TODO ...
264 const auto property = metaObject->property( offset + index );
265
266 event = new MetaCallEvent( call, metaObject, offset, index, 1 );
267
268 const auto type = property.userType();
269 const auto arg = args[ 0 ];
270
271#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
272 event->types()[0] = QMetaType( type );
273 event->args()[ 0 ] = QMetaType( type ).create( arg );
274#else
275 event->types()[0] = type;
276 event->args()[ 0 ] = QMetaType::create( type, arg );
277#endif
278 }
279
280 if ( receiver )
281 QCoreApplication::postEvent( receiver, event );
282 else
283 delete event;
284
285 break;
286 }
287 }
288}
289
290void qskInvokeMetaPropertyWrite( QObject* context, const QMetaProperty& property,
291 void* args[], Qt::ConnectionType connectionType )
292{
293 qskInvokeMetaPropertyWrite( context, property.enclosingMetaObject(),
294 property.propertyIndex(), args, connectionType );
295}
296
297void qskInvokeMetaPropertyWrite( QObject* context, const QMetaObject* metaObject,
298 int propertyIndex, void* args[], Qt::ConnectionType connectionType )
299{
300 // check for is writable ???
301
302 if ( metaObject && ( propertyIndex >= 0 ) &&
303 ( propertyIndex < metaObject->propertyCount() ) )
304 {
305 const auto offset = metaObject->propertyOffset();
306 const auto index = propertyIndex - offset;
307
308 qskInvokeMetaCall( context, metaObject, QMetaObject::WriteProperty,
309 offset, index, args + 1, connectionType );
310 }
311}
312
313void qskInvokeMetaMethod( QObject* object,
314 const QMetaMethod& method, void* args[],
315 Qt::ConnectionType connectionType )
316{
317 qskInvokeMetaMethod( object, method.enclosingMetaObject(),
318 method.methodIndex(), args, connectionType );
319}
320
321void qskInvokeMetaMethod( QObject* object,
322 const QMetaObject* metaObject, int methodIndex, void* args[],
323 Qt::ConnectionType connectionType )
324{
325 if ( metaObject && ( methodIndex >= 0 ) &&
326 ( methodIndex < metaObject->methodCount() ) )
327 {
328 const auto offset = metaObject->methodOffset();
329 const auto index = methodIndex - offset;
330
331 qskInvokeMetaCall( object, metaObject, QMetaObject::InvokeMetaMethod,
332 offset, index, args, connectionType );
333 }
334}
335
336QskMetaInvokable::QskMetaInvokable( const QMetaMethod& method )
337 : m_metaData{ method.enclosingMetaObject(), method.methodIndex() }
338 , m_type( MetaMethod )
339{
340}
341
342QskMetaInvokable::QskMetaInvokable( const QObject* object, const char* methodName )
343 : QskMetaInvokable( qskMetaMethod( object, methodName ) )
344{
345}
346
347QskMetaInvokable::QskMetaInvokable( const QMetaObject* metaObject, const char* methodName )
348 : QskMetaInvokable( qskMetaMethod( metaObject, methodName ) )
349{
350}
351
352QskMetaInvokable::QskMetaInvokable( const QMetaProperty& property )
353 : m_metaData{ property.enclosingMetaObject(), property.propertyIndex() }
354 , m_type( MetaProperty )
355{
356}
357
358QskMetaInvokable::QskMetaInvokable( const QskMetaFunction& function )
359 : m_functionData{ function.functionCall() }
360 , m_type( MetaFunction )
361{
362 Function::ref( m_functionData.functionCall );
363}
364
365QskMetaInvokable::QskMetaInvokable( const QskMetaInvokable& other )
366 : m_type( other.m_type )
367{
368 switch ( m_type )
369 {
370 case MetaMethod:
371 case MetaProperty:
372 {
373 m_metaData.metaObject = other.m_metaData.metaObject;
374 m_metaData.index = other.m_metaData.index;
375
376 break;
377 }
378 case MetaFunction:
379 {
380 m_functionData.functionCall = other.m_functionData.functionCall;
381 Function::ref( m_functionData.functionCall );
382
383 break;
384 }
385
386 default:
387 break;
388 }
389}
390
391QskMetaInvokable::~QskMetaInvokable()
392{
393 if ( m_type == MetaFunction )
394 Function::deref( m_functionData.functionCall );
395}
396
397QskMetaInvokable& QskMetaInvokable::operator=( const QskMetaInvokable& other )
398{
399 switch ( other.m_type )
400 {
401 case MetaMethod:
402 case MetaProperty:
403 {
404 if ( m_type == MetaFunction )
405 Function::deref( m_functionData.functionCall );
406
407 m_metaData.metaObject = other.m_metaData.metaObject;
408 m_metaData.index = other.m_metaData.index;
409
410 break;
411 }
412 case MetaFunction:
413 {
414 if ( m_type == MetaFunction )
415 Function::deref( m_functionData.functionCall );
416
417 m_functionData.functionCall = other.m_functionData.functionCall;
418 Function::ref( m_functionData.functionCall );
419
420 break;
421 }
422
423 default:
424 if ( m_type == MetaFunction )
425 Function::deref( m_functionData.functionCall );
426 }
427
428 m_type = other.m_type;
429
430 return *this;
431}
432
433bool QskMetaInvokable::operator==( const QskMetaInvokable& other ) const
434{
435 if ( m_type != other.m_type )
436 return false;
437
438 switch ( m_type )
439 {
440 case MetaMethod:
441 case MetaProperty:
442 {
443 return ( m_metaData.metaObject == other.m_metaData.metaObject ) &&
444 ( m_metaData.index == other.m_metaData.index );
445 }
446 case MetaFunction:
447 {
448 return m_functionData.functionCall == other.m_functionData.functionCall;
449 }
450 default:
451 {
452 return true;
453 }
454 }
455}
456
457bool QskMetaInvokable::isNull() const
458{
459 switch ( m_type )
460 {
461 case MetaMethod:
462 case MetaProperty:
463 {
464 const auto& d = m_metaData;
465
466 if ( d.metaObject == nullptr || d.index < 0 )
467 return true;
468
469 const int count = ( m_type == MetaMethod )
470 ? d.metaObject->methodCount() : d.metaObject->propertyCount();
471
472 return d.index >= count;
473 }
474 case MetaFunction:
475 {
476 return m_functionData.functionCall == nullptr;
477 }
478
479 default:
480 return true;
481 }
482}
483
484void QskMetaInvokable::reset()
485{
486 if ( m_type == MetaFunction )
487 Function::deref( m_functionData.functionCall );
488
489 m_type = Invalid;
490}
491
492int QskMetaInvokable::parameterCount() const
493{
494 switch ( m_type )
495 {
496 case MetaMethod:
497 {
498 // should be doable without QMetaMethod. TODO ...
499 const auto method = QskMetaInvokable::method();
500 return method.parameterCount();
501 }
502 case MetaProperty:
503 {
504 return 1;
505 }
506 case MetaFunction:
507 {
508 return function().parameterCount();
509 }
510 default:
511 break;
512 }
513
514 return 0;
515}
516
517int QskMetaInvokable::parameterType( int index ) const
518{
519 switch ( m_type )
520 {
521 case MetaMethod:
522 {
523 const auto method = QskMetaInvokable::method();
524 return method.parameterType( index );
525 }
526 case MetaProperty:
527 {
528 const auto property = QskMetaInvokable::property();
529 return property.userType();
530 }
531 case MetaFunction:
532 {
533 auto types = function().parameterTypes();
534 return types[ index ];
535 }
536 default:
537 {
538 return QMetaType::UnknownType;
539 }
540 }
541}
542
543int QskMetaInvokable::returnType() const
544{
545 switch ( m_type )
546 {
547 case MetaMethod:
548 {
549 return method().returnType();
550 }
551 case MetaFunction:
552 {
553 return function().returnType();
554 }
555 case MetaProperty:
556 {
557 return QMetaType::Void;
558 }
559 default:
560 {
561 return QMetaType::Void;
562 }
563 }
564}
565
566QByteArray QskMetaInvokable::name() const
567{
568 switch ( m_type )
569 {
570 case MetaMethod:
571 {
572 return method().name();
573 }
574 case MetaProperty:
575 {
576 return property().name();
577 }
578 case MetaFunction:
579 {
580 // what to do here ???
581 return QByteArray();
582 }
583 default:
584 {
585 return QByteArray();
586 }
587 }
588}
589
590QMetaMethod QskMetaInvokable::method() const
591{
592 if ( m_type == MetaMethod && m_metaData.metaObject )
593 return m_metaData.metaObject->method( m_metaData.index );
594
595 return QMetaMethod();
596}
597
598QMetaProperty QskMetaInvokable::property() const
599{
600 if ( m_type == MetaProperty && m_metaData.metaObject )
601 return m_metaData.metaObject->property( m_metaData.index );
602
603 return QMetaProperty();
604}
605
606QskMetaFunction QskMetaInvokable::function() const
607{
608 if ( m_type == MetaFunction && m_functionData.functionCall )
609 {
610 Function function( m_functionData.functionCall );
611 return *static_cast< QskMetaFunction* >( &function );
612 }
613
614 return QskMetaFunction();
615}
616
617void QskMetaInvokable::invoke( QObject* object, void* args[],
618 Qt::ConnectionType connectionType )
619{
620 if ( isNull() )
621 return;
622
623 switch ( m_type )
624 {
625 case MetaMethod:
626 {
627 qskInvokeMetaMethod( object, m_metaData.metaObject,
628 m_metaData.index, args, connectionType );
629
630 break;
631 }
632 case MetaProperty:
633 {
634 qskInvokeMetaPropertyWrite(
635 object, m_metaData.metaObject, m_metaData.index, args, connectionType );
636
637 break;
638 }
639 case MetaFunction:
640 {
641 if ( m_functionData.functionCall )
642 {
643 Function function( m_functionData.functionCall );
644 function.invoke( object, args, connectionType );
645 }
646
647 break;
648 }
649
650 default:
651 break;
652 }
653}