zenilib  0.5.3.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
chat.cpp
Go to the documentation of this file.
1 /*
2  CHAT: A chat client using the SDL example network and GUI libraries
3  Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
4 
5  This software is provided 'as-is', without any express or implied
6  warranty. In no event will the authors be held liable for any damages
7  arising from the use of this software.
8 
9  Permission is granted to anyone to use this software for any purpose,
10  including commercial applications, and to alter it and redistribute it
11  freely, subject to the following restrictions:
12 
13  1. The origin of this software must not be misrepresented; you must not
14  claim that you wrote the original software. If you use this software
15  in a product, an acknowledgment in the product documentation would be
16  appreciated but is not required.
17  2. Altered source versions must be plainly marked as such, and must not be
18  misrepresented as being the original software.
19  3. This notice may not be removed or altered from any source distribution.
20 */
21 
22 /* Note that this isn't necessarily the way to run a chat system.
23  This is designed to excercise the network code more than be really
24  functional.
25 */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include "SDL_net.h"
32 #ifdef macintosh
33 #include "GUI.h"
34 #include "GUI_widgets.h"
35 #else
36 #include <GUI/GUI.h>
37 #include <GUI/GUI_widgets.h>
38 #endif
39 #include "chat.h"
40 
41 
42 /* Global variables */
46 static UDPpacket **packets = NULL;
47 static struct {
48  int active;
49  Uint8 name[256+1];
51 
52 static GUI *gui = NULL;
53 static GUI_TermWin *termwin;
54 static GUI_TermWin *sendwin;
60 };
62  "quit.bmp", "scroll_up.bmp", "scroll_dn.bmp"
63 };
65 
66 
67 void SendHello(char *name)
68 {
69  IPaddress *myip;
70  char hello[1+1+256];
71  int i, n;
72 
73  /* No people are active at first */
74  for ( i=0; i<CHAT_MAXPEOPLE; ++i ) {
75  people[i].active = 0;
76  }
77  if ( tcpsock != NULL ) {
78  /* Get our chat handle */
79  if ( (name == NULL) &&
80  ((name=getenv("CHAT_USER")) == NULL) &&
81  ((name=getenv("USER")) == NULL ) ) {
82  name="Unknown";
83  }
84  termwin->AddText("Using name '%s'\n", name);
85 
86  /* Construct the packet */
87  hello[0] = CHAT_HELLO;
89  memcpy(&hello[CHAT_HELLO_PORT], &myip->port, 2);
90  if ( strlen(name) > 255 ) {
91  n = 255;
92  } else {
93  n = strlen(name);
94  }
95  hello[CHAT_HELLO_NLEN] = n;
96  strncpy(&hello[CHAT_HELLO_NAME], name, n);
97  hello[CHAT_HELLO_NAME+n++] = 0;
98 
99  /* Send it to the server */
100  SDLNet_TCP_Send(tcpsock, hello, CHAT_HELLO_NAME+n);
101  }
102 }
103 
104 void SendBuf(char *buf, int len)
105 {
106  int i;
107 
108  /* Redraw the prompt and add a newline to the buffer */
109  sendwin->Clear();
110  sendwin->AddText(CHAT_PROMPT);
111  buf[len++] = '\n';
112 
113  /* Send the text to each of our active channels */
114  for ( i=0; i < CHAT_MAXPEOPLE; ++i ) {
115  if ( people[i].active ) {
116  if ( len > packets[0]->maxlen ) {
117  len = packets[0]->maxlen;
118  }
119  memcpy(packets[0]->data, buf, len);
120  packets[0]->len = len;
121  SDLNet_UDP_Send(udpsock, i, packets[0]);
122  }
123  }
124 }
125 void SendKey(SDLKey key, Uint16 unicode)
126 {
127  static char keybuf[80-sizeof(CHAT_PROMPT)+1];
128  static int keypos = 0;
129  unsigned char ch;
130 
131  /* We don't handle wide UNICODE characters yet */
132  if ( unicode > 255 ) {
133  return;
134  }
135  ch = (unsigned char)unicode;
136 
137  /* Add the key to the buffer, and send it if we have a line */
138  switch (ch) {
139  case '\0':
140  break;
141  case '\r':
142  case '\n':
143  /* Send our line of text */
144  SendBuf(keybuf, keypos);
145  keypos = 0;
146  break;
147  case '\b':
148  /* If there's data, back up over it */
149  if ( keypos > 0 ) {
150  sendwin->AddText((char *)&ch, 1);
151  --keypos;
152  }
153  break;
154  default:
155  /* If the buffer is full, send it */
156  if ( keypos == (sizeof(keybuf)/sizeof(keybuf[0]))-1 ) {
157  SendBuf(keybuf, keypos);
158  keypos = 0;
159  }
160  /* Add the text to our send buffer */
161  sendwin->AddText((char *)&ch, 1);
162  keybuf[keypos++] = ch;
163  break;
164  }
165 }
166 
168 {
169  int used;
170 
171  switch (data[0]) {
172  case CHAT_ADD: {
173  Uint8 which;
174  IPaddress newip;
175 
176  /* Figure out which channel we got */
177  which = data[CHAT_ADD_SLOT];
178  if ((which >= CHAT_MAXPEOPLE) || people[which].active) {
179  /* Invalid channel?? */
180  break;
181  }
182  /* Get the client IP address */
183  newip.host=SDLNet_Read32(&data[CHAT_ADD_HOST]);
184  newip.port=SDLNet_Read16(&data[CHAT_ADD_PORT]);
185 
186  /* Copy name into channel */
187  memcpy(people[which].name, &data[CHAT_ADD_NAME], 256);
188  people[which].name[256] = 0;
189  people[which].active = 1;
190 
191  /* Let the user know what happened */
192  termwin->AddText(
193  "* New client on %d from %d.%d.%d.%d:%d (%s)\n", which,
194  (newip.host>>24)&0xFF, (newip.host>>16)&0xFF,
195  (newip.host>>8)&0xFF, newip.host&0xFF,
196  newip.port, people[which].name);
197 
198  /* Put the address back in network form */
199  newip.host = SDL_SwapBE32(newip.host);
200  newip.port = SDL_SwapBE16(newip.port);
201 
202  /* Bind the address to the UDP socket */
203  SDLNet_UDP_Bind(udpsock, which, &newip);
204  }
205  used = CHAT_ADD_NAME+data[CHAT_ADD_NLEN];
206  break;
207  case CHAT_DEL: {
208  Uint8 which;
209 
210  /* Figure out which channel we lost */
211  which = data[CHAT_DEL_SLOT];
212  if ( (which >= CHAT_MAXPEOPLE) ||
213  ! people[which].active ) {
214  /* Invalid channel?? */
215  break;
216  }
217  people[which].active = 0;
218 
219  /* Let the user know what happened */
220  termwin->AddText(
221  "* Lost client on %d (%s)\n", which, people[which].name);
222 
223  /* Unbind the address on the UDP socket */
224  SDLNet_UDP_Unbind(udpsock, which);
225  }
226  used = CHAT_DEL_LEN;
227  break;
228  case CHAT_BYE: {
229  termwin->AddText("* Chat server full\n");
230  }
231  used = CHAT_BYE_LEN;
232  break;
233  default: {
234  /* Unknown packet type?? */;
235  }
236  used = 0;
237  break;
238  }
239  return(used);
240 }
241 
242 void HandleServer(void)
243 {
244  Uint8 data[512];
245  int pos, len;
246  int used;
247 
248  /* Has the connection been lost with the server? */
249  len = SDLNet_TCP_Recv(tcpsock, (char *)data, 512);
250  if ( len <= 0 ) {
253  tcpsock = NULL;
254  termwin->AddText("Connection with server lost!\n");
255  } else {
256  pos = 0;
257  while ( len > 0 ) {
258  used = HandleServerData(&data[pos]);
259  pos += used;
260  len -= used;
261  if ( used == 0 ) {
262  /* We might lose data here.. oh well,
263  we got a corrupt packet from server
264  */
265  len = 0;
266  }
267  }
268  }
269 }
270 void HandleClient(void)
271 {
272  int n;
273 
274  n = SDLNet_UDP_RecvV(udpsock, packets);
275  while ( n-- > 0 ) {
276  if ( packets[n]->channel >= 0 ) {
277  termwin->AddText("[%s] ",
278  people[packets[n]->channel].name);
279  termwin->AddText((char *)packets[n]->data, packets[n]->len);
280  }
281  }
282 }
283 
284 GUI_status HandleNet(void)
285 {
287  if ( SDLNet_SocketReady(tcpsock) ) {
288  HandleServer();
289  }
290  if ( SDLNet_SocketReady(udpsock) ) {
291  HandleClient();
292  }
293 
294  /* Redraw the screen if the window changed */
295  if ( termwin->Changed() ) {
296  return(GUI_REDRAW);
297  } else {
298  return(GUI_PASS);
299  }
300 }
301 
302 void InitGUI(SDL_Surface *screen)
303 {
304  int x1, y1, y2;
305  SDL_Rect empty_rect = { 0, 0, 0, 0 };
306  GUI_Widget *widget;
307 
308  gui = new GUI(screen);
309 
310  /* Chat terminal window */
311  termwin = new GUI_TermWin(0, 0, 80*8, 50*8, NULL,NULL,CHAT_SCROLLBACK);
312  gui->AddWidget(termwin);
313 
314  /* Send-line window */
315  y1 = termwin->H()+2;
316  sendwin = new GUI_TermWin(0, y1, 80*8, 1*8, NULL, SendKey, 0);
317  sendwin->AddText(CHAT_PROMPT);
318  gui->AddWidget(sendwin);
319 
320  /* Add scroll buttons for main window */
321  y1 += sendwin->H()+2;
322  y2 = y1+images[IMAGE_SCROLL_UP]->h;
323  widget = new GUI_ScrollButtons(2, y1, images[IMAGE_SCROLL_UP],
324  empty_rect, 2, y2, images[IMAGE_SCROLL_DN],
325  SCROLLBAR_VERTICAL, termwin);
326  gui->AddWidget(widget);
327 
328  /* Add QUIT button */
329  x1 = (screen->w-images[IMAGE_QUIT]->w)/2;
330  y1 = sendwin->Y()+sendwin->H()+images[IMAGE_QUIT]->h/2;
331  widget = new GUI_Button(NULL, x1, y1, images[IMAGE_QUIT], NULL);
332  gui->AddWidget(widget);
333 
334  /* That's all folks */
335  return;
336 }
337 
338 extern "C"
339 void cleanup(int exitcode)
340 {
341  int i;
342 
343  /* Clean up the GUI */
344  if ( gui ) {
345  delete gui;
346  gui = NULL;
347  }
348  /* Clean up any images we have */
349  for ( i=0; i<NUM_IMAGES; ++i ) {
350  if ( images[i] ) {
351  SDL_FreeSurface(images[i]);
352  images[i] = NULL;
353  }
354  }
355  /* Close the network connections */
356  if ( tcpsock != NULL ) {
358  tcpsock = NULL;
359  }
360  if ( udpsock != NULL ) {
362  udpsock = NULL;
363  }
364  if ( socketset != NULL ) {
366  socketset = NULL;
367  }
368  if ( packets != NULL ) {
369  SDLNet_FreePacketV(packets);
370  packets = NULL;
371  }
372  SDLNet_Quit();
373  SDL_Quit();
374  exit(exitcode);
375 }
376 
377 int main(int argc, char *argv[])
378 {
379  SDL_Surface *screen;
380  int i;
381  char *server;
382  IPaddress serverIP;
383 
384  /* Check command line arguments */
385  if ( argv[1] == NULL ) {
386  fprintf(stderr, "Usage: %s <server>\n", argv[0]);
387  exit(1);
388  }
389 
390  /* Initialize SDL */
391  if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
392  fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
393  exit(1);
394  }
395 
396  /* Set a 640x480 video mode -- allows 80x50 window using 8x8 font */
397  screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
398  if ( screen == NULL ) {
399  fprintf(stderr, "Couldn't set video mode: %s\n",SDL_GetError());
400  SDL_Quit();
401  exit(1);
402  }
403 
404  /* Initialize the network */
405  if ( SDLNet_Init() < 0 ) {
406  fprintf(stderr, "Couldn't initialize net: %s\n",
407  SDLNet_GetError());
408  SDL_Quit();
409  exit(1);
410  }
411 
412  /* Get ready to initialize all of our data */
413 
414  /* Load the display font and other images */
415  for ( i=0; i<NUM_IMAGES; ++i ) {
416  images[i] = NULL;
417  }
418  for ( i=0; i<NUM_IMAGES; ++i ) {
419  images[i] = SDL_LoadBMP(image_files[i]);
420  if ( images[i] == NULL ) {
421  fprintf(stderr, "Couldn't load '%s': %s\n",
422  image_files[i], SDL_GetError());
423  cleanup(2);
424  }
425  }
426 
427  /* Go! */
428  InitGUI(screen);
429 
430  /* Allocate a vector of packets for client messages */
431  packets = SDLNet_AllocPacketV(4, CHAT_PACKETSIZE);
432  if ( packets == NULL ) {
433  fprintf(stderr, "Couldn't allocate packets: Out of memory\n");
434  cleanup(2);
435  }
436 
437  /* Connect to remote host and create UDP endpoint */
438  server = argv[1];
439  termwin->AddText("Connecting to %s ... ", server);
440  gui->Display();
441  SDLNet_ResolveHost(&serverIP, server, CHAT_PORT);
442  if ( serverIP.host == INADDR_NONE ) {
443  termwin->AddText("Couldn't resolve hostname\n");
444  } else {
445  /* If we fail, it's okay, the GUI shows the problem */
446  tcpsock = SDLNet_TCP_Open(&serverIP);
447  if ( tcpsock == NULL ) {
448  termwin->AddText("Connect failed\n");
449  } else {
450  termwin->AddText("Connected\n");
451  }
452  }
453  /* Try ports in the range {CHAT_PORT - CHAT_PORT+10} */
454  for ( i=0; (udpsock == NULL) && i<10; ++i ) {
456  }
457  if ( udpsock == NULL ) {
459  tcpsock = NULL;
460  termwin->AddText("Couldn't create UDP endpoint\n");
461  }
462 
463  /* Allocate the socket set for polling the network */
465  if ( socketset == NULL ) {
466  fprintf(stderr, "Couldn't create socket set: %s\n",
467  SDLNet_GetError());
468  cleanup(2);
469  }
472 
473  /* Run the GUI, handling network data */
474  SendHello(argv[2]);
475  gui->Run(HandleNet);
476  cleanup(0);
477 
478  /* Keep the compiler happy */
479  return(0);
480 }
DECLSPEC int SDLCALL SDLNet_TCP_Send(TCPsocket sock, const void *data, int len)
Definition: SDLnetTCP.c:236
char * image_files[NUM_IMAGES]
Definition: chat.cpp:61
DECLSPEC int SDLCALL SDLNet_CheckSockets(SDLNet_SocketSet set, Uint32 timeout)
Definition: SDLnetselect.c:110
DECLSPEC void SDLCALL SDL_FreeSurface(SDL_Surface *surface)
Definition: SDL_surface.c:1053
volatile SDL_bool active
Definition: SDL_events.c:76
Uint32 host
Definition: SDL_net.h:91
static GUI_TermWin * sendwin
Definition: chat.cpp:54
struct _UDPsocket * UDPsocket
Definition: SDL_net.h:182
DECLSPEC UDPsocket SDLCALL SDLNet_UDP_Open(Uint16 port)
Definition: SDLnetUDP.c:139
int main(int argc, char **argv)
Definition: bootstrap.cpp:102
#define SDLNet_SocketReady(sock)
Definition: SDL_net.h:347
#define NULL
Definition: ftobjs.h:61
#define SDL_LoadBMP(file)
Definition: SDL_surface.h:182
#define SDL_SWSURFACE
Definition: SDL_surface.h:52
GLclampd n
Definition: glew.h:7287
DECLSPEC void SDLCALL SDLNet_FreeSocketSet(SDLNet_SocketSet set)
Definition: SDLnetselect.c:156
static SDLNet_SocketSet socketset
Definition: chat.cpp:45
DECLSPEC int SDLCALL SDLNet_Init(void)
Definition: SDLnet.c:86
static GUI * gui
Definition: chat.cpp:52
#define CHAT_ADD_SLOT
Definition: chat.h:41
const char hello[]
Definition: example.c:29
A collection of pixels used in software blitting.
Definition: SDL_surface.h:69
#define CHAT_ADD_NLEN
Definition: chat.h:44
#define CHAT_DEL_LEN
Definition: chat.h:48
#define CHAT_SCROLLBACK
Definition: chat.h:28
SDL_Surface * images[NUM_IMAGES]
Definition: chat.cpp:64
EGLImageKHR EGLint * name
Definition: eglext.h:284
#define CHAT_BYE
Definition: chat.h:49
GLenum GLsizei len
Definition: glew.h:7035
DECLSPEC void SDLCALL SDL_Quit(void)
Definition: SDL.c:342
DECLSPEC const char *SDLCALL SDL_GetError(void)
Definition: SDL_error.c:204
GLuint GLfloat GLfloat GLfloat GLfloat y1
Definition: glew.h:11582
#define CHAT_ADD_HOST
Definition: chat.h:42
#define INADDR_NONE
Definition: SDL_net.h:105
GLfixed GLfixed GLfixed y2
Definition: glext.h:4559
void SendHello(char *name)
Definition: chat.cpp:67
#define SDL_SwapBE32(X)
Definition: SDL_endian.h:216
#define SDL_SwapBE16(X)
Definition: SDL_endian.h:215
int len
Definition: SDL_net.h:186
#define CHAT_PACKETSIZE
Definition: chat.h:30
DECLSPEC void SDLCALL SDLNet_UDP_Unbind(UDPsocket sock, int channel)
Definition: SDLnetUDP.c:287
static UDPpacket ** packets
Definition: chat.cpp:46
#define CHAT_DEL_SLOT
Definition: chat.h:47
#define CHAT_DEL
Definition: chat.h:46
#define CHAT_PROMPT
Definition: chat.h:29
static __inline__ int SDLNet_TCP_AddSocket(SDLNet_SocketSet set, TCPsocket sock)
Definition: SDL_net.h:313
static __inline__ int SDLNet_UDP_AddSocket(SDLNet_SocketSet set, UDPsocket sock)
Definition: SDL_net.h:317
int HandleServerData(Uint8 *data)
Definition: chat.cpp:167
void SendKey(SDLKey key, Uint16 unicode)
Definition: chat.cpp:125
#define CHAT_HELLO_NAME
Definition: chat.h:39
#define CHAT_ADD_PORT
Definition: chat.h:43
struct _SDLNet_SocketSet * SDLNet_SocketSet
Definition: SDL_net.h:298
GUI_status HandleNet(void)
Definition: chat.cpp:284
void HandleServer(void)
Definition: chat.cpp:242
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: gl2ext.h:848
DECLSPEC void SDLCALL SDLNet_FreePacketV(UDPpacket **packetV)
Definition: SDLnetUDP.c:121
DECLSPEC UDPpacket **SDLCALL SDLNet_AllocPacketV(int howmany, int size)
Definition: SDLnetUDP.c:98
DECLSPEC TCPsocket SDLCALL SDLNet_TCP_Open(IPaddress *ip)
Definition: SDLnetTCP.c:46
#define CHAT_ADD
Definition: chat.h:40
#define SDLNet_Read32(areap)
Definition: SDL_net.h:381
#define CHAT_HELLO
Definition: chat.h:36
Uint16 port
Definition: SDL_net.h:92
DECLSPEC void SDLCALL SDLNet_Quit(void)
Definition: SDLnet.c:110
static TCPsocket tcpsock
Definition: chat.cpp:43
DECLSPEC int SDLCALL SDLNet_UDP_Send(UDPsocket sock, int channel, UDPpacket *packet)
Definition: SDLnetUDP.c:399
void cleanup(int exitcode)
Definition: chat.cpp:339
void InitGUI(SDL_Surface *screen)
Definition: chat.cpp:302
GLuint GLfloat GLfloat GLfloat x1
Definition: glew.h:11582
DECLSPEC int SDLCALL SDLNet_ResolveHost(IPaddress *address, const char *host, Uint16 port)
Definition: SDLnet.c:138
#define SDL_INIT_VIDEO
Definition: SDL.h:110
#define CHAT_HELLO_PORT
Definition: chat.h:37
static struct @97 people[CHAT_MAXPEOPLE]
GLenum GLuint GLsizei const GLchar * buf
Definition: glew.h:2539
struct _TCPsocket * TCPsocket
Definition: SDL_net.h:131
#define memcpy
Definition: SDL_malloc.c:634
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:129
void SendBuf(char *buf, int len)
Definition: chat.cpp:104
static GUI_TermWin * termwin
Definition: chat.cpp:53
static __inline__ int SDLNet_TCP_DelSocket(SDLNet_SocketSet set, TCPsocket sock)
Definition: SDL_net.h:325
DECLSPEC void SDLCALL SDLNet_UDP_Close(UDPsocket sock)
Definition: SDLnetUDP.c:515
DECLSPEC int SDLCALL SDLNet_UDP_Bind(UDPsocket sock, int channel, const IPaddress *address)
Definition: SDLnetUDP.c:256
#define CHAT_MAXPEOPLE
Definition: chat.h:53
DECLSPEC IPaddress *SDLCALL SDLNet_UDP_GetPeerAddress(UDPsocket sock, int channel)
Definition: SDLnetUDP.c:298
DECLSPEC int SDLCALL SDL_Init(Uint32 flags)
Definition: SDL.c:235
DECLSPEC SDLNet_SocketSet SDLCALL SDLNet_AllocSocketSet(int maxsockets)
Definition: SDLnetselect.c:44
DECLSPEC int SDLCALL SDLNet_TCP_Recv(TCPsocket sock, void *data, int maxlen)
Definition: SDLnetTCP.c:269
uint16_t Uint16
An unsigned 16-bit integer type.
Definition: SDL_stdinc.h:137
static UDPsocket udpsock
Definition: chat.cpp:44
int maxlen
Definition: SDL_net.h:187
#define SDLNet_Read16(areap)
Definition: SDL_net.h:380
#define CHAT_HELLO_NLEN
Definition: chat.h:38
int i
Definition: pngrutil.c:1377
image_names
Definition: chat.cpp:55
#define CHAT_PORT
Definition: chat.h:33
#define CHAT_BYE_LEN
Definition: chat.h:50
DECLSPEC void SDLCALL SDLNet_TCP_Close(TCPsocket sock)
Definition: SDLnetTCP.c:289
A rectangle, with the origin at the upper left.
Definition: SDL_rect.h:63
#define CHAT_ADD_NAME
Definition: chat.h:45
DECLSPEC const char *SDLCALL SDLNet_GetError(void)
Definition: SDLnet.c:76
void HandleClient(void)
Definition: chat.cpp:270
DECLSPEC int SDLCALL SDLNet_UDP_RecvV(UDPsocket sock, UDPpacket **packets)
Definition: SDLnetUDP.c:439