Logo Search packages:      
Sourcecode: wbd version File versions  Download package

main.c

/*
 * Copyright (c) 1997 Loughborough University
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the LUTCHI Research
 *      Centre at Loughborough University.
 * 4. Neither the name of the University nor of the Centre may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */


/*
 *  File: main.c
 *
 *  Description: Whiteboard initialisation and main loop.
 *
 *  J.C.Highfield, 3/97.
 *
 */


#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#include <assert.h>
#include <io.h>
#include <process.h>
#include <fcntl.h>
#include <malloc.h>
#include <locale.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <winsock.h>
#else

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#endif

#include <tcl.h>
#include <tk.h>

#include "wb.h"
#include "db.h"
#include "proto.h"

#ifndef EXT_SCRIPT
extern char code[];
#endif

Tcl_Interp *tcl;
Tk_Window MainWin;

int help       = 0;
int maxhops    = 5;
int orient     = 1;
int readonly   = 0;
int pssize     = 32768;
int datarate   = 32768 * 2;
char *username = "";
char *confname = "Whiteboard";
char *geometry = "";
int relate     = 0;
int debugmode  = 0;
unsigned long repairTime=-1;

int   confPort       = 2323;
char  confGroup[32]  = "230.230.230.230";

static void       setargv _ANSI_ARGS_((int *argcPtr, char ***argvPtr));
static void       WishPanic _ANSI_ARGS_(TCL_VARARGS(char *,format));

Tk_ArgvInfo argTable[] = {
    {"-help", TK_ARGV_CONSTANT, (char *) 1, (char *) &help,
      "Show command usage information"},
    {"-N", TK_ARGV_STRING, NULL, (char *) &username,
        "Username"},
      {"-d", TK_ARGV_CONSTANT, (char *) 0, (char *) &debugmode,
        "Debug messages off"},
      {"+d", TK_ARGV_CONSTANT, (char *) 1, (char *) &debugmode,
        "Debug messages on"},
    {"-C", TK_ARGV_STRING, NULL, (char *) &confname,
        "Conference name"},
    {"-maxhops", TK_ARGV_INT, NULL, (char *) &maxhops,
      "Maximum TTL for advertising sessions"},
    {"-t", TK_ARGV_INT, NULL, (char *) &maxhops,
      "Maximum TTL for advertising sessions"},
      {"-relate", TK_ARGV_CONSTANT, (char *) 1, (char *) &relate,
      "Relate session"},
    {"-p", TK_ARGV_CONSTANT, (char *) 0, (char *) &orient,
      "Portrait mode"},
    {"-l", TK_ARGV_CONSTANT, (char *) 1, (char *) &orient,
      "Landscape mode"},
    {"+l", TK_ARGV_CONSTANT, (char *) 3, (char *) &orient,
      "Landscape mode"},
    {"-r", TK_ARGV_CONSTANT, (char *) 1, (char *) &readonly,
      "Read only"},
    {"+r", TK_ARGV_CONSTANT, (char *) 0, (char *) &readonly,
      "Write enabled"},
    {"-geometry", TK_ARGV_STRING, NULL, (char *) &geometry,
        "Window geometry"},
    {"-P", TK_ARGV_INT, NULL, (char *) &pssize,
      "Maximum Postscript file size allowed"},
    {"-R", TK_ARGV_INT, NULL, (char *) &datarate,
      "Maximum network data rate (bits/second)"},
    {NULL, TK_ARGV_END, NULL, NULL, NULL}
};

/* Global. */
int rsock;
int tsock;

/* Resize global. */
long resizeNeeded = 0;

#ifdef WIN32
 /*----------------------------------------------------------------------
 *
 * WinMain --
 *
 *    Main entry point from Windows.
 *
 * Results:
 *    Returns false if initialization fails, otherwise it never
 *    returns. 
 *
 * Side effects:
 *    Just about anything, since from here we call arbitrary Tcl code.
 *
 *----------------------------------------------------------------------
 */

