]> git.saurik.com Git - wxWidgets.git/blob - src/common/selectdispatcher.cpp
text updated events were not always sent out correctly, fixes #11462
[wxWidgets.git] / src / common / selectdispatcher.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/selectdispatcher.cpp
3 // Purpose: implements dispatcher for select() call
4 // Author: Lukasz Michalski and Vadim Zeitlin
5 // Created: December 2006
6 // RCS-ID: $Id$
7 // Copyright: (c) 2006 Lukasz Michalski
8 // License: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #if wxUSE_SELECT_DISPATCHER
27
28 #include "wx/private/selectdispatcher.h"
29 #include "wx/unix/private.h"
30
31 #ifndef WX_PRECOMP
32 #include "wx/hash.h"
33 #include "wx/log.h"
34 #include "wx/intl.h"
35 #endif
36
37 #include <errno.h>
38
39 #define wxSelectDispatcher_Trace wxT("selectdispatcher")
40
41 // ============================================================================
42 // implementation
43 // ============================================================================
44
45 // ----------------------------------------------------------------------------
46 // wxSelectSets
47 // ----------------------------------------------------------------------------
48
49 int wxSelectSets::ms_flags[wxSelectSets::Max] =
50 {
51 wxFDIO_INPUT,
52 wxFDIO_OUTPUT,
53 wxFDIO_EXCEPTION,
54 };
55
56 const char *wxSelectSets::ms_names[wxSelectSets::Max] =
57 {
58 "input",
59 "output",
60 "exceptional",
61 };
62
63 wxSelectSets::Callback wxSelectSets::ms_handlers[wxSelectSets::Max] =
64 {
65 &wxFDIOHandler::OnReadWaiting,
66 &wxFDIOHandler::OnWriteWaiting,
67 &wxFDIOHandler::OnExceptionWaiting,
68 };
69
70 wxSelectSets::wxSelectSets()
71 {
72 for ( int n = 0; n < Max; n++ )
73 {
74 wxFD_ZERO(&m_fds[n]);
75 }
76 }
77
78 bool wxSelectSets::HasFD(int fd) const
79 {
80 for ( int n = 0; n < Max; n++ )
81 {
82 if ( wxFD_ISSET(fd, (fd_set*) &m_fds[n]) )
83 return true;
84 }
85
86 return false;
87 }
88
89 bool wxSelectSets::SetFD(int fd, int flags)
90 {
91 wxCHECK_MSG( fd >= 0, false, wxT("invalid descriptor") );
92
93 for ( int n = 0; n < Max; n++ )
94 {
95 if ( flags & ms_flags[n] )
96 {
97 wxFD_SET(fd, &m_fds[n]);
98 }
99 else if ( wxFD_ISSET(fd, (fd_set*) &m_fds[n]) )
100 {
101 wxFD_CLR(fd, &m_fds[n]);
102 }
103 }
104
105 return true;
106 }
107
108 int wxSelectSets::Select(int nfds, struct timeval *tv)
109 {
110 return select(nfds, &m_fds[Read], &m_fds[Write], &m_fds[Except], tv);
111 }
112
113 bool wxSelectSets::Handle(int fd, wxFDIOHandler& handler) const
114 {
115 for ( int n = 0; n < Max; n++ )
116 {
117 if ( wxFD_ISSET(fd, (fd_set*) &m_fds[n]) )
118 {
119 wxLogTrace(wxSelectDispatcher_Trace,
120 wxT("Got %s event on fd %d"), ms_names[n], fd);
121 (handler.*ms_handlers[n])();
122 // callback can modify sets and destroy handler
123 // this forces that one event can be processed at one time
124 return true;
125 }
126 }
127
128 return false;
129 }
130
131 // ----------------------------------------------------------------------------
132 // wxSelectDispatcher
133 // ----------------------------------------------------------------------------
134
135 bool wxSelectDispatcher::RegisterFD(int fd, wxFDIOHandler *handler, int flags)
136 {
137 if ( !wxMappedFDIODispatcher::RegisterFD(fd, handler, flags) )
138 return false;
139
140 if ( !m_sets.SetFD(fd, flags) )
141 return false;
142
143 if ( fd > m_maxFD )
144 m_maxFD = fd;
145
146 wxLogTrace(wxSelectDispatcher_Trace,
147 wxT("Registered fd %d: input:%d, output:%d, exceptional:%d"), fd, (flags & wxFDIO_INPUT) == wxFDIO_INPUT, (flags & wxFDIO_OUTPUT), (flags & wxFDIO_EXCEPTION) == wxFDIO_EXCEPTION);
148 return true;
149 }
150
151 bool wxSelectDispatcher::ModifyFD(int fd, wxFDIOHandler *handler, int flags)
152 {
153 if ( !wxMappedFDIODispatcher::ModifyFD(fd, handler, flags) )
154 return false;
155
156 wxASSERT_MSG( fd <= m_maxFD, wxT("logic error: registered fd > m_maxFD?") );
157
158 wxLogTrace(wxSelectDispatcher_Trace,
159 wxT("Modified fd %d: input:%d, output:%d, exceptional:%d"), fd, (flags & wxFDIO_INPUT) == wxFDIO_INPUT, (flags & wxFDIO_OUTPUT) == wxFDIO_OUTPUT, (flags & wxFDIO_EXCEPTION) == wxFDIO_EXCEPTION);
160 return m_sets.SetFD(fd, flags);
161 }
162
163 bool wxSelectDispatcher::UnregisterFD(int fd)
164 {
165 m_sets.ClearFD(fd);
166
167 if ( !wxMappedFDIODispatcher::UnregisterFD(fd) )
168 return false;
169
170 // remove the handler if we don't need it any more
171 if ( !m_sets.HasFD(fd) )
172 {
173 if ( fd == m_maxFD )
174 {
175 // need to find new max fd
176 m_maxFD = -1;
177 for ( wxFDIOHandlerMap::const_iterator it = m_handlers.begin();
178 it != m_handlers.end();
179 ++it )
180 {
181 if ( it->first > m_maxFD )
182 {
183 m_maxFD = it->first;
184 }
185 }
186 }
187 }
188
189 wxLogTrace(wxSelectDispatcher_Trace,
190 wxT("Removed fd %d, current max: %d"), fd, m_maxFD);
191 return true;
192 }
193
194 int wxSelectDispatcher::ProcessSets(const wxSelectSets& sets)
195 {
196 int numEvents = 0;
197 for ( int fd = 0; fd <= m_maxFD; fd++ )
198 {
199 if ( !sets.HasFD(fd) )
200 continue;
201
202 wxFDIOHandler * const handler = FindHandler(fd);
203 if ( !handler )
204 {
205 wxFAIL_MSG( wxT("NULL handler in wxSelectDispatcher?") );
206 continue;
207 }
208
209 if ( sets.Handle(fd, *handler) )
210 numEvents++;
211 }
212
213 return numEvents;
214 }
215
216 int wxSelectDispatcher::DoSelect(wxSelectSets& sets, int timeout) const
217 {
218 struct timeval tv,
219 *ptv;
220 if ( timeout != TIMEOUT_INFINITE )
221 {
222 ptv = &tv;
223 tv.tv_sec = 0;
224 tv.tv_usec = timeout*1000;
225 }
226 else // no timeout
227 {
228 ptv = NULL;
229 }
230
231 int ret = sets.Select(m_maxFD + 1, ptv);
232
233 // TODO: we need to restart select() in this case but for now just return
234 // as if timeout expired
235 if ( ret == -1 && errno == EINTR )
236 ret = 0;
237
238 return ret;
239 }
240
241 bool wxSelectDispatcher::HasPending() const
242 {
243 wxSelectSets sets(m_sets);
244 return DoSelect(sets, 0) > 0;
245 }
246
247 int wxSelectDispatcher::Dispatch(int timeout)
248 {
249 wxSelectSets sets(m_sets);
250 switch ( DoSelect(sets, timeout) )
251 {
252 case -1:
253 wxLogSysError(_("Failed to monitor I/O channels"));
254 return -1;
255
256 case 0:
257 // timeout expired without anything happening
258 return 0;
259
260 default:
261 return ProcessSets(sets);
262 }
263 }
264
265 #endif // wxUSE_SELECT_DISPATCHER