Logo Search packages:      
Sourcecode: ia32-libs-sdl version File versions  Download package

chat.cpp

/*
    CHAT:  A chat client using the SDL example network and GUI libraries
    Copyright (C) 1997-1999  Sam Lantinga

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Sam Lantinga
    5635-34 Springhouse Dr.
    Pleasanton, CA 94588 (USA)
    slouken@devolution.com
*/

/* Note that this isn't necessarily the way to run a chat system.
   This is designed to excercise the network code more than be really
   functional.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "SDL_net.h"
#ifdef macintosh
#include "GUI.h"
#include "GUI_widgets.h"
#else
#include <GUI/GUI.h>
#include <GUI/GUI_widgets.h>
#endif
#include "chat.h"


/* Global variables */
static TCPsocket tcpsock = NULL;
static UDPsocket udpsock = NULL;
static SDLNet_SocketSet socketset = NULL;
static UDPpacket **packets = NULL;
static struct {
      int active;
      Uint8 name[256+1];
} people[CHAT_MAXPEOPLE];

static GUI *gui = NULL;
static GUI_TermWin *termwin;
static GUI_TermWin *sendwin;
enum image_names {
      IMAGE_QUIT,
      IMAGE_SCROLL_UP,
      IMAGE_SCROLL_DN,
      NUM_IMAGES
};
char *image_files[NUM_IMAGES] = {
      "quit.bmp", "scroll_up.bmp", "scroll_dn.bmp"
};
SDL_Surface *images[NUM_IMAGES];


void SendHello(char *name)
{
      IPaddress *myip;
      char hello[1+1+256];
      int i, n;

      /* No people are active at first */
      for ( i=0; i<CHAT_MAXPEOPLE; ++i ) {
            people[i].active = 0;
      }
      if ( tcpsock != NULL ) {
            /* Get our chat handle */
            if ( (name == NULL) &&
                 ((name=getenv("CHAT_USER")) == NULL) &&
                 ((name=getenv("USER")) == NULL ) ) {
                  name="Unknown";
            }
            termwin->AddText("Using name '%s'\n", name);

            /* Construct the packet */
            hello[0] = CHAT_HELLO;
            myip = SDLNet_UDP_GetPeerAddress(udpsock, -1);
            memcpy(&hello[CHAT_HELLO_PORT], &myip->port, 2);
            if ( strlen(name) > 255 ) {
                  n = 255;
            } else {
                  n = strlen(name);
            }
            hello[CHAT_HELLO_NLEN] = n;
            strncpy(&hello[CHAT_HELLO_NAME], name, n);
            hello[CHAT_HELLO_NAME+n++] = 0;

            /* Send it to the server */
            SDLNet_TCP_Send(tcpsock, hello, CHAT_HELLO_NAME+n);
      }
}

void SendBuf(char *buf, int len)
{
      int i;

      /* Redraw the prompt and add a newline to the buffer */
      sendwin->Clear();
      sendwin->AddText(CHAT_PROMPT);
      buf[len++] = '\n';

      /* Send the text to each of our active channels */
      for ( i=0; i < CHAT_MAXPEOPLE; ++i ) {
            if ( people[i].active ) {
                  if ( len > packets[0]->maxlen ) {
                        len = packets[0]->maxlen;
                  }
                  memcpy(packets[0]->data, buf, len);
                  packets[0]->len = len;
                  SDLNet_UDP_Send(udpsock, i, packets[0]);
            }
      }
}
void SendKey(SDLKey key, Uint16 unicode)
{
      static char keybuf[80-sizeof(CHAT_PROMPT)+1];
      static int  keypos = 0;
      unsigned char ch;

      /* We don't handle wide UNICODE characters yet */
      if ( unicode > 255 ) {
            return;
      }
      ch = (unsigned char)unicode;

      /* Add the key to the buffer, and send it if we have a line */
      switch (ch) {
            case '\0':
                  break;
            case '\r':
            case '\n':
                  /* Send our line of text */
                  SendBuf(keybuf, keypos);
                  keypos = 0;
                  break;
            case '\b':
                  /* If there's data, back up over it */
                  if ( keypos > 0 ) {
                        sendwin->AddText((char *)&ch, 1);
                        --keypos;
                  }
                  break;
            default:
                  /* If the buffer is full, send it */
                  if ( keypos == (sizeof(keybuf)/sizeof(keybuf[0]))-1 ) {
                        SendBuf(keybuf, keypos);
                        keypos = 0;
                  }
                  /* Add the text to our send buffer */
                  sendwin->AddText((char *)&ch, 1);
                  keybuf[keypos++] = ch;
                  break;
      }
}