int APIENTRY
WinMain(hInstance, hPrevInstance, lpszCmdLine, nCmdShow)
    HINSTANCE hInstance;
    HINSTANCE hPrevInstance;
    LPSTR lpszCmdLine;
    int nCmdShow;
{
    char **argv, *p;
    int argc;
    char buffer[MAX_PATH];
    WSADATA WSAdata;

    if (WSAStartup(MAKEWORD (1, 1), &WSAdata)) {
      perror("Windows Sockets init failed");
      abort();
    }

    Tcl_SetPanicProc(WishPanic);

    /*
     * Set up the default locale to be standard "C" locale so parsing
     * is performed correctly.
     */

    setlocale(LC_ALL, "C");


    /*
     * Increase the application queue size from default value of 8.
     * At the default value, cross application SendMessage of WM_KILLFOCUS
     * will fail because the handler will not be able to do a PostMessage!
     * This is only needed for Windows 3.x, since NT dynamically expands
     * the queue.
     */
    SetMessageQueue(64);

    /*
     * Create the console channels and install them as the standard
     * channels.  All I/O will be discarded until TkConsoleInit is
     * called to attach the console to a text widget.
     */

    //TkConsoleCreate();

    setargv(&argc, &argv);

    /*
     * Replace argv[0] with full pathname of executable, and forward
     * slashes substituted for backslashes.
     */

    GetModuleFileName(NULL, buffer, sizeof(buffer));
    argv[0] = buffer;
    for (p = buffer; *p != '\0'; p++) {
      if (*p == '\\') {
          *p = '/';
      }
    }
      TkWinXInit(hInstance);

    return main(argc, (const char**)argv);
}
void
perror(const char *msg)
{
    DWORD cMsgLen;
    CHAR *msgBuf;
    DWORD dwError = GetLastError();
    
    cMsgLen = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
                      FORMAT_MESSAGE_ALLOCATE_BUFFER | 40, NULL,
                      dwError,
                      MAKELANGID(0, SUBLANG_ENGLISH_US),
                      (LPTSTR) &msgBuf, 512,
                      NULL);
    if (!cMsgLen)
      fprintf(stderr, "%s%sError code %lu\n",
            msg?msg:"", msg?": ":"", dwError);
    else {
      fprintf(stderr, "%s%s%s\n", msg?msg:"", msg?": ":"", msgBuf);
      LocalFree((HLOCAL)msgBuf);
    }
}
#endif

#ifdef STAND_ALONE
void *TkSetPlatformInit(int (*func)(Tcl_Interp *));

int TkPlatformInit (Tcl_Interp *interp)
{
      Tcl_SetVar(interp, "tk_library", ".", TCL_GLOBAL_ONLY);
#ifndef WIN32
      TkCreateXEventSource();
#endif
      return (TCL_OK);
}
#endif

void usage (void)
{
    printf ("Usage: wbd [-N username] [-C confname] [-maxhops n]\n");
    printf ("           [-p|-l|+l] [-r|+r] [+d|-d] [-geometry wxh+x+y]  \n");
    printf ("           [-P filesize] [-R bandwidth] [-help] address/port\n");
}

void moreusage (void)
{
    printf ("\nUsage: wbd [-N username] [-C confname] [-maxhops n]\n");
    printf ("           [-p|-l|+l] [-r|+r] [-geometry wxh+x+y]  \n");
    printf ("           [-P filesize] [-R bandwidth] [-help] address/port\n");
    printf ("    -N username  Set the announced username.\n");
    printf ("    -C confname  Set the conference window name.\n");
    printf ("    -maxhops n   Set the TTL to n.\n");
    printf ("    -t n         Set the TTL to n, (as -maxhops).\n");
    printf ("    -p,-l,+l     Portrait/Landscape mode,\n");
    printf ("    -r,+r        Disable/Enable writing to the whiteboard.\n");
    printf ("    -geometry g  Set the window's geometry to g (wxh+x+y).\n");
    printf ("    -P size      Set the Postscript file size limit to size.\n");
    printf ("    -R bw        Set the bandwidth limit to bw (bits/s).\n");
      printf ("    -d, +d       Disable/Enable Debug Messages to stdout.\n");
    printf ("    -help        Display this message.\n\n");
}

