2
// Copyright ļæ½ 1997 - 2001, Paul C. Gregory
4
// Contact: pgregory@aqsis.com
6
// This library is free software; you can redistribute it and/or
7
// modify it under the terms of the GNU General Public
8
// License as published by the Free Software Foundation; either
9
// version 2 of the License, or (at your option) any later version.
11
// This library is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
// General Public License for more details.
16
// You should have received a copy of the GNU General Public
17
// License along with this library; if not, write to the Free Software
18
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
\brief Implements the base message handling functionality required by display drivers.
23
\author Paul C. Gregory (pgregory@aqsis.com)
24
\author Timothy M. Shead (tshead@k-3d.com)
33
#ifdef AQSIS_SYSTEM_WIN32
37
#else // AQSIS_SYSTEM_WIN32
42
#include <netinet/in.h>
44
#include <sys/types.h>
45
#include <sys/socket.h>
48
typedef sockaddr_in SOCKADDR_IN;
49
typedef sockaddr* PSOCKADDR;
51
static const int INVALID_SOCKET = -1;
52
static const int SOCKET_ERROR = -1;
53
static const int SD_BOTH = 2;
55
#endif // !AQSIS_SYSTEM_WIN32
58
#include "displaydriver.h"
60
using namespace Aqsis;
62
/** External function to handle a query message, must be provided by linking application.
64
TqInt Query( SOCKET s, SqDDMessageBase* pMsg );
65
/** External function to handle an open message, must be provided by linking application.
67
TqInt Open( SOCKET s, SqDDMessageBase* pMsg );
68
/** External function to handle a data message, must be provided by linking application.
70
TqInt Data( SOCKET s, SqDDMessageBase* pMsg );
71
/** External function to handle a close message, must be provided by linking application.
73
TqInt Close( SOCKET s, SqDDMessageBase* pMsg );
74
/** External function to handle an abandon message, must be provided by linking application.
76
TqInt Abandon( SOCKET s, SqDDMessageBase* pMsg );
77
/** External function to handle a general message, must be provided by linking application.
79
TqInt HandleMessage( SOCKET s, SqDDMessageBase* pMsg );
81
START_NAMESPACE( Aqsis )
83
static void CloseSocket( SOCKET& Socket );
85
/// Hides incompatibilities between sockets and WinSock
86
void InitializeSockets()
88
#ifdef AQSIS_SYSTEM_WIN32
90
WSAStartup( MAKEWORD( 2, 0 ), &wsaData );
91
#endif // AQSIS_SYSTEM_WIN32
94
/// Hides incompatibilities amongst platforms
95
hostent* GetHostByName(const std::string& HostName)
97
#ifdef AQSIS_SYSTEM_MACOSX
98
// Remove this conditional section and use gethostbyname() if Apple ever
99
// fixes the problem of resolving localhost without a connection to a DNS server
100
return gethostent(); // assumes localhost defined first in /etc/hosts
101
#else // AQSIS_SYSTEM_MACOSX
102
return gethostbyname( HostName.c_str() );
103
#endif // !AQSIS_SYSTEM_MACOSX
107
//----------------------------------------------------------------------
108
/** Receive a specified length of data from the specified socket.
111
TqInt DDReceiveSome( TqInt s, void* buffer, TqInt len )
113
TqInt tot = 0, need = len;
117
if ( ( n = recv( s, reinterpret_cast<char*>( buffer ) + tot, need, 0 ) ) >0 )
129
//----------------------------------------------------------------------
130
/** Receive a formatted message from the specified socket.
133
TqInt DDReceiveMsg( TqInt s, SqDDMessageBase*& pMsg )
135
SqDDMessageBase msghdr;
139
if ( ( ret = DDReceiveSome( s, &msghdr, sizeof( msghdr ) ) ) > 0 )
141
// Allocate space for the message.
142
char * msgbuffer = new char[ msghdr.m_MessageLength ];
144
memcpy( msgbuffer, &msghdr, ret );
145
pMsg = reinterpret_cast<SqDDMessageBase*>( msgbuffer );
147
if ( ret < msghdr.m_MessageLength )
149
if ( ( ret = DDReceiveSome( s, msgbuffer + ret, msghdr.m_MessageLength - ret ) ) <= 0 )
151
delete[] ( msgbuffer );
156
return ( msghdr.m_MessageLength );
163
//----------------------------------------------------------------------
164
/** Send a specified length of data to the specified socket.
167
TqInt DDSendSome( TqInt s, void* buffer, TqInt len )
169
TqInt tot = 0, need = len;
173
if ( ( n = send( s, reinterpret_cast<char*>( buffer ) + tot, need, 0 ) ) >0 )
185
//----------------------------------------------------------------------
186
/** Send a preformatted message to the specified port.
189
TqInt DDSendMsg( TqInt s, SqDDMessageBase* pMsg )
191
return ( DDSendSome( s, pMsg, pMsg->m_MessageLength ) );
195
static SOCKET g_Socket = INVALID_SOCKET;
197
//----------------------------------------------------------------------
198
/** Enter a loop processing messages from the server.
201
TqInt DDInitialise( const TqChar* phostname, TqInt port )
206
g_Socket = socket( AF_INET, SOCK_STREAM, 0 );
207
if ( g_Socket == INVALID_SOCKET )
210
// If no host name specified, use the local machine
211
std::string hostName(phostname ? phostname : "");
214
hostName.resize(256);
215
gethostname( &hostName[0], hostName.size() );
216
hostName.resize(strlen(hostName.c_str()));
219
hostent* const pHost = GetHostByName(hostName);
222
memset( &saTemp, 0, sizeof( saTemp ) );
223
saTemp.sin_family = AF_INET;
228
saTemp.sin_port = htons( port );
229
memcpy( &saTemp.sin_addr, pHost->h_addr, pHost->h_length );
231
if(SOCKET_ERROR == connect( g_Socket, PSOCKADDR(&saTemp), sizeof(saTemp)))
233
std::cerr << error << "Connecting to " << hostName.c_str() << ":" << port << " ... " << strerror(errno) << std::endl;
234
CloseSocket(g_Socket);
241
//----------------------------------------------------------------------
242
/** Process a single message synchronously (blocks), returning false iff there are no more messages to process
245
bool DDProcessMessage()
247
SqDDMessageBase * message = 0;
248
const TqInt length = DDReceiveMsg( g_Socket, message );
251
// Connection closed gracefully by server ...
252
CloseSocket( g_Socket );
255
else if ( length < 0 )
257
std::cerr << "Error reading from socket" << std::endl;
258
CloseSocket( g_Socket );
262
// Make sure our message gets deallocated ...
263
std::auto_ptr<SqDDMessageBase> messagestorage( message );
265
switch ( message->m_MessageID )
267
case MessageID_FormatQuery:
269
if ( 0 != Query( g_Socket, message ) )
271
CloseSocket( g_Socket );
279
if ( 0 != Open( g_Socket, message ) )
281
CloseSocket( g_Socket );
289
if ( 0 != Data( g_Socket, message ) )
291
CloseSocket( g_Socket );
297
case MessageID_Close:
299
if ( 0 != Close( g_Socket, message ) )
301
CloseSocket( g_Socket );
303
#ifdef DD_EXIT_AT_CLOSE
304
// Define DD_EXIT_AT_CLOSE for display device exit when rendering done
315
if ( 0 != HandleMessage( g_Socket, message ) )
317
CloseSocket( g_Socket );
327
//----------------------------------------------------------------------
328
/** Process a single message asynchronously (returns after the given timeout if there are no messages to process),
329
returning false iff there are no more messages to process
332
bool DDProcessMessageAsync( const TqUint TimeoutSeconds, const TqUint TimeoutMicroSeconds )
334
// Check to see if we have anything waiting ...
337
FD_SET( g_Socket, &files );
340
timeout.tv_sec = TimeoutSeconds;
341
timeout.tv_usec = TimeoutMicroSeconds;
343
const int ready = select( g_Socket + 1, &files, 0, 0, &timeout );
347
// We've got data waiting, so process it normally ...
348
return DDProcessMessage();
351
//----------------------------------------------------------------------
352
/** Enter a loop processing messages from the server.
355
TqInt DDProcessMessages()
359
SqDDMessageBase * pMsg;
362
if ( ( len = DDReceiveMsg( g_Socket, pMsg ) ) > 0 )
364
switch ( pMsg->m_MessageID )
366
case MessageID_FormatQuery:
368
if ( ( ret = Query( g_Socket, pMsg ) ) != 0 )
370
CloseSocket( g_Socket );
378
if ( ( ret = Open( g_Socket, pMsg ) ) != 0 )
380
CloseSocket( g_Socket );
388
if ( ( ret = Data( g_Socket, pMsg ) ) != 0 )
390
CloseSocket( g_Socket );
396
case MessageID_Close:
398
if ( ( ret = Close( g_Socket, pMsg ) ) != 0 )
400
CloseSocket( g_Socket );
402
#ifdef DD_EXIT_AT_CLOSE
403
// Define DD_EXIT_AT_CLOSE for display device exit when rendering done
412
case MessageID_Abandon:
414
if ( ( ret = Abandon( g_Socket, pMsg ) ) != 0 )
416
CloseSocket( g_Socket );
418
#ifdef DD_EXIT_AT_CLOSE
419
// Define DD_EXIT_AT_CLOSE for display device exit when rendering done
430
if ( ( ret = HandleMessage( g_Socket, pMsg ) ) != 0 )
432
CloseSocket( g_Socket );
441
// Connection closed gracefully by server.
442
CloseSocket( g_Socket );
447
std::cerr << "Error reading from socket" << std::endl;
448
CloseSocket( g_Socket );
454
/** Close the given socket.
456
static void CloseSocket( SOCKET& Socket )
458
#ifdef AQSIS_SYSTEM_WIN32
463
setsockopt( Socket, SOL_SOCKET, SO_LINGER, reinterpret_cast<const char*>( &ling ), sizeof( ling ) );
464
shutdown( Socket, SD_BOTH );
465
closesocket( Socket );
466
#else // AQSIS_SYSTEM_WIN32
467
shutdown( Socket, SD_BOTH );
469
#endif // !AQSIS_SYSTEM_WIN32
471
Socket = INVALID_SOCKET;
475
//---------------------------------------------------------------------
477
END_NAMESPACE( Aqsis )