wvqtstreamclone.cc

00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * Wraps another WvStream and attaches it to the normal Qt
00006  * event loop.  If you are using this object to manage all of your
00007  * streams, then you do not need to have a normal WvStreams
00008  * select()/callback() loop in your application at all.
00009  *
00010  * However, should you leave the Qt event loop and wish to continue
00011  * using this WvStream, call qt_detach() first, then run a normal
00012  * WvStreams event loop.  If you do not do this, events may be
00013  * lost!!  You may resume the Qt event loop at any time after the
00014  * WvStreams event loop has exited by calling qt_attach().
00015  *
00016  * Note: You do not need to add all of the WvStreams used in a Qt
00017  *       application to a single WvStreamList wrapped by a
00018  *       WvQtStreamClone so long as each top-level stream is wrapped
00019  *       by a WvQtStreamClone to take care of calling select()
00020  *       and callback() from within the Qt event loop.
00021  */
00022 #include "wvqtstreamclone.moc"
00023 
00024 // number of slots used by the separate chaining hashtable
00025 // note: can store more than this number of elements in the table
00026 #define NUM_SLOTS 41 // must be prime
00027 
00028 WvQtStreamClone::WvQtStreamClone(IWvStream *_cloned, int msec_timeout) :
00029     WvStreamClone(_cloned), msec_timeout(msec_timeout),
00030     pending_callback(false), first_time(true), select_in_progress(false),
00031     last_max_fd(-1),
00032     notify_readable(NUM_SLOTS),
00033     notify_writable(NUM_SLOTS),
00034     notify_exception(NUM_SLOTS)
00035 {
00036     setclone(_cloned);
00037     disassociate_on_close = true;
00038     notify_readable.setAutoDelete(true);
00039     notify_writable.setAutoDelete(true);
00040     notify_exception.setAutoDelete(true);
00041     qt_attach();
00042 }
00043 
00044 
00045 WvQtStreamClone::~WvQtStreamClone()
00046 {
00047 }
00048 
00049 
00050 void WvQtStreamClone::pre_poll()
00051 {
00052     // prepare lists of file descriptors
00053     bool sure = _build_selectinfo(si, msec_timeout, 
00054                                   false, false, false, true);
00055     if (sure)
00056     {
00057         pending_callback = true;
00058         si.msec_timeout = 0;
00059     }
00060 
00061     // set up a timer to wake us up to poll again (for alarms)
00062     // we don't try to catch the timer signal; we use it only to force
00063     // Qt's event loop to restart so our hook gets called again
00064     select_timer.stop();
00065     if (si.msec_timeout >= 0)
00066         select_timer.start(si.msec_timeout, true /*singleshot*/);
00067 
00068     // set up necessary QSocketNotifiers, unfortunately there is no
00069     // better way to iterate over the set of file descriptors
00070     for (int fd = 0; fd <= si.max_fd; ++fd)
00071     {
00072         if (FD_ISSET(fd, &si.read))
00073         {
00074             QSocketNotifier *n = notify_readable.find(fd);
00075             if (! n)
00076             {
00077                 n = new QSocketNotifier(fd, QSocketNotifier::Read);
00078                 notify_readable.insert(fd, n);
00079                 QObject::connect(n, SIGNAL(activated(int)),
00080                     this, SLOT(fd_readable(int)));
00081             }
00082         } else
00083             notify_readable.remove(fd);
00084         
00085         if (FD_ISSET(fd, &si.write))
00086         {
00087             QSocketNotifier *n = notify_writable.find(fd);
00088             if (! n)
00089             {
00090                 n = new QSocketNotifier(fd, QSocketNotifier::Write);
00091                 notify_writable.insert(fd, n);
00092                 QObject::connect(n, SIGNAL(activated(int)),
00093                     this, SLOT(fd_writable(int)));
00094             }
00095         } else
00096             notify_writable.remove(fd);
00097         
00098         if (FD_ISSET(fd, &si.except))
00099         {
00100             QSocketNotifier *n = notify_exception.find(fd);
00101             if (! n)
00102             {
00103                 n = new QSocketNotifier(fd, QSocketNotifier::Exception);
00104                 notify_exception.insert(fd, n);
00105                 QObject::connect(n, SIGNAL(activated(int)),
00106                     this, SLOT(fd_exception(int)));
00107             }
00108         } else
00109             notify_exception.remove(fd);
00110     }
00111 
00112     // remove stale notifiers
00113     for (int fd = si.max_fd + 1; fd <= last_max_fd; ++fd)
00114     {
00115         notify_readable.remove(fd);
00116         notify_writable.remove(fd);
00117         notify_exception.remove(fd);
00118     }
00119     last_max_fd = si.max_fd;
00120 
00121     // clear select lists
00122     FD_ZERO(&si.read);
00123     FD_ZERO(&si.write);
00124     FD_ZERO(&si.except);
00125 }
00126 
00127 
00128 void WvQtStreamClone::post_poll()
00129 {
00130     // cleanup and invoke callbacks
00131     bool sure = _process_selectinfo(si, true);
00132     if (sure || pending_callback)
00133     {
00134         pending_callback = false;
00135         callback();
00136         if (globalstream) globalstream->callback();
00137     }
00138 }
00139 
00140 
00141 void WvQtStreamClone::set_timeout(int msec_timeout)
00142 {
00143     this->msec_timeout = msec_timeout;
00144 }
00145 
00146 
00147 void WvQtStreamClone::qt_begin_event_loop_hook()
00148 {
00149     // select not done yet?
00150     if (select_in_progress) return;
00151 
00152     // finish the last polling stage
00153     if (! first_time)
00154         post_poll();
00155     else
00156         first_time = false;
00157     // start the next polling stage
00158     pre_poll();
00159     select_in_progress = true;
00160 }
00161 
00162 
00163 void WvQtStreamClone::qt_detach()
00164 {
00165     // finish the last polling stage
00166     if (! first_time)
00167     {
00168         select_in_progress = false;
00169         post_poll();
00170         last_max_fd = -1;
00171         first_time = true;
00172     }
00173     // remove any remaining Qt objects
00174     select_timer.stop();
00175     notify_readable.clear();
00176     notify_writable.clear();
00177     notify_exception.clear();
00178     QObject::disconnect(qApp, SIGNAL(guiThreadAwake()),
00179         this, SLOT(qt_begin_event_loop_hook()));
00180     QObject::disconnect(& select_timer, SIGNAL(timeout()),
00181         this, SLOT(select_timer_expired()));
00182 }
00183 
00184 
00185 void WvQtStreamClone::qt_attach()
00186 {
00187     // hook into the Qt event loop before each iteration
00188     QObject::connect(qApp, SIGNAL(guiThreadAwake()),
00189         this, SLOT(qt_begin_event_loop_hook()));
00190     QObject::connect(& select_timer, SIGNAL(timeout()),
00191         this, SLOT(select_timer_expired()));
00192 }
00193 
00194 
00195 void WvQtStreamClone::select_timer_expired()
00196 {
00197     select_in_progress = false;
00198 }
00199 
00200 
00201 void WvQtStreamClone::fd_readable(int fd)
00202 {
00203     FD_SET(fd, &si.read);
00204     pending_callback = true;
00205     select_in_progress = false;
00206 }
00207 
00208 
00209 void WvQtStreamClone::fd_writable(int fd)
00210 {
00211     FD_SET(fd, &si.write);
00212     pending_callback = true;
00213     select_in_progress = false;
00214 }
00215 
00216 
00217 void WvQtStreamClone::fd_exception(int fd)
00218 {
00219     FD_SET(fd, &si.except);
00220     pending_callback = true;
00221     select_in_progress = false;
00222 }
00223 
00224 void WvQtStreamClone::execute()
00225 {
00226     WvStreamClone::execute();
00227 }

Generated on Sun Sep 24 20:10:51 2006 for WvStreams by  doxygen 1.4.7