int HandleServerData(Uint8 *data)
{
      int used;

      switch (data[0]) {
            case CHAT_ADD: {
                  Uint8 which;
                  IPaddress newip;

                  /* Figure out which channel we got */
                  which = data[CHAT_ADD_SLOT];
                  if ((which >= CHAT_MAXPEOPLE) || people[which].active) {
                        /* Invalid channel?? */
                        break;
                  }
                  /* Get the client IP address */
                  newip.host=SDLNet_Read32(&data[CHAT_ADD_HOST]);
                  newip.port=SDLNet_Read16(&data[CHAT_ADD_PORT]);

                  /* Copy name into channel */
                  memcpy(people[which].name, &data[CHAT_ADD_NAME], 256);
                  people[which].name[256] = 0;
                  people[which].active = 1;

                  /* Let the user know what happened */
                  termwin->AddText(
      "* New client on %d from %d.%d.%d.%d:%d (%s)\n", which,
            (newip.host>>24)&0xFF, (newip.host>>16)&0xFF,
                  (newip.host>>8)&0xFF, newip.host&0xFF,
                              newip.port, people[which].name);

                  /* Put the address back in network form */
                  newip.host = SDL_SwapBE32(newip.host);
                  newip.port = SDL_SwapBE16(newip.port);

                  /* Bind the address to the UDP socket */
                  SDLNet_UDP_Bind(udpsock, which, &newip);
            }
            used = CHAT_ADD_NAME+data[CHAT_ADD_NLEN];
            break;
            case CHAT_DEL: {
                  Uint8 which;

                  /* Figure out which channel we lost */
                  which = data[CHAT_DEL_SLOT];
                  if ( (which >= CHAT_MAXPEOPLE) ||
                                    ! people[which].active ) {
                        /* Invalid channel?? */
                        break;
                  }
                  people[which].active = 0;

                  /* Let the user know what happened */
                  termwin->AddText(
      "* Lost client on %d (%s)\n", which, people[which].name);

                  /* Unbind the address on the UDP socket */
                  SDLNet_UDP_Unbind(udpsock, which);
            }
            used = CHAT_DEL_LEN;
            break;
            case CHAT_BYE: {
                  termwin->AddText("* Chat server full\n");
            }
            used = CHAT_BYE_LEN;
            break;
            default: {
                  /* Unknown packet type?? */;
            }
            used = 0;
            break;
      }
      return(used);
}

void HandleServer(void)
{
      Uint8 data[512];
      int pos, len;
      int used;

      /* Has the connection been lost with the server? */
      len = SDLNet_TCP_Recv(tcpsock, (char *)data, 512);
      if ( len <= 0 ) {
            SDLNet_TCP_DelSocket(socketset, tcpsock);
            SDLNet_TCP_Close(tcpsock);
            tcpsock = NULL;
            termwin->AddText("Connection with server lost!\n");
      } else {
            pos = 0;
            while ( len > 0 ) {
                  used = HandleServerData(&data[pos]);
                  pos += used;
                  len -= used;
                  if ( used == 0 ) {
                        /* We might lose data here.. oh well,
                           we got a corrupt packet from server
                         */
                        len = 0;
                  }
            }
      }
}
void HandleClient(void)
{
      int n;

      n = SDLNet_UDP_RecvV(udpsock, packets);
      while ( n-- > 0 ) {
            if ( packets[n]->channel >= 0 ) {
                  termwin->AddText("[%s] ", 
                        people[packets[n]->channel].name);
                  termwin->AddText((char *)packets[n]->data, packets[n]->len);
            }
      }
}

GUI_status HandleNet(void)
{
      SDLNet_CheckSockets(socketset, 0);
      if ( SDLNet_SocketReady(tcpsock) ) {
            HandleServer();
      }
      if ( SDLNet_SocketReady(udpsock) ) {
            HandleClient();
      }

      /* Redraw the screen if the window changed */
      if ( termwin->Changed() ) {
            return(GUI_REDRAW);
      } else {
            return(GUI_PASS);
      }
}

void InitGUI(SDL_Surface *screen)
{
      int x1, y1, y2;
      SDL_Rect empty_rect = { 0, 0, 0, 0 };
        GUI_Widget *widget;

      gui = new GUI(screen);

      /* Chat terminal window */
      termwin = new GUI_TermWin(0, 0, 80*8, 50*8, NULL,NULL,CHAT_SCROLLBACK);
      gui->AddWidget(termwin);

      /* Send-line window */
      y1 = termwin->H()+2;
      sendwin = new GUI_TermWin(0, y1, 80*8, 1*8, NULL, SendKey, 0);
      sendwin->AddText(CHAT_PROMPT);
      gui->AddWidget(sendwin);

      /* Add scroll buttons for main window */
      y1 += sendwin->H()+2;
      y2 = y1+images[IMAGE_SCROLL_UP]->h;
      widget = new GUI_ScrollButtons(2, y1, images[IMAGE_SCROLL_UP],
                         empty_rect, 2, y2, images[IMAGE_SCROLL_DN],
                              SCROLLBAR_VERTICAL, termwin);
      gui->AddWidget(widget);

      /* Add QUIT button */
      x1 = (screen->w-images[IMAGE_QUIT]->w)/2;
      y1 = sendwin->Y()+sendwin->H()+images[IMAGE_QUIT]->h/2;
      widget = new GUI_Button(NULL, x1, y1, images[IMAGE_QUIT], NULL);
      gui->AddWidget(widget);

      /* That's all folks */
      return;
}

