Add real threading support, including per-thread event loops, to QThread

pull/2/head
Timothy Pearson 12 years ago
parent 7aa5ac7f0e
commit 78125ea2f0

@ -68,6 +68,7 @@
#if defined(QT_THREAD_SUPPORT) #if defined(QT_THREAD_SUPPORT)
# include "qmutex.h" # include "qmutex.h"
# include "qthread.h" # include "qthread.h"
# include <private/qthreadinstance_p.h>
#endif // QT_THREAD_SUPPORT #endif // QT_THREAD_SUPPORT
#include <stdlib.h> #include <stdlib.h>
@ -383,7 +384,25 @@ Q_EXPORT Qt::HANDLE qt_get_application_thread_id()
} }
#endif // QT_THREAD_SUPPORT #endif // QT_THREAD_SUPPORT
#ifndef QT_THREAD_SUPPORT
QEventLoop *QApplication::eventloop = 0; // application event loop QEventLoop *QApplication::eventloop = 0; // application event loop
#endif
#ifdef QT_THREAD_SUPPORT
QEventLoop* QApplication::currentEventLoop() {
QThread* thread = QThread::currentThreadObject();
if (thread) {
if (thread->d) {
return thread->d->eventLoop;
}
}
return NULL;
}
#else
QEventLoop* QApplication::currentEventLoop() {
return QApplication::eventloop;
}
#endif
#ifndef QT_NO_ACCEL #ifndef QT_NO_ACCEL
extern bool qt_dispatchAccelEvent( QWidget*, QKeyEvent* ); // def in qaccel.cpp extern bool qt_dispatchAccelEvent( QWidget*, QKeyEvent* ); // def in qaccel.cpp
@ -516,6 +535,41 @@ QClipboard *qt_clipboard = 0; // global clipboard object
#endif #endif
QWidgetList * qt_modal_stack=0; // stack of modal widgets QWidgetList * qt_modal_stack=0; // stack of modal widgets
#ifdef QT_THREAD_SUPPORT
// thread wrapper for the main() thread
class QCoreApplicationThread : public QThread
{
public:
inline QCoreApplicationThread()
{
QThreadInstance::setCurrentThread(this);
// thread should be running and not finished for the lifetime
// of the application (even if QCoreApplication goes away)
d->running = true;
d->finished = false;
d->eventLoop = NULL;
}
inline ~QCoreApplicationThread()
{
// avoid warning from QThread
d->running = false;
}
private:
inline void run()
{
// this function should never be called, it is implemented
// only so that we can instantiate the object
qFatal("QCoreApplicationThread: internal error");
}
};
static QCoreApplicationThread qt_main_thread;
static QThread *mainThread() { return &qt_main_thread; }
#else
static QThread* mainThread() { return QThread::currentThread(); }
#endif
// Definitions for posted events // Definitions for posted events
struct QPostEvent { struct QPostEvent {
QPostEvent( QObject *r, QEvent *e ): receiver( r ), event( e ) {} QPostEvent( QObject *r, QEvent *e ): receiver( r ), event( e ) {}
@ -818,8 +872,8 @@ void QApplication::construct( int &argc, char **argv, Type type )
initialize( argc, argv ); initialize( argc, argv );
if ( qt_is_gui_used ) if ( qt_is_gui_used )
qt_maxWindowRect = desktop()->rect(); qt_maxWindowRect = desktop()->rect();
if ( eventloop ) if ( currentEventLoop() )
eventloop->appStartingUp(); currentEventLoop()->appStartingUp();
} }
/*! /*!
@ -874,8 +928,8 @@ QApplication::QApplication( Display* dpy, HANDLE visual, HANDLE colormap )
if ( qt_is_gui_used ) if ( qt_is_gui_used )
qt_maxWindowRect = desktop()->rect(); qt_maxWindowRect = desktop()->rect();
if ( eventloop ) if ( currentEventLoop() )
eventloop->appStartingUp(); currentEventLoop()->appStartingUp();
} }
/*! /*!
@ -916,13 +970,26 @@ QApplication::QApplication(Display *dpy, int argc, char **argv,
if ( qt_is_gui_used ) if ( qt_is_gui_used )
qt_maxWindowRect = desktop()->rect(); qt_maxWindowRect = desktop()->rect();
if ( eventloop ) if ( currentEventLoop() )
eventloop->appStartingUp(); currentEventLoop()->appStartingUp();
} }
#endif // Q_WS_X11 #endif // Q_WS_X11
#ifdef QT_THREAD_SUPPORT
QThread* QApplication::guiThread() {
return mainThread();
}
bool QApplication::isGuiThread() {
return (QThread::currentThreadObject() == guiThread());
}
#else
bool QApplication::isGuiThread() {
return true;
}
#endif
void QApplication::init_precmdline() void QApplication::init_precmdline()
{ {
@ -1030,8 +1097,8 @@ QApplication::~QApplication()
} }
#endif #endif
if ( eventloop ) if ( currentEventLoop() )
eventloop->appClosingDown(); currentEventLoop()->appClosingDown();
if ( postRList ) { if ( postRList ) {
QVFuncList::Iterator it = postRList->begin(); QVFuncList::Iterator it = postRList->begin();
while ( it != postRList->end() ) { // call post routines while ( it != postRList->end() ) { // call post routines
@ -2698,8 +2765,20 @@ bool QApplication::internalNotify( QObject *receiver, QEvent * e)
} }
if (!handled) if (!handled) {
#if defined(QT_THREAD_SUPPORT)
bool locked = QApplication::qt_mutex->locked();
if (locked) {
QApplication::qt_mutex->unlock();
}
#endif
consumed = receiver->event( e ); consumed = receiver->event( e );
#if defined(QT_THREAD_SUPPORT)
if (locked) {
QApplication::qt_mutex->lock();
}
#endif
}
e->spont = FALSE; e->spont = FALSE;
return consumed; return consumed;
} }
@ -2793,9 +2872,10 @@ void QApplication::processOneEvent()
*/ */
QEventLoop *QApplication::eventLoop() QEventLoop *QApplication::eventLoop()
{ {
if ( !eventloop && !is_app_closing ) if ( !currentEventLoop() && !is_app_closing ) {
(void) new QEventLoop( qApp, "default event loop" ); (void) new QEventLoop( qApp, "default event loop" );
return eventloop; }
return currentEventLoop();
} }
@ -3263,8 +3343,23 @@ void QApplication::postEvent( QObject *receiver, QEvent *event )
l->append( pe ); l->append( pe );
globalPostedEvents->append( pe ); globalPostedEvents->append( pe );
if (eventloop) #ifdef QT_THREAD_SUPPORT
eventloop->wakeUp(); if ( event->type() == QEvent::MetaCall ) {
// Wake up the receiver thread event loop
QThread* thread = receiver->contextThreadObject();
if (thread) {
if (thread->d) {
if (thread->d->eventLoop) {
thread->d->eventLoop->wakeUp();
}
}
}
return;
}
#endif
if (currentEventLoop())
currentEventLoop()->wakeUp();
} }
@ -3326,7 +3421,8 @@ void QApplication::sendPostedEvents( QObject *receiver, int event_type )
&& ( receiver == 0 // we send to all receivers && ( receiver == 0 // we send to all receivers
|| receiver == pe->receiver ) // we send to THAT receiver || receiver == pe->receiver ) // we send to THAT receiver
&& ( event_type == 0 // we send all types && ( event_type == 0 // we send all types
|| event_type == pe->event->type() ) ) { // we send THAT type || event_type == pe->event->type() ) // we send THAT type
&& ( (!pe->receiver) || (pe->receiver->contextThreadObject() == QThread::currentThreadObject()) ) ) { // only send if active thread is receiver object owning thread
// first, we diddle the event so that we can deliver // first, we diddle the event so that we can deliver
// it, and that noone will try to touch it later. // it, and that noone will try to touch it later.
pe->event->posted = FALSE; pe->event->posted = FALSE;

@ -63,6 +63,7 @@ class QWSDecoration;
#ifdef QT_THREAD_SUPPORT #ifdef QT_THREAD_SUPPORT
class QMutex; class QMutex;
class QThread;
#endif // QT_THREAD_SUPPORT #endif // QT_THREAD_SUPPORT
@ -369,7 +370,9 @@ private:
#ifndef QT_NO_CURSOR #ifndef QT_NO_CURSOR
static QCursor *app_cursor; static QCursor *app_cursor;
#endif #endif
#ifndef QT_THREAD_SUPPORT
static QEventLoop* eventloop; static QEventLoop* eventloop;
#endif
static int app_tracking; static int app_tracking;
static bool is_app_running; static bool is_app_running;
static bool is_app_closing; static bool is_app_closing;
@ -425,6 +428,7 @@ private:
static void removePostedEvent( QEvent * ); static void removePostedEvent( QEvent * );
static void removePostedEvents( QObject *receiver, int event_type ); static void removePostedEvents( QObject *receiver, int event_type );
friend class QObject;
friend class QWidget; friend class QWidget;
friend class QETWidget; friend class QETWidget;
friend class QDialog; friend class QDialog;
@ -444,6 +448,15 @@ private: // Disabled copy constructor and operator=
QApplication( const QApplication & ); QApplication( const QApplication & );
QApplication &operator=( const QApplication & ); QApplication &operator=( const QApplication & );
#endif #endif
private:
static QEventLoop* currentEventLoop();
public:
#ifdef QT_THREAD_SUPPORT
static QThread* guiThread();
#endif
static bool isGuiThread();
}; };
inline int QApplication::argc() const inline int QApplication::argc() const