void tkStringSet (const char *name, const char *value)
{
    char arg[1024];

    sprintf (arg, "set W(%s) {%s}", name, value);
    Tcl_Eval (tcl, arg);
}

void tkIntegerSet (const char *name, const int value)
{
    char arg[1024];

    sprintf (arg, "set W(%s) %d", name, value);
    Tcl_Eval (tcl, arg);
}

int main (int argc, char **argv)
{
    int  length;
    int  result;
    int  i, port;
      char buf[20];
    char *cmd;
      char *args;
    char *p;
    char arg[1024] = "";
    char host[20] = "";
    uint8  buffer[2048];
    struct sockaddr_in addr;
    unsigned long nowTime;

    /* Parse the command line. */
    tcl = Tcl_CreateInterp();
    cmd = Tcl_Concat(argc, argv);

    result = Tk_ParseArgv(tcl, NULL, &argc, argv, argTable, 0);
    if  (argc < 2)
    {
        usage();
        exit(-1);
    }
    if (help)
    {
        moreusage();
        exit (0);
    }

    /* Parse remaining argument. */
    if (argc > 1)
    {
        if ((p = strchr (argv[argc-1], '/')) != NULL)
        {
            *p++ = '\0';
            port = -1;
            sscanf (p, "%d", &confPort);
        }

        strcpy (confGroup, argv[argc-1]);
    }
  /*
     * Make command-line arguments available in the Tcl variables "argc"
     * and "argv".
     */
    args = Tcl_Merge(argc-2, argv+1);
    Tcl_SetVar(tcl, "argv", args, TCL_GLOBAL_ONLY);
    ckfree(args);
    sprintf(buf, "%d", argc-1);
    Tcl_SetVar(tcl, "argc", buf, TCL_GLOBAL_ONLY);

    /* Check some settings for validity. */
    if (pssize > (1024*1024))
        pssize = (1024*1024);
    if (datarate > (512*1024))
        datarate = (512*1024);
    if (datarate < 8192)
        datarate = 8192;

      /*
       * There is no easy way of preventing the Init functions from
       * loading the library files. Ignore error returns and load
       * built in versions.
       */
      Tcl_Init(tcl);
      Tk_Init(tcl);

    Tcl_StaticPackage(tcl, "Tk", Tk_Init, (Tcl_PackageInitProc *) NULL);

    MainWin = Tk_MainWindow(tcl);

    /* Create commands. */
    Tcl_CreateCommand(tcl, "Draw", Draw, (ClientData) NULL,
        (void (*)()) NULL);
    Tcl_CreateCommand(tcl, "UnDelete", UnDelete, (ClientData) NULL,
        (void (*)()) NULL);
    Tcl_CreateCommand(tcl, "SendPS", SendPS, (ClientData) NULL,
        (void (*)()) NULL);
    Tcl_CreateCommand(tcl, "PageNew", PageNew, (ClientData) NULL,
        (void (*)()) NULL);
    Tcl_CreateCommand(tcl, "PageSet", PageSetTcl, (ClientData) NULL,
        (void (*)()) NULL);
    Tcl_CreateCommand(tcl, "PageCanvasSize", PageCanvasSize, (ClientData) NULL,
        (void (*)()) NULL);
    Tcl_CreateCommand(tcl, "WbDump", WbDump, (ClientData) NULL,
        (void (*)()) NULL);
    Tcl_CreateCommand(tcl, "WbReload", WbReload, (ClientData) NULL,
        (void (*)()) NULL);
    Tcl_CreateCommand(tcl, "Sender", Sender, (ClientData) NULL,
        (void (*)()) NULL);
    Tcl_CreateCommand(tcl, "SetSend", SetSend, (ClientData) NULL,
        (void (*)()) NULL);

    /* Pass command line variables to tcl. */
    if (strlen(username) > 0)
        ascii_source (username);
    tkStringSet ("username",  ascii_source(NULL));
    tkStringSet ("confname",  confname);
    tkStringSet ("confGroup", confGroup);
    tkIntegerSet ("confPort", confPort);
    tkIntegerSet ("confTTL",  maxhops);
    tkStringSet  ("geometry", geometry);
    tkIntegerSet ("orientation", orient);
    tkIntegerSet ("readonly", readonly);
    tkIntegerSet ("pssize", pssize);
    tkIntegerSet ("relate", relate);

    /* Run the initialisation script. */
#ifdef EXT_SCRIPT
    if (Tcl_VarEval(tcl, "source ", "wbd.tcl", (char *) NULL) != TCL_OK)
    {
        WishPanic(tcl->result);
        exit (-1);
    }
#else
    if (Tcl_Eval(tcl, (char *)code) != TCL_OK)
    {
           WishPanic(tcl->result);
    }

#endif

    /* Create network name with wildcards. */
    memset (&addr, 0, sizeof (struct sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr (confGroup);
    addr.sin_port = htons(confPort);

    /* Open a multicast socket for receiving. */
    rsock = recv_sock_init(&addr, confPort, confGroup);
    if (rsock < 0)
    {
        perror ("no receive socket");
        exit (-1);
    }


    /* Open a multicast socket for transmitting. */
    tsock = send_sock_init(&addr, confPort, confGroup);
    if (tsock < 0)
    {
        perror ("no transmit socket");
        exit (-1);
    }


    /* Install the window resize handler. */
    Tk_CreateEventHandler(MainWin, StructureNotifyMask,
          ResizeProc, (ClientData) NULL);

    /* Note initial sender name. */
    SenderAdd (this_ip(), time_stamp(), time_stamp(), 
                        PK_SESSION, ascii_source(NULL));

    /* Run round in circles; Display windows! */

    while (Tk_GetNumMainWindows() > 0)
    {
        if (resizeNeeded == 1)
        {
            resizeNeeded = 0;
            DoResize();
        }

            for (i=0; i<16; i++) {
                  if (!Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT)) break; 
            }
        /* Update timer and send queues. */
        CheckQueues();
        Tcl_ResetResult (tcl);

            for (i=0; i<30; i++) {
              length = NetFetch(rsock, &addr, buffer, 2048);
              if (length > 0) NetParse(buffer, length);
                    else break;
            }

            /* if we've had repair packet(s) then redraw page */
            nowTime = time (NULL);
            if (nowTime > repairTime) {
                  PageDraw (PageCurrent(), 1);
                  repairTime=-1;
            }
    }
    return 0;
}


/* When the user resizes the window take a note */
/* and do it later - avoid reentrancy problems. */
void ResizeProc (ClientData clientData, XEvent *eventPtr)
{
    if (eventPtr->type != ConfigureNotify)
        return;

    resizeNeeded = 1;
}


void DoResize (void)
{
    char arg[1024];
    extern Tcl_Interp *tcl;
    Tk_Window edge, info;
    int h, w;

    edge   = Tk_NameToWindow (tcl, ".edge",  MainWin);
    info   = Tk_NameToWindow (tcl, ".info",  MainWin);

    h = Tk_Height (MainWin) - Tk_ReqHeight (info) - 4;
    w = Tk_Width (MainWin)  - Tk_ReqWidth (edge)  - 4;

/*  printf ("%d %d::%d %d\n", Tk_Height (MainWin),
         Tk_Width (MainWin), Tk_ReqHeight (info), 
                              Tk_ReqWidth (edge));*/

    sprintf (arg, "CanvasResize %d %d", w, h);
    if (Tcl_Eval (tcl, arg) != TCL_OK)
        printf ("%s RESULT %s\n", arg, tcl->result);

    sprintf (arg, "update");
    if (Tcl_Eval (tcl, arg) != TCL_OK)
        printf ("%s RESULT %s\n", arg, tcl->result);
}

/*
 *----------------------------------------------------------------------
 *
 * WishPanic --
 *
 *    Display a message and exit.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Exits the program.
 *
 *----------------------------------------------------------------------
 */

void
WishPanic TCL_VARARGS_DEF(char *,arg1)
{
    va_list argList;
    char buf[1024];
    char *format;
    
    format = TCL_VARARGS_START(char *,arg1,argList);
    vsprintf(buf, format, argList);

#ifdef WIN32
    MessageBeep(MB_ICONEXCLAMATION);
    MessageBox(NULL, buf, "Fatal Error in Wish",
          MB_ICONSTOP | MB_OK | MB_TASKMODAL | MB_SETFOREGROUND);
    ExitProcess(1);
#else
    exit(1);
#endif
}

#ifdef WIN32
/*
 *-------------------------------------------------------------------------
 *
 * setargv --
 *
 *    Parse the Windows command line string into argc/argv.  Done here
 *    because we don't trust the builtin argument parser in crt0.  
 *    Windows applications are responsible for breaking their command
 *    line into arguments.
 *
 *    2N backslashes + quote -> N backslashes + begin quoted string
 *    2N + 1 backslashes + quote -> literal
 *    N backslashes + non-quote -> literal
 *    quote + quote in a quoted string -> single quote
 *    quote + quote not in quoted string -> empty string
 *    quote -> begin quoted string
 *
 * Results:
 *    Fills argcPtr with the number of arguments and argvPtr with the
 *    array of arguments.
 *
 * Side effects:
 *    Memory allocated.
 *
 *--------------------------------------------------------------------------
 */

static void
setargv(int *argcPtr, char ***argvPtr)
{
    char *cmdLine, *p, *arg, *argSpace;
    char **argv;
    int argc, size, inquote, copy, slashes;
    
    cmdLine = GetCommandLine();

    /*
     * Precompute an overly pessimistic guess at the number of arguments
     * in the command line by counting non-space spans.
     */

    size = 2;
    for (p = cmdLine; *p != '\0'; p++) {
      if (isspace(*p)) {
          size++;
          while (isspace(*p)) {
            p++;
          }
          if (*p == '\0') {
            break;
          }
      }
    }
    argSpace = (char *) ckalloc((unsigned) (size * sizeof(char *) 
          + strlen(cmdLine) + 1));
    argv = (char **) argSpace;
    argSpace += size * sizeof(char *);
    size--;

    p = cmdLine;
    for (argc = 0; argc < size; argc++) {
      argv[argc] = arg = argSpace;
      while (isspace(*p)) {
          p++;
      }
      if (*p == '\0') {
          break;
      }

      inquote = 0;
      slashes = 0;
      while (1) {
          copy = 1;
          while (*p == '\\') {
            slashes++;
            p++;
          }
          if (*p == '"') {
            if ((slashes & 1) == 0) {
                copy = 0;
                if ((inquote) && (p[1] == '"')) {
                  p++;
                  copy = 1;
                } else {
                  inquote = !inquote;
                }
                }
                slashes >>= 1;
            }

            while (slashes) {
            *arg = '\\';
            arg++;
            slashes--;
          }

          if ((*p == '\0') || (!inquote && isspace(*p))) {
            break;
          }
          if (copy != 0) {
            *arg = *p;
            arg++;
          }
          p++;
        }
      *arg = '\0';
      argSpace = arg + 1;
    }
    argv[argc] = NULL;

    *argcPtr = argc;
    *argvPtr = argv;
}
#endif

Generated by  Doxygen 1.6.0   Back to index