WvStreams
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     _cloned->addRef();
00037     setclone(_cloned);
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     _build_selectinfo(si, msec_timeout, 
00054                       false, false, false, true);
00055 
00056     // set up a timer to wake us up to poll again (for alarms)
00057     // we don't try to catch the timer signal; we use it only to force
00058     // Qt's event loop to restart so our hook gets called again
00059     select_timer.stop();
00060     if (si.msec_timeout >= 0)
00061         select_timer.start(si.msec_timeout, true /*singleshot*/);
00062 
00063     // set up necessary QSocketNotifiers, unfortunately there is no
00064     // better way to iterate over the set of file descriptors
00065     for (int fd = 0; fd <= si.max_fd; ++fd)
00066     {
00067         if (FD_ISSET(fd, &si.read))
00068         {
00069             QSocketNotifier *n = notify_readable.find(fd);
00070             if (! n)
00071             {
00072                 n = new QSocketNotifier(fd, QSocketNotifier::Read);
00073                 notify_readable.insert(fd, n);
00074                 QObject::connect(n, SIGNAL(activated(int)),
00075                     this, SLOT(fd_readable(int)));
00076             }
00077         } else
00078             notify_readable.remove(fd);
00079         
00080         if (FD_ISSET(fd, &si.write))
00081         {
00082             QSocketNotifier *n = notify_writable.find(fd);
00083             if (! n)
00084             {
00085                 n = new QSocketNotifier(fd, QSocketNotifier::Write);
00086                 notify_writable.insert(fd, n);
00087                 QObject::connect(n, SIGNAL(activated(int)),
00088                     this, SLOT(fd_writable(int)));
00089             }
00090         } else
00091             notify_writable.remove(fd);
00092         
00093         if (FD_ISSET(fd, &si.except))
00094         {
00095             QSocketNotifier *n = notify_exception.find(fd);
00096             if (! n)
00097             {
00098                 n = new QSocketNotifier(fd, QSocketNotifier::Exception);
00099                 notify_exception.insert(fd, n);
00100                 QObject::connect(n, SIGNAL(activated(int)),
00101                     this, SLOT(fd_exception(int)));
00102             }
00103         } else
00104             notify_exception.remove(fd);
00105     }
00106 
00107     // remove stale notifiers
00108     for (int fd = si.max_fd + 1; fd <= last_max_fd; ++fd)
00109     {
00110         notify_readable.remove(fd);
00111         notify_writable.remove(fd);
00112         notify_exception.remove(fd);
00113     }
00114     last_max_fd = si.max_fd;
00115 
00116     // clear select lists
00117     FD_ZERO(&si.read);
00118     FD_ZERO(&si.write);
00119     FD_ZERO(&si.except);
00120 }
00121 
00122 
00123 void WvQtStreamClone::post_poll()
00124 {
00125     // cleanup and invoke callbacks
00126     bool sure = _process_selectinfo(si, true);
00127     if (sure || pending_callback)
00128     {
00129         pending_callback = false;
00130         callback();
00131         if (globalstream) globalstream->callback();
00132     }
00133 }
00134 
00135 
00136 void WvQtStreamClone::set_timeout(int msec_timeout)
00137 {
00138     this->msec_timeout = msec_timeout;
00139 }
00140 
00141 
00142 void WvQtStreamClone::qt_begin_event_loop_hook()
00143 {
00144     // select not done yet?
00145     if (select_in_progress) return;
00146 
00147     // finish the last polling stage
00148     if (! first_time)
00149         post_poll();
00150     else
00151         first_time = false;
00152     // start the next polling stage
00153     pre_poll();
00154     select_in_progress = true;
00155 }
00156 
00157 
00158 void WvQtStreamClone::qt_detach()
00159 {
00160     // finish the last polling stage
00161     if (! first_time)
00162     {
00163         select_in_progress = false;
00164         post_poll();
00165         last_max_fd = -1;
00166         first_time = true;
00167     }
00168     // remove any remaining Qt objects
00169     select_timer.stop();
00170     notify_readable.clear();
00171     notify_writable.clear();
00172     notify_exception.clear();
00173     QObject::disconnect(qApp, SIGNAL(guiThreadAwake()),
00174         this, SLOT(qt_begin_event_loop_hook()));
00175     QObject::disconnect(& select_timer, SIGNAL(timeout()),
00176         this, SLOT(select_timer_expired()));
00177 }
00178 
00179 
00180 void WvQtStreamClone::qt_attach()
00181 {
00182     // hook into the Qt event loop before each iteration
00183     QObject::connect(qApp, SIGNAL(guiThreadAwake()),
00184         this, SLOT(qt_begin_event_loop_hook()));
00185     QObject::connect(& select_timer, SIGNAL(timeout()),
00186         this, SLOT(select_timer_expired()));
00187 }
00188 
00189 
00190 void WvQtStreamClone::select_timer_expired()
00191 {
00192     select_in_progress = false;
00193 }
00194 
00195 
00196 void WvQtStreamClone::fd_readable(int fd)
00197 {
00198     FD_SET(fd, &si.read);
00199     pending_callback = true;
00200     select_in_progress = false;
00201 }
00202 
00203 
00204 void WvQtStreamClone::fd_writable(int fd)
00205 {
00206     FD_SET(fd, &si.write);
00207     pending_callback = true;
00208     select_in_progress = false;
00209 }
00210 
00211 
00212 void WvQtStreamClone::fd_exception(int fd)
00213 {
00214     FD_SET(fd, &si.except);
00215     pending_callback = true;
00216     select_in_progress = false;
00217 }
00218 
00219 void WvQtStreamClone::execute()
00220 {
00221     WvStreamClone::execute();
00222 }
00223 
00224 
00225 void WvQtStreamClone::setclone(IWvStream *newclone)
00226 {
00227     WvStreamClone::setclone(newclone);
00228     
00229     if (newclone != NULL)
00230         my_type = WvString("WvQtStreamClone:%s", newclone->wstype());
00231     else
00232         my_type = "WvQtStreamClone:(none)";
00233 }