@ -137,6 +137,8 @@ public:
HelpRequest = 95, // CE (?) button pressed HelpRequest = 95, // CE (?) button pressed
WindowStateChange = 96, // window state has changed WindowStateChange = 96, // window state has changed
IconDrag = 97, // proxy icon dragged IconDrag = 97, // proxy icon dragged
MetaCall = 98, // meta method call (internal)
ThreadChange = 99, // thread changed
User = 1000, // first user event id User = 1000, // first user event id
MaxUser = 65535 // last user event id MaxUser = 65535 // last user event id
}; };

@ -41,6 +41,11 @@
#include "qapplication.h" #include "qapplication.h"
#include "qdatetime.h" #include "qdatetime.h"
#ifdef QT_THREAD_SUPPORT
# include "qthread.h"
# include "qthreadinstance_p.h"
#endif
/*! /*!
\class QEventLoop \class QEventLoop
\brief The QEventLoop class manages the event queue. \brief The QEventLoop class manages the event queue.
@ -100,15 +105,27 @@ QEventLoop::QEventLoop( QObject *parent, const char *name )
: QObject( parent, name ) : QObject( parent, name )
{ {
#if defined(QT_CHECK_STATE) #if defined(QT_CHECK_STATE)
if ( QApplication::eventloop ) if ( QApplication::currentEventLoop() )
qFatal( "QEventLoop: there must be only one event loop object. \nConstruct it before QApplication." ); qFatal( "QEventLoop: there must be only one event loop object per thread. \nIf this is supposed to be the main GUI event loop, construct it before QApplication." );
// for now ;) if (!QThread::currentThreadObject()) {
qFatal( "QEventLoop: this object can only be used in threads constructed via QThread." );
}
#endif // QT_CHECK_STATE #endif // QT_CHECK_STATE
d = new QEventLoopPrivate; d = new QEventLoopPrivate;
init(); init();
#ifdef QT_THREAD_SUPPORT
QThread* thread = QThread::currentThreadObject();
if (thread) {
if (thread->d) {
thread->d->eventLoop = this;
}
}
#else
QApplication::eventloop = this; QApplication::eventloop = this;
#endif
} }
/*! /*!
@ -118,7 +135,16 @@ QEventLoop::~QEventLoop()
{ {
cleanup(); cleanup();
delete d; delete d;
#ifdef QT_THREAD_SUPPORT
QThread* thread = QThread::currentThreadObject();
if (thread) {
if (thread->d) {
thread->d->eventLoop = 0;
}
}
#else
QApplication::eventloop = 0; QApplication::eventloop = 0;
#endif
} }
/*! /*!

@ -40,6 +40,7 @@
#include "qeventloop.h" #include "qeventloop.h"
#include "qapplication.h" #include "qapplication.h"
#include "qbitarray.h" #include "qbitarray.h"
#include "qmutex.h"
#include <stdlib.h> #include <stdlib.h>
#include <sys/types.h> #include <sys/types.h>

@ -44,6 +44,7 @@
#include "qeventloop.h" #include "qeventloop.h"
#include "qapplication.h" #include "qapplication.h"
#include "qbitarray.h" #include "qbitarray.h"
#include "qmutex.h"
#include <stdlib.h> #include <stdlib.h>
#include <sys/types.h> #include <sys/types.h>
@ -578,17 +579,14 @@ int QEventLoop::activateSocketNotifiers()
while ( (sn=it.current()) ) { while ( (sn=it.current()) ) {
++it; ++it;
d->sn_pending_list.removeRef( sn ); d->sn_pending_list.removeRef( sn );
if ( sn->pending ) { if ( sn->pending ) {
#ifdef DEBUG_QT_GLIBMAINLOOP
#ifdef DEBUG_QT_GLIBMAINLOOP printf("activate sn : send event fd=%d\n", sn->gPollFD.fd );
printf("activate sn : send event fd=%d\n", sn->gPollFD.fd ); #endif
#endif sn->pending = FALSE;
QApplication::sendEvent( sn->obj, &event );
n_act++;
sn->pending = FALSE; }
QApplication::sendEvent( sn->obj, &event );
n_act++;
}
} }
return n_act; return n_act;

@ -146,55 +146,57 @@ bool QEventLoop::processEvents( ProcessEventsFlags flags )
if ( qt_is_gui_used ) { if ( qt_is_gui_used ) {
QApplication::sendPostedEvents(); QApplication::sendPostedEvents();
// Two loops so that posted events accumulate if (QApplication::isGuiThread()) {
while ( XPending( QPaintDevice::x11AppDisplay() ) ) { // Two loops so that posted events accumulate
// also flushes output buffer while ( XPending( QPaintDevice::x11AppDisplay() ) ) {
while ( XPending( QPaintDevice::x11AppDisplay() ) ) { // also flushes output buffer
if ( d->shortcut ) { while ( XPending( QPaintDevice::x11AppDisplay() ) ) {
return FALSE; if ( d->shortcut ) {
} return FALSE;
}
XNextEvent( QPaintDevice::x11AppDisplay(), &event );
XNextEvent( QPaintDevice::x11AppDisplay(), &event );
if ( flags & ExcludeUserInput ) {
switch ( event.type ) { if ( flags & ExcludeUserInput ) {
case ButtonPress: switch ( event.type ) {
case ButtonRelease: case ButtonPress:
case MotionNotify: case ButtonRelease:
case XKeyPress: case MotionNotify:
case XKeyRelease: case XKeyPress:
case EnterNotify: case XKeyRelease:
case LeaveNotify: case EnterNotify:
continue; case LeaveNotify:
continue;
case ClientMessage:
{ case ClientMessage:
// from qapplication_x11.cpp {
extern Atom qt_wm_protocols; // from qapplication_x11.cpp
extern Atom qt_wm_take_focus; extern Atom qt_wm_protocols;
extern Atom qt_qt_scrolldone; extern Atom qt_wm_take_focus;
extern Atom qt_qt_scrolldone;
// only keep the wm_take_focus and
// qt_qt_scrolldone protocols, discard all // only keep the wm_take_focus and
// other client messages // qt_qt_scrolldone protocols, discard all
if ( event.xclient.format != 32 ) // other client messages
continue; if ( event.xclient.format != 32 )
continue;
if ( event.xclient.message_type == qt_wm_protocols ||
(Atom) event.xclient.data.l[0] == qt_wm_take_focus ) if ( event.xclient.message_type == qt_wm_protocols ||
break; (Atom) event.xclient.data.l[0] == qt_wm_take_focus )
if ( event.xclient.message_type == qt_qt_scrolldone ) break;
break; if ( event.xclient.message_type == qt_qt_scrolldone )
break;
}
default: break;
}
}
nevents++;
if ( qApp->x11ProcessEvent( &event ) == 1 )
return TRUE;
} }
default: break;
}
} }
nevents++;
if ( qApp->x11ProcessEvent( &event ) == 1 )
return TRUE;
}
} }
} }
@ -261,7 +263,7 @@ bool QEventLoop::processEvents( ProcessEventsFlags flags )
FD_ZERO( &d->sn_vec[2].select_fds ); FD_ZERO( &d->sn_vec[2].select_fds );
} }
if ( qt_is_gui_used ) { if ( qt_is_gui_used && QApplication::isGuiThread() ) {
// select for events on the event socket - only on X11 // select for events on the event socket - only on X11
FD_SET( d->xfd, &d->sn_vec[0].select_fds ); FD_SET( d->xfd, &d->sn_vec[0].select_fds );
highest = QMAX( highest, d->xfd ); highest = QMAX( highest, d->xfd );

@ -39,7 +39,6 @@
** **
**********************************************************************/ **********************************************************************/
#include "qeventloop_glib_p.h" // includes qplatformdefs.h #include "qeventloop_glib_p.h" // includes qplatformdefs.h
#include "qeventloop.h" #include "qeventloop.h"
#include "qapplication.h" #include "qapplication.h"
@ -58,23 +57,21 @@
// Qt-GSource Structure and Callbacks // Qt-GSource Structure and Callbacks
typedef struct { typedef struct {
GSource source; GSource source;
QEventLoop * qeventLoop; QEventLoop * qeventLoop;
} QtGSource; } QtGSource;
static gboolean qt_gsource_prepare ( GSource *source, static gboolean qt_gsource_prepare ( GSource *source, gint *timeout );
gint *timeout );
static gboolean qt_gsource_check ( GSource *source ); static gboolean qt_gsource_check ( GSource *source );
static gboolean qt_gsource_dispatch ( GSource *source, static gboolean qt_gsource_dispatch ( GSource *source, GSourceFunc callback, gpointer user_data );
GSourceFunc callback, gpointer user_data );
static GSourceFuncs qt_gsource_funcs = { static GSourceFuncs qt_gsource_funcs = {
qt_gsource_prepare, qt_gsource_prepare,
qt_gsource_check, qt_gsource_check,
qt_gsource_dispatch, qt_gsource_dispatch,
NULL, NULL,
NULL, NULL,
NULL NULL
}; };
// forward main loop callbacks to QEventLoop methods! // forward main loop callbacks to QEventLoop methods!
@ -82,25 +79,25 @@ static GSourceFuncs qt_gsource_funcs = {
static gboolean qt_gsource_prepare ( GSource *source, static gboolean qt_gsource_prepare ( GSource *source,
gint *timeout ) gint *timeout )
{ {
QtGSource * qtGSource; QtGSource * qtGSource;
qtGSource = (QtGSource*) source; qtGSource = (QtGSource*) source;
return qtGSource->qeventLoop->gsourcePrepare(source, timeout); return qtGSource->qeventLoop->gsourcePrepare(source, timeout);
} }
static gboolean qt_gsource_check ( GSource *source ) static gboolean qt_gsource_check ( GSource *source )
{ {
QtGSource * qtGSource = (QtGSource*) source; QtGSource * qtGSource = (QtGSource*) source;
return qtGSource->qeventLoop->gsourceCheck(source); return qtGSource->qeventLoop->gsourceCheck(source);
} }
static gboolean qt_gsource_dispatch ( GSource *source, static gboolean qt_gsource_dispatch ( GSource *source,
GSourceFunc callback, gpointer user_data ) GSourceFunc callback, gpointer user_data )
{ {
Q_UNUSED(callback); Q_UNUSED(callback);
Q_UNUSED(user_data); Q_UNUSED(user_data);
QtGSource * qtGSource = (QtGSource*) source; QtGSource * qtGSource = (QtGSource*) source;
return qtGSource->qeventLoop->gsourceDispatch(source); return qtGSource->qeventLoop->gsourceDispatch(source);
} }
@ -134,82 +131,84 @@ static QVFuncList *qt_postselect_handler = 0;
void qt_install_preselect_handler( VFPTR handler ) void qt_install_preselect_handler( VFPTR handler )
{ {
if ( !qt_preselect_handler ) if ( !qt_preselect_handler ) {
qt_preselect_handler = new QVFuncList; qt_preselect_handler = new QVFuncList;
qt_preselect_handler->append( handler ); }
qt_preselect_handler->append( handler );
} }
void qt_remove_preselect_handler( VFPTR handler ) void qt_remove_preselect_handler( VFPTR handler )
{ {
if ( qt_preselect_handler ) { if ( qt_preselect_handler ) {
QVFuncList::Iterator it = qt_preselect_handler->find( handler ); QVFuncList::Iterator it = qt_preselect_handler->find( handler );
if ( it != qt_preselect_handler->end() ) if ( it != qt_preselect_handler->end() ) {
qt_preselect_handler->remove( it ); qt_preselect_handler->remove( it );
} }
}
} }
void qt_install_postselect_handler( VFPTR handler ) void qt_install_postselect_handler( VFPTR handler )
{ {
if ( !qt_postselect_handler ) if ( !qt_postselect_handler ) {
qt_postselect_handler = new QVFuncList; qt_postselect_handler = new QVFuncList;
qt_postselect_handler->prepend( handler ); }
qt_postselect_handler->prepend( handler );
} }
void qt_remove_postselect_handler( VFPTR handler ) void qt_remove_postselect_handler( VFPTR handler )
{ {
if ( qt_postselect_handler ) { if ( qt_postselect_handler ) {
QVFuncList::Iterator it = qt_postselect_handler->find( handler ); QVFuncList::Iterator it = qt_postselect_handler->find( handler );
if ( it != qt_postselect_handler->end() ) if ( it != qt_postselect_handler->end() ) {
qt_postselect_handler->remove( it ); qt_postselect_handler->remove( it );
} }
}
} }
void QEventLoop::init() void QEventLoop::init()
{ {
// initialize ProcessEventFlags (all events & wait for more) // initialize ProcessEventFlags (all events & wait for more)
d->pev_flags = AllEvents | WaitForMore; d->pev_flags = AllEvents | WaitForMore;
// initialize the common parts of the event loop // initialize the common parts of the event loop
if (pipe( d->thread_pipe ) < 0) { if (pipe( d->thread_pipe ) < 0) {
// Error! // Error!
} }
fcntl(d->thread_pipe[0], F_SETFD, FD_CLOEXEC); fcntl(d->thread_pipe[0], F_SETFD, FD_CLOEXEC);
fcntl(d->thread_pipe[1], F_SETFD, FD_CLOEXEC); fcntl(d->thread_pipe[1], F_SETFD, FD_CLOEXEC);
// intitialize the X11 parts of the event loop // intitialize the X11 parts of the event loop
d->xfd = -1; d->xfd = -1;
if ( qt_is_gui_used ) if ( qt_is_gui_used && QApplication::isGuiThread() ) {
d->xfd = XConnectionNumber( QPaintDevice::x11AppDisplay() ); d->xfd = XConnectionNumber( QPaintDevice::x11AppDisplay() );
}
// new GSource // new GSource
QtGSource * qtGSource = (QtGSource*) g_source_new(&qt_gsource_funcs, sizeof(QtGSource));
QtGSource * qtGSource = (QtGSource*) g_source_new(&qt_gsource_funcs, g_source_set_can_recurse ((GSource*)qtGSource, TRUE);
sizeof(QtGSource));
qtGSource->qeventLoop = this;
g_source_set_can_recurse ((GSource*)qtGSource, TRUE);
qtGSource->qeventLoop = this;
// init main loop and attach gsource // init main loop and attach gsource
#ifdef DEBUG_QT_GLIBMAINLOOP #ifdef DEBUG_QT_GLIBMAINLOOP
printf("inside init(1)\n"); printf("inside init(1)\n");
#endif #endif
g_main_loop_new (NULL, 1); g_main_loop_new (NULL, 1);
g_source_attach( (GSource*)qtGSource, NULL ); g_source_attach( (GSource*)qtGSource, NULL );
d->gSource = (GSource*) qtGSource; d->gSource = (GSource*) qtGSource;
// poll for X11 events // poll for X11 events
if ( qt_is_gui_used ) { if ( qt_is_gui_used && QApplication::isGuiThread() ) {
d->x_gPollFD.fd = d->xfd; d->x_gPollFD.fd = d->xfd;
d->x_gPollFD.events = G_IO_IN | G_IO_HUP; d->x_gPollFD.events = G_IO_IN | G_IO_HUP;
g_source_add_poll(d->gSource, &d->x_gPollFD); g_source_add_poll(d->gSource, &d->x_gPollFD);
} }
// poll thread-pipe // poll thread-pipe
@ -218,21 +217,21 @@ void QEventLoop::init()
g_source_add_poll(d->gSource, &d->threadPipe_gPollFD); g_source_add_poll(d->gSource, &d->threadPipe_gPollFD);
#ifdef DEBUG_QT_GLIBMAINLOOP #ifdef DEBUG_QT_GLIBMAINLOOP
printf("inside init(2)\n"); printf("inside init(2)\n");
#endif #endif
} }
void QEventLoop::cleanup() void QEventLoop::cleanup()
{ {
// cleanup the common parts of the event loop // cleanup the common parts of the event loop
close( d->thread_pipe[0] ); close( d->thread_pipe[0] );
close( d->thread_pipe[1] ); close( d->thread_pipe[1] );
cleanupTimers(); cleanupTimers();
// cleanup the X11 parts of the event loop // cleanup the X11 parts of the event loop
d->xfd = -1; d->xfd = -1;
// todo: destroy gsource // todo: destroy gsource
} }
@ -240,186 +239,189 @@ void QEventLoop::cleanup()
bool QEventLoop::processEvents( ProcessEventsFlags flags ) bool QEventLoop::processEvents( ProcessEventsFlags flags )
{ {
#ifdef DEBUG_QT_GLIBMAINLOOP #ifdef DEBUG_QT_GLIBMAINLOOP
printf("inside processEvents(1) looplevel=%d\n", d->looplevel ); printf("inside processEvents(1) looplevel=%d\n", d->looplevel );
#endif #endif
ProcessEventsFlags save_flags;
int rval;
save_flags = d->pev_flags;
d->pev_flags = flags; ProcessEventsFlags save_flags;
int rval;
rval = g_main_context_iteration(NULL, flags & WaitForMore ? TRUE : FALSE); save_flags = d->pev_flags;
d->pev_flags = save_flags;
#ifdef DEBUG_QT_GLIBMAINLOOP d->pev_flags = flags;
printf("inside processEvents(2) looplevel=%d rval=%d\n", d->looplevel, rval );
#endif
return rval; // were events processed? rval = g_main_context_iteration(NULL, flags & WaitForMore ? TRUE : FALSE);
d->pev_flags = save_flags;
#ifdef DEBUG_QT_GLIBMAINLOOP
printf("inside processEvents(2) looplevel=%d rval=%d\n", d->looplevel, rval );
#endif
return rval; // were events processed?
} }
bool QEventLoop::processX11Events() bool QEventLoop::processX11Events()
{ {
ProcessEventsFlags flags = d->pev_flags; ProcessEventsFlags flags = d->pev_flags;
// process events from the X server // process events from the X server
XEvent event; XEvent event;
int nevents = 0; int nevents = 0;
#if defined(QT_THREAD_SUPPORT) #if defined(QT_THREAD_SUPPORT)
QMutexLocker locker( QApplication::qt_mutex ); QMutexLocker locker( QApplication::qt_mutex );
#endif #endif
// handle gui and posted events // handle gui and posted events
if ( qt_is_gui_used ) { if ( qt_is_gui_used ) {
QApplication::sendPostedEvents(); QApplication::sendPostedEvents();
// Two loops so that posted events accumulate if (QApplication::isGuiThread()) {
while ( XPending( QPaintDevice::x11AppDisplay() ) ) { // Two loops so that posted events accumulate
// also flushes output buffer while ( XPending( QPaintDevice::x11AppDisplay() ) ) {
while ( XPending( QPaintDevice::x11AppDisplay() ) ) { // also flushes output buffer
if ( d->shortcut ) { while ( XPending( QPaintDevice::x11AppDisplay() ) ) {
return FALSE; if ( d->shortcut ) {
} return FALSE;
}
XNextEvent( QPaintDevice::x11AppDisplay(), &event );
XNextEvent( QPaintDevice::x11AppDisplay(), &event );
if ( flags & ExcludeUserInput ) {
switch ( event.type ) { if ( flags & ExcludeUserInput ) {
case ButtonPress: switch ( event.type ) {
case ButtonRelease: case ButtonPress:
case MotionNotify: case ButtonRelease:
case XKeyPress: case MotionNotify:
case XKeyRelease: case XKeyPress:
case EnterNotify: case XKeyRelease:
case LeaveNotify: case EnterNotify:
continue; case LeaveNotify:
continue;
case ClientMessage:
{ case ClientMessage:
// from qapplication_x11.cpp {
extern Atom qt_wm_protocols; // from qapplication_x11.cpp
extern Atom qt_wm_take_focus; extern Atom qt_wm_protocols;
extern Atom qt_qt_scrolldone; extern Atom qt_wm_take_focus;
extern Atom qt_qt_scrolldone;
// only keep the wm_take_focus and
// qt_qt_scrolldone protocols, discard all // only keep the wm_take_focus and
// other client messages // qt_qt_scrolldone protocols, discard all
if ( event.xclient.format != 32 ) // other client messages
continue; if ( event.xclient.format != 32 )
continue;
if ( event.xclient.message_type == qt_wm_protocols ||
(Atom) event.xclient.data.l[0] == qt_wm_take_focus ) if ( event.xclient.message_type == qt_wm_protocols ||
break; (Atom) event.xclient.data.l[0] == qt_wm_take_focus )
if ( event.xclient.message_type == qt_qt_scrolldone ) break;
break; if ( event.xclient.message_type == qt_qt_scrolldone )
break;
}
default: break;
}
}
nevents++;
if ( qApp->x11ProcessEvent( &event ) == 1 )
return TRUE;
}
} }
default: break;
}
} }
nevents++;
if ( qApp->x11ProcessEvent( &event ) == 1 )
return TRUE;
}
} }
}
if ( d->shortcut ) {
if ( d->shortcut ) { return FALSE;
}
QApplication::sendPostedEvents();
const uint exclude_all = ExcludeSocketNotifiers | 0x08;
// 0x08 == ExcludeTimers for X11 only
if ( nevents > 0 && ( flags & exclude_all ) == exclude_all && ( flags & WaitForMore ) ) {
return TRUE;
}
return FALSE; return FALSE;
}
QApplication::sendPostedEvents();
const uint exclude_all = ExcludeSocketNotifiers | 0x08;
// 0x08 == ExcludeTimers for X11 only
if ( nevents > 0 && ( flags & exclude_all ) == exclude_all &&
( flags & WaitForMore ) ) {
return TRUE;
}
return FALSE;
} }
bool QEventLoop::gsourcePrepare(GSource *gs, int * timeout) bool QEventLoop::gsourcePrepare(GSource *gs, int * timeout)
{ {
Q_UNUSED(gs); Q_UNUSED(gs);
#ifdef DEBUG_QT_GLIBMAINLOOP #ifdef DEBUG_QT_GLIBMAINLOOP
printf("inside gsourcePrepare(1)\n"); printf("inside gsourcePrepare(1)\n");
#endif #endif
ProcessEventsFlags flags = d->pev_flags; ProcessEventsFlags flags = d->pev_flags;
#if defined(QT_THREAD_SUPPORT) #if defined(QT_THREAD_SUPPORT)
QMutexLocker locker( QApplication::qt_mutex ); QMutexLocker locker( QApplication::qt_mutex );
#endif #endif
// don't block if exitLoop() or exit()/quit() has been called. // don't block if exitLoop() or exit()/quit() has been called.
bool canWait = d->exitloop || d->quitnow ? FALSE : (flags & WaitForMore); bool canWait = d->exitloop || d->quitnow ? FALSE : (flags & WaitForMore);
// Process timers and socket notifiers - the common UNIX stuff // Process timers and socket notifiers - the common UNIX stuff
// return the maximum time we can wait for an event. // return the maximum time we can wait for an event.
static timeval zerotm; static timeval zerotm;
timeval *tm = 0; timeval *tm = 0;
if ( ! ( flags & 0x08 ) ) { // 0x08 == ExcludeTimers for X11 only if ( ! ( flags & 0x08 ) ) { // 0x08 == ExcludeTimers for X11 only
tm = qt_wait_timer(); // wait for timer or X event tm = qt_wait_timer(); // wait for timer or X event
if ( !canWait ) { if ( !canWait ) {
if ( !tm ) if ( !tm ) {
tm = &zerotm; tm = &zerotm;
tm->tv_sec = 0; // no time to wait }
tm->tv_usec = 0; tm->tv_sec = 0; // no time to wait
} tm->tv_usec = 0;
}
// include or exclude SocketNotifiers (by setting or cleaning poll events)
if ( ! ( flags & ExcludeSocketNotifiers ) ) {
QPtrListIterator<QSockNotGPollFD> it( d->sn_list );
QSockNotGPollFD *sn;
while ( (sn=it.current()) ) {
++it;
sn->gPollFD.events = sn->events; // restore poll events
} }
} else { }
QPtrListIterator<QSockNotGPollFD> it( d->sn_list );
QSockNotGPollFD *sn; // include or exclude SocketNotifiers (by setting or cleaning poll events)
while ( (sn=it.current()) ) { if ( ! ( flags & ExcludeSocketNotifiers ) ) {
++it; QPtrListIterator<QSockNotGPollFD> it( d->sn_list );
sn->gPollFD.events = 0; // delete poll events QSockNotGPollFD *sn;
while ( (sn=it.current()) ) {
++it;
sn->gPollFD.events = sn->events; // restore poll events
}
}
else {
QPtrListIterator<QSockNotGPollFD> it( d->sn_list );
QSockNotGPollFD *sn;
while ( (sn=it.current()) ) {
++it;
sn->gPollFD.events = 0; // delete poll events
} }
} }
#ifdef DEBUG_QT_GLIBMAINLOOP #ifdef DEBUG_QT_GLIBMAINLOOP
printf("inside gsourcePrepare(2) canwait=%d\n", canWait); printf("inside gsourcePrepare(2) canwait=%d\n", canWait);
#endif #endif
if ( canWait )
emit aboutToBlock();
if ( qt_preselect_handler ) { if ( canWait ) {
QVFuncList::Iterator it, end = qt_preselect_handler->end(); emit aboutToBlock();
for ( it = qt_preselect_handler->begin(); it != end; ++it ) }
(**it)();
}
// unlock the GUI mutex and select. when we return from this function, there is if ( qt_preselect_handler ) {
// something for us to do QVFuncList::Iterator it, end = qt_preselect_handler->end();
for ( it = qt_preselect_handler->begin(); it != end; ++it )
(**it)();
}
// unlock the GUI mutex and select. when we return from this function, there is
// something for us to do
#if defined(QT_THREAD_SUPPORT) #if defined(QT_THREAD_SUPPORT)
locker.mutex()->unlock(); locker.mutex()->unlock();
#endif #endif
#ifdef DEBUG_QT_GLIBMAINLOOP #ifdef DEBUG_QT_GLIBMAINLOOP
printf("inside gsourcePrepare(2.1) canwait=%d\n", canWait); printf("inside gsourcePrepare(2.1) canwait=%d\n", canWait);
#endif #endif
// do we have to dispatch events? // do we have to dispatch events?
if (hasPendingEvents()) { if (hasPendingEvents()) {
*timeout = 0; // no time to stay in poll *timeout = 0; // no time to stay in poll
#ifdef DEBUG_QT_GLIBMAINLOOP #ifdef DEBUG_QT_GLIBMAINLOOP
@ -427,8 +429,8 @@ bool QEventLoop::gsourcePrepare(GSource *gs, int * timeout)
#endif #endif
return FALSE; return FALSE;
} }
// stay in poll until something happens? // stay in poll until something happens?
if (!tm) { // fixme if (!tm) { // fixme
*timeout = -1; // wait forever *timeout = -1; // wait forever
@ -439,25 +441,24 @@ bool QEventLoop::gsourcePrepare(GSource *gs, int * timeout)
return FALSE; return FALSE;
} }
// else timeout >=0 // else timeout >=0
*timeout = tm->tv_sec * 1000 + tm->tv_usec/1000; *timeout = tm->tv_sec * 1000 + tm->tv_usec/1000;
#ifdef DEBUG_QT_GLIBMAINLOOP #ifdef DEBUG_QT_GLIBMAINLOOP
printf("inside gsourcePrepare(3c) timeout=%d \n", *timeout); printf("inside gsourcePrepare(3c) timeout=%d \n", *timeout);
#endif #endif
return FALSE; return FALSE;
} }
bool QEventLoop::gsourceCheck(GSource *gs) { bool QEventLoop::gsourceCheck(GSource *gs) {
Q_UNUSED(gs); Q_UNUSED(gs);
#ifdef DEBUG_QT_GLIBMAINLOOP #ifdef DEBUG_QT_GLIBMAINLOOP
printf("inside gsourceCheck(1)\n"); printf("inside gsourceCheck(1)\n");
#endif #endif
// Socketnotifier events? // Socketnotifier events?
@ -476,45 +477,45 @@ bool QEventLoop::gsourceCheck(GSource *gs) {
//} //}
if (d->x_gPollFD.revents) { if (d->x_gPollFD.revents) {
#ifdef DEBUG_QT_GLIBMAINLOOP #ifdef DEBUG_QT_GLIBMAINLOOP
printf("inside gsourceCheck(2) xfd!\n"); printf("inside gsourceCheck(2) xfd!\n");
#endif #endif
return TRUE; // we got events! return TRUE; // we got events!
} }
if (d->threadPipe_gPollFD.revents) { if (d->threadPipe_gPollFD.revents) {
#ifdef DEBUG_QT_GLIBMAINLOOP #ifdef DEBUG_QT_GLIBMAINLOOP
printf("inside gsourceCheck(2) threadpipe!!\n"); printf("inside gsourceCheck(2) threadpipe!!\n");
#endif #endif
return TRUE; // we got events! return TRUE; // we got events!
} }
if (hasPendingEvents()) { if (hasPendingEvents()) {
#ifdef DEBUG_QT_GLIBMAINLOOP #ifdef DEBUG_QT_GLIBMAINLOOP
printf("inside gsourceCheck(2) pendingEvents!\n"); printf("inside gsourceCheck(2) pendingEvents!\n");
#endif #endif
return TRUE; // we got more X11 events! return TRUE; // we got more X11 events!
} }
// check if we have timers to activate? // check if we have timers to activate?
timeval * tm =qt_wait_timer(); timeval * tm =qt_wait_timer();
if (tm && (tm->tv_sec == 0 && tm->tv_usec == 0 )) { if (tm && (tm->tv_sec == 0 && tm->tv_usec == 0 )) {
#ifdef DEBUG_QT_GLIBMAINLOOP #ifdef DEBUG_QT_GLIBMAINLOOP
printf("inside gsourceCheck(2) qtwaittimer!\n"); printf("inside gsourceCheck(2) qtwaittimer!\n");
#endif #endif
return TRUE; return TRUE;
} }
// nothing to dispatch // nothing to dispatch
#ifdef DEBUG_QT_GLIBMAINLOOP #ifdef DEBUG_QT_GLIBMAINLOOP
printf("inside gsourceCheck(2) nothing to dispatch!\n"); printf("inside gsourceCheck(2) nothing to dispatch!\n");
#endif #endif
return FALSE; return FALSE;
} }
@ -533,37 +534,35 @@ bool QEventLoop::gsourceDispatch(GSource *gs) {
ProcessEventsFlags flags = d->pev_flags; ProcessEventsFlags flags = d->pev_flags;
#ifdef DEBUG_QT_GLIBMAINLOOP #ifdef DEBUG_QT_GLIBMAINLOOP
printf("inside gsourceDispatch(1)\n"); printf("inside gsourceDispatch(1)\n");
#endif #endif
// we are awake, broadcast it
emit awake();
emit qApp->guiThreadAwake();
// some other thread woke us up... consume the data on the thread pipe so that
// select doesn't immediately return next time
// we are awake, broadcast it
emit awake();
emit qApp->guiThreadAwake();
// some other thread woke us up... consume the data on the thread pipe so that
// select doesn't immediately return next time
if ( d->threadPipe_gPollFD.revents) { if ( d->threadPipe_gPollFD.revents) {
char c; char c;
if (::read( d->thread_pipe[0], &c, 1 ) < 0) { if (::read( d->thread_pipe[0], &c, 1 ) < 0) {
// Error! // Error!
} }
} }
if ( qt_postselect_handler ) { if ( qt_postselect_handler ) {
QVFuncList::Iterator it, end = qt_postselect_handler->end(); QVFuncList::Iterator it, end = qt_postselect_handler->end();
for ( it = qt_postselect_handler->begin(); it != end; ++it ) for ( it = qt_postselect_handler->begin(); it != end; ++it )
(**it)(); (**it)();
} }
// activate socket notifiers // activate socket notifiers
if ( ! ( flags & ExcludeSocketNotifiers )) { if ( ! ( flags & ExcludeSocketNotifiers )) {
// if select says data is ready on any socket, then set the socket notifier // if select says data is ready on any socket, then set the socket notifier
// to pending // to pending
// if ( &d->sn_list ) { // if ( &d->sn_list ) {
QPtrList<QSockNotGPollFD> *list = &d->sn_list; QPtrList<QSockNotGPollFD> *list = &d->sn_list;
QSockNotGPollFD *sn = list->first(); QSockNotGPollFD *sn = list->first();
while ( sn ) { while ( sn ) {
@ -572,39 +571,35 @@ bool QEventLoop::gsourceDispatch(GSource *gs) {
sn = list->next(); sn = list->next();
} }
// } // }
nevents += activateSocketNotifiers(); nevents += activateSocketNotifiers();
} }
// activate timers // activate timers
if ( ! ( flags & 0x08 ) ) { if ( ! ( flags & 0x08 ) ) {
// 0x08 == ExcludeTimers for X11 only // 0x08 == ExcludeTimers for X11 only
nevents += activateTimers(); nevents += activateTimers();
} }
// return true if we handled events, false otherwise // return true if we handled events, false otherwise
//return (nevents > 0); //return (nevents > 0);
// now process x11 events! // now process x11 events!
#ifdef DEBUG_QT_GLIBMAINLOOP #ifdef DEBUG_QT_GLIBMAINLOOP
printf("inside gsourceDispatch(2) hasPendingEvents=%d\n", hasPendingEvents()); printf("inside gsourceDispatch(2) hasPendingEvents=%d\n", hasPendingEvents());
#endif #endif
if (hasPendingEvents()) { if (hasPendingEvents()) {
// color approx. optimization - only on X11 // color approx. optimization - only on X11
qt_reset_color_avail(); qt_reset_color_avail();
processX11Events(); processX11Events();
} }
#if defined(QT_THREAD_SUPPORT) #if defined(QT_THREAD_SUPPORT)
locker.mutex()->unlock(); locker.mutex()->unlock();
#endif #endif
if (d->singletoolkit) { if (d->singletoolkit) {
@ -617,21 +612,22 @@ bool QEventLoop::gsourceDispatch(GSource *gs) {
bool QEventLoop::hasPendingEvents() const bool QEventLoop::hasPendingEvents() const
{ {
extern uint qGlobalPostedEventsCount(); // from qapplication.cpp extern uint qGlobalPostedEventsCount(); // from qapplication.cpp
return ( qGlobalPostedEventsCount() || ( qt_is_gui_used ? XPending( QPaintDevice::x11AppDisplay() ) : 0)); return ( qGlobalPostedEventsCount() || ( (qt_is_gui_used && QApplication::isGuiThread()) ? XPending( QPaintDevice::x11AppDisplay() ) : 0));
} }
void QEventLoop::appStartingUp() void QEventLoop::appStartingUp()
{ {
if ( qt_is_gui_used ) if ( qt_is_gui_used ) {
d->xfd = XConnectionNumber( QPaintDevice::x11AppDisplay() ); d->xfd = XConnectionNumber( QPaintDevice::x11AppDisplay() );
}
} }
void QEventLoop::appClosingDown() void QEventLoop::appClosingDown()
{ {
d->xfd = -1; d->xfd = -1;
} }
void QEventLoop::setSingleToolkitEventHandling(bool enabled) { void QEventLoop::setSingleToolkitEventHandling(bool enabled) {
d->singletoolkit = enabled; d->singletoolkit = enabled;
} }

@ -50,22 +50,107 @@
#include "qptrvector.h" #include "qptrvector.h"
#ifdef QT_THREAD_SUPPORT #ifdef QT_THREAD_SUPPORT
#include <qmutex.h> #include "qmutex.h"
#include <private/qmutexpool_p.h> #include <private/qmutexpool_p.h>
#include "qthread.h"
#endif #endif
#include <ctype.h> #include <ctype.h>
#include <stdlib.h>
#ifndef QT_NO_USERDATA #ifndef QT_NO_USERDATA
class QObjectPrivate : public QPtrVector<QObjectUserData> class QObjectPrivate : public QPtrVector<QObjectUserData>
#else
class QObjectPrivate {
#endif
{ {
public: public:
#ifndef QT_NO_USERDATA
QObjectPrivate( uint s ) : QPtrVector<QObjectUserData>(s){ setAutoDelete( TRUE ); } QObjectPrivate( uint s ) : QPtrVector<QObjectUserData>(s){ setAutoDelete( TRUE ); }
#endif
QThread* ownThread;
}; };
#else
class QObjectPrivate { #if defined(QT_THREAD_SUPPORT)
void QObject::moveToThread_helper(QThread *targetThread)
{
QEvent e(QEvent::ThreadChange);
QApplication::sendEvent(this, &e);
if (childObjects) {
QObject *child;
QObjectListIt it(*childObjects);
while ( (child=it.current()) ) {
++it;
child->moveToThread_helper(targetThread);
}
}
}
void QObject::setThreadObject_helper(QThread *targetThread)
{
d->ownThread = targetThread;
if (childObjects) {
QObject *child;
QObjectListIt it(*childObjects);
while ( (child=it.current()) ) {
++it;
child->moveToThread_helper(targetThread);
}
}
}
/*!
Changes the thread affinity for this object and its children. The
object cannot be moved if it has a parent. Event processing will
continue in the \a targetThread. To move an object to the main
thread, pass QApplication::guiThread() as the \a targetThread.
Note that all active timers for the object will be reset. The
timers are first stopped in the current thread and restarted (with
the same interval) in the \a targetThread. As a result, constantly
moving an object between threads can postpone timer events
indefinitely.
\sa contextThreadObject()
*/
void QObject::moveToThread(QThread *targetThread)
{
QMutexLocker locker( QApplication::qt_mutex );
if (parentObj) {
#if defined(QT_DEBUG)
qWarning( "QObject::moveToThread: Cannot move objects with a parent" );
#endif
return;
}
if (isWidget) {
#if defined(QT_DEBUG)
qWarning( "QObject::moveToThread: Widgets cannot be moved to a new thread" );
#endif
return;
}
QThread *objectThread = contextThreadObject();
QThread *currentThread = QThread::currentThreadObject();
if (objectThread != currentThread) {
#if defined(QT_DEBUG)
qWarning( "QObject::moveToThread: Current thread is not the object's thread" );
#endif
return;
}
if (objectThread == targetThread) {
return;
}
moveToThread_helper(targetThread);
setThreadObject_helper(targetThread);
} }
#endif #endif
class QSenderObjectList : public QObjectList, public QShared class QSenderObjectList : public QObjectList, public QShared
@ -75,6 +160,41 @@ public:
QObject *currentSender; QObject *currentSender;
}; };
class Q_EXPORT QMetaCallEvent : public QEvent
{
public:
enum MetaCallType {
MetaCallEmit = 0,
MetaCallInvoke = 1
};
public:
QMetaCallEvent(int id, QObject *sender, QUObject *data, MetaCallType type);
~QMetaCallEvent();
inline int id() const { return id_; }
inline QObject *sender() const { return sender_; }
inline QUObject *data() const { return data_; }
inline MetaCallType type() const { return type_; }
private:
const int id_;
QObject *sender_;
QUObject *data_;
const MetaCallType type_;
};
/*! \internal
*/
QMetaCallEvent::QMetaCallEvent(int id, QObject *sender, QUObject *data, MetaCallType type)
:QEvent(MetaCall), id_(id), sender_(sender), data_(data), type_(type)
{ }
/*! \internal
*/
QMetaCallEvent::~QMetaCallEvent()
{ }
/*! /*!
\class Qt qnamespace.h \class Qt qnamespace.h
@ -269,7 +389,21 @@ void *qt_find_obj_child( QObject *parent, const char *type, const char *name )
return 0; return 0;
} }
#ifdef QT_THREAD_SUPPORT
/*!
Returns a pointer to the QThread* associated with
the current thread affinity of this object.
\sa moveToThread()
*/
QThread* QObject::contextThreadObject() const
{
return d->ownThread;
}
#endif
#ifndef QT_NO_PRELIMINARY_SIGNAL_SPY #ifndef QT_NO_PRELIMINARY_SIGNAL_SPY
/* /*
@ -436,6 +570,11 @@ QObject::QObject( QObject *parent, const char *name )
insert_tree( this ); insert_tree( this );
isTree = TRUE; isTree = TRUE;
} }
if ( !d )
d = new QObjectPrivate(0);
d->ownThread = QThread::currentThreadObject();
} }
@ -720,6 +859,36 @@ QObject* QObject::child( const char *objName, const char *inheritsClass,
return obj; return obj;
} }
/*! \internal */
QUObject* deepCopyQUObjectArray(QUObject* origArray)
{
QUObject* newArray;
int count = 0;
while (!((origArray+count)->isLastObject)) {
count++;
}
count++;
newArray = (QUObject*)malloc(sizeof(QUObject)*count);
for (int i=0; i<count; i++) {
(origArray+i)->deepCopy(newArray+i);
}
return newArray;
}
/*! \internal */
void destroyDeepCopiedQUObjectArray(QUObject* uArray)
{
int count = 0;
while (!((uArray+count)->isLastObject)) {
count++;
}
count++;
for (int i=0; i<count; i++) {
(uArray+i)->~QUObject();
}
free(uArray);
}
/*! /*!
\fn bool QObject::isWidgetType() const \fn bool QObject::isWidgetType() const
@ -777,6 +946,40 @@ bool QObject::event( QEvent *e )
delete this; delete this;
return TRUE; return TRUE;
case QEvent::MetaCall:
{
QMetaCallEvent* metaEvent = dynamic_cast<QMetaCallEvent*>(e);
if (metaEvent) {
if (d->ownThread == QThread::currentThreadObject()) {
QSenderObjectList* sol;
QObject* oldSender = 0;
sol = senderObjects;
if ( sol ) {
oldSender = sol->currentSender;
sol->ref();
sol->currentSender = metaEvent->sender();
}
QUObject *o = metaEvent->data();
if (metaEvent->type() == QMetaCallEvent::MetaCallEmit) {
qt_emit( metaEvent->id(), o );
}
if (metaEvent->type() == QMetaCallEvent::MetaCallInvoke) {
qt_invoke( metaEvent->id(), o );
}
if (sol ) {
sol->currentSender = oldSender;
if ( sol->deref() ) {
delete sol;
}
}
}
else {
qWarning("QObject: Ignoring metacall event from non-owning thread");
}
destroyDeepCopiedQUObjectArray(metaEvent->data());
}
}
default: default:
if ( e->type() >= QEvent::User ) { if ( e->type() >= QEvent::User ) {
customEvent( (QCustomEvent*) e ); customEvent( (QCustomEvent*) e );
@ -2337,6 +2540,7 @@ void QObject::activate_signal( int signal )
if ( !signalsBlocked() && signal >= 0 && if ( !signalsBlocked() && signal >= 0 &&
( !connections || !connections->at( signal ) ) ) { ( !connections || !connections->at( signal ) ) ) {
QUObject o[1]; QUObject o[1];
o[0].isLastObject = true;
qt_spy_signal( this, signal, o ); qt_spy_signal( this, signal, o );
return; return;
} }
@ -2349,6 +2553,7 @@ void QObject::activate_signal( int signal )
if ( !clist ) if ( !clist )
return; return;
QUObject o[1]; QUObject o[1];
o[0].isLastObject = true;
activate_signal( clist, o ); activate_signal( clist, o );
} }
@ -2364,6 +2569,8 @@ void QObject::activate_signal( QConnectionList *clist, QUObject *o )
qt_spy_signal( this, connections->findRef( clist), o ); qt_spy_signal( this, connections->findRef( clist), o );
#endif #endif
const QThread *currentThread = QThread::currentThreadObject();
QObject *object; QObject *object;
QSenderObjectList* sol; QSenderObjectList* sol;
QObject* oldSender = 0; QObject* oldSender = 0;
@ -2377,10 +2584,26 @@ void QObject::activate_signal( QConnectionList *clist, QUObject *o )
sol->ref(); sol->ref();
sol->currentSender = this; sol->currentSender = this;
} }
if ( c->memberType() == QSIGNAL_CODE ) if ( c->memberType() == QSIGNAL_CODE ) {
object->qt_emit( c->member(), o ); if (object->d->ownThread == currentThread) {
else object->qt_emit( c->member(), o );
object->qt_invoke( c->member(), o ); }
else {
if (object->d->ownThread && !object->d->ownThread->finished()) {
QApplication::postEvent(object, new QMetaCallEvent(c->member(), this, deepCopyQUObjectArray(o), QMetaCallEvent::MetaCallEmit));
}
}
}
else {
if (object->d->ownThread == currentThread) {
object->qt_invoke( c->member(), o );
}
else {
if (object->d->ownThread && !object->d->ownThread->finished()) {
QApplication::postEvent(object, new QMetaCallEvent(c->member(), this, deepCopyQUObjectArray(o), QMetaCallEvent::MetaCallInvoke));
}
}
}
if ( sol ) { if ( sol ) {
sol->currentSender = oldSender; sol->currentSender = oldSender;
if ( sol->deref() ) if ( sol->deref() )
@ -2401,10 +2624,26 @@ void QObject::activate_signal( QConnectionList *clist, QUObject *o )
sol->ref(); sol->ref();
sol->currentSender = this; sol->currentSender = this;
} }
if ( c->memberType() == QSIGNAL_CODE ) if ( c->memberType() == QSIGNAL_CODE ) {
object->qt_emit( c->member(), o ); if (object->d->ownThread == currentThread) {
else object->qt_emit( c->member(), o );
object->qt_invoke( c->member(), o ); }
else {
if (object->d->ownThread && !object->d->ownThread->finished()) {
QApplication::postEvent(object, new QMetaCallEvent(c->member(), this, deepCopyQUObjectArray(o), QMetaCallEvent::MetaCallEmit));
}
}
}
else {
if (object->d->ownThread == currentThread) {
object->qt_invoke( c->member(), o );
}
else {
if (object->d->ownThread && !object->d->ownThread->finished()) {
QApplication::postEvent(object, new QMetaCallEvent(c->member(), this, deepCopyQUObjectArray(o), QMetaCallEvent::MetaCallInvoke));
}
}
}
if (sol ) { if (sol ) {
sol->currentSender = oldSender; sol->currentSender = oldSender;
if ( sol->deref() ) if ( sol->deref() )
@ -2435,39 +2674,42 @@ void QObject::activate_signal( QConnectionList *clist, QUObject *o )
*/ */
#ifndef QT_NO_PRELIMINARY_SIGNAL_SPY #ifndef QT_NO_PRELIMINARY_SIGNAL_SPY
#define ACTIVATE_SIGNAL_WITH_PARAM(FNAME,TYPE) \ #define ACTIVATE_SIGNAL_WITH_PARAM(FNAME,TYPE) \
void QObject::FNAME( int signal, TYPE param ) \ void QObject::FNAME( int signal, TYPE param ) \
{ \ { \
if ( qt_preliminary_signal_spy ) { \ if ( qt_preliminary_signal_spy ) { \
if ( !signalsBlocked() && signal >= 0 && \ if ( !signalsBlocked() && signal >= 0 && \
( !connections || !connections->at( signal ) ) ) { \ ( !connections || !connections->at( signal ) ) ) { \
QUObject o[2]; \ QUObject o[2]; \
static_QUType_##TYPE.set( o+1, param ); \ o[1].isLastObject = true; \
qt_spy_signal( this, signal, o ); \ static_QUType_##TYPE.set( o+1, param ); \
return; \ qt_spy_signal( this, signal, o ); \
} \ return; \
} \ } \
if ( !connections || signalsBlocked() || signal < 0 ) \ } \
return; \ if ( !connections || signalsBlocked() || signal < 0 ) \
QConnectionList *clist = connections->at( signal ); \ return; \
if ( !clist ) \ QConnectionList *clist = connections->at( signal ); \
return; \ if ( !clist ) \
QUObject o[2]; \ return; \
static_QUType_##TYPE.set( o+1, param ); \ QUObject o[2]; \
activate_signal( clist, o ); \ o[1].isLastObject = true; \
static_QUType_##TYPE.set( o+1, param ); \
activate_signal( clist, o ); \
} }
#else #else
#define ACTIVATE_SIGNAL_WITH_PARAM(FNAME,TYPE) \ #define ACTIVATE_SIGNAL_WITH_PARAM(FNAME,TYPE) \
void QObject::FNAME( int signal, TYPE param ) \ void QObject::FNAME( int signal, TYPE param ) \
{ \ { \
if ( !connections || signalsBlocked() || signal < 0 ) \ if ( !connections || signalsBlocked() || signal < 0 ) \
return; \ return; \
QConnectionList *clist = connections->at( signal ); \ QConnectionList *clist = connections->at( signal ); \
if ( !clist ) \ if ( !clist ) \
return; \ return; \
QUObject o[2]; \ QUObject o[2]; \
static_QUType_##TYPE.set( o+1, param ); \ o[1].isLastObject = true; \
activate_signal( clist, o ); \ static_QUType_##TYPE.set( o+1, param ); \
activate_signal( clist, o ); \
} }
#endif #endif

@ -63,6 +63,10 @@ class QObjectUserData;
#endif #endif
struct QUObject; struct QUObject;
#ifdef QT_THREAD_SUPPORT
class QThread;
#endif
class Q_EXPORT QObject: public Qt class Q_EXPORT QObject: public Qt
{ {
Q_OBJECT Q_OBJECT
@ -217,6 +221,18 @@ private: // Disabled copy constructor and operator=
QObject( const QObject & ); QObject( const QObject & );
QObject &operator=( const QObject & ); QObject &operator=( const QObject & );
#endif #endif
public:
#ifdef QT_THREAD_SUPPORT
QThread* contextThreadObject() const;
void moveToThread(QThread *targetThread);
#endif
private:
#ifdef QT_THREAD_SUPPORT
void moveToThread_helper(QThread *targetThread);
void setThreadObject_helper(QThread *targetThread);
#endif
}; };

@ -41,6 +41,7 @@
#include "qplatformdefs.h" #include "qplatformdefs.h"
#include "qthread.h" #include "qthread.h"
#include "qeventloop.h"
#include <private/qthreadinstance_p.h> #include <private/qthreadinstance_p.h>
#ifndef QT_H #ifndef QT_H
@ -238,4 +239,20 @@ void QThread::postEvent( QObject * receiver, QEvent * event )
} }
#endif #endif
QEventLoopThread::QEventLoopThread() : QThread()
{
//
}
QEventLoopThread::~QEventLoopThread()
{
//
}
void QEventLoopThread::run()
{
QEventLoop* eventLoop = QApplication::eventLoop();
if (eventLoop) eventLoop->exec();
}
#endif // QT_THREAD_SUPPORT #endif // QT_THREAD_SUPPORT

@ -118,11 +118,25 @@ protected:
private: private:
QThreadInstance * d; QThreadInstance * d;
friend class QThreadInstance; friend class QThreadInstance;
friend class QCoreApplicationThread;
friend class QApplication;
friend class QEventLoop;
#if defined(Q_DISABLE_COPY) #if defined(Q_DISABLE_COPY)
QThread( const QThread & ); QThread( const QThread & );
QThread &operator=( const QThread & ); QThread &operator=( const QThread & );
#endif // Q_DISABLE_COPY #endif // Q_DISABLE_COPY
public:
static QThread* currentThreadObject();
};
class Q_EXPORT QEventLoopThread : public QThread
{
public:
QEventLoopThread();
~QEventLoopThread();
virtual void run();
}; };
#endif // QT_THREAD_SUPPORT #endif // QT_THREAD_SUPPORT

@ -52,11 +52,6 @@ typedef pthread_mutex_t Q_MUTEX_T;
#include <sched.h> #include <sched.h>
static QThreadInstance main_instance = {
0, { 0, &main_instance }, 0, 0, 1, 0, PTHREAD_COND_INITIALIZER, 0
};
static QMutexPool *qt_thread_mutexpool = 0; static QMutexPool *qt_thread_mutexpool = 0;
@ -82,10 +77,20 @@ static void create_storage_key()
** QThreadInstance ** QThreadInstance
*************************************************************************/ *************************************************************************/
void QThreadInstance::setCurrentThread(QThread *thread)
{
pthread_once(&storage_key_once, create_storage_key);
pthread_setspecific(storage_key, thread);
}
QThreadInstance *QThreadInstance::current() QThreadInstance *QThreadInstance::current()
{ {
QThreadInstance *ret = NULL;
pthread_once( &storage_key_once, create_storage_key ); pthread_once( &storage_key_once, create_storage_key );
QThreadInstance *ret = (QThreadInstance *) pthread_getspecific( storage_key ); QThread *thread = (QThread *) pthread_getspecific( storage_key );
if (thread) {
ret = thread->d;
}
return ret; return ret;
} }
@ -101,6 +106,8 @@ void QThreadInstance::init(unsigned int stackSize)
pthread_cond_init(&thread_done, NULL); pthread_cond_init(&thread_done, NULL);
thread_id = 0; thread_id = 0;
eventLoop = 0;
// threads have not been initialized yet, do it now // threads have not been initialized yet, do it now
if (! qt_thread_mutexpool) QThread::initialize(); if (! qt_thread_mutexpool) QThread::initialize();
} }
@ -114,8 +121,8 @@ void *QThreadInstance::start( void *_arg )
{ {
void **arg = (void **) _arg; void **arg = (void **) _arg;
pthread_once( &storage_key_once, create_storage_key ); setCurrentThread( (QThread *) arg[0] );
pthread_setspecific( storage_key, arg[1] );
pthread_cleanup_push( QThreadInstance::finish, arg[1] ); pthread_cleanup_push( QThreadInstance::finish, arg[1] );
pthread_testcancel(); pthread_testcancel();
@ -192,9 +199,6 @@ void QThread::initialize()
qt_global_mutexpool = new QMutexPool( TRUE, 73 ); qt_global_mutexpool = new QMutexPool( TRUE, 73 );
if ( ! qt_thread_mutexpool ) if ( ! qt_thread_mutexpool )
qt_thread_mutexpool = new QMutexPool( FALSE, 127 ); qt_thread_mutexpool = new QMutexPool( FALSE, 127 );
pthread_once( &storage_key_once, create_storage_key );
pthread_setspecific( storage_key, &main_instance );
} }
/*! \internal /*! \internal
@ -206,11 +210,6 @@ void QThread::cleanup()
delete qt_thread_mutexpool; delete qt_thread_mutexpool;
qt_global_mutexpool = 0; qt_global_mutexpool = 0;
qt_thread_mutexpool = 0; qt_thread_mutexpool = 0;
QThreadInstance::finish(&main_instance);
pthread_once( &storage_key_once, create_storage_key );
pthread_setspecific( storage_key, 0 );
} }
/*! /*!
@ -470,5 +469,20 @@ bool QThread::wait( unsigned long time )
return (ret == 0); return (ret == 0);
} }
/*!
Returns a pointer to the currently executing QThread. If the
current thread was not started using the QThread API, this
function returns zero.
Note that QApplication creates a QThread object to represent the
main thread; calling this function from main() after creating
QApplication will return a valid pointer.
*/
QThread *QThread::currentThreadObject()
{
pthread_once(&storage_key_once, create_storage_key);
return reinterpret_cast<QThread *>(pthread_getspecific(storage_key));
}
#endif // QT_THREAD_SUPPORT #endif // QT_THREAD_SUPPORT