extern "C"
void cleanup(void)
{
      int i;

      /* Clean up the GUI */
      if ( gui ) {
            delete gui;
            gui = NULL;
      }
      /* Clean up any images we have */
      for ( i=0; i<NUM_IMAGES; ++i ) {
            if ( images[i] ) {
                  SDL_FreeSurface(images[i]);
                  images[i] = NULL;
            }
      }
      /* Close the network connections */
      if ( tcpsock != NULL ) {
            SDLNet_TCP_Close(tcpsock);
            tcpsock = NULL;
      }
      if ( udpsock != NULL ) {
            SDLNet_UDP_Close(udpsock);
            udpsock = NULL;
      }
      if ( socketset != NULL ) {
            SDLNet_FreeSocketSet(socketset);
            socketset = NULL;
      }
      if ( packets != NULL ) {
            SDLNet_FreePacketV(packets);
            packets = NULL;
      }
}

main(int argc, char *argv[])
{
        SDL_Surface *screen;
      int i;
      char *server;
      IPaddress serverIP;

      /* Check command line arguments */
      if ( argv[1] == NULL ) {
            fprintf(stderr, "Usage: %s <server>\n", argv[0]);
            exit(1);
      }
      
        /* Initialize SDL */
        if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
                fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
                exit(1);
      }
      atexit(SDL_Quit);

      /* Set a 640x480 video mode -- allows 80x50 window using 8x8 font */
      screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
      if ( screen == NULL ) {
                fprintf(stderr, "Couldn't set video mode: %s\n",SDL_GetError());
                exit(1);
      }

      /* Initialize the network */
      if ( SDLNet_Init() < 0 ) {
            fprintf(stderr, "Couldn't initialize net: %s\n",
                                    SDLNet_GetError());
            exit(1);
      }
      atexit(SDLNet_Quit);

      /* Get ready to initialize all of our data */
      atexit(cleanup);

      /* Load the display font and other images */
      for ( i=0; i<NUM_IMAGES; ++i ) {
            images[i] = NULL;
      }
      for ( i=0; i<NUM_IMAGES; ++i ) {
            images[i] = SDL_LoadBMP(image_files[i]);
            if ( images[i] == NULL ) {
                  fprintf(stderr, "Couldn't load '%s': %s\n",
                              image_files[i], SDL_GetError());
                  exit(2);
            }
      }

      /* Go! */
      InitGUI(screen);

      /* Allocate a vector of packets for client messages */
      packets = SDLNet_AllocPacketV(4, CHAT_PACKETSIZE);
      if ( packets == NULL ) {
            fprintf(stderr, "Couldn't allocate packets: Out of memory\n");
            exit(2);
      }

      /* Connect to remote host and create UDP endpoint */
      server = argv[1];
      termwin->AddText("Connecting to %s ... ", server);
      gui->Display();
      SDLNet_ResolveHost(&serverIP, server, CHAT_PORT);
      if ( serverIP.host == INADDR_NONE ) {
            termwin->AddText("Couldn't resolve hostname\n");
      } else {
            /* If we fail, it's okay, the GUI shows the problem */
            tcpsock = SDLNet_TCP_Open(&serverIP);
            if ( tcpsock == NULL ) {
                  termwin->AddText("Connect failed\n");
            } else {
                  termwin->AddText("Connected\n");
            }
      }
      /* Try ports in the range {CHAT_PORT - CHAT_PORT+10} */
      for ( i=0; (udpsock == NULL) && i<10; ++i ) {
            udpsock = SDLNet_UDP_Open(CHAT_PORT+i);
      }
      if ( udpsock == NULL ) {
            SDLNet_TCP_Close(tcpsock);
            tcpsock = NULL;
            termwin->AddText("Couldn't create UDP endpoint\n");
      }

      /* Allocate the socket set for polling the network */
      socketset = SDLNet_AllocSocketSet(2);
      if ( socketset == NULL ) {
            fprintf(stderr, "Couldn't create socket set: %s\n",
                                    SDLNet_GetError());
            exit(2);
      }
      SDLNet_TCP_AddSocket(socketset, tcpsock);
      SDLNet_UDP_AddSocket(socketset, udpsock);

      /* Run the GUI, handling network data */
      SendHello(argv[2]);
      gui->Run(HandleNet);
      exit(0);

      /* Keep the compiler happy */
      return(0);
}

Generated by  Doxygen 1.6.0   Back to index