]> git.saurik.com Git - wxWidgets.git/blame - src/common/socket.cpp
install wxUniv headers in make install and include wxUniv sources in make dist
[wxWidgets.git] / src / common / socket.cpp
CommitLineData
56d8adc0 1/////////////////////////////////////////////////////////////////////////////
f4ada568
GL
2// Name: socket.cpp
3// Purpose: Socket handler classes
56d8adc0 4// Authors: Guilhem Lavaux, Guillermo Rodriguez Garcia
f4ada568 5// Created: April 1997
d3ea6527
GRG
6// Copyright: (C) 1999-1997, Guilhem Lavaux
7// (C) 2000-1999, Guillermo Rodriguez Garcia
f4ada568
GL
8// RCS_ID: $Id$
9// License: see wxWindows license
56d8adc0
GRG
10/////////////////////////////////////////////////////////////////////////////
11
bffc1eaa
GRG
12// ==========================================================================
13// Declarations
14// ==========================================================================
15
384b4373 16#ifdef __GNUG__
f4ada568 17#pragma implementation "socket.h"
f4ada568
GL
18#endif
19
fcc6dddd
JS
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24#pragma hdrstop
25#endif
26
35a4dab7
GL
27#if wxUSE_SOCKETS
28
dc5c1114 29#include "wx/app.h"
56d8adc0
GRG
30#include "wx/defs.h"
31#include "wx/object.h"
32#include "wx/string.h"
33#include "wx/timer.h"
34#include "wx/utils.h"
35#include "wx/module.h"
36#include "wx/log.h"
58c837a4 37#include "wx/intl.h"
f187448d 38#include "wx/event.h"
52a07708
VZ
39
40#if wxUSE_GUI
f187448d 41 #include "wx/gdicmn.h" // for wxPendingDelete
52a07708 42#endif // wxUSE_GUI
81b92e17 43
56d8adc0
GRG
44#include "wx/sckaddr.h"
45#include "wx/socket.h"
3b4183d8 46
bffc1eaa
GRG
47// --------------------------------------------------------------------------
48// macros and constants
49// --------------------------------------------------------------------------
ef57d866 50
dc5c1114
GRG
51// discard buffer
52#define MAX_DISCARD_SIZE (10 * 1024)
96db102a 53
2f10f423
VZ
54// what to do within waits: in wxBase we don't do anything as we don't have
55// the event loop anyhow (for now). In GUI apps we have 2 cases: from the main
56// thread itself we have to call wxYield() to let the events (including the
57// GUI events and the low-level (not wxWindows) events from GSocket) be
58// processed. From another thread it is enough to just call wxThread::Yield()
59// which will give away the rest of our time slice: the explanation is that
60// the events will be processed by the main thread anyhow, without calling
61// wxYield(), but we don't want to eat the CPU time uselessly while sitting
62// in the loop waiting for the data
f187448d 63#if wxUSE_GUI
2f10f423
VZ
64 #if wxUSE_THREADS
65 #define PROCESS_EVENTS() \
66 { \
67 if ( wxThread::IsMain() ) \
68 wxYield(); \
69 else \
70 wxThread::Yield(); \
71 }
72 #else // !wxUSE_THREADS
73 #define PROCESS_EVENTS() wxYield()
74 #endif // wxUSE_THREADS/!wxUSE_THREADS
75#else // !wxUSE_GUI
76 #define PROCESS_EVENTS()
77#endif // wxUSE_GUI/!wxUSE_GUI
96db102a 78
ef57d866 79// --------------------------------------------------------------------------
bffc1eaa 80// wxWin macros
ef57d866 81// --------------------------------------------------------------------------
81b92e17 82
a737331d
GL
83IMPLEMENT_CLASS(wxSocketBase, wxObject)
84IMPLEMENT_CLASS(wxSocketServer, wxSocketBase)
85IMPLEMENT_CLASS(wxSocketClient, wxSocketBase)
dc5c1114 86IMPLEMENT_CLASS(wxDatagramSocket, wxSocketBase)
a737331d 87IMPLEMENT_DYNAMIC_CLASS(wxSocketEvent, wxEvent)
f4ada568 88
bffc1eaa
GRG
89// --------------------------------------------------------------------------
90// private classes
91// --------------------------------------------------------------------------
92
56d8adc0
GRG
93class wxSocketState : public wxObject
94{
a324a7bc 95public:
71622a7a 96 wxSocketFlags m_flags;
bffc1eaa
GRG
97 wxSocketEventFlags m_eventmask;
98 bool m_notify;
99 void *m_clientData;
100#if WXWIN_COMPATIBILITY
81b92e17
GRG
101 wxSocketBase::wxSockCbk m_cbk;
102 char *m_cdata;
bffc1eaa 103#endif // WXWIN_COMPATIBILITY
a324a7bc
GL
104
105public:
106 wxSocketState() : wxObject() {}
107};
108
ef57d866
GRG
109// ==========================================================================
110// wxSocketBase
111// ==========================================================================
112
113// --------------------------------------------------------------------------
114// Ctor and dtor
115// --------------------------------------------------------------------------
56d8adc0 116
71622a7a 117void wxSocketBase::Init()
f4ada568 118{
71622a7a
GRG
119 m_socket = NULL;
120 m_type = wxSOCKET_UNINIT;
121
122 // state
123 m_flags = 0;
124 m_connected =
125 m_establishing =
126 m_reading =
127 m_writing =
128 m_error = FALSE;
129 m_lcount = 0;
130 m_timeout = 600;
131 m_beingDeleted = FALSE;
132
133 // pushback buffer
134 m_unread = NULL;
135 m_unrd_size = 0;
136 m_unrd_cur = 0;
137
138 // events
71622a7a 139 m_id = -1;
f187448d
GRG
140 m_handler = NULL;
141 m_clientData = NULL;
bffc1eaa
GRG
142 m_notify = FALSE;
143 m_eventmask = 0;
144#if WXWIN_COMPATIBILITY
71622a7a
GRG
145 m_cbk = NULL;
146 m_cdata = NULL;
bffc1eaa 147#endif // WXWIN_COMPATIBILITY
f4ada568
GL
148}
149
f187448d
GRG
150wxSocketBase::wxSocketBase()
151{
152 Init();
153}
71622a7a
GRG
154
155wxSocketBase::wxSocketBase(wxSocketFlags flags, wxSocketType type)
f4ada568 156{
71622a7a
GRG
157 Init();
158
159 m_flags = flags;
160 m_type = type;
f4ada568
GL
161}
162
f4ada568
GL
163wxSocketBase::~wxSocketBase()
164{
9181a383
GRG
165 // Just in case the app called Destroy() *and* then deleted
166 // the socket immediately: don't leave dangling pointers.
167#if wxUSE_GUI
168 wxPendingDelete.DeleteObject(this);
169#endif
a737331d 170
56d8adc0 171 // Shutdown and close the socket
9181a383
GRG
172 if (!m_beingDeleted)
173 Close();
f4ada568 174
56d8adc0 175 // Destroy the GSocket object
a324a7bc
GL
176 if (m_socket)
177 GSocket_destroy(m_socket);
9181a383
GRG
178
179 // Free the pushback buffer
180 if (m_unread)
181 free(m_unread);
f4ada568
GL
182}
183
ef57d866
GRG
184bool wxSocketBase::Destroy()
185{
186 // Delayed destruction: the socket will be deleted during the next
187 // idle loop iteration. This ensures that all pending events have
188 // been processed.
ef57d866 189 m_beingDeleted = TRUE;
9181a383
GRG
190
191 // Shutdown and close the socket
ef57d866
GRG
192 Close();
193
bffc1eaa
GRG
194 // Supress events from now on
195 Notify(FALSE);
196
9181a383 197#if wxUSE_GUI
bffc1eaa
GRG
198 if ( !wxPendingDelete.Member(this) )
199 wxPendingDelete.Append(this);
9181a383
GRG
200#else
201 delete this;
202#endif
ef57d866
GRG
203
204 return TRUE;
205}
9181a383 206
ef57d866 207// --------------------------------------------------------------------------
bffc1eaa 208// Basic IO calls
ef57d866
GRG
209// --------------------------------------------------------------------------
210
211// The following IO operations update m_error and m_lcount:
212// {Read, Write, ReadMsg, WriteMsg, Peek, Unread, Discard}
213//
214// TODO: Should Connect, Accept and AcceptWith update m_error?
215
f4ada568
GL
216bool wxSocketBase::Close()
217{
81b92e17 218 // Interrupt pending waits
02d15cad 219 InterruptWait();
81b92e17 220
31528cd3 221 if (m_socket)
8c14576d 222 {
56d8adc0
GRG
223 // Disable callbacks
224 GSocket_UnsetCallback(m_socket, GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG |
225 GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG);
f4ada568 226
56d8adc0 227 // Shutdown the connection
a324a7bc 228 GSocket_Shutdown(m_socket);
f4ada568
GL
229 }
230
ef57d866 231 m_connected = FALSE;
04e1eb03 232 m_establishing = FALSE;
f4ada568
GL
233 return TRUE;
234}
235
f187448d 236wxSocketBase& wxSocketBase::Read(void* buffer, wxUint32 nbytes)
f4ada568 237{
96db102a
GRG
238 // Mask read events
239 m_reading = TRUE;
a324a7bc 240
96db102a 241 m_lcount = _Read(buffer, nbytes);
17aa2bec 242
d3ea6527
GRG
243 // If in wxSOCKET_WAITALL mode, all bytes should have been read.
244 if (m_flags & wxSOCKET_WAITALL)
96db102a
GRG
245 m_error = (m_lcount != nbytes);
246 else
247 m_error = (m_lcount == 0);
f4ada568 248
dc5c1114 249 // Allow read events from now on
96db102a 250 m_reading = FALSE;
17aa2bec 251
96db102a
GRG
252 return *this;
253}
254
f187448d 255wxUint32 wxSocketBase::_Read(void* buffer, wxUint32 nbytes)
96db102a
GRG
256{
257 int total;
258 int ret = 1;
a324a7bc 259
04e1eb03 260 // Try the pushback buffer first
96db102a
GRG
261 total = GetPushback(buffer, nbytes, FALSE);
262 nbytes -= total;
f187448d 263 buffer = (char *)buffer + total;
96db102a 264
5f3629ba
GRG
265 // Return now in one of the following cases:
266 // - the socket is invalid,
267 // - we got all the data,
268 // - we got *some* data and we are not using wxSOCKET_WAITALL.
269 if ( !m_socket ||
270 !nbytes ||
271 ((total != 0) && !(m_flags & wxSOCKET_WAITALL)) )
96db102a 272 return total;
f4ada568 273
af2fd961 274 // Possible combinations (they are checked in this order)
81b92e17 275 // wxSOCKET_NOWAIT
5c9eff30 276 // wxSOCKET_WAITALL (with or without wxSOCKET_BLOCK)
81b92e17
GRG
277 // wxSOCKET_BLOCK
278 // wxSOCKET_NONE
af2fd961 279 //
81b92e17 280 if (m_flags & wxSOCKET_NOWAIT)
af2fd961 281 {
5c9eff30 282 GSocket_SetNonBlocking(m_socket, 1);
f187448d 283 ret = GSocket_Read(m_socket, (char *)buffer, nbytes);
5c9eff30 284 GSocket_SetNonBlocking(m_socket, 0);
af2fd961
GRG
285
286 if (ret > 0)
96db102a 287 total += ret;
af2fd961 288 }
5c9eff30 289 else
56d8adc0 290 {
5c9eff30
GRG
291 bool more = TRUE;
292
293 while (more)
56d8adc0 294 {
5c9eff30
GRG
295 if ( !(m_flags & wxSOCKET_BLOCK) && !WaitForRead() )
296 break;
81b92e17 297
f187448d 298 ret = GSocket_Read(m_socket, (char *)buffer, nbytes);
a324a7bc 299
81b92e17
GRG
300 if (ret > 0)
301 {
302 total += ret;
81b92e17 303 nbytes -= ret;
f187448d 304 buffer = (char *)buffer + ret;
81b92e17 305 }
a324a7bc 306
5c9eff30
GRG
307 // If we got here and wxSOCKET_WAITALL is not set, we can leave
308 // now. Otherwise, wait until we recv all the data or until there
309 // is an error.
310 //
311 more = (ret > 0 && nbytes > 0 && (m_flags & wxSOCKET_WAITALL));
81b92e17 312 }
a324a7bc 313 }
f4ada568 314
96db102a 315 return total;
f4ada568
GL
316}
317
f187448d 318wxSocketBase& wxSocketBase::ReadMsg(void* buffer, wxUint32 nbytes)
062c4861 319{
96db102a
GRG
320 wxUint32 len, len2, sig, total;
321 bool error;
17aa2bec 322 int old_flags;
17aa2bec
GRG
323 struct
324 {
96db102a
GRG
325 unsigned char sig[4];
326 unsigned char len[4];
062c4861
GL
327 } msg;
328
96db102a
GRG
329 // Mask read events
330 m_reading = TRUE;
062c4861 331
96db102a
GRG
332 total = 0;
333 error = TRUE;
17aa2bec 334 old_flags = m_flags;
81b92e17 335 SetFlags((m_flags & wxSOCKET_BLOCK) | wxSOCKET_WAITALL);
17aa2bec 336
f187448d 337 if (_Read(&msg, sizeof(msg)) != sizeof(msg))
96db102a 338 goto exit;
062c4861 339
f187448d 340 sig = (wxUint32)msg.sig[0];
96db102a
GRG
341 sig |= (wxUint32)(msg.sig[1] << 8);
342 sig |= (wxUint32)(msg.sig[2] << 16);
343 sig |= (wxUint32)(msg.sig[3] << 24);
062c4861
GL
344
345 if (sig != 0xfeeddead)
17aa2bec 346 {
5c9eff30 347 wxLogWarning(_("wxSocket: invalid signature in ReadMsg."));
96db102a 348 goto exit;
17aa2bec
GRG
349 }
350
96db102a
GRG
351 len = (wxUint32)msg.len[0];
352 len |= (wxUint32)(msg.len[1] << 8);
353 len |= (wxUint32)(msg.len[2] << 16);
354 len |= (wxUint32)(msg.len[3] << 24);
355
17aa2bec
GRG
356 if (len > nbytes)
357 {
062c4861
GL
358 len2 = len - nbytes;
359 len = nbytes;
360 }
361 else
362 len2 = 0;
363
04e1eb03 364 // Don't attemp to read if the msg was zero bytes long.
96db102a 365 if (len)
17aa2bec 366 {
96db102a
GRG
367 total = _Read(buffer, len);
368
369 if (total != len)
370 goto exit;
17aa2bec
GRG
371 }
372 if (len2)
373 {
96db102a 374 char *discard_buffer = new char[MAX_DISCARD_SIZE];
17aa2bec
GRG
375 long discard_len;
376
96db102a 377 // NOTE: discarded bytes don't add to m_lcount.
17aa2bec
GRG
378 do
379 {
96db102a 380 discard_len = ((len2 > MAX_DISCARD_SIZE)? MAX_DISCARD_SIZE : len2);
479cd5de
VZ
381 discard_len = _Read(discard_buffer, (wxUint32)discard_len);
382 len2 -= (wxUint32)discard_len;
17aa2bec
GRG
383 }
384 while ((discard_len > 0) && len2);
385
386 delete [] discard_buffer;
387
388 if (len2 != 0)
96db102a 389 goto exit;
17aa2bec 390 }
f187448d 391 if (_Read(&msg, sizeof(msg)) != sizeof(msg))
96db102a 392 goto exit;
062c4861 393
96db102a
GRG
394 sig = (wxUint32)msg.sig[0];
395 sig |= (wxUint32)(msg.sig[1] << 8);
396 sig |= (wxUint32)(msg.sig[2] << 16);
397 sig |= (wxUint32)(msg.sig[3] << 24);
a324a7bc 398
062c4861 399 if (sig != 0xdeadfeed)
17aa2bec 400 {
5c9eff30 401 wxLogWarning(_("wxSocket: invalid signature in ReadMsg."));
96db102a 402 goto exit;
17aa2bec 403 }
062c4861 404
96db102a
GRG
405 // everything was OK
406 error = FALSE;
407
408exit:
409 m_error = error;
410 m_lcount = total;
411 m_reading = FALSE;
17aa2bec 412 SetFlags(old_flags);
96db102a 413
062c4861
GL
414 return *this;
415}
416
f187448d 417wxSocketBase& wxSocketBase::Peek(void* buffer, wxUint32 nbytes)
f4ada568 418{
96db102a
GRG
419 // Mask read events
420 m_reading = TRUE;
421
422 m_lcount = _Read(buffer, nbytes);
dc5c1114 423 Pushback(buffer, m_lcount);
96db102a 424
81b92e17
GRG
425 // If in wxSOCKET_WAITALL mode, all bytes should have been read.
426 if (m_flags & wxSOCKET_WAITALL)
96db102a
GRG
427 m_error = (m_lcount != nbytes);
428 else
429 m_error = (m_lcount == 0);
430
dc5c1114 431 // Allow read events again
96db102a 432 m_reading = FALSE;
f4ada568
GL
433
434 return *this;
435}
436
f187448d 437wxSocketBase& wxSocketBase::Write(const void *buffer, wxUint32 nbytes)
f4ada568 438{
96db102a
GRG
439 // Mask write events
440 m_writing = TRUE;
56d8adc0 441
96db102a
GRG
442 m_lcount = _Write(buffer, nbytes);
443
81b92e17
GRG
444 // If in wxSOCKET_WAITALL mode, all bytes should have been written.
445 if (m_flags & wxSOCKET_WAITALL)
96db102a
GRG
446 m_error = (m_lcount != nbytes);
447 else
448 m_error = (m_lcount == 0);
449
dc5c1114 450 // Allow write events again
96db102a
GRG
451 m_writing = FALSE;
452
96db102a
GRG
453 return *this;
454}
455
f187448d 456wxUint32 wxSocketBase::_Write(const void *buffer, wxUint32 nbytes)
96db102a
GRG
457{
458 wxUint32 total = 0;
459 int ret = 1;
56d8adc0 460
5c9eff30
GRG
461 // If the socket is invalid or parameters are ill, return immediately
462 if (!m_socket || !buffer || !nbytes)
96db102a 463 return 0;
a324a7bc 464
af2fd961 465 // Possible combinations (they are checked in this order)
81b92e17 466 // wxSOCKET_NOWAIT
5c9eff30 467 // wxSOCKET_WAITALL (with or without wxSOCKET_BLOCK)
81b92e17
GRG
468 // wxSOCKET_BLOCK
469 // wxSOCKET_NONE
af2fd961 470 //
81b92e17 471 if (m_flags & wxSOCKET_NOWAIT)
af2fd961 472 {
5c9eff30 473 GSocket_SetNonBlocking(m_socket, 1);
f187448d 474 ret = GSocket_Write(m_socket, (const char *)buffer, nbytes);
5c9eff30 475 GSocket_SetNonBlocking(m_socket, 0);
af2fd961
GRG
476
477 if (ret > 0)
96db102a 478 total = ret;
af2fd961 479 }
5c9eff30 480 else
56d8adc0 481 {
5c9eff30
GRG
482 bool more = TRUE;
483
c3e646b4 484 while (more)
56d8adc0 485 {
5c9eff30
GRG
486 if ( !(m_flags & wxSOCKET_BLOCK) && !WaitForWrite() )
487 break;
81b92e17 488
f187448d 489 ret = GSocket_Write(m_socket, (const char *)buffer, nbytes);
5c9eff30 490
81b92e17
GRG
491 if (ret > 0)
492 {
493 total += ret;
81b92e17 494 nbytes -= ret;
f187448d 495 buffer = (const char *)buffer + ret;
81b92e17 496 }
a324a7bc 497
5c9eff30
GRG
498 // If we got here and wxSOCKET_WAITALL is not set, we can leave
499 // now. Otherwise, wait until we send all the data or until there
500 // is an error.
501 //
502 more = (ret > 0 && nbytes > 0 && (m_flags & wxSOCKET_WAITALL));
81b92e17 503 }
56d8adc0 504 }
a324a7bc 505
96db102a 506 return total;
f4ada568
GL
507}
508
f187448d 509wxSocketBase& wxSocketBase::WriteMsg(const void *buffer, wxUint32 nbytes)
062c4861 510{
96db102a
GRG
511 wxUint32 total;
512 bool error;
17aa2bec 513 int old_flags;
f187448d
GRG
514 struct
515 {
96db102a
GRG
516 unsigned char sig[4];
517 unsigned char len[4];
062c4861
GL
518 } msg;
519
96db102a
GRG
520 // Mask write events
521 m_writing = TRUE;
522
523 error = TRUE;
524 total = 0;
525 old_flags = m_flags;
81b92e17 526 SetFlags((m_flags & wxSOCKET_BLOCK) | wxSOCKET_WAITALL);
96db102a 527
96db102a
GRG
528 msg.sig[0] = (unsigned char) 0xad;
529 msg.sig[1] = (unsigned char) 0xde;
530 msg.sig[2] = (unsigned char) 0xed;
531 msg.sig[3] = (unsigned char) 0xfe;
062c4861 532
04e1eb03
GRG
533 msg.len[0] = (unsigned char) (nbytes & 0xff);
534 msg.len[1] = (unsigned char) ((nbytes >> 8) & 0xff);
535 msg.len[2] = (unsigned char) ((nbytes >> 16) & 0xff);
536 msg.len[3] = (unsigned char) ((nbytes >> 24) & 0xff);
17aa2bec 537
f187448d 538 if (_Write(&msg, sizeof(msg)) < sizeof(msg))
96db102a
GRG
539 goto exit;
540
541 total = _Write(buffer, nbytes);
542
543 if (total < nbytes)
544 goto exit;
062c4861 545
96db102a
GRG
546 msg.sig[0] = (unsigned char) 0xed;
547 msg.sig[1] = (unsigned char) 0xfe;
548 msg.sig[2] = (unsigned char) 0xad;
549 msg.sig[3] = (unsigned char) 0xde;
062c4861 550 msg.len[0] = msg.len[1] = msg.len[2] = msg.len[3] = (char) 0;
062c4861 551
f187448d 552 if ((_Write(&msg, sizeof(msg))) < sizeof(msg))
96db102a 553 goto exit;
17aa2bec 554
96db102a
GRG
555 // everything was OK
556 error = FALSE;
557
558exit:
559 m_error = error;
560 m_lcount = total;
561 m_writing = FALSE;
562
062c4861 563 return *this;
062c4861
GL
564}
565
f187448d 566wxSocketBase& wxSocketBase::Unread(const void *buffer, wxUint32 nbytes)
f4ada568 567{
56d8adc0 568 if (nbytes != 0)
96db102a 569 Pushback(buffer, nbytes);
f4ada568 570
96db102a
GRG
571 m_error = FALSE;
572 m_lcount = nbytes;
f4ada568 573
96db102a 574 return *this;
a324a7bc
GL
575}
576
96db102a 577wxSocketBase& wxSocketBase::Discard()
f4ada568 578{
96db102a 579 int old_flags;
04e1eb03
GRG
580 char *buffer = new char[MAX_DISCARD_SIZE];
581 wxUint32 ret;
17aa2bec 582 wxUint32 total = 0;
f4ada568 583
96db102a
GRG
584 // Mask read events
585 m_reading = TRUE;
586
587 old_flags = m_flags;
81b92e17 588 SetFlags(wxSOCKET_NOWAIT);
384b4373 589
04e1eb03 590 do
8c14576d 591 {
04e1eb03
GRG
592 ret = _Read(buffer, MAX_DISCARD_SIZE);
593 total += ret;
f4ada568 594 }
04e1eb03 595 while (ret == MAX_DISCARD_SIZE);
f4ada568 596
04e1eb03 597 delete[] buffer;
17aa2bec 598 m_lcount = total;
04e1eb03 599 m_error = FALSE;
17aa2bec 600
dc5c1114 601 // Allow read events again
96db102a
GRG
602 m_reading = FALSE;
603
96db102a 604 return *this;
f4ada568
GL
605}
606
ef57d866
GRG
607// --------------------------------------------------------------------------
608// Wait functions
609// --------------------------------------------------------------------------
f4ada568 610
ef57d866
GRG
611// All Wait functions poll the socket using GSocket_Select() to
612// check for the specified combination of conditions, until one
613// of these conditions become true, an error occurs, or the
614// timeout elapses. The polling loop calls PROCESS_EVENTS(), so
615// this won't block the GUI.
f4ada568 616
bffc1eaa
GRG
617bool wxSocketBase::_Wait(long seconds,
618 long milliseconds,
d3ea6527 619 wxSocketEventFlags flags)
375abe3d 620{
56d8adc0 621 GSocketEventFlags result;
56d8adc0 622 long timeout;
375abe3d 623
81b92e17
GRG
624 // Set this to TRUE to interrupt ongoing waits
625 m_interrupt = FALSE;
626
ef57d866 627 // Check for valid socket
af2fd961
GRG
628 if (!m_socket)
629 return FALSE;
630
71622a7a 631 // Check for valid timeout value.
56d8adc0
GRG
632 if (seconds != -1)
633 timeout = seconds * 1000 + milliseconds;
634 else
635 timeout = m_timeout * 1000;
a324a7bc 636
bffc1eaa 637 // Wait in an active polling loop.
af2fd961 638 //
bffc1eaa
GRG
639 // NOTE: We duplicate some of the code in OnRequest, but this doesn't
640 // hurt. It has to be here because the (GSocket) event might arrive
641 // a bit delayed, and it has to be in OnRequest as well because we
642 // don't know whether the Wait functions are being used.
af2fd961 643 //
96db102a 644 // Do this at least once (important if timeout == 0, when
d3ea6527 645 // we are just polling). Also, if just polling, do not yield.
ef57d866 646
71622a7a
GRG
647 wxStopWatch chrono;
648 bool done = FALSE;
649
650 while (!done)
56d8adc0 651 {
af2fd961
GRG
652 result = GSocket_Select(m_socket, flags | GSOCK_LOST_FLAG);
653
af2fd961
GRG
654 // Incoming connection (server) or connection established (client)
655 if (result & GSOCK_CONNECTION_FLAG)
656 {
af2fd961
GRG
657 m_connected = TRUE;
658 m_establishing = FALSE;
659 return TRUE;
660 }
661
04e1eb03 662 // Data available or output buffer ready
81b92e17 663 if ((result & GSOCK_INPUT_FLAG) || (result & GSOCK_OUTPUT_FLAG))
af2fd961 664 {
81b92e17 665 return TRUE;
af2fd961
GRG
666 }
667
c0043a50
GRG
668 // Connection lost
669 if (result & GSOCK_LOST_FLAG)
670 {
ef57d866 671 m_connected = FALSE;
04e1eb03 672 m_establishing = FALSE;
378d2bd3 673 return (flags & GSOCK_LOST_FLAG) != 0;
c0043a50
GRG
674 }
675
81b92e17 676 // Wait more?
71622a7a
GRG
677 if ((!timeout) || (chrono.Time() > timeout) || (m_interrupt))
678 done = TRUE;
d3ea6527
GRG
679 else
680 PROCESS_EVENTS();
56d8adc0 681 }
f4ada568 682
af2fd961 683 return FALSE;
f4ada568 684}
f4ada568 685
a737331d 686bool wxSocketBase::Wait(long seconds, long milliseconds)
f4ada568 687{
d80d1aba
GRG
688 return _Wait(seconds, milliseconds, GSOCK_INPUT_FLAG |
689 GSOCK_OUTPUT_FLAG |
690 GSOCK_CONNECTION_FLAG |
691 GSOCK_LOST_FLAG);
f4ada568 692}
f4ada568 693
a737331d 694bool wxSocketBase::WaitForRead(long seconds, long milliseconds)
f4ada568 695{
ef57d866 696 // Check pushback buffer before entering _Wait
96db102a
GRG
697 if (m_unread)
698 return TRUE;
699
ef57d866
GRG
700 // Note that GSOCK_INPUT_LOST has to be explicitly passed to
701 // _Wait becuase of the semantics of WaitForRead: a return
702 // value of TRUE means that a GSocket_Read call will return
703 // immediately, not that there is actually data to read.
704
705 return _Wait(seconds, milliseconds, GSOCK_INPUT_FLAG |
706 GSOCK_LOST_FLAG);
f4ada568
GL
707}
708
a737331d 709bool wxSocketBase::WaitForWrite(long seconds, long milliseconds)
f4ada568 710{
af2fd961 711 return _Wait(seconds, milliseconds, GSOCK_OUTPUT_FLAG);
a737331d 712}
f4ada568 713
a737331d
GL
714bool wxSocketBase::WaitForLost(long seconds, long milliseconds)
715{
a324a7bc 716 return _Wait(seconds, milliseconds, GSOCK_LOST_FLAG);
f4ada568 717}
a737331d 718
ef57d866
GRG
719// --------------------------------------------------------------------------
720// Miscellaneous
721// --------------------------------------------------------------------------
722
723//
724// Get local or peer address
725//
726
727bool wxSocketBase::GetPeer(wxSockAddress& addr_man) const
728{
729 GAddress *peer;
730
731 if (!m_socket)
732 return FALSE;
733
734 peer = GSocket_GetPeer(m_socket);
735 addr_man.SetAddress(peer);
736 GAddress_destroy(peer);
737
738 return TRUE;
739}
740
741bool wxSocketBase::GetLocal(wxSockAddress& addr_man) const
742{
743 GAddress *local;
744
745 if (!m_socket)
746 return FALSE;
747
748 local = GSocket_GetLocal(m_socket);
749 addr_man.SetAddress(local);
750 GAddress_destroy(local);
751
752 return TRUE;
753}
754
755//
756// Save and restore socket state
757//
758
759void wxSocketBase::SaveState()
760{
761 wxSocketState *state;
762
763 state = new wxSocketState();
764
bffc1eaa
GRG
765 state->m_flags = m_flags;
766 state->m_notify = m_notify;
767 state->m_eventmask = m_eventmask;
768 state->m_clientData = m_clientData;
769#if WXWIN_COMPATIBILITY
770 state->m_cbk = m_cbk;
771 state->m_cdata = m_cdata;
772#endif // WXWIN_COMPATIBILITY
ef57d866
GRG
773
774 m_states.Append(state);
775}
776
777void wxSocketBase::RestoreState()
778{
779 wxNode *node;
780 wxSocketState *state;
781
782 node = m_states.Last();
783 if (!node)
784 return;
785
786 state = (wxSocketState *)node->Data();
787
bffc1eaa
GRG
788 m_flags = state->m_flags;
789 m_notify = state->m_notify;
790 m_eventmask = state->m_eventmask;
791 m_clientData = state->m_clientData;
792#if WXWIN_COMPATIBILITY
793 m_cbk = state->m_cbk;
794 m_cdata = state->m_cdata;
795#endif // WXWIN_COMPATIBILITY
ef57d866
GRG
796
797 delete node;
798 delete state;
799}
800
801//
802// Timeout and flags
803//
804
17aa2bec
GRG
805void wxSocketBase::SetTimeout(long seconds)
806{
807 m_timeout = seconds;
808
809 if (m_socket)
e8f4c584 810 GSocket_SetTimeout(m_socket, m_timeout * 1000);
17aa2bec
GRG
811}
812
71622a7a 813void wxSocketBase::SetFlags(wxSocketFlags flags)
f4ada568 814{
71622a7a 815 m_flags = flags;
f4ada568 816}
f0a56ab0 817
f187448d 818
ef57d866
GRG
819// --------------------------------------------------------------------------
820// Callbacks (now obsolete - use events instead)
821// --------------------------------------------------------------------------
56d8adc0 822
bffc1eaa
GRG
823#if WXWIN_COMPATIBILITY
824
56d8adc0 825wxSocketBase::wxSockCbk wxSocketBase::Callback(wxSockCbk cbk_)
f4ada568 826{
56d8adc0 827 wxSockCbk old_cbk = cbk_;
f4ada568 828
56d8adc0
GRG
829 m_cbk = cbk_;
830 return old_cbk;
831}
832
833char *wxSocketBase::CallbackData(char *data)
834{
835 char *old_data = m_cdata;
836
837 m_cdata = data;
838 return old_data;
f4ada568
GL
839}
840
bffc1eaa
GRG
841#endif // WXWIN_COMPATIBILITY
842
ef57d866 843// --------------------------------------------------------------------------
f187448d 844// Event handling
ef57d866 845// --------------------------------------------------------------------------
a324a7bc 846
bffc1eaa
GRG
847// A note on how events are processed, which is probably the most
848// difficult thing to get working right while keeping the same API
849// and functionality for all platforms.
850//
851// When GSocket detects an event, it calls wx_socket_callback, which in
852// turn just calls wxSocketBase::OnRequest in the corresponding wxSocket
853// object. OnRequest does some housekeeping, and if the event is to be
854// propagated to the user, it creates a new wxSocketEvent object and
855// posts it. The event is not processed immediately, but delayed with
856// AddPendingEvent instead. This is necessary in order to decouple the
857// event processing from wx_socket_callback; otherwise, subsequent IO
858// calls made from the user event handler would fail, as gtk callbacks
859// are not reentrant.
860//
861// Note that, unlike events, user callbacks (now deprecated) are _not_
862// decoupled from wx_socket_callback and thus they suffer from a variety
863// of problems. Avoid them where possible and use events instead.
96db102a 864
90350682
VZ
865extern "C"
866void LINKAGEMODE wx_socket_callback(GSocket * WXUNUSED(socket),
867 GSocketEvent notification,
868 char *cdata)
a324a7bc
GL
869{
870 wxSocketBase *sckobj = (wxSocketBase *)cdata;
871
bffc1eaa 872 sckobj->OnRequest((wxSocketNotify) notification);
56d8adc0 873}
a324a7bc 874
bffc1eaa 875void wxSocketBase::OnRequest(wxSocketNotify notification)
f4ada568 876{
bffc1eaa
GRG
877 // NOTE: We duplicate some of the code in _Wait, but this doesn't
878 // hurt. It has to be here because the (GSocket) event might arrive
879 // a bit delayed, and it has to be in _Wait as well because we don't
880 // know whether the Wait functions are being used.
ef57d866 881
bffc1eaa 882 switch(notification)
d80d1aba 883 {
04e1eb03
GRG
884 case wxSOCKET_CONNECTION:
885 m_establishing = FALSE;
886 m_connected = TRUE;
887 break;
888
d3ea6527
GRG
889 // If we are in the middle of a R/W operation, do not
890 // propagate events to users. Also, filter 'late' events
891 // which are no longer valid.
ef57d866 892
04e1eb03
GRG
893 case wxSOCKET_INPUT:
894 if (m_reading || !GSocket_Select(m_socket, GSOCK_INPUT_FLAG))
895 return;
896 break;
897
898 case wxSOCKET_OUTPUT:
899 if (m_writing || !GSocket_Select(m_socket, GSOCK_OUTPUT_FLAG))
900 return;
901 break;
902
903 case wxSOCKET_LOST:
904 m_connected = FALSE;
905 m_establishing = FALSE;
906 break;
907
908 default:
909 break;
96db102a
GRG
910 }
911
f187448d
GRG
912 // Schedule the event
913
52fbdb58 914 wxSocketEventFlags flag = 0;
bffc1eaa 915 switch (notification)
f187448d
GRG
916 {
917 case GSOCK_INPUT: flag = GSOCK_INPUT_FLAG; break;
918 case GSOCK_OUTPUT: flag = GSOCK_OUTPUT_FLAG; break;
919 case GSOCK_CONNECTION: flag = GSOCK_CONNECTION_FLAG; break;
920 case GSOCK_LOST: flag = GSOCK_LOST_FLAG; break;
52fbdb58 921 default:
5c9eff30 922 wxLogWarning(_("wxSocket: unknown event!."));
52fbdb58 923 return;
f187448d
GRG
924 }
925
bffc1eaa 926 if (((m_eventmask & flag) == flag) && m_notify)
d80d1aba 927 {
f187448d 928 if (m_handler)
bffc1eaa
GRG
929 {
930 wxSocketEvent event(m_id);
931 event.m_event = notification;
932 event.m_clientData = m_clientData;
933 event.SetEventObject(this);
934
f187448d 935 m_handler->AddPendingEvent(event);
bffc1eaa 936 }
56d8adc0 937
bffc1eaa 938#if WXWIN_COMPATIBILITY
56d8adc0 939 if (m_cbk)
bffc1eaa
GRG
940 m_cbk(*this, notification, m_cdata);
941#endif // WXWIN_COMPATIBILITY
f4ada568 942 }
f4ada568
GL
943}
944
f187448d
GRG
945void wxSocketBase::Notify(bool notify)
946{
bffc1eaa 947 m_notify = notify;
f187448d
GRG
948}
949
950void wxSocketBase::SetNotify(wxSocketEventFlags flags)
951{
bffc1eaa 952 m_eventmask = flags;
f187448d
GRG
953}
954
71622a7a 955void wxSocketBase::SetEventHandler(wxEvtHandler& handler, int id)
f4ada568 956{
71622a7a
GRG
957 m_handler = &handler;
958 m_id = id;
f4ada568
GL
959}
960
ef57d866
GRG
961// --------------------------------------------------------------------------
962// Pushback buffer
963// --------------------------------------------------------------------------
db131261 964
f187448d 965void wxSocketBase::Pushback(const void *buffer, wxUint32 size)
f4ada568 966{
dc5c1114
GRG
967 if (!size) return;
968
a324a7bc 969 if (m_unread == NULL)
f187448d 970 m_unread = malloc(size);
dc5c1114
GRG
971 else
972 {
f187448d 973 void *tmp;
f4ada568 974
f187448d
GRG
975 tmp = malloc(m_unrd_size + size);
976 memcpy((char *)tmp + size, m_unread, m_unrd_size);
41895a05 977 free(m_unread);
a324a7bc
GL
978
979 m_unread = tmp;
41895a05 980 }
31528cd3 981
f4ada568 982 m_unrd_size += size;
a324a7bc
GL
983
984 memcpy(m_unread, buffer, size);
f4ada568
GL
985}
986
f187448d 987wxUint32 wxSocketBase::GetPushback(void *buffer, wxUint32 size, bool peek)
f4ada568
GL
988{
989 if (!m_unrd_size)
990 return 0;
991
a324a7bc
GL
992 if (size > (m_unrd_size-m_unrd_cur))
993 size = m_unrd_size-m_unrd_cur;
96db102a 994
f187448d 995 memcpy(buffer, (char *)m_unread + m_unrd_cur, size);
f4ada568 996
dc5c1114
GRG
997 if (!peek)
998 {
a324a7bc 999 m_unrd_cur += size;
dc5c1114
GRG
1000 if (m_unrd_size == m_unrd_cur)
1001 {
f4ada568
GL
1002 free(m_unread);
1003 m_unread = NULL;
a324a7bc
GL
1004 m_unrd_size = 0;
1005 m_unrd_cur = 0;
f4ada568
GL
1006 }
1007 }
1008
1009 return size;
1010}
1011
f187448d 1012
ef57d866 1013// ==========================================================================
c3e646b4 1014// wxSocketServer
ef57d866 1015// ==========================================================================
f4ada568 1016
ef57d866
GRG
1017// --------------------------------------------------------------------------
1018// Ctor
1019// --------------------------------------------------------------------------
56d8adc0 1020
f4ada568 1021wxSocketServer::wxSocketServer(wxSockAddress& addr_man,
71622a7a
GRG
1022 wxSocketFlags flags)
1023 : wxSocketBase(flags, wxSOCKET_SERVER)
f4ada568 1024{
56d8adc0 1025 // Create the socket
a324a7bc 1026 m_socket = GSocket_new();
f4ada568 1027
a324a7bc 1028 if (!m_socket)
f4ada568 1029 return;
384b4373 1030
56d8adc0 1031 // Setup the socket as server
a324a7bc 1032 GSocket_SetLocal(m_socket, addr_man.GetAddress());
56d8adc0
GRG
1033 if (GSocket_SetServer(m_socket) != GSOCK_NOERROR)
1034 {
a324a7bc
GL
1035 GSocket_destroy(m_socket);
1036 m_socket = NULL;
f4ada568
GL
1037 return;
1038 }
a737331d 1039
e8f4c584 1040 GSocket_SetTimeout(m_socket, m_timeout * 1000);
56d8adc0
GRG
1041 GSocket_SetCallback(m_socket, GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG |
1042 GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG,
1043 wx_socket_callback, (char *)this);
1044
f4ada568
GL
1045}
1046
ef57d866
GRG
1047// --------------------------------------------------------------------------
1048// Accept
1049// --------------------------------------------------------------------------
8c14576d 1050
d80d1aba 1051bool wxSocketServer::AcceptWith(wxSocketBase& sock, bool wait)
f4ada568 1052{
a324a7bc 1053 GSocket *child_socket;
f4ada568 1054
17aa2bec
GRG
1055 if (!m_socket)
1056 return FALSE;
1057
04e1eb03 1058 // If wait == FALSE, then the call should be nonblocking.
56d8adc0
GRG
1059 // When we are finished, we put the socket to blocking mode
1060 // again.
d80d1aba
GRG
1061
1062 if (!wait)
5c9eff30 1063 GSocket_SetNonBlocking(m_socket, 1);
d80d1aba 1064
a324a7bc 1065 child_socket = GSocket_WaitConnection(m_socket);
384b4373 1066
d80d1aba 1067 if (!wait)
5c9eff30 1068 GSocket_SetNonBlocking(m_socket, 0);
d80d1aba 1069
04e1eb03 1070 if (!child_socket)
d80d1aba
GRG
1071 return FALSE;
1072
71622a7a 1073 sock.m_type = wxSOCKET_BASE;
a324a7bc 1074 sock.m_socket = child_socket;
f4ada568
GL
1075 sock.m_connected = TRUE;
1076
e8f4c584 1077 GSocket_SetTimeout(sock.m_socket, sock.m_timeout * 1000);
56d8adc0
GRG
1078 GSocket_SetCallback(sock.m_socket, GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG |
1079 GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG,
1080 wx_socket_callback, (char *)&sock);
1081
f4ada568
GL
1082 return TRUE;
1083}
1084
d80d1aba 1085wxSocketBase *wxSocketServer::Accept(bool wait)
f4ada568
GL
1086{
1087 wxSocketBase* sock = new wxSocketBase();
1088
71622a7a 1089 sock->SetFlags(m_flags);
f4ada568 1090
d80d1aba 1091 if (!AcceptWith(*sock, wait))
c3e646b4
VZ
1092 {
1093 sock->Destroy();
1094 sock = NULL;
1095 }
f4ada568 1096
f4ada568
GL
1097 return sock;
1098}
1099
17aa2bec 1100bool wxSocketServer::WaitForAccept(long seconds, long milliseconds)
d80d1aba 1101{
af2fd961 1102 return _Wait(seconds, milliseconds, GSOCK_CONNECTION_FLAG);
d80d1aba
GRG
1103}
1104
ef57d866 1105// ==========================================================================
8c14576d 1106// wxSocketClient
ef57d866 1107// ==========================================================================
f4ada568 1108
ef57d866
GRG
1109// --------------------------------------------------------------------------
1110// Ctor and dtor
1111// --------------------------------------------------------------------------
56d8adc0 1112
71622a7a
GRG
1113wxSocketClient::wxSocketClient(wxSocketFlags flags)
1114 : wxSocketBase(flags, wxSOCKET_CLIENT)
f4ada568
GL
1115{
1116}
1117
f4ada568
GL
1118wxSocketClient::~wxSocketClient()
1119{
1120}
1121
ef57d866
GRG
1122// --------------------------------------------------------------------------
1123// Connect
1124// --------------------------------------------------------------------------
dc5c1114 1125
791b24c4 1126bool wxSocketClient::Connect(wxSockAddress& addr_man, bool wait)
f4ada568 1127{
791b24c4
GRG
1128 GSocketError err;
1129
ef57d866 1130 if (m_socket)
04e1eb03
GRG
1131 {
1132 // Shutdown and destroy the socket
f4ada568 1133 Close();
a324a7bc 1134 GSocket_destroy(m_socket);
04e1eb03 1135 }
a324a7bc 1136
a324a7bc 1137 m_socket = GSocket_new();
d80d1aba
GRG
1138 m_connected = FALSE;
1139 m_establishing = FALSE;
384b4373 1140
a324a7bc 1141 if (!m_socket)
f4ada568 1142 return FALSE;
384b4373 1143
e8f4c584 1144 GSocket_SetTimeout(m_socket, m_timeout * 1000);
96db102a
GRG
1145 GSocket_SetCallback(m_socket, GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG |
1146 GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG,
1147 wx_socket_callback, (char *)this);
56d8adc0 1148
04e1eb03 1149 // If wait == FALSE, then the call should be nonblocking.
56d8adc0
GRG
1150 // When we are finished, we put the socket to blocking mode
1151 // again.
f4ada568 1152
791b24c4 1153 if (!wait)
5c9eff30 1154 GSocket_SetNonBlocking(m_socket, 1);
791b24c4 1155
a324a7bc 1156 GSocket_SetPeer(m_socket, addr_man.GetAddress());
791b24c4
GRG
1157 err = GSocket_Connect(m_socket, GSOCK_STREAMED);
1158
1159 if (!wait)
5c9eff30 1160 GSocket_SetNonBlocking(m_socket, 0);
791b24c4
GRG
1161
1162 if (err != GSOCK_NOERROR)
56d8adc0
GRG
1163 {
1164 if (err == GSOCK_WOULDBLOCK)
1165 m_establishing = TRUE;
1166
f4ada568 1167 return FALSE;
56d8adc0 1168 }
9111db68 1169
f4ada568
GL
1170 m_connected = TRUE;
1171 return TRUE;
1172}
1173
d80d1aba 1174bool wxSocketClient::WaitOnConnect(long seconds, long milliseconds)
f4ada568 1175{
af2fd961 1176 if (m_connected) // Already connected
d80d1aba
GRG
1177 return TRUE;
1178
af2fd961 1179 if (!m_establishing || !m_socket) // No connection in progress
d80d1aba
GRG
1180 return FALSE;
1181
2b396caa
GRG
1182 return _Wait(seconds, milliseconds, GSOCK_CONNECTION_FLAG |
1183 GSOCK_LOST_FLAG);
f4ada568
GL
1184}
1185
ef57d866 1186// ==========================================================================
dc5c1114 1187// wxDatagramSocket
ef57d866 1188// ==========================================================================
dc5c1114
GRG
1189
1190/* NOTE: experimental stuff - might change */
1191
71622a7a
GRG
1192wxDatagramSocket::wxDatagramSocket( wxSockAddress& addr,
1193 wxSocketFlags flags )
1194 : wxSocketBase( flags, wxSOCKET_DATAGRAM )
dc5c1114
GRG
1195{
1196 // Create the socket
1197 m_socket = GSocket_new();
1198
1199 if(!m_socket)
1200 return;
1201
1202 // Setup the socket as non connection oriented
1203 GSocket_SetLocal(m_socket, addr.GetAddress());
1204 if( GSocket_SetNonOriented(m_socket) != GSOCK_NOERROR )
1205 {
1206 GSocket_destroy(m_socket);
1207 m_socket = NULL;
1208 return;
1209 }
1210
1211 // Initialize all stuff
1212 m_connected = FALSE;
1213 m_establishing = FALSE;
1214 GSocket_SetTimeout( m_socket, m_timeout );
1215 GSocket_SetCallback( m_socket, GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG |
1216 GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG,
1217 wx_socket_callback, (char*)this );
1218
1219}
1220
1221wxDatagramSocket& wxDatagramSocket::RecvFrom( wxSockAddress& addr,
f187448d 1222 void* buf,
dc5c1114
GRG
1223 wxUint32 nBytes )
1224{
1225 Read(buf, nBytes);
1226 GetPeer(addr);
1227 return (*this);
1228}
1229
1230wxDatagramSocket& wxDatagramSocket::SendTo( wxSockAddress& addr,
f187448d 1231 const void* buf,
dc5c1114
GRG
1232 wxUint32 nBytes )
1233{
1234 GSocket_SetPeer(m_socket, addr.GetAddress());
1235 Write(buf, nBytes);
1236 return (*this);
1237}
1238
ef57d866 1239// ==========================================================================
a58d5df4 1240// wxSocketModule
ef57d866 1241// ==========================================================================
dc5c1114 1242
bffc1eaa 1243class WXDLLEXPORT wxSocketModule : public wxModule
dc5c1114 1244{
a58d5df4 1245 DECLARE_DYNAMIC_CLASS(wxSocketModule)
04e1eb03 1246
dc5c1114 1247public:
1f0500b3 1248 bool OnInit() { return GSocket_Init() != 0; }
04e1eb03 1249 void OnExit() { GSocket_Cleanup(); }
a58d5df4
GL
1250};
1251
1252IMPLEMENT_DYNAMIC_CLASS(wxSocketModule, wxModule)
1253
35a4dab7
GL
1254#endif
1255 // wxUSE_SOCKETS