WvStreams
wvqtstreamclone.cc
1 /*
2  * Worldvisions Weaver Software:
3  * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4  *
5  * Wraps another WvStream and attaches it to the normal Qt
6  * event loop. If you are using this object to manage all of your
7  * streams, then you do not need to have a normal WvStreams
8  * select()/callback() loop in your application at all.
9  *
10  * However, should you leave the Qt event loop and wish to continue
11  * using this WvStream, call qt_detach() first, then run a normal
12  * WvStreams event loop. If you do not do this, events may be
13  * lost!! You may resume the Qt event loop at any time after the
14  * WvStreams event loop has exited by calling qt_attach().
15  *
16  * Note: You do not need to add all of the WvStreams used in a Qt
17  * application to a single WvStreamList wrapped by a
18  * WvQtStreamClone so long as each top-level stream is wrapped
19  * by a WvQtStreamClone to take care of calling select()
20  * and callback() from within the Qt event loop.
21  */
22 #include "wvqtstreamclone.moc"
23 
24 // number of slots used by the separate chaining hashtable
25 // note: can store more than this number of elements in the table
26 #define NUM_SLOTS 41 // must be prime
27 
28 WvQtStreamClone::WvQtStreamClone(IWvStream *_cloned, int msec_timeout) :
29  WvStreamClone(_cloned), msec_timeout(msec_timeout),
30  pending_callback(false), first_time(true), select_in_progress(false),
31  last_max_fd(-1),
32  notify_readable(NUM_SLOTS),
33  notify_writable(NUM_SLOTS),
34  notify_exception(NUM_SLOTS)
35 {
36  _cloned->addRef();
37  setclone(_cloned);
38  notify_readable.setAutoDelete(true);
39  notify_writable.setAutoDelete(true);
40  notify_exception.setAutoDelete(true);
41  qt_attach();
42 }
43 
44 
45 WvQtStreamClone::~WvQtStreamClone()
46 {
47 }
48 
49 
50 void WvQtStreamClone::pre_poll()
51 {
52  // prepare lists of file descriptors
53  _build_selectinfo(si, msec_timeout,
54  false, false, false, true);
55 
56  // set up a timer to wake us up to poll again (for alarms)
57  // we don't try to catch the timer signal; we use it only to force
58  // Qt's event loop to restart so our hook gets called again
59  select_timer.stop();
60  if (si.msec_timeout >= 0)
61  select_timer.start(si.msec_timeout, true /*singleshot*/);
62 
63  // set up necessary QSocketNotifiers, unfortunately there is no
64  // better way to iterate over the set of file descriptors
65  for (int fd = 0; fd <= si.max_fd; ++fd)
66  {
67  if (FD_ISSET(fd, &si.read))
68  {
69  QSocketNotifier *n = notify_readable.find(fd);
70  if (! n)
71  {
72  n = new QSocketNotifier(fd, QSocketNotifier::Read);
73  notify_readable.insert(fd, n);
74  QObject::connect(n, SIGNAL(activated(int)),
75  this, SLOT(fd_readable(int)));
76  }
77  } else
78  notify_readable.remove(fd);
79 
80  if (FD_ISSET(fd, &si.write))
81  {
82  QSocketNotifier *n = notify_writable.find(fd);
83  if (! n)
84  {
85  n = new QSocketNotifier(fd, QSocketNotifier::Write);
86  notify_writable.insert(fd, n);
87  QObject::connect(n, SIGNAL(activated(int)),
88  this, SLOT(fd_writable(int)));
89  }
90  } else
91  notify_writable.remove(fd);
92 
93  if (FD_ISSET(fd, &si.except))
94  {
95  QSocketNotifier *n = notify_exception.find(fd);
96  if (! n)
97  {
98  n = new QSocketNotifier(fd, QSocketNotifier::Exception);
99  notify_exception.insert(fd, n);
100  QObject::connect(n, SIGNAL(activated(int)),
101  this, SLOT(fd_exception(int)));
102  }
103  } else
104  notify_exception.remove(fd);
105  }
106 
107  // remove stale notifiers
108  for (int fd = si.max_fd + 1; fd <= last_max_fd; ++fd)
109  {
110  notify_readable.remove(fd);
111  notify_writable.remove(fd);
112  notify_exception.remove(fd);
113  }
114  last_max_fd = si.max_fd;
115 
116  // clear select lists
117  FD_ZERO(&si.read);
118  FD_ZERO(&si.write);
119  FD_ZERO(&si.except);
120 }
121 
122 
123 void WvQtStreamClone::post_poll()
124 {
125  // cleanup and invoke callbacks
126  bool sure = _process_selectinfo(si, true);
127  if (sure || pending_callback)
128  {
129  pending_callback = false;
130  callback();
131  if (globalstream) globalstream->callback();
132  }
133 }
134 
135 
136 void WvQtStreamClone::set_timeout(int msec_timeout)
137 {
138  this->msec_timeout = msec_timeout;
139 }
140 
141 
142 void WvQtStreamClone::qt_begin_event_loop_hook()
143 {
144  // select not done yet?
145  if (select_in_progress) return;
146 
147  // finish the last polling stage
148  if (! first_time)
149  post_poll();
150  else
151  first_time = false;
152  // start the next polling stage
153  pre_poll();
154  select_in_progress = true;
155 }
156 
157 
158 void WvQtStreamClone::qt_detach()
159 {
160  // finish the last polling stage
161  if (! first_time)
162  {
163  select_in_progress = false;
164  post_poll();
165  last_max_fd = -1;
166  first_time = true;
167  }
168  // remove any remaining Qt objects
169  select_timer.stop();
170  notify_readable.clear();
171  notify_writable.clear();
172  notify_exception.clear();
173  QObject::disconnect(qApp, SIGNAL(guiThreadAwake()),
174  this, SLOT(qt_begin_event_loop_hook()));
175  QObject::disconnect(& select_timer, SIGNAL(timeout()),
176  this, SLOT(select_timer_expired()));
177 }
178 
179 
180 void WvQtStreamClone::qt_attach()
181 {
182  // hook into the Qt event loop before each iteration
183  QObject::connect(qApp, SIGNAL(guiThreadAwake()),
184  this, SLOT(qt_begin_event_loop_hook()));
185  QObject::connect(& select_timer, SIGNAL(timeout()),
186  this, SLOT(select_timer_expired()));
187 }
188 
189 
190 void WvQtStreamClone::select_timer_expired()
191 {
192  select_in_progress = false;
193 }
194 
195 
196 void WvQtStreamClone::fd_readable(int fd)
197 {
198  FD_SET(fd, &si.read);
199  pending_callback = true;
200  select_in_progress = false;
201 }
202 
203 
204 void WvQtStreamClone::fd_writable(int fd)
205 {
206  FD_SET(fd, &si.write);
207  pending_callback = true;
208  select_in_progress = false;
209 }
210 
211 
212 void WvQtStreamClone::fd_exception(int fd)
213 {
214  FD_SET(fd, &si.except);
215  pending_callback = true;
216  select_in_progress = false;
217 }
218 
219 void WvQtStreamClone::execute()
220 {
222 }
223 
224 
226 {
227  WvStreamClone::setclone(newclone);
228 
229  if (newclone != NULL)
230  my_type = WvString("WvQtStreamClone:%s", newclone->wstype());
231  else
232  my_type = "WvQtStreamClone:(none)";
233 }
WvQtStreamClone::setclone
virtual void setclone(IWvStream *clone)
WvStreamClone takes ownership of the given stream; it will WVRELEASE() the stream when you setclone()...
Definition: wvqtstreamclone.cc:225
WvStreamClone::execute
virtual void execute()
The callback() function calls execute(), and then calls the user- specified callback if one is define...
Definition: wvstreamclone.cc:272
IWvStream
Definition: iwvstream.h:24
WvStream::callback
virtual void callback()
if the stream has a callback function defined, call it now.
Definition: wvstream.cc:401
WvString
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
WvQtStreamClone::WvQtStreamClone
WvQtStreamClone(IWvStream *_cloned=NULL, int msec_timeout=-1)
WvQtStreamClone takes ownership of the stream you give it just like WvStreamClone.
Definition: wvqtstreamclone.cc:28
WvStreamClone
WvStreamClone simply forwards all requests to the "cloned" stream.
Definition: wvstreamclone.h:23
WvStreamClone::setclone
virtual void setclone(IWvStream *clone)
WvStreamClone takes ownership of the given stream; it will WVRELEASE() the stream when you setclone()...
Definition: wvstreamclone.cc:181
IObject::addRef
virtual unsigned int addRef()=0
Indicate you are using this object.