@ -56,6 +56,9 @@
#include "qstyle.h" #include "qstyle.h"
#include "qmetaobject.h" #include "qmetaobject.h"
#include "qguardedptr.h" #include "qguardedptr.h"
#if defined(QT_THREAD_SUPPORT)
#include "qthread.h"
#endif
#if defined(QT_ACCESSIBILITY_SUPPORT) #if defined(QT_ACCESSIBILITY_SUPPORT)
#include "qaccessible.h" #include "qaccessible.h"
#endif #endif
@ -887,6 +890,12 @@ QWidget::QWidget( QWidget *parent, const char *name, WFlags f, NFlags n )
} }
#endif #endif
#if defined(QT_THREAD_SUPPORT) && defined(QT_CHECK_STATE)
if (QThread::currentThreadObject() != QApplication::guiThread()) {
qFatal( "QWidget: Cannot create a QWidget outside of the main GUI thread" );
}
#endif
fstrut_dirty = 1; fstrut_dirty = 1;
isWidget = TRUE; // is a widget isWidget = TRUE; // is a widget

@ -3193,6 +3193,7 @@ void generateClass() // generate C++ source code for a class
offset++; offset++;
} }
} }
fprintf( out, " o[%d].isLastObject = true;\n", f->args->count() + 0 );
fprintf( out, " activate_signal( clist, o );\n" ); fprintf( out, " activate_signal( clist, o );\n" );
// get return values from inOut parameters // get return values from inOut parameters

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,58 +1,166 @@
#define CHAR_VAL 257
#define INT_VAL 258 /* A Bison parser, made by GNU Bison 2.4.1. */
#define DOUBLE_VAL 259
#define STRING 260 /* Skeleton interface for Bison's Yacc-like parsers in C
#define IDENTIFIER 261
#define FRIEND 262 Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
#define TYPEDEF 263 Free Software Foundation, Inc.
#define AUTO 264
#define REGISTER 265 This program is free software: you can redistribute it and/or modify
#define STATIC 266 it under the terms of the GNU General Public License as published by
#define EXTERN 267 the Free Software Foundation, either version 3 of the License, or
#define INLINE 268 (at your option) any later version.
#define VIRTUAL 269
#define CONST 270 This program is distributed in the hope that it will be useful,
#define VOLATILE 271 but WITHOUT ANY WARRANTY; without even the implied warranty of
#define CHAR 272 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#define SHORT 273 GNU General Public License for more details.
#define INT 274
#define LONG 275 You should have received a copy of the GNU General Public License
#define SIGNED 276 along with this program. If not, see <http://www.gnu.org/licenses/>. */
#define UNSIGNED 277
#define FLOAT 278 /* As a special exception, you may create a larger work that contains
#define DOUBLE 279 part or all of the Bison parser skeleton and distribute that work
#define VOID 280 under terms of your choice, so long as that work isn't itself a
#define ENUM 281 parser generator using the skeleton or a modified version thereof
#define CLASS 282 as a parser skeleton. Alternatively, if you modify or redistribute
#define STRUCT 283 the parser skeleton itself, you may (at your option) remove this
#define UNION 284 special exception, which will cause the skeleton and the resulting
#define ASM 285 Bison output files to be licensed under the GNU General Public
#define PRIVATE 286 License without this special exception.
#define PROTECTED 287
#define PUBLIC 288 This special exception was added by the Free Software Foundation in
#define OPERATOR 289 version 2.2 of Bison. */
#define DBL_COLON 290
#define TRIPLE_DOT 291
#define TEMPLATE 292 /* Tokens. */
#define NAMESPACE 293 #ifndef YYTOKENTYPE
#define USING 294 # define YYTOKENTYPE
#define MUTABLE 295 /* Put the tokens into the symbol table, so that GDB and other debuggers
#define THROW 296 know about them. */
#define SIGNALS 297 enum yytokentype {
#define SLOTS 298 CHAR_VAL = 258,
#define Q_OBJECT 299 INT_VAL = 259,
#define Q_PROPERTY 300 DOUBLE_VAL = 260,
#define Q_OVERRIDE 301 STRING = 261,
#define Q_CLASSINFO 302 IDENTIFIER = 262,
#define Q_ENUMS 303 FRIEND = 263,
#define Q_SETS 304 TYPEDEF = 264,
#define READ 305 AUTO = 265,
#define WRITE 306 REGISTER = 266,
#define STORED 307 STATIC = 267,
#define DESIGNABLE 308 EXTERN = 268,
#define SCRIPTABLE 309 INLINE = 269,
#define RESET 310 VIRTUAL = 270,
typedef union { CONST = 271,
VOLATILE = 272,
CHAR = 273,
SHORT = 274,
INT = 275,
LONG = 276,
SIGNED = 277,
UNSIGNED = 278,
FLOAT = 279,
DOUBLE = 280,
VOID = 281,
ENUM = 282,
CLASS = 283,
STRUCT = 284,
UNION = 285,
ASM = 286,
PRIVATE = 287,
PROTECTED = 288,
PUBLIC = 289,
OPERATOR = 290,
DBL_COLON = 291,
TRIPLE_DOT = 292,
TEMPLATE = 293,
NAMESPACE = 294,
USING = 295,
MUTABLE = 296,
THROW = 297,
SIGNALS = 298,
SLOTS = 299,
Q_OBJECT = 300,
Q_PROPERTY = 301,
Q_OVERRIDE = 302,
Q_CLASSINFO = 303,
Q_ENUMS = 304,
Q_SETS = 305,
READ = 306,
WRITE = 307,
STORED = 308,
DESIGNABLE = 309,
SCRIPTABLE = 310,
RESET = 311
};
#endif
/* Tokens. */
#define CHAR_VAL 258
#define INT_VAL 259
#define DOUBLE_VAL 260
#define STRING 261
#define IDENTIFIER 262
#define FRIEND 263
#define TYPEDEF 264
#define AUTO 265
#define REGISTER 266
#define STATIC 267
#define EXTERN 268
#define INLINE 269
#define VIRTUAL 270
#define CONST 271
#define VOLATILE 272
#define CHAR 273
#define SHORT 274
#define INT 275
#define LONG 276
#define SIGNED 277
#define UNSIGNED 278
#define FLOAT 279
#define DOUBLE 280
#define VOID 281
#define ENUM 282
#define CLASS 283
#define STRUCT 284
#define UNION 285
#define ASM 286
#define PRIVATE 287
#define PROTECTED 288
#define PUBLIC 289
#define OPERATOR 290
#define DBL_COLON 291
#define TRIPLE_DOT 292
#define TEMPLATE 293
#define NAMESPACE 294
#define USING 295
#define MUTABLE 296
#define THROW 297
#define SIGNALS 298
#define SLOTS 299
#define Q_OBJECT 300
#define Q_PROPERTY 301
#define Q_OVERRIDE 302
#define Q_CLASSINFO 303
#define Q_ENUMS 304
#define Q_SETS 305
#define READ 306
#define WRITE 307
#define STORED 308
#define DESIGNABLE 309
#define SCRIPTABLE 310
#define RESET 311
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
{
/* Line 1676 of yacc.c */
#line 692 "moc.y"
char char_val; char char_val;
int int_val; int int_val;
double double_val; double double_val;
@ -61,5 +169,17 @@ typedef union {
Function *function; Function *function;
ArgList *arg_list; ArgList *arg_list;
Argument *arg; Argument *arg;
/* Line 1676 of yacc.c */
#line 177 "moc_yacc.h"
} YYSTYPE; } YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
#endif
extern YYSTYPE yylval; extern YYSTYPE yylval;

@ -74,7 +74,6 @@ typedef pthread_mutex_t Q_MUTEX_T;
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
// Private class declarations // Private class declarations
class QRealMutexPrivate : public QMutexPrivate { class QRealMutexPrivate : public QMutexPrivate {

@ -62,8 +62,12 @@
#include <pthread.h> #include <pthread.h>
#endif #endif
class QThread;
class QEventLoop;
class QThreadInstance { class QThreadInstance {
public: public:
static void setCurrentThread(QThread *thread);
static QThreadInstance *current(); static QThreadInstance *current();
void init(unsigned int stackSize); void init(unsigned int stackSize);
@ -95,6 +99,8 @@ public:
static unsigned int __stdcall start( void * ); static unsigned int __stdcall start( void * );
static void finish( QThreadInstance * ); static void finish( QThreadInstance * );
#endif // Q_OS_WIN32 #endif // Q_OS_WIN32
QEventLoop* eventLoop;
}; };
#endif // QT_THREAD_SUPPORT #endif // QT_THREAD_SUPPORT

@ -39,6 +39,9 @@
**********************************************************************/ **********************************************************************/
#include "qucom_p.h" #include "qucom_p.h"
#include "qucomextra_p.h"
#include "qvariant.h"
// Standard types // Standard types
@ -545,3 +548,24 @@ void QUType_QString::clear( QUObject *o )
delete (QString*)o->payload.ptr; delete (QString*)o->payload.ptr;
o->payload.ptr = 0; o->payload.ptr = 0;
} }
QUObject* QUObject::deepCopy(QUObject* newLocation) {
QUObject* ret;
if (newLocation) {
ret = new(newLocation) QUObject(*this);
}
else {
ret = new QUObject(*this);
}
// Any type that has a clear() method must be copied here!
if (*(type->uuid()) == TID_QUType_charstar) {
static_QUType_charstar.set( ret, (const char *)static_QUType_charstar.get(this), true );
}
if (*(type->uuid()) == TID_QUType_QString) {
static_QUType_QString.set( ret, (QString)static_QUType_QString.get(this) );
}
if (*(type->uuid()) == TID_QUType_QVariant) {
static_QUType_QVariant.set( ret, (QVariant)static_QUType_QVariant.get(this) );
}
return ret;
}

@ -127,7 +127,7 @@ extern Q_EXPORT QUType_Null static_QUType_Null;
struct Q_EXPORT QUObject struct Q_EXPORT QUObject
{ {
public: // scary MSVC bug makes this necessary public: // scary MSVC bug makes this necessary
QUObject() : type( &static_QUType_Null ) {} QUObject() : type( &static_QUType_Null ), isLastObject(false) {}
~QUObject() { type->clear( this ); } ~QUObject() { type->clear( this ); }
QUType *type; QUType *type;
@ -184,6 +184,8 @@ public: // scary MSVC bug makes this necessary
} payload; } payload;
QUObject* deepCopy(QUObject*);
bool isLastObject;
}; };

Loading…
Cancel
Save